/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { useMemo, useState } from 'react' import { CalendarDays } from 'lucide-react' import { useTranslation } from 'react-i18next' import dayjs from '@/lib/dayjs' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' interface CompactDateTimeRangePickerProps { start?: Date end?: Date onChange: (range: { start?: Date; end?: Date }) => void className?: string } function toInputValue(date?: Date): string { return date ? dayjs(date).format('YYYY-MM-DDTHH:mm') : '' } function fromInputValue(value: string): Date | undefined { if (!value) return undefined const date = new Date(value) return Number.isNaN(date.getTime()) ? undefined : date } export function CompactDateTimeRangePicker({ start, end, onChange, className, }: CompactDateTimeRangePickerProps) { const { t } = useTranslation() const [open, setOpen] = useState(false) const [draftStart, setDraftStart] = useState(toInputValue(start)) const [draftEnd, setDraftEnd] = useState(toInputValue(end)) const label = useMemo(() => { if (!start && !end) return t('Date Range') // The popover's only supports minute // precision, so seconds are always 00 (manual pick) or 59 (preset // end-of-day). Hide them in the trigger label to keep the button // width compact while still showing the meaningful timestamp. const startText = start ? dayjs(start).format('YYYY-MM-DD HH:mm') : '-' const endText = end ? dayjs(end).format('YYYY-MM-DD HH:mm') : '-' return `${startText} ~ ${endText}` }, [end, start, t]) const handleOpenChange = (nextOpen: boolean) => { if (nextOpen) { setDraftStart(toInputValue(start)) setDraftEnd(toInputValue(end)) } setOpen(nextOpen) } const applyDraft = () => { onChange({ start: fromInputValue(draftStart), end: fromInputValue(draftEnd), }) setOpen(false) } const applyPreset = (kind: 'today' | '7d' | 'week' | '30d' | 'month') => { const now = dayjs() const presets = { today: { start: now.startOf('day').toDate(), end: now.endOf('day').toDate(), }, '7d': { start: now.subtract(6, 'day').startOf('day').toDate(), end: now.endOf('day').toDate(), }, week: { start: now.startOf('week').toDate(), end: now.endOf('week').toDate(), }, '30d': { start: now.subtract(29, 'day').startOf('day').toDate(), end: now.endOf('day').toDate(), }, month: { start: now.startOf('month').toDate(), end: now.endOf('month').toDate(), }, } const range = presets[kind] setDraftStart(toInputValue(range.start)) setDraftEnd(toInputValue(range.end)) onChange(range) setOpen(false) } return ( } > {label}
{t('Start Time')}
setDraftStart(e.target.value)} className='h-8 text-sm leading-5 tabular-nums' />
~
{t('End Time')}
setDraftEnd(e.target.value)} className='h-8 text-sm leading-5 tabular-nums' />
) }