Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

238 select weekspan rebase #266

Merged
merged 9 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/Api/Consultants/ConsultantController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public ActionResult<List<ConsultantReadModel>> Get(
[FromRoute] string orgUrlKey,
[FromQuery(Name = "Year")] int? selectedYearParam = null,
[FromQuery(Name = "Week")] int? selectedWeekParam = null,
[FromQuery(Name = "weeks")] int numberOfWeeks = 8,
[FromQuery(Name = "WeekSpan")] int numberOfWeeks = 8,
[FromQuery(Name = "includeOccupied")] bool includeOccupied = true)
{
var selectedYear = selectedYearParam ?? DateTime.Now.Year;
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/app/[organisation]/bemanning/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ export default async function Bemanning({
searchParams,
}: {
params: { organisation: string };
searchParams: { selectedWeek?: string };
searchParams: { selectedWeek?: string; weekSpan?: string };
}) {
const selectedWeek = stringToWeek(searchParams.selectedWeek || undefined);
const weekSpan = searchParams.weekSpan || undefined;

const consultants =
(await fetchWithToken<Consultant[]>(
`${params.organisation}/consultants${
selectedWeek
? `?Year=${selectedWeek.year}&Week=${selectedWeek.weekNumber}`
: ""
}`,
}${weekSpan ? `${selectedWeek ? "&" : "?"}WeekSpan=${weekSpan}` : ""}`,
)) ?? [];

const departments =
Expand All @@ -36,7 +37,7 @@ export default async function Bemanning({
>
<StaffingSidebar />

<div className="main pl-12 p-6 w-full flex flex-col gap-8">
<div className="main p-6 w-full flex flex-col gap-8">
<h1>Bemanning</h1>
<FilteredConsultantsList />
<InfoPillDescriptions />
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
}

@layer components {
.dropdown-shadow {
box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.3);
}
}

@layer utilities {
Expand Down Expand Up @@ -149,7 +152,7 @@

.main {
grid-area: main;
overflow-y: scroll;
overflow: scroll;
height: 100%;
}

Expand Down
38 changes: 29 additions & 9 deletions frontend/src/components/ConsultantRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Moon,
Sun,
} from "react-feather";
import InfoPill from "./InfoPill";
import InfoPill, { InfoPillProps, InfoPillVariant } from "./InfoPill";

interface ConsultantListElementProps {
consultant: Consultant;
Expand All @@ -24,6 +24,8 @@ export default function ConsultantRows({
const [isListElementVisible, setIsListElementVisible] = useState(false);
const [isButtonHovered, setIsButtonHovered] = useState(false);

const columnCount = consultant.bookings.length ?? 0;

function toggleListElementVisibility() {
setIsListElementVisible(!isListElementVisible);
}
Expand Down Expand Up @@ -68,7 +70,7 @@ export default function ConsultantRows({
</div>
</td>
{consultant.bookings?.map((b) => (
<td key={b.weekNumber} className="h-[52px] p-0.5">
<td key={b.weekNumber} className="h-[52px] p-0.5 min-w-fit">
<div
className={`flex flex-col gap-1 p-2 justify-end rounded w-full h-full ${
b.bookingModel.totalOverbooking > 0
Expand All @@ -84,27 +86,32 @@ export default function ConsultantRows({
text={b.bookingModel.totalOffered.toFixed(1)}
colors="bg-offer_light text-offer_dark"
icon={<FileText size="12" />}
variant={getInfopillVariantByColumnCount(columnCount)}
/>
)}
{b.bookingModel.totalSellableTime > 0 && (
<InfoPill
text={b.bookingModel.totalSellableTime.toFixed(1)}
colors="bg-free_light text-free_dark"
icon={<Coffee size="12" />}
/>
)}
{b.bookingModel.totalSellableTime > 0 &&
getInfopillVariantByColumnCount(columnCount) !== "narrow" && (
<InfoPill
text={b.bookingModel.totalSellableTime.toFixed(1)}
colors="bg-free_light text-free_dark"
icon={<Coffee size="12" />}
variant={getInfopillVariantByColumnCount(columnCount)}
/>
)}
{b.bookingModel.totalVacationHours > 0 && (
<InfoPill
text={b.bookingModel.totalVacationHours.toFixed(1)}
colors="bg-vacation_light text-vacation_dark"
icon={<Sun size="12" />}
variant={getInfopillVariantByColumnCount(columnCount)}
/>
)}
{b.bookingModel.totalOverbooking > 0 && (
<InfoPill
text={b.bookingModel.totalOverbooking.toFixed(1)}
colors="bg-overbooking_dark text-overbooking_light"
icon={<AlertTriangle size="12" />}
variant={getInfopillVariantByColumnCount(columnCount)}
/>
)}
</div>
Expand Down Expand Up @@ -219,3 +226,16 @@ function getIconByBookingType(type: BookingType): ReactElement {
return <></>;
}
}

function getInfopillVariantByColumnCount(count: number): InfoPillVariant {
switch (true) {
case 26 <= count:
return "narrow";
case 12 <= count && count < 26:
return "medium";
case count < 12:
return "wide";
default:
return "wide";
}
}
61 changes: 61 additions & 0 deletions frontend/src/components/DropDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client";
import { Check, ChevronDown } from "react-feather";
import { ChevronUp } from "react-feather";
import { useState } from "react";

export default function DropDown({
startingOption,
dropDownOptions,
dropDownFunction,
}: {
startingOption: string;
dropDownOptions: string[];
dropDownFunction: Function;
}) {
const [isDropDownOpen, setIsDropDownOpen] = useState(false);
const [chosenOption, setChosenOption] = useState(startingOption);

return (
<div className="relative">
<button
className={`w-[120px] py-2 px-3 flex flex-row justify-between items-center rounded-lg hover:bg-primary_default hover:bg-opacity-10 border hover:border-primary_default ${
isDropDownOpen ? "border-primary_default" : "border-primary_l1"
}`}
onClick={() => setIsDropDownOpen(!isDropDownOpen)}
>
<p className="text-primary_default interaction-button">
{chosenOption}
</p>
{isDropDownOpen ? (
<ChevronUp className="text-primary_default w-6 h-6" />
) : (
<ChevronDown className="text-primary_default w-6 h-6" />
)}
</button>
<div
className={`w-full z-50 bg-white flex flex-col p-1 absolute top-full mt-1 rounded-lg dropdown-shadow ${
!isDropDownOpen && "hidden"
}`}
>
{dropDownOptions?.map((option: string, index: number) => (
<button
key={index}
onClick={() => {
dropDownFunction(option);
setIsDropDownOpen(false);
setChosenOption(option);
}}
className="hover:bg-primary_default px-3 py-2 rounded-md hover:bg-opacity-10 flex flex-row justify-between items-center"
>
<p className="h-6 flex items-center interaction-button text-primary_default">
{option}
</p>
{option == chosenOption && (
<Check className="h-6 w-6 text-primary_default" />
)}
</button>
))}
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/src/components/FilteredConsultantsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function FilteredConsultantList() {
<ActiveFilters />
<WeekSelection />
</div>
<table className="w-full table-fixed">
<table className="min-w-full table-fixed">
<colgroup>
<col span={1} className="w-14" />
<col span={1} className="w-[190px]" />
Expand Down
22 changes: 16 additions & 6 deletions frontend/src/components/InfoPill.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { ReactElement } from "react";

interface InfoPill {
export type InfoPillProps = {
text: string;
icon: ReactElement;
colors: string;
}
variant: InfoPillVariant;
};

export type InfoPillVariant = "wide" | "medium" | "narrow" | "none";

export default function InfoPill({ text, icon, colors }: InfoPill) {
return (
export default function InfoPill({
text,
icon,
colors,
variant,
}: InfoPillProps) {
return variant == "none" ? (
<></>
) : (
<div
className={`flex flex-row gap-0.5 detail-pill-text p-0.5 items-center w-fit h-4 rounded-sm ${colors}`}
>
{icon}
<p>{text}</p>
{(variant == "wide" || variant == "narrow") && icon}
{(variant == "wide" || variant == "medium") && <p>{text}</p>}
</div>
);
}
5 changes: 5 additions & 0 deletions frontend/src/components/InfoPillDescriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,31 @@ export default function InfoPillDescriptions() {
text={"Ledig tid"}
colors="bg-free_light text-free_dark"
icon={<Coffee size="12" />}
variant={"wide"}
/>
<InfoPill
text={"Tilbud"}
colors="bg-offer_light text-offer_dark"
icon={<FileText size="12" />}
variant={"wide"}
/>
<InfoPill
text={"Ferie"}
colors="bg-vacation_light text-vacation_dark"
icon={<Sun size="12" />}
variant={"wide"}
/>
<InfoPill
text={"Helligdag"}
colors="bg-holiday_light text-holiday_dark"
icon={<Calendar size="12" />}
variant={"wide"}
/>
<InfoPill
text={"Overbooket"}
colors="bg-overbooking_dark text-overbooking_light"
icon={<AlertTriangle size="12" />}
variant={"wide"}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/StaffingSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function StaffingSidebar() {
<>
<button
onClick={() => setIsSidebarHidden(false)}
className="bg-primary_l3 rounded-r p-2 mt-6 hover:bg-primary_default hover:bg-opacity-20"
className="bg-primary_l3 rounded-r p-2 mt-16 hover:bg-primary_default hover:bg-opacity-20"
>
<Filter className="text-primary_default" size="20" />
</button>
Expand Down
17 changes: 15 additions & 2 deletions frontend/src/components/WeekSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@ import { useFilteredConsultants } from "@/hooks/useFilteredConsultants";
import SecondaryButton from "@/components/SecondaryButton";
import { ArrowLeft, ArrowRight } from "react-feather";
import IconSecondaryButton from "./IconSecondaryButton";
import DropDown from "./DropDown";

const weekSpanOptions = ["8 uker", "12 uker", "26 uker"];

export default function WeekSelection() {
const { incrementSelectedWeek, resetSelectedWeek, decrementSelectedWeek } =
useFilteredConsultants();
const {
incrementSelectedWeek,
resetSelectedWeek,
decrementSelectedWeek,
setWeekSpan,
weekSpan,
} = useFilteredConsultants();

return (
<div className="flex flex-row gap-1">
<DropDown
startingOption={weekSpan ? weekSpan + " uker" : weekSpanOptions[0]}
dropDownOptions={weekSpanOptions}
dropDownFunction={setWeekSpan}
/>
<SecondaryButton label={"Denne uka"} onClick={resetSelectedWeek} />
<IconSecondaryButton
icon={<ArrowLeft size={24} />}
Expand Down
20 changes: 17 additions & 3 deletions frontend/src/hooks/useFilteredConsultants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface UpdateFilterParams {
departments?: string;
years?: string;
week?: Week;
numWeeks?: number;
availability?: Boolean;
}

Expand All @@ -29,6 +30,7 @@ export function useFilteredConsultants() {
const selectedWeek = stringToWeek(
searchParams.get("selectedWeek") || undefined,
);
const weekSpan = Number.parseInt(searchParams.get("weekSpan") ?? "8");

const [activeNameSearch, setActiveNameSearch] =
useState<string>(searchFilter);
Expand All @@ -42,12 +44,16 @@ export function useFilteredConsultants() {
const { departments = departmentFilter } = updateParams;
const { years = yearFilter } = updateParams;
const { week = selectedWeek } = updateParams;
const { numWeeks = weekSpan } = updateParams;
const { availability = availabilityFilter } = updateParams;

console.log("Update route with numWeeks = " + numWeeks);
router.push(
`${pathname}?search=${search}&depFilter=${departments}&yearFilter=${years}${
week ? `&selectedWeek=${weekToString(week)}` : ""
}&availabilityFilter=${availability}`,
}&availabilityFilter=${availability}&${
numWeeks ? `&weekSpan=${numWeeks}` : ""
}`,
);
},
[
Expand All @@ -56,6 +62,7 @@ export function useFilteredConsultants() {
router,
searchFilter,
selectedWeek,
weekSpan,
yearFilter,
availabilityFilter,
],
Expand All @@ -69,7 +76,7 @@ export function useFilteredConsultants() {
})
: DateTime.now();

let newDate = date.plus({ week: 7 }); //TODO: change to shownWeeks when week filter is added
let newDate = date.plus({ week: weekSpan - 1 });
updateRoute({
week: { year: newDate.year, weekNumber: newDate.weekNumber },
});
Expand All @@ -83,7 +90,7 @@ export function useFilteredConsultants() {
})
: DateTime.now();

let newDate = date.plus({ week: -7 });
let newDate = date.plus({ week: weekSpan - 1 });
updateRoute({
week: { year: newDate.year, weekNumber: newDate.weekNumber },
});
Expand All @@ -95,6 +102,11 @@ export function useFilteredConsultants() {
);
}

function setWeekSpan(weekSpanString: string) {
const weekSpanNum = parseInt(weekSpanString.split(" ")[0]);
updateRoute({ numWeeks: weekSpanNum });
}

useEffect(() => {
let nameSearchDebounceTimer = setTimeout(() => {
if (
Expand Down Expand Up @@ -219,6 +231,8 @@ export function useFilteredConsultants() {
incrementSelectedWeek,
decrementSelectedWeek,
resetSelectedWeek,
weekSpan,
setWeekSpan,
};
}

Expand Down
Loading
Loading