From df0452ecba7e08677c032ab5634581e4497dc50f Mon Sep 17 00:00:00 2001 From: AKILIMAILI CIZUNGU Innocent <51681130+Innocent-Akim@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:01:09 +0200 Subject: [PATCH] [Feat]: Implement dynamic date range picker for timesheet filtering (#3213) * feat: Implement dynamic date range picker for timesheet filtering * fix: Resolved * improve: timesheet date filter --- .../timesheet/components/TimesheetFilter.tsx | 66 +++--- .../components/TimesheetFilterDate.tsx | 197 ++++++++++++++++++ .../[locale]/timesheet/components/index.tsx | 1 + 3 files changed, 227 insertions(+), 37 deletions(-) create mode 100644 apps/web/app/[locale]/timesheet/components/TimesheetFilterDate.tsx diff --git a/apps/web/app/[locale]/timesheet/components/TimesheetFilter.tsx b/apps/web/app/[locale]/timesheet/components/TimesheetFilter.tsx index 9249a9036..0430a0aab 100644 --- a/apps/web/app/[locale]/timesheet/components/TimesheetFilter.tsx +++ b/apps/web/app/[locale]/timesheet/components/TimesheetFilter.tsx @@ -1,47 +1,39 @@ import { FilterWithStatus } from './FilterWithStatus'; -import { FilterTaskActionMenu, FrequencySelect } from '.'; +import { FrequencySelect, TimesheetFilterDate } from '.'; import { Button } from 'lib/components'; -import { TimeSheetFilterPopover } from './time-sheet-filter-popover'; -import { Cross2Icon } from '@radix-ui/react-icons'; +import { SettingFilterIcon } from '@/assets/svg'; export function TimesheetFilter() { return ( - <> - -
-
- { - // TODO: Implement filter toggle handler - }} - /> -
-
-
- -
- - -
- - -
+
+
+ { + console.log(label) + }} + /> +
+
+
+
+ + + +
- +
) } diff --git a/apps/web/app/[locale]/timesheet/components/TimesheetFilterDate.tsx b/apps/web/app/[locale]/timesheet/components/TimesheetFilterDate.tsx new file mode 100644 index 000000000..f6532210c --- /dev/null +++ b/apps/web/app/[locale]/timesheet/components/TimesheetFilterDate.tsx @@ -0,0 +1,197 @@ +import { cn } from "@/lib/utils" +import { DatePicker } from "@components/ui/DatePicker" +import { Button } from "@components/ui/button" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@components/ui/popover" +import { CalendarIcon } from "@radix-ui/react-icons" +import { format } from "date-fns" +import React from "react" +import { MdKeyboardArrowRight } from "react-icons/md" +import { PiCalendarDotsThin } from "react-icons/pi" + +interface DatePickerInputProps { + date: Date | null; + label: string; +} +interface TimesheetFilterDateProps { + onChange?: (range: { from: Date | null; to: Date | null }) => void; + initialRange?: { from: Date | null; to: Date | null }; + minDate?: Date; + maxDate?: Date; +} + +export function TimesheetFilterDate({ + onChange, + initialRange, + minDate, + maxDate +}: TimesheetFilterDateProps) { + + const [dateRange, setDateRange] = React.useState<{ from: Date | null; to: Date | null }>({ + from: initialRange?.from ?? new Date(), + to: initialRange?.to ?? new Date(), + }); + + const handleFromChange = (fromDate: Date | null) => { + if (maxDate && fromDate && fromDate > maxDate) { + return; + } + setDateRange((prev) => ({ ...prev, from: fromDate })); + onChange?.({ ...dateRange, from: fromDate }); + }; + + const handleToChange = (toDate: Date | null) => { + if (dateRange.from && toDate && toDate < dateRange.from) { + return; + } + setDateRange((prev) => ({ ...prev, to: toDate })); + }; + + const handlePresetClick = (preset: string) => { + const today = new Date(); + switch (preset) { + case 'Today': + setDateRange({ from: today, to: today }); + break; + case 'Last 7 days': + setDateRange({ + from: new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7), + to: today + }); + break; + case 'Last 30 days': + setDateRange({ + from: new Date(today.getFullYear(), today.getMonth(), today.getDate() - 30), + to: today + }); + break; + case `This year (${today.getFullYear()})`: + setDateRange({ + from: new Date(today.getFullYear(), 0, 1), + to: today + }); + break; + case 'Custom Date Range': + setDateRange({ from: null, to: null }); + break; + default: + break; + } + }; + + + return (<> + + + + + +
+ + +
+
+ {["Today", "Last 7 days", "Last 30 days", `This year (${new Date().getFullYear()})`, "Custom Date Range"].map((label, index) => ( + + ))} +
+
+
+ + ) +} + + +const DatePickerInput: React.FC = ({ date, label }) => ( + <> + + + +); + +export function DatePickerFilter({ + label, + date, + setDate, + minDate, + maxDate +}: { + label: string; + date: Date | null; + setDate: (date: Date | null) => void; + minDate?: Date | null; + maxDate?: Date | null + +}) { + const isDateDisabled = React.useCallback((date: Date) => { + if (minDate && date < minDate) return true; + if (maxDate && date > maxDate) return true; + return false; + }, [minDate, maxDate]); + + return ( +
+ } + mode="single" + numberOfMonths={1} + initialFocus + defaultMonth={date ?? new Date()} + selected={date ?? new Date()} + onSelect={(selectedDate) => { + if (selectedDate && !isDateDisabled(selectedDate)) { + setDate(selectedDate); + } + }} + modifiersClassNames={{ + disabled: 'text-gray-300 cursor-not-allowed', + }} + disabled={[ + ...(minDate ? [{ before: minDate }] : []), + ...(maxDate ? [{ after: maxDate }] : []) + ]} + /> +
+ ); +} diff --git a/apps/web/app/[locale]/timesheet/components/index.tsx b/apps/web/app/[locale]/timesheet/components/index.tsx index 12cf616a7..ed9e14172 100644 --- a/apps/web/app/[locale]/timesheet/components/index.tsx +++ b/apps/web/app/[locale]/timesheet/components/index.tsx @@ -4,3 +4,4 @@ export * from './CalendarView'; export * from './TimesheetFilter'; export * from './FrequencySelect'; export * from './FilterWithStatus'; +export * from './TimesheetFilterDate';