From 87bd11d2c3051a56d56967479ac6609b8deb2544 Mon Sep 17 00:00:00 2001 From: Innocent-akim Date: Sun, 24 Nov 2024 19:46:08 +0200 Subject: [PATCH 1/4] Add sortable headers to Timesheet component with dynamic sorting logic for columns --- .../[memberId]/components/TimesheetLoader.tsx | 11 ++ .../timesheet/[memberId]/components/index.tsx | 1 + .../hooks/features/useTimelogFilterOptions.ts | 14 ++ .../calendar/table-time-sheet.tsx | 123 ++++++++++++++++-- 4 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx new file mode 100644 index 000000000..f3a4e5f3f --- /dev/null +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx @@ -0,0 +1,11 @@ +import React from 'react' + +function TimesheetLoader() { + return ( +
+ +
+ ) +} + +export default TimesheetLoader diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx index 5c0dea0a2..06948073c 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/index.tsx @@ -9,3 +9,4 @@ export * from './TimeSheetFilterPopover' export * from './TimesheetAction'; export * from './RejectSelectedModal'; export * from './EditTaskModal'; +export * from './TimesheetLoader' diff --git a/apps/web/app/hooks/features/useTimelogFilterOptions.ts b/apps/web/app/hooks/features/useTimelogFilterOptions.ts index b37953048..a8fdc2fd8 100644 --- a/apps/web/app/hooks/features/useTimelogFilterOptions.ts +++ b/apps/web/app/hooks/features/useTimelogFilterOptions.ts @@ -10,6 +10,7 @@ export function useTimelogFilterOptions() { const [selectTimesheet, setSelectTimesheet] = useAtom(timesheetDeleteState); const [timesheetGroupByDays, setTimesheetGroupByDays] = useAtom(timesheetGroupByDayState); const [puTimesheetStatus, setPuTimesheetStatus] = useAtom(timesheetUpdateStatus) + const [selectedItems, setSelectedItems] = React.useState<{ status: string; date: string }[]>([]); const employee = employeeState; const project = projectState; @@ -26,6 +27,17 @@ export function useTimelogFilterOptions() { const handleSelectRowTimesheet = (items: string) => { setSelectTimesheet((prev) => prev.includes(items) ? prev.filter((filter) => filter !== items) : [...prev, items]) } + + const handleSelectRowByStatusAndDate = (status: string, date: string) => { + setSelectedItems((prev) => + prev.some((item) => item.status === status && item.date === date) + ? prev.filter((item) => !(item.status === status && item.date === date)) + : [...prev, { status, date }] + ); + } + + + React.useEffect(() => { return () => setSelectTimesheet([]); }, []); @@ -40,6 +52,8 @@ export function useTimelogFilterOptions() { setTaskState, setStatusState, handleSelectRowTimesheet, + handleSelectRowByStatusAndDate, + selectedItems, selectTimesheet, setSelectTimesheet, timesheetGroupByDays, 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 6b9112294..f3b955843 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -38,7 +38,9 @@ import { MdKeyboardDoubleArrowLeft, MdKeyboardDoubleArrowRight, MdKeyboardArrowLeft, - MdKeyboardArrowRight + MdKeyboardArrowRight, + MdKeyboardArrowUp, + MdKeyboardArrowDown } from 'react-icons/md'; import { ConfirmStatusChange, StatusBadge, statusOptions, dataSourceTimeSheet, TimeSheet } from '.'; import { useModal, useTimelogFilterOptions } from '@app/hooks'; @@ -153,7 +155,7 @@ export const columns: ColumnDef[] = [ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { const { isOpen, openModal, closeModal } = useModal(); const { deleteTaskTimesheet, loadingDeleteTimesheet, getStatusTimesheet } = useTimesheet({}); - const { handleSelectRowTimesheet, selectTimesheet, setSelectTimesheet, timesheetGroupByDays } = useTimelogFilterOptions(); + const { handleSelectRowTimesheet, selectTimesheet, setSelectTimesheet, timesheetGroupByDays, handleSelectRowByStatusAndDate } = useTimelogFilterOptions(); const [isDialogOpen, setIsDialogOpen] = React.useState(false); const handleConfirm = () => { try { @@ -195,7 +197,9 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { rowSelection } }); - + const handleSort = (key: string, order: SortOrder) => { + console.log(`Sorting ${key} in ${order} order`); + }; const handleButtonClick = (action: StatusAction) => { switch (action) { case 'Approved': @@ -211,7 +215,6 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { console.error(`Unsupported action: ${action}`); } }; - return (
- {Object.entries(getStatusTimesheet(plan.tasks)).map(([status, rows]) => ( - { + return rows.length > 0 && status && + handleSelectRowByStatusAndDate(status, plan.date)} + data={rows} + status={status} + onSort={handleSort} + /> {rows?.map((task) => (
- {task.timesheet.status} + {task.timesheet.status === 'DENIED' ? 'REJECTED' : task.timesheet.status}
- ))} + } + )}
} - )}
@@ -388,7 +398,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) {
- + ); } @@ -555,3 +565,94 @@ const getBadgeColor = (timesheetStatus: TimesheetStatus | null) => { return 'bg-gray-100'; } }; + + +type SortOrder = "ASC" | "DESC"; + +const HeaderColumn = ({ + label, + onSort, + currentSort, +}: { + label: string; + onSort: () => void; + currentSort: SortOrder | null; +}) => ( +
+ {label} + +
+); + +const HeaderRow = ({ status, onSort, data, handleSelectRowByStatusAndDate }: { status: string; onSort: (key: string, order: SortOrder) => void, data: TimesheetLog[], handleSelectRowByStatusAndDate: (status: string, date: string) => void }) => { + const { bg, bgOpacity } = statusColor(status); + + const [sortState, setSortState] = React.useState<{ [key: string]: SortOrder | null }>({ + Task: null, + Project: null, + Employee: null, + Status: null, + }); + + const handleSort = (key: string) => { + const newOrder = sortState[key] === "ASC" ? "DESC" : "ASC"; + setSortState({ ...sortState, [key]: newOrder }); + onSort(key, newOrder); + }; + + return ( +
+ handleSelectRowByStatusAndDate} className="w-5 h-5" /> +
+ handleSort("Task")} + currentSort={sortState["Task"]} + /> +
+
+ handleSort("Project")} + currentSort={sortState["Project"]} + /> +
+
+ handleSort("Employee")} + currentSort={sortState["Employee"]} + /> +
+
+ handleSort("Status")} + currentSort={sortState["Status"]} + /> +
+
+ Time +
+
+ ); +}; From 770ef16236e74af098bb6b0db4960fbb3674cc99 Mon Sep 17 00:00:00 2001 From: Innocent-akim Date: Mon, 25 Nov 2024 14:32:04 +0200 Subject: [PATCH 2/4] fix:deepscan --- .../lib/features/integrations/calendar/table-time-sheet.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 f3b955843..d76242fd3 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -279,7 +279,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { {status === 'DENIED' ? 'REJECTED' : status} - ({rows?.length}) + ({rows.length}) - {rows?.map((task) => ( + {rows.map((task) => (
Date: Tue, 26 Nov 2024 02:18:53 +0200 Subject: [PATCH 3/4] fix: coderabbitai --- .../[memberId]/components/TimesheetLoader.tsx | 28 +++++++++++----- .../[memberId]/components/TimesheetView.tsx | 33 +++++++++++-------- .../[locale]/timesheet/[memberId]/page.tsx | 5 +-- .../calendar/table-time-sheet.tsx | 18 ++++++++-- 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx index f3a4e5f3f..326e9bdfb 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetLoader.tsx @@ -1,11 +1,23 @@ -import React from 'react' +import { BackdropLoader } from "@/lib/components"; +import { useEffect, useState } from "react"; -function TimesheetLoader() { - return ( -
+export function TimesheetLoader({ show = false }: { show?: boolean }) { + const [dots, setDots] = useState(""); -
- ) -} + useEffect(() => { + if (!show) { + setDots(""); // Reset the dots when loader is hidden + return; + } + + const interval = setInterval(() => { + setDots((prev) => (prev.length < 3 ? prev + "." : "")); + }, 1000); // Update dots every second -export default TimesheetLoader + return () => clearInterval(interval); // Cleanup interval on unmount or when `show` changes + }, [show]); + + return ( + + ); +} diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx index aed47e2c3..a41437bfe 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx @@ -2,23 +2,28 @@ import { GroupedTimesheet } from '@/app/hooks/features/useTimesheet'; import { DataTableTimeSheet } from 'lib/features/integrations/calendar'; import { useTranslations } from 'next-intl'; -export function TimesheetView({ data }: { data?: GroupedTimesheet[] }) { +export function TimesheetView({ data, loading }: { data?: GroupedTimesheet[]; loading?: boolean }) { const t = useTranslations(); + + if (!data) { + return ( +
+

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

+
+ ); + } + + if (data.length === 0) { + return ( +
+

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

+
+ ); + } + return (
- {data ? ( - data.length > 0 ? ( - - ) : ( -
-

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

-
- ) - ) : ( -
-

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

-
- )} +
); } diff --git a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx index 14218b4de..863759acc 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/page.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/page.tsx @@ -43,7 +43,7 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb from: startOfDay(new Date()), to: endOfDay(new Date()) }); - const { timesheet, statusTimesheet } = useTimesheet({ + const { timesheet, statusTimesheet, loadingTimesheet } = useTimesheet({ startDate: dateRange.from ?? '', endDate: dateRange.to ?? '' }); @@ -195,7 +195,8 @@ const TimeSheet = React.memo(function TimeSheetPage({ params }: { params: { memb {/* */}
{timesheetNavigator === 'ListView' ? ( - + ) : ( )} 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 d76242fd3..22d98ee03 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -301,6 +301,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { data={rows} status={status} onSort={handleSort} + date={plan.date} /> {rows.map((task) => (
); -const HeaderRow = ({ status, onSort, data, handleSelectRowByStatusAndDate }: { status: string; onSort: (key: string, order: SortOrder) => void, data: TimesheetLog[], handleSelectRowByStatusAndDate: (status: string, date: string) => void }) => { - const { bg, bgOpacity } = statusColor(status); +const HeaderRow = ({ + status, + onSort, + data, + handleSelectRowByStatusAndDate, date +}: { + status: string; + onSort: (key: string, order: SortOrder) => void, + data: TimesheetLog[], + handleSelectRowByStatusAndDate: (status: string, date: string) => void, + date?: string +}) => { + const { bg, bgOpacity } = statusColor(status); const [sortState, setSortState] = React.useState<{ [key: string]: SortOrder | null }>({ Task: null, Project: null, @@ -621,7 +633,7 @@ const HeaderRow = ({ status, onSort, data, handleSelectRowByStatusAndDate }: { s style={{ backgroundColor: bgOpacity, borderBottomColor: bg }} className="flex items-center text-[#71717A] font-medium border-b border-t dark:border-gray-600 space-x-4 p-1 h-[60px] w-full" > - handleSelectRowByStatusAndDate} className="w-5 h-5" /> + handleSelectRowByStatusAndDate(status, date!)} className="w-5 h-5" />
Date: Tue, 26 Nov 2024 02:39:04 +0200 Subject: [PATCH 4/4] fix: coderabbitai --- .../timesheet/[memberId]/components/TimesheetView.tsx | 2 +- .../features/integrations/calendar/table-time-sheet.tsx | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx index a41437bfe..4fd852b36 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/TimesheetView.tsx @@ -5,7 +5,7 @@ import { useTranslations } from 'next-intl'; export function TimesheetView({ data, loading }: { data?: GroupedTimesheet[]; loading?: boolean }) { const t = useTranslations(); - if (!data) { + if (loading || !data) { return (

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

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 22d98ee03..5326b964c 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -579,10 +579,11 @@ const HeaderColumn = ({ onSort: () => void; currentSort: SortOrder | null; }) => ( -
+
{label}