From e421a25846d23298c2c21ca041b31b84ba3156f6 Mon Sep 17 00:00:00 2001 From: Wayne Date: Thu, 16 May 2024 00:32:34 +0900 Subject: [PATCH] Past entries wip --- src/app/api/projects/index.ts | 1 - src/app/utils/formatUtil.ts | 89 +++++-- src/components/Breadcrumb/Breadcrumb.tsx | 71 +++--- src/components/Breadcrumb/Clock.tsx | 51 ++-- .../CreateProject/MilestoneSection.tsx | 54 +++-- .../PastEntryCalendar/PastEntryCalendar.tsx | 106 ++++++++ .../PastEntryCalendarModal.tsx | 99 ++++++++ src/components/PastEntryCalendar/index.ts | 1 + .../StyledDataGrid/StyledDataGrid.tsx | 4 + .../TimesheetTable/MobileTimesheetEntry.tsx | 91 +------ .../TimesheetTable/ProjectSelect.tsx | 229 +++++++++++------- .../TimesheetTable/TimeEntryCard.tsx | 87 +++++++ .../UserWorkspacePage/ProjectGrid.tsx | 19 +- .../UserWorkspacePage/UserWorkspacePage.tsx | 35 ++- src/theme/devias-material-kit/components.ts | 10 + 15 files changed, 637 insertions(+), 310 deletions(-) create mode 100644 src/components/PastEntryCalendar/PastEntryCalendar.tsx create mode 100644 src/components/PastEntryCalendar/PastEntryCalendarModal.tsx create mode 100644 src/components/PastEntryCalendar/index.ts create mode 100644 src/components/TimesheetTable/TimeEntryCard.tsx diff --git a/src/app/api/projects/index.ts b/src/app/api/projects/index.ts index 697ac98..90d1f4a 100644 --- a/src/app/api/projects/index.ts +++ b/src/app/api/projects/index.ts @@ -68,7 +68,6 @@ export interface AssignedProject extends ProjectWithTasks { hoursSpent: number; hoursSpentOther: number; hoursAllocated: number; - hoursAllocatedOther: number; } export const preloadProjects = () => { diff --git a/src/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts index 4d773e9..150b350 100644 --- a/src/app/utils/formatUtil.ts +++ b/src/app/utils/formatUtil.ts @@ -21,41 +21,52 @@ export const OUTPUT_DATE_FORMAT = "YYYY/MM/DD"; export const OUTPUT_TIME_FORMAT = "HH:mm:ss"; -export const convertDateToString = (date: Date, format: string = OUTPUT_DATE_FORMAT) => { - return dayjs(date).format(format) -} +export const convertDateToString = ( + date: Date, + format: string = OUTPUT_DATE_FORMAT, +) => { + return dayjs(date).format(format); +}; -export const convertDateArrayToString = (dateArray: number[], format: string = OUTPUT_DATE_FORMAT, needTime: boolean = false) => { +export const convertDateArrayToString = ( + dateArray: number[], + format: string = OUTPUT_DATE_FORMAT, + needTime: boolean = false, +) => { if (dateArray.length === 6) { if (!needTime) { - const dateString = `${dateArray[0]}-${dateArray[1]}-${dateArray[2]}` - return dayjs(dateString).format(format) + const dateString = `${dateArray[0]}-${dateArray[1]}-${dateArray[2]}`; + return dayjs(dateString).format(format); } } if (dateArray.length === 3) { if (!needTime) { - const dateString = `${dateArray[0]}-${dateArray[1]}-${dateArray[2]}` - return dayjs(dateString).format(format) + const dateString = `${dateArray[0]}-${dateArray[1]}-${dateArray[2]}`; + return dayjs(dateString).format(format); } } -} +}; -export const convertTimeArrayToString = (timeArray: number[], format: string = OUTPUT_TIME_FORMAT, needTime: boolean = false) => { - let timeString = ''; +export const convertTimeArrayToString = ( + timeArray: number[], + format: string = OUTPUT_TIME_FORMAT, + needTime: boolean = false, +) => { + let timeString = ""; if (timeArray !== null && timeArray !== undefined) { - const hour = timeArray[0] || 0; - const minute = timeArray[1] || 0; - - timeString = dayjs() - .set('hour', hour) - .set('minute', minute) - .set('second', 0) - .format('HH:mm:ss'); + const hour = timeArray[0] || 0; + const minute = timeArray[1] || 0; + + timeString = dayjs() + .set("hour", hour) + .set("minute", minute) + .set("second", 0) + .format("HH:mm:ss"); } - - return timeString -} + + return timeString; +}; const shortDateFormatter_en = new Intl.DateTimeFormat("en-HK", { weekday: "short", @@ -81,6 +92,36 @@ export const shortDateFormatter = (locale?: string) => { } }; +const clockFormatOptions: Intl.DateTimeFormatOptions = { + year: "numeric", + month: "long", + day: "numeric", + weekday: "long", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: true, +}; + +const clockTimeFormatter_en = new Intl.DateTimeFormat( + "en-HK", + clockFormatOptions, +); +const clockTimeformatter_zh = new Intl.DateTimeFormat( + "zh-HK", + clockFormatOptions, +); + +export const clockTimeFormatter = (locale?: string) => { + switch (locale) { + case "zh": + return clockTimeformatter_zh; + case "en": + default: + return clockTimeFormatter_en; + } +}; + export function convertLocaleStringToNumber(numberString: string): number { const numberWithoutCommas = numberString.replace(/,/g, ""); return parseFloat(numberWithoutCommas); @@ -91,6 +132,6 @@ export function timestampToDateString(timestamp: string): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); - console.log(`${year}-${month}-${day}`) + console.log(`${year}-${month}-${day}`); return `${year}-${month}-${day}`; -} \ No newline at end of file +} diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 1679019..516aca2 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -7,7 +7,7 @@ import MUILink from "@mui/material/Link"; import { usePathname } from "next/navigation"; import { useTranslation } from "react-i18next"; import Clock from "./Clock"; -import { Grid } from "@mui/material"; +import { Box, Grid } from "@mui/material"; import { I18nProvider } from "@/i18n"; const pathToLabelMap: { [path: string]: string } = { @@ -46,42 +46,43 @@ const Breadcrumb = () => { // const { t } = useTranslation("customer"); - return ( - - - - {segments.map((segment, index) => { - const href = segments.slice(0, index + 1).join("/"); - const label = pathToLabelMap[href] || segment; + + + {segments.map((segment, index) => { + const href = segments.slice(0, index + 1).join("/"); + const label = pathToLabelMap[href] || segment; - if (index === segments.length - 1) { - return ( - - {label} - {/* {t(label)} */} - - ); - } else { - return ( - - {label} - - ); - } - })} - - - - - - + if (index === segments.length - 1) { + return ( + + {label} + {/* {t(label)} */} + + ); + } else { + return ( + + {label} + + ); + } + })} + + + + + ); }; diff --git a/src/components/Breadcrumb/Clock.tsx b/src/components/Breadcrumb/Clock.tsx index 8ddf42b..9e34340 100644 --- a/src/components/Breadcrumb/Clock.tsx +++ b/src/components/Breadcrumb/Clock.tsx @@ -1,32 +1,33 @@ -"use client" -import { useState, useEffect, useLayoutEffect } from 'react'; -import Typography from "@mui/material/Typography"; -import { useTranslation } from 'react-i18next'; +"use client"; +import React, { useState, useLayoutEffect } from "react"; +import Typography, { TypographyProps } from "@mui/material/Typography"; +import { useTranslation } from "react-i18next"; +import { clockTimeFormatter } from "@/app/utils/formatUtil"; +import { NoSsr } from "@mui/material"; -const Clock = () => { - const { - i18n: { language }, - } = useTranslation(); - const [currentDateTime, setCurrentDateTime] = useState(new Date()); +const Clock: React.FC = (props) => { + const { + i18n: { language }, + } = useTranslation(); + const [currentDateTime, setCurrentDateTime] = useState(new Date()); - useLayoutEffect(() => { - const timer = setInterval(() => { - setCurrentDateTime(new Date()); - }, 1000); + useLayoutEffect(() => { + const timer = setInterval(() => { + setCurrentDateTime(new Date()); + }, 1000); - return () => { - clearInterval(timer); - }; - }, []); + return () => { + clearInterval(timer); + }; + }, []); - const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true }; - const formattedDateTime = new Intl.DateTimeFormat(language, options).format(currentDateTime) - - return ( - - {formattedDateTime} - - ); + return ( + + + {clockTimeFormatter(language).format(currentDateTime)} + + + ); }; export default Clock; diff --git a/src/components/CreateProject/MilestoneSection.tsx b/src/components/CreateProject/MilestoneSection.tsx index a6d6ccc..969f0c5 100644 --- a/src/components/CreateProject/MilestoneSection.tsx +++ b/src/components/CreateProject/MilestoneSection.tsx @@ -58,8 +58,8 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => { const apiRef = useGridApiRef(); const addRow = useCallback(() => { // const id = Date.now(); - const minId = Math.min(...payments.map((payment) => payment.id!!)); - const id = minId >= 0 ? -1 : minId - 1 + const minId = Math.min(...payments.map((payment) => payment.id!)); + const id = minId >= 0 ? -1 : minId - 1; setPayments((p) => [...p, { id, _isNew: true }]); setRowModesModel((model) => ({ ...model, @@ -241,26 +241,30 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => { - { - if (!date) return; - const milestones = getValues("milestones"); - setValue("milestones", { - ...milestones, - [taskGroupId]: { - ...milestones[taskGroupId], - startDate: date.format(INPUT_DATE_FORMAT), - }, - }); - }} - slotProps={{ - textField: { - error: startDate === "Invalid Date" || new Date(startDate) > new Date(endDate) || (Boolean(formState.errors.milestones) && !Boolean(startDate)), - }, - }} - /> + { + if (!date) return; + const milestones = getValues("milestones"); + setValue("milestones", { + ...milestones, + [taskGroupId]: { + ...milestones[taskGroupId], + startDate: date.format(INPUT_DATE_FORMAT), + }, + }); + }} + slotProps={{ + textField: { + error: + startDate === "Invalid Date" || + new Date(startDate) > new Date(endDate) || + (Boolean(formState.errors.milestones) && + !Boolean(startDate)), + }, + }} + /> @@ -281,7 +285,11 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => { }} slotProps={{ textField: { - error: endDate === "Invalid Date" || new Date(startDate) > new Date(endDate) || (Boolean(formState.errors.milestones) && !Boolean(endDate)), + error: + endDate === "Invalid Date" || + new Date(startDate) > new Date(endDate) || + (Boolean(formState.errors.milestones) && + !Boolean(endDate)), }, }} /> diff --git a/src/components/PastEntryCalendar/PastEntryCalendar.tsx b/src/components/PastEntryCalendar/PastEntryCalendar.tsx new file mode 100644 index 0000000..a9e6a14 --- /dev/null +++ b/src/components/PastEntryCalendar/PastEntryCalendar.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { + RecordTimesheetInput, + RecordLeaveInput, +} from "@/app/api/timesheets/actions"; +import { + DateCalendar, + LocalizationProvider, + PickersDay, + PickersDayProps, +} from "@mui/x-date-pickers"; +import { useTranslation } from "react-i18next"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import dayjs, { Dayjs } from "dayjs"; +import "dayjs/locale/zh-hk"; +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; +import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; + +dayjs.extend(utc); +dayjs.extend(timezone); + +dayjs.tz.guess(); + +export interface Props { + timesheet: RecordTimesheetInput; + leaves: RecordLeaveInput; + onDateSelect: (date: string) => void; +} + +const getColor = ( + hasTimeInput: boolean, + hasLeave: boolean, +): string | undefined => { + if (hasTimeInput && hasLeave) { + return "success.light"; + } else if (hasTimeInput) { + return "info.light"; + } else if (hasLeave) { + return "warning.light"; + } else { + return undefined; + } +}; + +const EntryDay: React.FC & Props> = ({ + timesheet, + leaves, + ...pickerProps +}) => { + const timesheetDays = Object.keys(timesheet); + const leaveDays = Object.keys(leaves); + + const hasTimesheetInput = timesheetDays.some((day) => + dayjs(day).isSame(pickerProps.day, "day"), + ); + + const hasLeaveInput = leaveDays.some((day) => + dayjs(day).isSame(pickerProps.day, "day"), + ); + + return ( + + ); +}; + +const PastEntryCalendar: React.FC = ({ + timesheet, + leaves, + onDateSelect, +}) => { + const { + i18n: { language }, + } = useTranslation("home"); + + const onChange = (day: Dayjs) => { + onDateSelect(day.format(INPUT_DATE_FORMAT)); + }; + + return ( + + + + ); +}; + +export default PastEntryCalendar; diff --git a/src/components/PastEntryCalendar/PastEntryCalendarModal.tsx b/src/components/PastEntryCalendar/PastEntryCalendarModal.tsx new file mode 100644 index 0000000..53fd8dc --- /dev/null +++ b/src/components/PastEntryCalendar/PastEntryCalendarModal.tsx @@ -0,0 +1,99 @@ +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Stack, + Typography, + styled, +} from "@mui/material"; +import PastEntryCalendar, { + Props as PastEntryCalendarProps, +} from "./PastEntryCalendar"; +import { useCallback, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { ArrowBack } from "@mui/icons-material"; + +interface Props extends Omit { + open: boolean; + handleClose: () => void; +} + +const Indicator = styled(Box)(() => ({ + borderRadius: "50%", + width: "1rem", + height: "1rem", +})); + +const PastEntryCalendarModal: React.FC = ({ + handleClose, + open, + timesheet, + leaves, +}) => { + const { t } = useTranslation("home"); + + const [selectedDate, setSelectedDate] = useState(""); + + const clearDate = useCallback(() => { + setSelectedDate(""); + }, []); + + const onClose = useCallback(() => { + handleClose(); + }, [handleClose]); + + return ( + + {t("Past Entries")} + + {selectedDate ? ( + {selectedDate} + ) : ( + + + + + + {t("Has timesheet entry")} + + + + + + {t("Has leave entry")} + + + + + + {t("Has both timesheet and leave entry")} + + + + + + )} + + {selectedDate && ( + + + + )} + + ); +}; + +export default PastEntryCalendarModal; diff --git a/src/components/PastEntryCalendar/index.ts b/src/components/PastEntryCalendar/index.ts new file mode 100644 index 0000000..1c7ee56 --- /dev/null +++ b/src/components/PastEntryCalendar/index.ts @@ -0,0 +1 @@ +export { default } from "./PastEntryCalendar"; diff --git a/src/components/StyledDataGrid/StyledDataGrid.tsx b/src/components/StyledDataGrid/StyledDataGrid.tsx index 743d288..3ea0dce 100644 --- a/src/components/StyledDataGrid/StyledDataGrid.tsx +++ b/src/components/StyledDataGrid/StyledDataGrid.tsx @@ -28,6 +28,10 @@ const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ borderRadius: 0, maxHeight: 50, }, + "& .MuiAutocomplete-root .MuiFilledInput-root": { + borderRadius: 0, + maxHeight: 50, + }, })); export default StyledDataGrid; diff --git a/src/components/TimesheetTable/MobileTimesheetEntry.tsx b/src/components/TimesheetTable/MobileTimesheetEntry.tsx index 9f709d8..03a0487 100644 --- a/src/components/TimesheetTable/MobileTimesheetEntry.tsx +++ b/src/components/TimesheetTable/MobileTimesheetEntry.tsx @@ -17,6 +17,7 @@ import { AssignedProject, ProjectWithTasks } from "@/app/api/projects"; import TimesheetEditModal, { Props as TimesheetEditModalProps, } from "./TimesheetEditModal"; +import TimeEntryCard from "./TimeEntryCard"; interface Props { date: string; @@ -119,91 +120,13 @@ const MobileTimesheetEntry: React.FC = ({ const task = project?.tasks.find((t) => t.id === entry.taskId); return ( - - - - - - {project - ? `${project.code} - ${project.name}` - : t("Non-billable Task")} - - {task && ( - - {task.name} - - )} - - - - - - - - - {t("Hours")} - - - {manhourFormatter.format(entry.inputHours || 0)} - - - - - {t("Other Hours")} - - - {manhourFormatter.format(entry.otHours || 0)} - - - - {entry.remark && ( - - - {t("Remark")} - - {entry.remark} - - )} - - + project={project} + task={task} + entry={entry} + onEdit={openEditModal(entry)} + /> ); }) ) : ( diff --git a/src/components/TimesheetTable/ProjectSelect.tsx b/src/components/TimesheetTable/ProjectSelect.tsx index 762512c..3c1ab51 100644 --- a/src/components/TimesheetTable/ProjectSelect.tsx +++ b/src/components/TimesheetTable/ProjectSelect.tsx @@ -10,6 +10,7 @@ import { import { AssignedProject, ProjectWithTasks } from "@/app/api/projects"; import { useTranslation } from "react-i18next"; import differenceBy from "lodash/differenceBy"; +import { TFunction } from "i18next"; interface Props { allProjects: ProjectWithTasks[]; @@ -18,119 +19,159 @@ interface Props { onProjectSelect: (projectId: number | string) => void; } -// const AutocompleteProjectSelect: React.FC = ({ -// allProjects, -// assignedProjects, -// value, -// onProjectSelect, -// }) => { -// const { t } = useTranslation("home"); -// const nonAssignedProjects = useMemo(() => { -// return differenceBy(allProjects, assignedProjects, "id"); -// }, [allProjects, assignedProjects]); - -// const options = useMemo(() => { -// return [ -// { -// value: "", -// label: t("None"), -// group: "non-billable", -// }, -// ...assignedProjects.map((p) => ({ -// value: p.id, -// label: `${p.code} - ${p.name}`, -// group: "assigned", -// })), -// ...nonAssignedProjects.map((p) => ({ -// value: p.id, -// label: `${p.code} - ${p.name}`, -// group: "non-assigned", -// })), -// ]; -// }, [assignedProjects, nonAssignedProjects, t]); - -// return ( -// option.group} -// getOptionLabel={(option) => option.label} -// options={options} -// renderInput={(params) => } -// /> -// ); -// }; +const getGroupName = (t: TFunction, groupName: string): string => { + switch (groupName) { + case "non-billable": + return t("Non-billable"); + case "assigned": + return t("Assigned Projects"); + case "non-assigned": + return t("Non-assigned Projects"); + default: + return t("Ungrouped"); + } +}; -const ProjectSelect: React.FC = ({ +const AutocompleteProjectSelect: React.FC = ({ allProjects, assignedProjects, value, onProjectSelect, }) => { const { t } = useTranslation("home"); - const nonAssignedProjects = useMemo(() => { return differenceBy(allProjects, assignedProjects, "id"); }, [allProjects, assignedProjects]); + const options = useMemo(() => { + return [ + { + value: "", + label: t("None"), + group: "non-billable", + }, + ...assignedProjects.map((p) => ({ + value: p.id, + label: `${p.code} - ${p.name}`, + group: "assigned", + })), + ...nonAssignedProjects.map((p) => ({ + value: p.id, + label: `${p.code} - ${p.name}`, + group: "non-assigned", + })), + ]; + }, [assignedProjects, nonAssignedProjects, t]); + + const currentValue = options.find((o) => o.value === value) || options[0]; + const onChange = useCallback( - (event: SelectChangeEvent) => { - const newValue = event.target.value; - onProjectSelect(newValue); + (event: React.SyntheticEvent, newValue: { value: number | string }) => { + onProjectSelect(newValue.value); }, [onProjectSelect], ); return ( - + renderInput={(params) => } + /> ); }; -export default ProjectSelect; +// const ProjectSelect: React.FC = ({ +// allProjects, +// assignedProjects, +// value, +// onProjectSelect, +// }) => { +// const { t } = useTranslation("home"); + +// const nonAssignedProjects = useMemo(() => { +// return differenceBy(allProjects, assignedProjects, "id"); +// }, [allProjects, assignedProjects]); + +// const onChange = useCallback( +// (event: SelectChangeEvent) => { +// const newValue = event.target.value; +// onProjectSelect(newValue); +// }, +// [onProjectSelect], +// ); + +// return ( +// +// ); +// }; + +export default AutocompleteProjectSelect; diff --git a/src/components/TimesheetTable/TimeEntryCard.tsx b/src/components/TimesheetTable/TimeEntryCard.tsx new file mode 100644 index 0000000..445e182 --- /dev/null +++ b/src/components/TimesheetTable/TimeEntryCard.tsx @@ -0,0 +1,87 @@ +import { ProjectWithTasks } from "@/app/api/projects"; +import { Task } from "@/app/api/tasks"; +import { TimeEntry } from "@/app/api/timesheets/actions"; +import { manhourFormatter } from "@/app/utils/formatUtil"; +import { Edit } from "@mui/icons-material"; +import { Box, Card, CardContent, IconButton, Typography } from "@mui/material"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +interface Props { + project?: ProjectWithTasks; + task?: Task; + entry: TimeEntry; + onEdit?: () => void; +} + +const TimeEntryCard: React.FC = ({ project, task, entry, onEdit }) => { + const { t } = useTranslation("home"); + return ( + + + + + + {project + ? `${project.code} - ${project.name}` + : t("Non-billable Task")} + + {task && ( + + {task.name} + + )} + + {onEdit && ( + + + + )} + + + + + {t("Hours")} + + + {manhourFormatter.format(entry.inputHours || 0)} + + + + + {t("Other Hours")} + + + {manhourFormatter.format(entry.otHours || 0)} + + + + {entry.remark && ( + + + {t("Remark")} + + {entry.remark} + + )} + + + ); +}; + +export default TimeEntryCard; diff --git a/src/components/UserWorkspacePage/ProjectGrid.tsx b/src/components/UserWorkspacePage/ProjectGrid.tsx index ad44116..275d48d 100644 --- a/src/components/UserWorkspacePage/ProjectGrid.tsx +++ b/src/components/UserWorkspacePage/ProjectGrid.tsx @@ -56,9 +56,6 @@ const ProjectGrid: React.FC = ({ projects }) => { )})`} {/* Hours Allocated */} - - {t("Hours Allocated:")} - = ({ projects }) => { alignItems: "baseline", }} > - {t("Normal")} + + {t("Hours Allocated:")} + {manhourFormatter.format(project.hoursAllocated)} - - {t("(Others)")} - {`(${manhourFormatter.format( - project.hoursAllocatedOther, - )})`} - diff --git a/src/components/UserWorkspacePage/UserWorkspacePage.tsx b/src/components/UserWorkspacePage/UserWorkspacePage.tsx index 062e233..0c8615f 100644 --- a/src/components/UserWorkspacePage/UserWorkspacePage.tsx +++ b/src/components/UserWorkspacePage/UserWorkspacePage.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import { Add } from "@mui/icons-material"; -import { Typography } from "@mui/material"; +import { Box, Typography } from "@mui/material"; import ButtonGroup from "@mui/material/ButtonGroup"; import AssignedProjects from "./AssignedProjects"; import TimesheetModal from "../TimesheetModal"; @@ -16,6 +16,8 @@ import { } from "@/app/api/timesheets/actions"; import LeaveModal from "../LeaveModal"; import { LeaveType } from "@/app/api/timesheets"; +import { CalendarIcon } from "@mui/x-date-pickers"; +import PastEntryCalendarModal from "../PastEntryCalendar/PastEntryCalendarModal"; export interface Props { leaveTypes: LeaveType[]; @@ -36,6 +38,7 @@ const UserWorkspacePage: React.FC = ({ }) => { const [isTimeheetModalVisible, setTimeheetModalVisible] = useState(false); const [isLeaveModalVisible, setLeaveModalVisible] = useState(false); + const [isPastEventModalVisible, setPastEventModalVisible] = useState(false); const { t } = useTranslation("home"); const handleAddTimesheetButtonClick = useCallback(() => { @@ -54,6 +57,14 @@ const UserWorkspacePage: React.FC = ({ setLeaveModalVisible(false); }, []); + const handlePastEventClick = useCallback(() => { + setPastEventModalVisible(true); + }, []); + + const handlePastEventClose = useCallback(() => { + setPastEventModalVisible(false); + }, []); + return ( <> = ({ {t("User Workspace")} - + + - + +