Skip to content

Commit

Permalink
Refactor design of navbar (#284)
Browse files Browse the repository at this point in the history
  • Loading branch information
mathildehaugum authored Nov 16, 2023
1 parent a2e18d2 commit c966ba4
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 105 deletions.
176 changes: 97 additions & 79 deletions frontend/src/components/ConsultantRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Coffee,
FileText,
Moon,
Plus,
Sun,
} from "react-feather";
import InfoPill, { InfoPillVariant } from "./InfoPill";
Expand Down Expand Up @@ -76,10 +77,12 @@ export default function ConsultantRows({
key={index}
bookedHoursPerWeek={b}
isListElementVisible={isListElementVisible}
setIsListElementVisible={setIsListElementVisible}
consultant={consultant}
setHoveredRowWeek={setHoveredRowWeek}
hoveredRowWeek={hoveredRowWeek}
columnCount={columnCount}
isLastCol={index == consultant.bookings.length - 1}
/>
))}
</tr>
Expand All @@ -90,23 +93,21 @@ export default function ConsultantRows({
key={index}
consultant={consultant}
detailedBooking={db}
isListElementVisible={isListElementVisible}
/>
))}
{isListElementVisible && (
<tr>
<td className={`${"border-l-secondary border-l-2"}`}></td>
<td>
<button
disabled
className="xsmall text-black/75 text-sm font-semibold leading-none"
>
+ Legg til bemanning
</button>
<div className="flex flex-row items-center gap-2">
<button className="w-8 h-8 flex justify-center items-center rounded bg-primary/0 hover:bg-primary/10">
<Plus size={16} className="text-primary" />
</button>
<p className="small text-primary">Legg til bemanning</p>
</div>
</td>
</tr>
)}
<tr className="h-1" />
</>
);
}
Expand Down Expand Up @@ -144,18 +145,22 @@ function getIconByBookingType(type: BookingType): ReactElement {
function WeekCell(props: {
bookedHoursPerWeek: BookedHoursPerWeek;
isListElementVisible: boolean;
setIsListElementVisible: Function;
consultant: Consultant;
setHoveredRowWeek: (number: number) => void;
hoveredRowWeek: number;
columnCount: number;
isLastCol: boolean;
}) {
const {
bookedHoursPerWeek: bookedHoursPerWeek,
isListElementVisible,
setIsListElementVisible,
consultant,
setHoveredRowWeek,
hoveredRowWeek,
columnCount,
isLastCol,
} = props;

let pillNumber = 0;
Expand All @@ -177,9 +182,12 @@ function WeekCell(props: {
}

return (
<td key={bookedHoursPerWeek.weekNumber} className="h-[52px] px-0.5">
<td
key={bookedHoursPerWeek.weekNumber}
className={`h-[52px] ${isLastCol ? "py-0.5 pl-0.5" : "p-0.5"}`}
>
<div
className={`flex flex-col gap-1 p-2 justify-end rounded w-full h-full relative ${
className={`flex flex-col gap-1 p-2 justify-end rounded w-full h-full relative border border-transparent hover:border-primary/50 hover:cursor-pointer ${
bookedHoursPerWeek.bookingModel.totalOverbooking > 0
? `bg-black text-white`
: bookedHoursPerWeek.bookingModel.totalSellableTime > 0
Expand All @@ -188,12 +196,16 @@ function WeekCell(props: {
}`}
onMouseEnter={() => setHoveredRowWeek(bookedHoursPerWeek.weekNumber)}
onMouseLeave={() => setHoveredRowWeek(-1)}
onClick={() => setIsListElementVisible(!isListElementVisible)}
>
<HoveredWeek
hoveredRowWeek={hoveredRowWeek}
bookedHoursPerWeek={bookedHoursPerWeek}
consultant={consultant}
/>
{hoveredRowWeek != -1 &&
hoveredRowWeek == bookedHoursPerWeek.weekNumber && (
<HoveredWeek
hoveredRowWeek={hoveredRowWeek}
consultant={consultant}
isLastCol={isLastCol}
/>
)}
<div className="flex flex-row justify-end gap-1">
{bookedHoursPerWeek.bookingModel.totalOffered > 0 && (
<InfoPill
Expand Down Expand Up @@ -245,92 +257,98 @@ function WeekCell(props: {
);
}

function isWeekBookingZeroHours(
detailedBooking: DetailedBooking,
hoveredRowWeek: number,
): boolean {
return (
detailedBooking.hours.filter(
(weekHours) =>
weekHours.week % 100 == hoveredRowWeek && weekHours.hours != 0,
).length == 0
);
}

function HoveredWeek(props: {
hoveredRowWeek: number;
bookedHoursPerWeek: BookedHoursPerWeek;
consultant: Consultant;
isLastCol: boolean;
}) {
const { hoveredRowWeek, bookedHoursPerWeek, consultant } = props;
const { hoveredRowWeek, consultant, isLastCol } = props;

const nonZeroHoursDetailedBookings = consultant.detailedBooking.filter(
(d) => !isWeekBookingZeroHours(d, hoveredRowWeek),
);

return (
<div
className={`absolute bottom-full left-1/2 -translate-x-1/2 flex flex-col items-center z-20 pointer-events-none ${
(hoveredRowWeek != bookedHoursPerWeek.weekNumber ||
consultant.detailedBooking
.map((d) =>
d.hours
.filter((h) => h.week % 100 == bookedHoursPerWeek.weekNumber)
.reduce((sum, h) => sum + h.hours, 0),
)
.reduce((a, b) => a + b, 0) == 0) &&
"hidden"
}`}
>
<div className="rounded-lg bg-white flex flex-col gap-2 min-w-[222px] p-3 shadow-xl">
{consultant.detailedBooking.map((detailedBooking, index) => (
<div key={index}>
{detailedBooking.hours.find(
(hour) => hour.week % 100 == bookedHoursPerWeek.weekNumber,
)?.hours != 0 && (
<>
<div
className={`rounded-lg bg-white gap-3 min-w-[222px] p-3 shadow-xl absolute bottom-full mb-2 flex flex-col z-20 ${
isLastCol ? "right-0 " : "left-1/2 -translate-x-1/2"
} ${nonZeroHoursDetailedBookings.length == 0 && "hidden"}`}
>
{nonZeroHoursDetailedBookings.map((detailedBooking, index) => (
<div
key={index}
className={`flex flex-row gap-2 justify-between items-center
${
index < nonZeroHoursDetailedBookings.length - 1 &&
"pb-3 border-b border-black/10"
}`}
>
<div className="flex flex-row gap-2 items-center">
<div
key={index}
className={`flex flex-row gap-2 justify-between items-center
${
index <
consultant.detailedBooking.filter(
(db) =>
db.hours.find(
(hour) =>
hour.week % 100 == bookedHoursPerWeek.weekNumber,
)?.hours != 0,
).length -
1 && "border-b pb-2 border-black/10"
}`}
className={`h-8 w-8 flex justify-center align-middle items-center rounded ${getColorByStaffingType(
detailedBooking.bookingDetails.type,
)}`}
>
<div className="flex flex-row gap-2 items-center">
<div
className={`h-8 w-8 flex justify-center align-middle items-center rounded ${getColorByStaffingType(
detailedBooking.bookingDetails.type,
)}`}
>
{getIconByBookingType(detailedBooking.bookingDetails.type)}
</div>
<p className="text-black whitespace-nowrap">
{detailedBooking.bookingDetails.name}
</p>
</div>
<p className="text-black text-opacity-60">
{
detailedBooking.hours.find(
(hour) =>
hour.week % 100 == bookedHoursPerWeek.weekNumber,
)?.hours
}
{getIconByBookingType(detailedBooking.bookingDetails.type)}
</div>
<div className="flex flex-col">
<p
className={`xsmall text-black/75 ${
detailedBooking.bookingDetails.type == "Vacation" &&
"hidden"
}`}
>
{detailedBooking.bookingDetails.type}
</p>
<p className="small text-black whitespace-nowrap">
{detailedBooking.bookingDetails.name}
</p>
</div>
)}
</div>
<p className="small text-black/75">
{
detailedBooking.hours.find(
(hour) => hour.week % 100 == hoveredRowWeek,
)?.hours
}
</p>
</div>
))}
</div>
<div className="w-0 h-0 border-l-[12px] border-l-transparent border-t-[16px] border-t-white border-r-[12px] border-r-transparent"></div>
</div>
<div
className={`absolute bottom-full left-1/2 -translate-x-1/2 flex items-center z-50 w-0 h-0 border-l-[8px] border-l-transparent border-t-[8px] border-t-white border-r-[8px] border-r-transparent ${
nonZeroHoursDetailedBookings.length == 0 && "hidden"
}`}
></div>
</>
);
}

function DetailedBookingRows(props: {
consultant: Consultant;
detailedBooking: DetailedBooking;
isListElementVisible: true;
}) {
const { consultant, detailedBooking, isListElementVisible } = props;
const { consultant, detailedBooking } = props;
return (
<tr
key={`${consultant.id}-details-${detailedBooking.bookingDetails.name}`}
className="h-fit"
>
<td
className={`${isListElementVisible && "border-l-secondary border-l-2"}`}
></td>
<td className="flex flex-row gap-2 justify-start h-8">
<td className="border-l-secondary border-l-2"></td>
<td className="flex flex-row gap-2 justify-start p-0.5">
<div
className={`h-8 w-8 flex justify-center align-middle items-center rounded ${getColorByStaffingType(
detailedBooking.bookingDetails.type,
Expand All @@ -355,10 +373,10 @@ function DetailedBookingRows(props: {
className="h-8 p-0.5"
>
<p
className={`text-right small-medium px-2 py-1 rounded h-full
className={`small-medium p-2 rounded h-full flex items-center justify-end
${getColorByStaffingType(
detailedBooking.bookingDetails.type ?? BookingType.Offer,
)}`}
)} ${hours.hours == 0 && "bg-opacity-30"}`}
>
{hours.hours}
</p>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/FilteredConsultantsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function FilteredConsultantList() {

return (
<div className="flex flex-col gap-8">
<div className="flex flex-col gap-6">
<div className="flex flex-row justify-between items-center">
<ActiveFilters />
<WeekSelection />
</div>
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/InfoPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type InfoPillProps = {
icon: ReactElement;
colors: string;
variant: InfoPillVariant;
isInfoPillDesc?: boolean;
};

export type InfoPillVariant = "wide" | "medium" | "narrow" | "circle" | "none";
Expand All @@ -14,6 +15,7 @@ export default function InfoPill({
icon,
colors,
variant,
isInfoPillDesc = false,
}: InfoPillProps) {
return variant == "none" ? (
<></>
Expand All @@ -31,7 +33,7 @@ export default function InfoPill({
>
{(variant == "wide" || variant == "narrow") && icon}
{(variant == "wide" || variant == "medium") && (
<p className="hidden lg:flex">{text}</p>
<p className={`${!isInfoPillDesc && "hidden"} lg:flex`}>{text}</p>
)}
</div>
</>
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/InfoPillDescriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,49 @@ export default function InfoPillDescriptions() {
colors="bg-primary/[3%] text-black"
icon={<Briefcase size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Ledig tid"}
colors="bg-available text-available_darker"
icon={<Coffee size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Tilbud"}
colors="bg-offer text-primary_darker"
icon={<FileText size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Ferie"}
colors="bg-vacation text-vacation_darker"
icon={<Sun size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Helligdag"}
colors="bg-holiday text-holiday_darker"
icon={<Calendar size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Overbooket"}
colors="bg-overbooked_darker text-white"
icon={<AlertTriangle size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
<InfoPill
text={"Fravær"}
colors="bg-absence text-absence_darker"
icon={<Moon size="12" />}
variant={"wide"}
isInfoPillDesc={true}
/>
</div>
);
Expand Down
22 changes: 12 additions & 10 deletions frontend/src/components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import NavBarUserIcon from "./NavBarUserIcon";

export default function NavBar() {
return (
<div className="bg-primary w-full h-[52px] flex flex-row justify-between header">
<div className="flex flex-row">
<div className="bg-primary w-full flex flex-row justify-between header px-4">
<div className="flex flex-row gap-8">
<NavBarLink text="Bemanning" path={`bemanning`} />
</div>
<div className="flex flex-row gap-6 items-center">
<Image
className="variant-logo"
alt="Variant logo"
src="/images/variant-logo.svg"
width="65"
height="16"
/>
<div className="flex flex-row gap-4 items-center">
<div className="border-r border-white/20 py-2">
<Image
className="variant-logo mr-4"
alt="Variant logo"
src="/images/variant-logo.svg"
width="65"
height="16"
/>
</div>
<NavBarUserIcon />
</div>
</div>
Expand Down
Loading

0 comments on commit c966ba4

Please sign in to comment.