"use client" import * as React from "react" import type { DateRange } from "react-day-picker" import { formatYmdForDisplay, localDateToYmd, ymdToLocalDate } from "../../lib/date-filter" import { Calendar } from "../ui/calendar" function ymdToDateRange( startYmd: string | undefined, endYmd: string | undefined, ): DateRange | undefined { const from = ymdToLocalDate(startYmd) const to = ymdToLocalDate(endYmd) if (!from && !to) return undefined return { from, to } } /** Inline range calendar for table filters — first click start, second click end; stays open. */ export function FilterDateRangeControl({ values, onChangeYmd, label, open = true, onRangeComplete, }: { values: string[] onChangeYmd: (start: string | undefined, end: string | undefined) => void label: string /** When false, calendar is hidden — sync selection when it opens again. */ open?: boolean /** Optional — e.g. close the filter pill after both dates are chosen. */ onRangeComplete?: () => void }) { const [timeZone] = React.useState(() => typeof Intl !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : undefined, ) // Committed start/end are scalars so render-time sync can compare them. // `values` is a fresh array on every render. const [startYmd, endYmd] = values const [pendingRange, setPendingRange] = React.useState( () => ymdToDateRange(startYmd, endYmd), ) const [month, setMonth] = React.useState(() => { const initial = ymdToDateRange(startYmd, endYmd) return initial?.from ?? initial?.to ?? new Date() }) // The previous `open` lives in state, not a ref, because render has to stay // pure: React can replay or discard a render, and a ref written there would // carry the "already opened" answer out of work that never committed, so the // next real open would skip its sync. Same shape as `wasCleared` below. const [wasOpen, setWasOpen] = React.useState(open) if (open !== wasOpen) { setWasOpen(open) if (open) { const next = ymdToDateRange(startYmd, endYmd) setPendingRange(next) setMonth(next?.from ?? next?.to ?? new Date()) } } const cleared = !startYmd && !endYmd const [wasCleared, setWasCleared] = React.useState(cleared) if (cleared !== wasCleared) { setWasCleared(cleared) if (cleared) setPendingRange(undefined) } if (!open) return null return (
{ setPendingRange(range) if (!range?.from && !range?.to) { onChangeYmd(undefined, undefined) return } const start = range.from ? localDateToYmd(range.from) : undefined const end = range.to ? localDateToYmd(range.to) : undefined onChangeYmd(start, end) if (range.from && range.to) { onRangeComplete?.() } }} />
) } export function formatDateRangeFilterLabel(values: string[]): string { if (!values[0] && !values[1]) return "…" if (values[0] && values[1]) { return `${formatYmdForDisplay(values[0])} to ${formatYmdForDisplay(values[1])}` } if (values[0]) return `from ${formatYmdForDisplay(values[0])}` return `until ${formatYmdForDisplay(values[1]!)}` }