From 7cf00f092955dedbb3e191b493e3652b6dc8ae76 Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Sun, 8 Dec 2024 19:29:41 +0200 Subject: [PATCH] [Feat]: Added Bulk Deletion Functionality For Time Entries (#3402) * feat: added bulk deletion functionality for time entries * fix: coderabbitai --- .../[memberId]/components/EditTaskModal.tsx | 2 +- .../hooks/features/useTimelogFilterOptions.ts | 7 +- apps/web/app/hooks/features/useTimesheet.ts | 8 +- .../components/alert-dialog-confirmation.tsx | 72 +++++++++++++++ .../calendar/table-time-sheet.tsx | 92 +++++++++++++------ 5 files changed, 147 insertions(+), 34 deletions(-) diff --git a/apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx b/apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx index 10a66b369..2bb6083bf 100644 --- a/apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx +++ b/apps/web/app/[locale]/timesheet/[memberId]/components/EditTaskModal.tsx @@ -131,7 +131,7 @@ export function EditTaskModal({ isOpen, closeModal, dataTimesheet }: IEditTaskMo isOpen={isOpen} showCloseIcon title={'Edit Task'} - className="bg-light--theme-light dark:bg-dark--theme-light p-5 rounded-xl w-full md:min-w-[32rem] justify-start h-[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">
diff --git a/apps/web/app/hooks/features/useTimelogFilterOptions.ts b/apps/web/app/hooks/features/useTimelogFilterOptions.ts index a79fcb1ad..bcbe64a23 100644 --- a/apps/web/app/hooks/features/useTimelogFilterOptions.ts +++ b/apps/web/app/hooks/features/useTimelogFilterOptions.ts @@ -11,6 +11,7 @@ export function useTimelogFilterOptions() { const [timesheetGroupByDays, setTimesheetGroupByDays] = useAtom(timesheetGroupByDayState); const [puTimesheetStatus, setPuTimesheetStatus] = useAtom(timesheetUpdateStatus) const [selectedItems, setSelectedItems] = React.useState<{ status: string; date: string }[]>([]); + const [selectTimesheetId, setSelectTimesheetId] = React.useState([]) const employee = employeeState; const project = projectState; @@ -29,7 +30,7 @@ export function useTimelogFilterOptions() { }; const handleSelectRowTimesheet = (items: string) => { - setSelectTimesheet((prev) => prev.includes(items) ? prev.filter((filter) => filter !== items) : [...prev, items]) + setSelectTimesheetId((prev) => prev.includes(items) ? prev.filter((filter) => filter !== items) : [...prev, items]) } const handleSelectRowByStatusAndDate = (status: string, date: string) => { @@ -43,7 +44,7 @@ export function useTimelogFilterOptions() { React.useEffect(() => { - return () => setSelectTimesheet([]); + return () => setSelectTimesheetId([]); }, []); return { @@ -56,6 +57,8 @@ export function useTimelogFilterOptions() { setTaskState, setStatusState, handleSelectRowTimesheet, + selectTimesheetId, + setSelectTimesheetId, handleSelectRowByStatusAndDate, selectedItems, selectTimesheet, diff --git a/apps/web/app/hooks/features/useTimesheet.ts b/apps/web/app/hooks/features/useTimesheet.ts index 9add0836d..2f3c6599b 100644 --- a/apps/web/app/hooks/features/useTimesheet.ts +++ b/apps/web/app/hooks/features/useTimesheet.ts @@ -95,7 +95,7 @@ export function useTimesheet({ }: TimesheetParams) { const { user } = useAuthenticateUser(); const [timesheet, setTimesheet] = useAtom(timesheetRapportState); - const { employee, project, task, statusState, selectTimesheet: logIds, timesheetGroupByDays, puTimesheetStatus } = useTimelogFilterOptions(); + const { employee, project, task, statusState, timesheetGroupByDays, puTimesheetStatus } = useTimelogFilterOptions(); const { loading: loadingTimesheet, queryCall: queryTimesheet } = useQuery(getTaskTimesheetLogsApi); const { loading: loadingDeleteTimesheet, queryCall: queryDeleteTimesheet } = useQuery(deleteTaskTimesheetLogsApi); const { loading: loadingUpdateTimesheetStatus, queryCall: queryUpdateTimesheetStatus } = useQuery(updateStatusTimesheetFromApi) @@ -239,7 +239,7 @@ export function useTimesheet({ } - const deleteTaskTimesheet = useCallback(async () => { + const deleteTaskTimesheet = useCallback(async ({ logIds }: { logIds: string[] }) => { if (!user) { throw new Error('User not authenticated'); } @@ -253,14 +253,14 @@ export function useTimesheet({ logIds }); setTimesheet(prevTimesheet => - prevTimesheet.filter(item => !logIds.includes(item.timesheet.id)) + prevTimesheet.filter(item => !logIds.includes(item.id)) ); } catch (error) { console.error('Failed to delete timesheets:', error); throw error; } - }, [user, logIds, queryDeleteTimesheet, setTimesheet]); + }, [user, queryDeleteTimesheet, setTimesheet]); const timesheetElementGroup = useMemo(() => { diff --git a/apps/web/lib/components/alert-dialog-confirmation.tsx b/apps/web/lib/components/alert-dialog-confirmation.tsx index 494a40f12..d16a9afe6 100644 --- a/apps/web/lib/components/alert-dialog-confirmation.tsx +++ b/apps/web/lib/components/alert-dialog-confirmation.tsx @@ -8,6 +8,7 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@components/ui/alert-dialog" +import { Button, Card, Modal, Text } from 'lib/components'; import { ReloadIcon } from "@radix-ui/react-icons"; import React from "react"; @@ -70,3 +71,74 @@ export function AlertDialogConfirmation({ ); } + + + +export const AlertConfirmationModal = ({ + open, + close, + title, + description, + onAction, + loading, + confirmText = "Continue", + cancelText = "Cancel", + countID = 0 +}: { + open: boolean; + close: () => void; + onAction: () => any; + title: string; + description: string; + loading: boolean; + confirmText?: string; + cancelText?: string; + countID?: number +}) => { + return ( + <> + + +
+
+ + {title} + +
+ + {description} + + {countID > 0 && {countID}} +
+
+
+ + +
+
+
+
+ + ); +}; 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 117d90d43..db7616a22 100644 --- a/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx +++ b/apps/web/lib/features/integrations/calendar/table-time-sheet.tsx @@ -47,7 +47,7 @@ import { useModal, useTimelogFilterOptions } from '@app/hooks'; import { Checkbox } from '@components/ui/checkbox'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@components/ui/accordion'; import { clsxm } from '@/app/utils'; -import { AlertDialogConfirmation, statusColor } from '@/lib/components'; +import { AlertConfirmationModal, statusColor } from '@/lib/components'; import { Badge } from '@components/ui/badge'; import { EditTaskModal, @@ -64,6 +64,8 @@ import { formatDate } from '@/app/helpers'; import { GroupedTimesheet, useTimesheet } from '@/app/hooks/features/useTimesheet'; import { DisplayTimeForTimesheet, TaskNameInfoDisplay, TotalDurationByDate, TotalTimeDisplay } from '../../task/task-displays'; import { TimesheetLog, TimesheetStatus } from '@/app/interfaces'; +import { toast } from '@components/ui/use-toast'; +import { ToastAction } from '@components/ui/toast'; export const columns: ColumnDef[] = [ { @@ -155,19 +157,22 @@ export const columns: ColumnDef[] = [ ]; export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { - const { isOpen, openModal, closeModal } = useModal(); - + const modal = useModal(); + const alertConfirmationModal = useModal(); + const { isOpen, openModal, closeModal } = modal; + const { isOpen: isOpenAlert, openModal: openAlertConfirmation, closeModal: closeAlertConfirmation } = alertConfirmationModal; const { deleteTaskTimesheet, loadingDeleteTimesheet, getStatusTimesheet, updateTimesheetStatus } = useTimesheet({}); - const { handleSelectRowTimesheet, selectTimesheet, setSelectTimesheet, timesheetGroupByDays, handleSelectRowByStatusAndDate } = useTimelogFilterOptions(); + const { timesheetGroupByDays, handleSelectRowByStatusAndDate, handleSelectRowTimesheet, selectTimesheetId, setSelectTimesheetId } = useTimelogFilterOptions(); + + - const [isDialogOpen, setIsDialogOpen] = React.useState(false); const handleConfirm = () => { try { - deleteTaskTimesheet() + deleteTaskTimesheet({ logIds: selectTimesheetId }) .then(() => { - setSelectTimesheet([]); - setIsDialogOpen(false); + setSelectTimesheetId([]); + closeAlertConfirmation() }) .catch((error) => { console.error('Delete timesheet error:', error); @@ -176,9 +181,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { console.error('Delete timesheet error:', error); } }; - const handleCancel = () => { - setIsDialogOpen(false); - }; + const t = useTranslations(); const [sorting, setSorting] = React.useState([]); const [columnFilters, setColumnFilters] = React.useState([]); @@ -208,10 +211,10 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { const handleButtonClick = async (action: StatusAction) => { switch (action) { case 'Approved': - if (selectTimesheet.length > 0) { + if (selectTimesheetId.length > 0) { await updateTimesheetStatus({ status: 'APPROVED', - ids: selectTimesheet + ids: selectTimesheetId }) } break; @@ -219,24 +222,24 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { openModal(); break; case 'Deleted': - setIsDialogOpen(true); + openAlertConfirmation(); break; default: console.error(`Unsupported action: ${action}`); } }; + + return (
- { @@ -300,7 +303,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) {
- {getTimesheetButtons(status as StatusType, t, true, handleButtonClick)} + {getTimesheetButtons(status as StatusType, t, false, handleButtonClick)}
@@ -327,7 +330,7 @@ export function DataTableTimeSheet({ data }: { data?: GroupedTimesheet[] }) { handleSelectRowTimesheet(task.id)} - checked={selectTimesheet.includes(task.id)} + checked={selectTimesheetId.includes(task.id)} />
{ const { isOpen: isEditTask, openModal: isOpenModalEditTask, closeModal: isCloseModalEditTask } = useModal(); + const { isOpen: isOpenAlert, openModal: openAlertConfirmation, closeModal: closeAlertConfirmation } = useModal(); + const { deleteTaskTimesheet, loadingDeleteTimesheet } = useTimesheet({}); + const t = useTranslations(); + const handleDeleteTask = () => { + deleteTaskTimesheet({ logIds: [dataTimesheet.id] }) + .then(() => { + toast({ + title: 'Deletion Confirmed', + description: "The timesheet has been successfully deleted.", + variant: 'default', + className: 'bg-red-50 text-red-600 border-red-500', + action: Undo + }); + }) + .catch((error) => { + toast({ + title: 'Error during deletion', + description: `An error occurred: ${error}.The timesheet could not be deleted.`, + variant: 'destructive', + className: 'bg-red-50 text-red-600 border-red-500' + }); + }); + }; return ( <> - { + } + />