2025-09-07 22:57:43 +00:00
|
|
|
import OneMonth from "./svgs/oneMonth";
|
|
|
|
|
import Realtime from "./svgs/realtime";
|
|
|
|
|
import Arrow from "./svgs/arrow";
|
|
|
|
|
import { DateRangeEnum } from "@/app/constants/dateRange";
|
|
|
|
|
import { useState,useEffect } from "react";
|
|
|
|
|
|
2025-10-15 12:17:36 +00:00
|
|
|
export default function CalenderSelector({ dateString, is_small, onRangeChange, isOpen: controlledOpen, onOpenChange }: { dateString: string, is_ri: boolean, is_small: boolean, onRangeChange?: (start: Date, end: Date) => void, isOpen?: boolean, onOpenChange?: (open: boolean) => void }) {
|
|
|
|
|
const CUSTOM = -1; // 커스텀(년/월) 선택 표기용
|
|
|
|
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
|
|
|
const isOpen = typeof controlledOpen === 'boolean' ? controlledOpen : internalOpen;
|
|
|
|
|
const setOpen = (next: boolean) => { if (onOpenChange) onOpenChange(next); else setInternalOpen(next); };
|
2025-09-07 22:57:43 +00:00
|
|
|
const [selected, setSelected] = useState<number>(DateRangeEnum.ONE_MONTH);
|
|
|
|
|
const [rangeStart, setRangeStart] = useState<Date>(new Date());
|
|
|
|
|
const [rangeEnd, setRangeEnd] = useState<Date>(new Date());
|
2025-10-15 12:17:36 +00:00
|
|
|
const [yearInput, setYearInput] = useState<string>('');
|
|
|
|
|
const [monthInput, setMonthInput] = useState<string>('');
|
|
|
|
|
const [customLabel, setCustomLabel] = useState<string>('');
|
|
|
|
|
const [isCustomActive, setIsCustomActive] = useState<boolean>(false);
|
2025-09-07 22:57:43 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
|
|
|
const endDate = new Date(Date.now());
|
|
|
|
|
setRangeStart(startDate);
|
|
|
|
|
setRangeEnd(endDate);
|
|
|
|
|
onRangeChange?.(startDate, endDate);
|
2025-10-15 12:17:36 +00:00
|
|
|
const now = new Date();
|
|
|
|
|
setYearInput(String(now.getFullYear()));
|
|
|
|
|
setMonthInput(String(now.getMonth() + 1).padStart(2,'0'));
|
2025-09-07 22:57:43 +00:00
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<div className="relative inline-block">
|
|
|
|
|
<div className={`
|
|
|
|
|
${is_small ? 'w-[267px] h-[28px]':'w-[267px] h-[36px]'}
|
|
|
|
|
${isOpen ? 'border-[#F94B37] bg-[#FFF3F2] bor' : 'border-border-pale bg-white '}
|
|
|
|
|
border-1 rounded-lg flex flex-row items-center justify-between gap-2 px-2 cursor-pointer`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
console.log('click');
|
2025-10-15 12:17:36 +00:00
|
|
|
setOpen(!isOpen);
|
2025-09-07 22:57:43 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex-shrink-0">
|
2025-09-09 07:31:48 +00:00
|
|
|
{selected === DateRangeEnum.ALL ? (
|
|
|
|
|
<Realtime color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />
|
|
|
|
|
) : (
|
|
|
|
|
<OneMonth color="#848484" width={is_small ? 16 : 20} height={is_small ? 16 : 20} />
|
|
|
|
|
)}
|
2025-09-07 22:57:43 +00:00
|
|
|
</div>
|
|
|
|
|
<div className={`flex-1 min-w-0 font-semibold ${is_small ? 'text-xs':'text-sm'}`}>
|
2025-10-15 12:17:36 +00:00
|
|
|
{customLabel || (
|
|
|
|
|
selected === DateRangeEnum.ONE_MONTH ? '최근 1개월' :
|
|
|
|
|
selected === DateRangeEnum.ONE_WEEK ? '최근 1주일' :
|
|
|
|
|
selected === DateRangeEnum.TWO_MONTHS ? '최근 2개월' :
|
|
|
|
|
selected === DateRangeEnum.THREE_MONTHS ? '최근 3개월' :
|
|
|
|
|
selected === DateRangeEnum.SIX_MONTHS ? '최근 6개월' :
|
|
|
|
|
selected === DateRangeEnum.ONE_YEAR ? '최근 1년' :
|
|
|
|
|
selected === DateRangeEnum.ALL ? '전체' : ''
|
|
|
|
|
)}
|
2025-09-07 22:57:43 +00:00
|
|
|
</div>
|
|
|
|
|
<div className={`pt-[3px] transition-transform ${isOpen ? 'rotate-180' : ''} flex-shrink-0`}>
|
|
|
|
|
<Arrow color="#A4A0A0" width={is_small ? 12 : 18} height={is_small ? 8 : 12} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
{isOpen && (
|
|
|
|
|
<div className={`absolute left-0 top-full mt-1 ${is_small ? 'w-[267px]' : 'w-[267px]'} bg-white border-1 border-border-pale rounded-lg shadow-sm p-1 z-50`}>
|
|
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.ALL ? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.ALL);
|
|
|
|
|
const end = new Date();
|
2025-10-15 12:17:36 +00:00
|
|
|
const start = new Date(Date.UTC(2025, 0, 1));
|
2025-09-07 22:57:43 +00:00
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-07 22:57:43 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<Realtime color={selected === DateRangeEnum.ALL ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">전체</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
2025-09-09 06:15:34 +00:00
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.ONE_WEEK? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.ONE_WEEK);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 7*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-09 06:15:34 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.ONE_WEEK ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 1주일</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
2025-09-07 22:57:43 +00:00
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.ONE_MONTH? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.ONE_MONTH);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 30*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-07 22:57:43 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.ONE_MONTH ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 1개월</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
2025-09-09 06:15:34 +00:00
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.TWO_MONTHS? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.TWO_MONTHS);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 60*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-09 06:15:34 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.TWO_MONTHS ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 2개월</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
|
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.THREE_MONTHS? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.THREE_MONTHS);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 90*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-09 06:15:34 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.THREE_MONTHS ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 3개월</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
|
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.SIX_MONTHS? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.SIX_MONTHS);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 182*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-09 06:15:34 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.SIX_MONTHS ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 6개월</span>
|
|
|
|
|
</button>
|
|
|
|
|
<div className="h-1 "></div>
|
|
|
|
|
<button
|
|
|
|
|
className={`flex items-center gap-2 px-2 h-[32px] rounded-md hover:bg-[#f6f6f6] text-left ${selected === DateRangeEnum.ONE_YEAR? 'bg-[#FFF3F2] font-semibold' : ''}`}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSelected(DateRangeEnum.ONE_YEAR);
|
|
|
|
|
const end = new Date();
|
|
|
|
|
const start = new Date(end.getTime() - 365*24*60*60*1000);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
2025-10-15 12:17:36 +00:00
|
|
|
setCustomLabel('');
|
|
|
|
|
setIsCustomActive(false);
|
|
|
|
|
setOpen(false);
|
2025-09-09 06:15:34 +00:00
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<OneMonth color={selected === DateRangeEnum.ONE_YEAR ? '#F94B37' : '#848484'} width={16} height={16} />
|
|
|
|
|
<span className="text-sm">최근 1년</span>
|
|
|
|
|
</button>
|
2025-09-09 07:31:48 +00:00
|
|
|
|
2025-09-07 22:57:43 +00:00
|
|
|
</div>
|
2025-10-15 12:17:36 +00:00
|
|
|
<div className="mt-2 pt-2 border-t-1 border-border-pale">
|
|
|
|
|
<div className={`flex items-center gap-2 px-1 ${isCustomActive ? 'bg-[#FFF3F2] font-semibold rounded-md py-2' : ''}`}>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
className="w-[88px] h-[32px] border-1 border-border-pale rounded px-2 pr-1 text-sm"
|
|
|
|
|
placeholder="YYYY"
|
|
|
|
|
min={2000}
|
|
|
|
|
max={2100}
|
|
|
|
|
value={yearInput}
|
|
|
|
|
onChange={(e)=> setYearInput(e.target.value.replace(/[^0-9]/g,''))}
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-sm">년</span>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
className="w-[50px] h-[32px] border-1 border-border-pale rounded px-2 pr-1 text-sm"
|
|
|
|
|
placeholder="MM"
|
|
|
|
|
min={1}
|
|
|
|
|
max={12}
|
|
|
|
|
value={monthInput}
|
|
|
|
|
onChange={(e)=> {
|
|
|
|
|
const v = e.target.value.replace(/[^0-9]/g,'');
|
|
|
|
|
setMonthInput(v);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-sm">월</span>
|
|
|
|
|
<button
|
|
|
|
|
className="ml-auto h-[32px] min-w-[80px] px-4 shrink-0 whitespace-nowrap rounded-md bg-[#F94B37] border-1 border-[#D73B29] text-white text-sm font-semibold hover:bg-[#D73B29]"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
const y = parseInt(yearInput, 10);
|
|
|
|
|
const m = parseInt(monthInput, 10);
|
|
|
|
|
if (!Number.isFinite(y) || y < 2000 || y > 2100) return;
|
|
|
|
|
if (!Number.isFinite(m) || m < 1 || m > 12) return;
|
|
|
|
|
const start = new Date(y, m - 1, 1);
|
|
|
|
|
const end = new Date(y, m, 0, 23, 59, 59, 999);
|
|
|
|
|
setRangeStart(start);
|
|
|
|
|
setRangeEnd(end);
|
|
|
|
|
onRangeChange?.(start, end);
|
|
|
|
|
const mm = String(m).padStart(2, '0');
|
|
|
|
|
setCustomLabel(`${y}.${mm}`);
|
|
|
|
|
setIsCustomActive(true);
|
|
|
|
|
setSelected(CUSTOM);
|
|
|
|
|
setOpen(false);
|
|
|
|
|
}}
|
|
|
|
|
>조회</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-07 22:57:43 +00:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|