diff options
| author | joonhoekim <26rote@gmail.com> | 2025-08-14 00:26:53 +0000 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-08-14 00:26:53 +0000 |
| commit | dd20ba9785cdbd3d61f6b014d003d3bd9646ad13 (patch) | |
| tree | 4e99d62311a6c115dbc894084714a29c34bca11a /lib/risk-management/table/risks-date-range-picker.tsx | |
| parent | 33be47506f0aa62b969d82521580a29e95080268 (diff) | |
(고건) 리스크 관리 페이지 추가
Diffstat (limited to 'lib/risk-management/table/risks-date-range-picker.tsx')
| -rw-r--r-- | lib/risk-management/table/risks-date-range-picker.tsx | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/lib/risk-management/table/risks-date-range-picker.tsx b/lib/risk-management/table/risks-date-range-picker.tsx new file mode 100644 index 00000000..96acff6c --- /dev/null +++ b/lib/risk-management/table/risks-date-range-picker.tsx @@ -0,0 +1,157 @@ +'use client'; + +/* IMPORT */ +import { format } from 'date-fns'; +import { Button, type ButtonProps } from '@/components/ui/button'; +import { Calendar } from '@/components/ui/calendar'; +import { CalendarIcon, X } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { parseAsString, useQueryStates } from 'nuqs'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { type ComponentPropsWithoutRef, type MouseEvent, useMemo } from 'react'; +import { type DateRange } from 'react-day-picker'; + +// ---------------------------------------------------------------------------------------------------- + +/* TYPES */ +interface RisksDateRangePickerProps extends ComponentPropsWithoutRef<typeof PopoverContent> { + defaultDateRange?: DateRange; + placeholder?: string; + triggerVariant?: Exclude<ButtonProps['variant'], 'destructive' | 'link'>; + triggerSize?: Exclude<ButtonProps['size'], 'icon'>; + triggerClassName?: string; + shallow?: boolean; + showClearButton?: boolean; +} + +// ---------------------------------------------------------------------------------------------------- + +/* RISKS DATE RANGE PICKER COMPONENT */ +function RisksDateRangePicker(props: RisksDateRangePickerProps) { + const { + defaultDateRange, + placeholder = '날짜를 선택하세요.', + triggerVariant = 'outline', + triggerSize = 'default', + triggerClassName, + showClearButton = false, + shallow = true, + className, + ...otherProps + } = props; + const [dateParams, setDateParams] = useQueryStates( + { + from: parseAsString.withDefault( + defaultDateRange?.from?.toISOString() ?? '' + ), + to: parseAsString.withDefault(defaultDateRange?.to?.toISOString() ?? ''), + }, + { + clearOnDefault: true, + shallow, + } + ) + + const date = useMemo(() => { + function parseDate(dateString: string | null) { + if (!dateString) { + return undefined; + } + const parsedDate = new Date(dateString); + return isNaN(parsedDate.getTime()) ? undefined : parsedDate; + } + + return { + from: parseDate(dateParams.from) ?? defaultDateRange?.from, + to: parseDate(dateParams.to) ?? defaultDateRange?.to, + }; + }, [dateParams, defaultDateRange]); + + const clearDates = (e: MouseEvent) => { + e.stopPropagation(); + void setDateParams({ + from: "", + to: "", + }); + }; + + const hasSelectedDate = Boolean(date?.from || date?.to); + + return ( + <div className="grid gap-2"> + <Popover> + <PopoverTrigger asChild> + <Button + variant={triggerVariant} + size={triggerSize} + className={cn( + 'relative w-full justify-start gap-2 truncate text-left font-normal', + !date && 'text-muted-foreground', + triggerClassName + )} + > + <CalendarIcon className="size-4" /> + {date?.from ? ( + date.to ? ( + <> + {format(date.from, 'LLL dd, y')} -{' '} + {format(date.to, 'LLL dd, y')} + </> + ) : ( + format(date.from, 'LLL dd, y') + ) + ) : ( + <span>{placeholder}</span> + )} + + {showClearButton && hasSelectedDate && ( + <Button + type="button" + variant="ghost" + size="icon" + className="absolute right-0 top-0 h-full rounded-l-none px-3 hover:bg-background" + onClick={clearDates} + > + <X className="size-4" /> + <span className="sr-only">초기화</span> + </Button> + )} + </Button> + </PopoverTrigger> + <PopoverContent className={cn("w-auto p-0", className)} {...otherProps}> + <Calendar + initialFocus + mode="range" + defaultMonth={date?.from} + selected={date?.from && date?.to ? date : undefined} + onSelect={(newDateRange) => { + const from = newDateRange?.from; + const to = newDateRange?.to; + + if (from && to && from > to) { + void setDateParams({ + from: to.toISOString(), + to: from.toISOString(), + }); + } else { + void setDateParams({ + from: from?.toISOString() ?? '', + to: to?.toISOString() ?? '', + }); + } + }} + numberOfMonths={2} + /> + </PopoverContent> + </Popover> + </div> + ) +} + +// ---------------------------------------------------------------------------------------------------- + +export default RisksDateRangePicker; |
