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

Make it possible to edit existing absence periods #656

Merged
merged 5 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
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 });
handlerFunction.current = (selection: {
choice: boolean;
startDate: Date;
endDate: Date;
}) => {
resolve(selection);
setState({ isOpen: false });
};
});
},
[setState]
);

return (
<EditPeriodDialog.Provider value={selectDates}>
{children}
<ModalDialog
{...state}
onCancel={() =>
handlerFunction.current({
choice: false,
startDate: updatedAbsenceStartDate,
endDate: updatedAbsenceEndDate,
})
}
onConfirm={() =>
handlerFunction.current({
choice: true,
startDate: updatedAbsenceStartDate,
endDate: updatedAbsenceEndDate,
})
}
>
{UpdateAbsenceRangesContainer()}
</ModalDialog>
</EditPeriodDialog.Provider>
);
};

export const useSelectDates = () => {
return useContext(EditPeriodDialog);
};
106 changes: 60 additions & 46 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 @@ -156,57 +158,73 @@ export const AbsencePlanner = () => {
return allRemoved;
};

const onUpdateAbsenceRanges = async (
oldEntryIds: number[],
newAbsenceStart: Date,
newAbsenceEnd: 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 onUpdateAbsenceRanges = async (oldEntryIds: number[]) => {
const selection: { choice: boolean; startDate: Date; endDate: Date } =
await selectDates({
title: "Change your absence period",
confirmButtonLabel: "Update",
});
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 {
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 @@ -736,11 +754,7 @@ export const AbsencePlanner = () => {
<button
onClick={() => {
toggleLoadingPage(true);
onUpdateAbsenceRanges(
element.entryIds,
new Date("2023-01-03"),
new Date("2023-01-03")
);
onUpdateAbsenceRanges(element.entryIds);
toggleLoadingPage(false);
}}
className="edit-range-button"
Expand Down