'use client'; import { ReactNode, useCallback } from 'react'; import { format, startOfYear, endOfYear, subMonths, startOfMonth, endOfMonth, subDays } from 'date-fns'; import { Button } from '@/components/ui/button'; import { DatePicker } from '@/components/ui/date-picker'; import { DateRangePicker } from '@/components/ui/date-range-picker'; import { ScrollableButtonGroup } from '@/components/atoms/ScrollableButtonGroup'; /** * 날짜 범위 프리셋 타입 */ export type DatePreset = 'thisYear' | 'fiveMonthsAgo' | 'fourMonthsAgo' | 'threeMonthsAgo' | 'twoMonthsAgo' | 'lastMonth' | 'thisMonth' | 'yesterday' | 'today'; /** * 프리셋 레이블 (한국어) */ const PRESET_LABELS: Record = { thisYear: '당해년도', fiveMonthsAgo: 'D-5월', fourMonthsAgo: 'D-4월', threeMonthsAgo: 'D-3월', twoMonthsAgo: '전전월', lastMonth: '전월', thisMonth: '당월', yesterday: '어제', today: '오늘', }; /** * 기본 프리셋 순서 */ const DEFAULT_PRESETS: DatePreset[] = ['today', 'yesterday', 'thisMonth', 'lastMonth', 'twoMonthsAgo', 'thisYear' ]; interface DateRangeSelectorProps { /** 시작 날짜 (yyyy-MM-dd 형식) */ startDate: string; /** 종료 날짜 (yyyy-MM-dd 형식) */ endDate: string; /** 시작 날짜 변경 핸들러 */ onStartDateChange: (date: string) => void; /** 종료 날짜 변경 핸들러 */ onEndDateChange: (date: string) => void; /** 표시할 프리셋 목록 (기본: 전체) */ presets?: DatePreset[]; /** 프리셋 레이블 커스텀 오버라이드 */ presetLabels?: Partial>; /** 추가 액션 (엑셀 다운로드, 등록 버튼 등) */ extraActions?: ReactNode; /** 프리셋 버튼 숨김 */ hidePresets?: boolean; /** 날짜 입력 숨김 */ hideDateInputs?: boolean; /** 날짜 입력 너비 */ dateInputWidth?: string; /** 프리셋 버튼 위치: 'inline' (날짜 옆), 'below' (별도 줄) */ presetsPosition?: 'inline' | 'below'; /** 날짜 입력 변형: 'split' (DatePicker 2개), 'combined' (DateRangePicker 1개) */ variant?: 'split' | 'combined'; } /** * 날짜 범위 선택 컴포넌트 * * 달력(날짜 입력) + 기간 프리셋 버튼 (당해년도, 전전월, 전월, 당월, 어제, 오늘) * * @example * ```tsx * * * * * } * /> * ``` */ export function DateRangeSelector({ startDate, endDate, onStartDateChange, onEndDateChange, presets = DEFAULT_PRESETS, presetLabels: customLabels, extraActions, hidePresets = false, hideDateInputs = false, dateInputWidth = 'w-[140px]', presetsPosition = 'inline', variant = 'combined', }: DateRangeSelectorProps) { // 프리셋 클릭 핸들러 const handlePresetClick = useCallback((preset: DatePreset) => { const today = new Date(); switch (preset) { case 'thisYear': onStartDateChange(format(startOfYear(today), 'yyyy-MM-dd')); onEndDateChange(format(endOfYear(today), 'yyyy-MM-dd')); break; case 'twoMonthsAgo': { const twoMonthsAgo = subMonths(today, 2); onStartDateChange(format(startOfMonth(twoMonthsAgo), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(twoMonthsAgo), 'yyyy-MM-dd')); break; } case 'lastMonth': { const lastMonth = subMonths(today, 1); onStartDateChange(format(startOfMonth(lastMonth), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(lastMonth), 'yyyy-MM-dd')); break; } case 'thisMonth': onStartDateChange(format(startOfMonth(today), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(today), 'yyyy-MM-dd')); break; case 'yesterday': { const yesterday = subDays(today, 1); onStartDateChange(format(yesterday, 'yyyy-MM-dd')); onEndDateChange(format(yesterday, 'yyyy-MM-dd')); break; } case 'today': onStartDateChange(format(today, 'yyyy-MM-dd')); onEndDateChange(format(today, 'yyyy-MM-dd')); break; case 'threeMonthsAgo': { const threeMonthsAgo = subMonths(today, 3); onStartDateChange(format(startOfMonth(threeMonthsAgo), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(threeMonthsAgo), 'yyyy-MM-dd')); break; } case 'fourMonthsAgo': { const fourMonthsAgo = subMonths(today, 4); onStartDateChange(format(startOfMonth(fourMonthsAgo), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(fourMonthsAgo), 'yyyy-MM-dd')); break; } case 'fiveMonthsAgo': { const fiveMonthsAgo = subMonths(today, 5); onStartDateChange(format(startOfMonth(fiveMonthsAgo), 'yyyy-MM-dd')); onEndDateChange(format(endOfMonth(fiveMonthsAgo), 'yyyy-MM-dd')); break; } } }, [onStartDateChange, onEndDateChange]); // 프리셋 버튼 렌더링 const renderPresets = () => { if (hidePresets || presets.length === 0) return null; return ( {presets.map((preset) => ( ))} ); }; // 날짜 입력 영역 렌더링 const renderDateInputs = () => { if (hideDateInputs) return null; if (variant === 'combined') { return (
); } return (
~
); }; // presetsPosition이 'below'일 때: 달력+extraActions 같은 줄, 프리셋은 아래 줄 if (presetsPosition === 'below') { return (
{/* 1줄: 날짜 + extraActions */}
{renderDateInputs()} {extraActions}
{/* 2줄: 프리셋 버튼들 */} {renderPresets()}
); } // presetsPosition이 'inline' (기본값) // PC(1280px+): 달력 | 프리셋버튼 | 검색창 (한 줄, 넘치면 줄바꿈) // 태블릿: 달력 / 프리셋버튼 / 검색창 (세 줄) // Note: w-full 제거 - 부모 컨테이너에서 다른 요소들과 자연스럽게 한 줄에 배치되도록 함 return (
{/* 날짜 범위 선택 */} {renderDateInputs()} {/* 기간 버튼들 - 달력 바로 옆 */} {renderPresets()} {/* extraActions (검색창 등) - 마지막에 배치 */} {extraActions}
); }