"use client"; import { useState, useEffect } from "react"; import dayjs from "@/lib/dayjs"; import { Button } from "@/components/ui/button"; import { useTranslation } from "react-i18next"; import { motion, AnimatePresence } from "framer-motion"; import { Calendar as CalendarIcon, Clock, Sun, CalendarDays, CalendarRange, CalendarClock, CalendarCheck, } from "lucide-react"; import { Calendar } from "@/components/ui/calendar"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { cn } from "@/lib/utils"; import { format } from "date-fns"; import { zhCN } from "date-fns/locale"; export type TimeRangeType = | "today" | "week" | "month" | "30days" | "all" | "custom"; interface TimeRangeSelectorProps { timeRange: [Date, Date]; timeRangeType: TimeRangeType; availableTimeRange: { minTime: Date; maxTime: Date; }; onTimeRangeChange: (range: [Date, Date], type: TimeRangeType) => void; } const checkTimeRangeType = ( startTime: dayjs.Dayjs, endTime: dayjs.Dayjs, availableTimeRange: { minTime: Date; maxTime: Date } ): TimeRangeType => { if ( dayjs(startTime).isSame(availableTimeRange.minTime, "hour") && dayjs(endTime).isSame(availableTimeRange.maxTime, "hour") ) { return "all"; } const now = dayjs(); const isToday = startTime.isSame(now.startOf("day")) && endTime.isSame(now); const isWeek = startTime.isSame(now.startOf("week")) && endTime.isSame(now); const isMonth = startTime.isSame(now.startOf("month")) && endTime.isSame(now); const is30Days = startTime.isSame(now.subtract(30, "day"), "hour") && endTime.isSame(now); if (isToday) return "today"; if (isWeek) return "week"; if (isMonth) return "month"; if (is30Days) return "30days"; return "custom"; }; export default function TimeRangeSelector({ timeRange, timeRangeType, availableTimeRange, onTimeRangeChange, }: TimeRangeSelectorProps) { const { t, i18n } = useTranslation("common"); const [isCustomOpen, setIsCustomOpen] = useState(false); const [startOpen, setStartOpen] = useState(false); const [endOpen, setEndOpen] = useState(false); const [startDate, setStartDate] = useState(timeRange[0]); const [endDate, setEndDate] = useState(timeRange[1]); useEffect(() => { setStartDate(timeRange[0]); setEndDate(timeRange[1]); }, [timeRange]); const timeOptions = [ { id: "today", type: "today" as TimeRangeType, label: t("panel.timeRange.timeOptions.day"), icon: Sun, getRange: () => [dayjs().startOf("day").toDate(), dayjs().endOf("day").toDate()] as [ Date, Date ], }, { id: "week", type: "week" as TimeRangeType, label: t("panel.timeRange.timeOptions.week"), icon: CalendarDays, getRange: () => [dayjs().startOf("week").toDate(), dayjs().endOf("week").toDate()] as [ Date, Date ], }, { id: "month", type: "month" as TimeRangeType, label: t("panel.timeRange.timeOptions.month"), icon: CalendarRange, getRange: () => [ dayjs().startOf("month").toDate(), dayjs().endOf("month").toDate(), ] as [Date, Date], }, { id: "30days", type: "30days" as TimeRangeType, label: t("panel.timeRange.timeOptions.30Days"), icon: CalendarClock, getRange: () => [ dayjs().subtract(29, "days").startOf("day").toDate(), dayjs().endOf("day").toDate(), ] as [Date, Date], }, { id: "all", type: "all" as TimeRangeType, label: t("panel.timeRange.timeOptions.all"), icon: CalendarCheck, getRange: () => [ dayjs(availableTimeRange.minTime).startOf("day").toDate(), dayjs(availableTimeRange.maxTime).endOf("day").toDate(), ] as [Date, Date], }, ]; const handleTimeOptionClick = (type: TimeRangeType) => { const option = timeOptions.find((opt) => opt.type === type); if (!option) return; const range = option.getRange(); setStartDate(range[0]); setEndDate(range[1]); setIsCustomOpen(false); onTimeRangeChange(range, type); }; const handleCustomButtonClick = () => { const isOpening = !isCustomOpen; setIsCustomOpen(isOpening); if (isOpening) { onTimeRangeChange([startDate, endDate], "custom"); } }; const handleDateChange = (start?: Date, end?: Date) => { if (!start || !end) return; const newStart = dayjs(start).startOf("day").toDate(); const newEnd = dayjs(end).endOf("day").toDate(); setStartDate(newStart); setEndDate(newEnd); onTimeRangeChange([newStart, newEnd], "custom"); }; const formatDate = (date?: Date) => { if (!date) return t("panel.timeRange.selectDate"); return format(date, "yyyy-MM-dd", { locale: i18n.language === "zh" ? zhCN : undefined, }); }; return (

{t("panel.timeRange.title")}

{timeOptions.map(({ id, type, label, icon: Icon }) => ( ))}
{isCustomOpen && (
{t("panel.timeRange.customRange")}
{ if (date) { handleDateChange(date, endDate); setStartOpen(false); } }} disabled={(date) => endDate ? dayjs(date).isAfter(endDate, "day") : false } initialFocus locale={i18n.language === "zh" ? zhCN : undefined} /> { if (date) { handleDateChange(startDate, date); setEndOpen(false); } }} disabled={(date) => startDate ? dayjs(date).isBefore(startDate, "day") : false } initialFocus locale={i18n.language === "zh" ? zhCN : undefined} />
)}
); }