Skip to content

Commit

Permalink
Merge pull request #656 from NBISweden/dev/edit-dialog
Browse files Browse the repository at this point in the history
Make it possible to edit existing absence periods
  • Loading branch information
kusalananda authored Sep 20, 2022
2 parents b1ea27b + c7fbf81 commit b66f74e
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 78 deletions.
79 changes: 41 additions & 38 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AuthProvider } from "./components/AuthProvider";
import { ProtectedRoute } from "./components/ProtectedRoute";
import { getISOWeek } from "date-fns";
import { ConfirmDialogProvider } from "./components/ConfirmDialogProvider";
import { EditPeriodDialogProvider } from "./components/EditPeriodDialogProvider";

// Route calls
// Order of routes is critical.
Expand All @@ -23,44 +24,46 @@ export const App = () => {
<React.StrictMode>
<BrowserRouter>
<AuthProvider>
<ConfirmDialogProvider>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/report/:year/:week"
element={
<ProtectedRoute>
<Report />
</ProtectedRoute>
}
/>
<Route
path="/*"
element={
<Navigate
replace
to={`/report/${currentYear}/${currentWeek}`}
/>
}
/>
<Route
path="/help"
element={
<ProtectedRoute>
<Help />
</ProtectedRoute>
}
/>
<Route
path="/absence"
element={
<ProtectedRoute>
<AbsencePlanner />
</ProtectedRoute>
}
/>
</Routes>
</ConfirmDialogProvider>
<EditPeriodDialogProvider>
<ConfirmDialogProvider>
<Routes>
<Route path="/login" element={<Login />} />
<Route
path="/report/:year/:week"
element={
<ProtectedRoute>
<Report />
</ProtectedRoute>
}
/>
<Route
path="/*"
element={
<Navigate
replace
to={`/report/${currentYear}/${currentWeek}`}
/>
}
/>
<Route
path="/help"
element={
<ProtectedRoute>
<Help />
</ProtectedRoute>
}
/>
<Route
path="/absence"
element={
<ProtectedRoute>
<AbsencePlanner />
</ProtectedRoute>
}
/>
</Routes>
</ConfirmDialogProvider>
</EditPeriodDialogProvider>
</AuthProvider>
</BrowserRouter>
</React.StrictMode>
Expand Down
118 changes: 118 additions & 0 deletions frontend/src/components/EditPeriodDialogProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, {
createContext,
useState,
useRef,
useCallback,
useContext,
} from "react";
import { ModalDialog } from "./ModalDialog";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import sv from "date-fns/locale/sv";
import { dateFormat, isWeekday } from "../utils";

const EditPeriodDialog = createContext(null);

export const EditPeriodDialogProvider = ({ children }) => {
const [updatedAbsenceStartDate, setUpdatedAbsenceStartDate] =
useState<Date>(undefined);
const [updatedAbsenceEndDate, setUpdatedAbsenceEndDate] =
useState<Date>(undefined);
const [state, setState] = useState<{}>({ isOpen: false });
const handlerFunction =
useRef<
(selection: { choice: boolean; startDate: Date; endDate: Date }) => void
>();

const UpdateAbsenceRangesContainer = (): JSX.Element => (
<div>
<p>Please enter below the new start and end date of your absence.</p>
<p>From</p>
<DatePicker
filterDate={isWeekday}
dateFormat={dateFormat}
isClearable={true}
selected={updatedAbsenceStartDate ? updatedAbsenceStartDate : undefined}
onChange={(date: Date) => {
setUpdatedAbsenceStartDate(date);
}}
showWeekNumbers
minDate={new Date()}
maxDate={new Date("2030-01-01")}
locale={sv}
showYearDropdown
todayButton="Idag"
selectsStart
startDate={updatedAbsenceStartDate}
endDate={updatedAbsenceEndDate}
monthsShown={1}
/>
<p>To</p>
<DatePicker
filterDate={isWeekday}
dateFormat={dateFormat}
isClearable={true}
selected={updatedAbsenceEndDate ? updatedAbsenceEndDate : undefined}
onChange={(date: Date) => {
setUpdatedAbsenceEndDate(date);
}}
showWeekNumbers
minDate={updatedAbsenceStartDate}
maxDate={new Date("2030-01-01")}
locale={sv}
showYearDropdown
todayButton="Idag"
selectsEnd
startDate={updatedAbsenceStartDate}
endDate={updatedAbsenceEndDate}
monthsShown={1}
/>
</div>
);

const selectDates = useCallback(
(forwarded) => {
return new Promise((resolve) => {
setState({ ...forwarded, isOpen: true });
setUpdatedAbsenceStartDate(forwarded.startDate);
setUpdatedAbsenceEndDate(forwarded.endDate);
handlerFunction.current = (selection: {
choice: boolean;
startDate: Date;
endDate: Date;
}) => {
resolve(selection);
setState({ isOpen: false });
};
});
},
[setState]
);

const onChoiceMade = (choice: boolean) => {
handlerFunction.current({
choice: choice,
startDate: updatedAbsenceStartDate,
endDate: updatedAbsenceEndDate,
});
setUpdatedAbsenceStartDate(undefined);
setUpdatedAbsenceEndDate(undefined);
};

return (
<EditPeriodDialog.Provider value={selectDates}>
{children}
<ModalDialog
{...state}
onCancel={() => onChoiceMade(false)}
onConfirm={() => onChoiceMade(true)}
>
{UpdateAbsenceRangesContainer()}
</ModalDialog>
</EditPeriodDialog.Provider>
);
};

export const useSelectDates = () => {
return useContext(EditPeriodDialog);
};
117 changes: 77 additions & 40 deletions frontend/src/pages/AbsencePlanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { Chart } from "react-google-charts";
import trash from "../icons/trash.svg";
import pencil from "../icons/pencil.svg";
import { useConfirm } from "../components/ConfirmDialogProvider";
import { useSelectDates } from "../components/EditPeriodDialogProvider";

export const AbsencePlanner = () => {
const [startDate, setStartDate] = useState<Date>(undefined);
Expand All @@ -48,6 +49,7 @@ export const AbsencePlanner = () => {
const [reloadPage, setReloadPage] = useState<boolean>(false);
const [reportedDates, setReportedDates] = useState<string[]>([]);
const confirm: ({}) => any = useConfirm();
const selectDates: ({}) => any = useSelectDates();

let today = new Date();
const absenceFrom: Date = new Date(new Date().setMonth(today.getMonth() - 1));
Expand Down Expand Up @@ -158,55 +160,90 @@ export const AbsencePlanner = () => {

const onUpdateAbsenceRanges = async (
oldEntryIds: number[],
newAbsenceStart: Date,
newAbsenceEnd: Date
oldStartDate: Date,
oldEndDate: Date
) => {
const reportedEntries: FetchedTimeEntry[] = await hasAlreadyReported(
newAbsenceStart,
newAbsenceEnd
);
const filteredReportedEntries: FetchedTimeEntry[] = reportedEntries.filter(
(entry: FetchedTimeEntry) => {
return !oldEntryIds.includes(entry.id);
}
);
if (filteredReportedEntries.length > 0) {
setToastList([
...toastList,
{
type: "warning",
timeout: 8000,
message:
"The absence plan was not updated. Time has already been reported on this period",
},
]);
const selection: { choice: boolean; startDate: Date; endDate: Date } =
await selectDates({
title: "Change your absence period",
confirmButtonLabel: "Update",
startDate: oldStartDate,
endDate: oldEndDate,
});
if (!selection.choice) {
return;
} else {
const removedAll = await removeTimeEntries(oldEntryIds);
if (removedAll) {
const reportable_days = getReportableDays(
newAbsenceStart,
newAbsenceEnd
if (!selection.startDate || !selection.endDate) {
setToastList([
...toastList,
{
type: "warning",
timeout: 10000,
message:
"Please fill in both the starting and end date of your absence",
},
]);
} else if (
selection.startDate.getTime() &&
selection.endDate.getTime() &&
selection.endDate < selection.startDate
) {
setToastList([
...toastList,
{
type: "warning",
timeout: 10000,
message: "Invalid reporting period date ranges",
},
]);
} else {
const reportedEntries: FetchedTimeEntry[] = await hasAlreadyReported(
selection.startDate,
selection.endDate
);
const added = await reportAbsenceTime(reportable_days);
if (added) {
setToastList([
...toastList,
{
type: "info",
timeout: 8000,
message: "The absence period was successfully updated",
},
]);
} else {
const filteredReportedEntries: FetchedTimeEntry[] =
reportedEntries.filter((entry: FetchedTimeEntry) => {
return !oldEntryIds.includes(entry.id);
});
if (filteredReportedEntries.length > 0) {
setToastList([
...toastList,
{
type: "warning",
timeout: 8000,
message:
"Something went wrong! Your absence plan could not be updated",
"The absence plan was not updated. Time has already been reported on this period",
},
]);
} else {
const removedAll = await removeTimeEntries(oldEntryIds);
if (removedAll) {
const reportable_days = getReportableDays(
selection.startDate,
selection.endDate
);
const added = await reportAbsenceTime(reportable_days);
if (added) {
setToastList([
...toastList,
{
type: "info",
timeout: 8000,
message: "The absence period was successfully updated",
},
]);
} else {
setToastList([
...toastList,
{
type: "warning",
timeout: 8000,
message:
"Something went wrong! Your absence plan could not be updated",
},
]);
}
}
}
}
}
Expand Down Expand Up @@ -738,8 +775,8 @@ export const AbsencePlanner = () => {
toggleLoadingPage(true);
onUpdateAbsenceRanges(
element.entryIds,
new Date("2023-01-03"),
new Date("2023-01-03")
element.startDate,
element.endDate
);
toggleLoadingPage(false);
}}
Expand Down

0 comments on commit b66f74e

Please sign in to comment.