From 169a2ea07a0e457f0f785444a0fe54c25d19c129 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:41:01 +0200 Subject: [PATCH 1/3] Feat(timesheet): Add TimesheetDetailModal for displaying pending (#3382) * feat(timesheet): add TimesheetDetailModal for displaying pending timesheet details * fix: coderabbitai --- .../[memberId]/components/CalendarView.tsx | 2 +- .../[memberId]/components/TimesheetCard.tsx | 119 +++++++++++++++++- .../components/TimesheetDetailModal.tsx | 41 ++++++ .../[locale]/timesheet/[memberId]/page.tsx | 27 +++- apps/web/app/hooks/features/useTimesheet.ts | 3 +- .../calendar/table-time-sheet.tsx | 2 +- 6 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetDetailModal.tsx diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx index ca6cd2900..584cf19df 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/CalendarView.tsx @@ -10,7 +10,7 @@ import { EmployeeAvatar } from "./CompactTimesheetComponent"; import { formatDate } from "@/app/helpers"; import { ClockIcon } from "lucide-react"; -export function CalendarView({ data }: { data?: GroupedTimesheet[] }) { +export function CalendarView({ data, loading }: { data?: GroupedTimesheet[], loading: boolean }) { const t = useTranslations(); return (
diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx index 131b88f61..251ce3e8a 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetCard.tsx @@ -1,9 +1,17 @@ +import { formatDate } from '@/app/helpers'; +import { DisplayTimeForTimesheet, TaskNameInfoDisplay, TotalDurationByDate, TotalTimeDisplay } from '@/lib/features'; import { clsxm } from '@app/utils'; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; +import { Badge } from '@components/ui/badge'; import { ArrowRightIcon } from 'assets/svg'; -import { Button, Card } from 'lib/components'; +import { Button, Card, statusColor } from 'lib/components'; import { useTranslations } from 'next-intl'; import { ReactNode } from 'react'; +import { EmployeeAvatar } from './CompactTimesheetComponent'; +import { useTimesheet } from '@/app/hooks/features/useTimesheet'; +import { useTimelogFilterOptions } from '@/app/hooks'; +import { TimesheetLog, TimesheetStatus } from '@/app/interfaces'; interface ITimesheetCard { title?: string; @@ -64,3 +72,112 @@ export function TimesheetCard({ ...props }: ITimesheetCard) { ) } + + + +export const TimesheetCardDetail = ({ data }: { data?: Record }) => { + + const { getStatusTimesheet, groupByDate } = useTimesheet({}); + const { timesheetGroupByDays } = useTimelogFilterOptions(); + const timesheetGroupByDate = groupByDate(data?.PENDING || []) + const t = useTranslations(); + return ( +
+ {timesheetGroupByDate.map((plan, index) => { + return
+
+
+ {timesheetGroupByDays === 'Weekly' && ( + Week {index + 1} + )} + {formatDate(plan.date)} +
+ +
+ + {Object.entries(getStatusTimesheet(plan.tasks)).map(([status, rows]) => { + return rows.length > 0 && status && + +
+
+
+
+ + {status === 'DENIED' ? 'REJECTED' : status} + + ({rows.length}) +
+ + {t('timer.TOTAL_HOURS')} + + +
+
+
+ + {rows.map((task) => ( +
+
+ +
+
+ + {task.employee.fullName} +
+ +
+ ))} +
+
+ })} +
+
+ })} +
+ ) +} diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetDetailModal.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetDetailModal.tsx new file mode 100644 index 000000000..d02ca47ce --- /dev/null +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetDetailModal.tsx @@ -0,0 +1,41 @@ +import { TimesheetLog, TimesheetStatus } from '@/app/interfaces'; +import { Modal } from '@/lib/components'; +import React from 'react' +import { TimesheetCardDetail } from './TimesheetCard'; +import { useTranslations } from 'next-intl'; + +export interface IAddTaskModalProps { + isOpen: boolean; + closeModal: () => void; + timesheet?: Record +} + +function TimesheetDetailModal({ closeModal, isOpen, timesheet }: IAddTaskModalProps) { + const t = useTranslations() + + return ( + +
+
+ { + timesheet?.PENDING.length === 0 ? ( +
+

{t('pages.timesheet.NO_ENTRIES_FOUND')}

+
+ ) : + } +
+
+ +
+ + ) +} + +export default TimesheetDetailModal diff --git a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx index a90e0378a..20de348bc 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx @@ -22,6 +22,7 @@ import { GoSearch } from 'react-icons/go'; import { getGreeting } from '@/app/helpers'; import { useTimesheet } from '@/app/hooks/features/useTimesheet'; import { endOfDay, startOfDay } from 'date-fns'; +import TimesheetDetailModal from './components/TimesheetDetailModal'; type TimesheetViewMode = 'ListView' | 'CalendarView'; @@ -73,6 +74,13 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb openModal: openManualTimeModal, closeModal: closeManualTimeModal } = useModal(); + + const { + isOpen: isTimesheetDetailOpen, + openModal: openTimesheetDetail, + closeModal: closeTimesheetDetail + } = useModal(); + const username = user?.name || user?.firstName || user?.lastName || user?.username; const [timesheetNavigator, setTimesheetNavigator] = useLocalStorageState( @@ -95,6 +103,13 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb ); return ( <> + {isTimesheetDetailOpen + && } + } classNameIcon="bg-[#FBB650] shadow-[#fbb75095]" + onClick={() => openTimesheetDetail()} /> */}
{timesheetNavigator === 'ListView' ? ( - + ) : ( - + )}
diff --git a/apps/web/app/hooks/features/useTimesheet.ts b/apps/web/app/hooks/features/useTimesheet.ts index 4afc705b2..91e320c53 100644 --- a/apps/web/app/hooks/features/useTimesheet.ts +++ b/apps/web/app/hooks/features/useTimesheet.ts @@ -291,6 +291,7 @@ export function useTimesheet({ createTimesheet, loadingCreateTimesheet, updateTimesheet, - loadingUpdateTimesheet + loadingUpdateTimesheet, + groupByDate }; } diff --git a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx index 746940e0f..2ee67b72e 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -584,7 +584,7 @@ export const StatusTask = ({ timesheet }: { timesheet: TimesheetLog }) => { ); }; -const getBadgeColor = (timesheetStatus: TimesheetStatus | null) => { +export const getBadgeColor = (timesheetStatus: TimesheetStatus | null) => { switch (timesheetStatus) { case 'DRAFT': return 'bg-gray-300'; From 5062e7ccc851950fea822e88dbfaf64acc6c5587 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:41:47 +0200 Subject: [PATCH 2/3] Add visibility condition for timesheet and limit counter to 100+ (#3383) * Add visibility condition for timesheet and limit counter to 100+ * fix: CodeRabbit --- .../components/TimeSheetFilterPopover.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx index f93bc0b05..8eb858acc 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimeSheetFilterPopover.tsx @@ -8,6 +8,7 @@ import { SettingFilterIcon } from '@/assets/svg'; import { useTranslations } from 'next-intl'; import { clsxm } from '@/app/utils'; import { useTimelogFilterOptions } from '@/app/hooks'; +import { useTimesheet } from '@/app/hooks/features/useTimesheet'; export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover() { const [shouldRemoveItems, setShouldRemoveItems] = React.useState(false); @@ -16,12 +17,17 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover const t = useTranslations(); const { setEmployeeState, setProjectState, setStatusState, setTaskState, employee, project, statusState, task } = useTimelogFilterOptions(); + const { timesheet, statusTimesheet } = useTimesheet({}) React.useEffect(() => { if (shouldRemoveItems) { setShouldRemoveItems(false); } }, [shouldRemoveItems]); + const totalItems = React.useMemo(() => { + if (!statusTimesheet) return 0; + return Object.values(statusTimesheet).reduce((sum, status) => sum + status.length, 0); + }, [statusTimesheet]); return ( <> @@ -33,6 +39,15 @@ export const TimeSheetFilterPopover = React.memo(function TimeSheetFilterPopover > {t('common.FILTER')} + {timesheet && timesheet.length > 0 && ( + + {totalItems > 100 ? "100+" : totalItems} + + )} From 9caa7b9e7faff83f2b454ddc11b46b3bf17bd796 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:42:38 +0200 Subject: [PATCH 3/3] [Fix]: Handle Timesheet Update (#3384) * Fix: handle timesheet update with precise time ranges and billing information * Fix: deepscan * Fix: deepscan * Fix: deepscan * fix: deep scan * fix: deep scan --- .../[memberId]/components/AddTaskModal.tsx | 70 ++++---- .../[memberId]/components/EditTaskModal.tsx | 152 +++++++++++++----- .../[locale]/timesheet/[memberId]/page.tsx | 2 +- .../hooks/features/useTimelogFilterOptions.ts | 10 +- apps/web/app/interfaces/timer/ITimerLog.ts | 12 +- apps/web/lib/components/combobox/index.tsx | 5 +- .../manual-time/manage-member-component.tsx | 6 +- 7 files changed, 175 insertions(+), 82 deletions(-) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx index 3a13f5688..f9ba3321a 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/AddTaskModal.tsx @@ -60,9 +60,9 @@ export function AddTaskModal({ closeModal, isOpen }: IAddTaskModalProps) { closeModal={closeModal} title={'+ Add Time Entry'} showCloseIcon - className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:w-40 md:min-w-[30rem] justify-start h-[auto] overflow-y-auto" + className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:w-40 md:min-w-[32rem] justify-start h-[auto]" titleClass="font-bold flex justify-start w-full"> -
+