summaryrefslogtreecommitdiff
path: root/lib/risk-management/table/risks-date-range-picker.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-08-14 00:26:53 +0000
committerjoonhoekim <26rote@gmail.com>2025-08-14 00:26:53 +0000
commitdd20ba9785cdbd3d61f6b014d003d3bd9646ad13 (patch)
tree4e99d62311a6c115dbc894084714a29c34bca11a /lib/risk-management/table/risks-date-range-picker.tsx
parent33be47506f0aa62b969d82521580a29e95080268 (diff)
(고건) 리스크 관리 페이지 추가
Diffstat (limited to 'lib/risk-management/table/risks-date-range-picker.tsx')
-rw-r--r--lib/risk-management/table/risks-date-range-picker.tsx157
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;