| 15 | const DISPLAY_FORMAT = "YYYY-MM-DD HH:mm"; |
| 16 | |
| 17 | export function TimeRangePicker({ |
| 18 | params, |
| 19 | onParamsChange, |
| 20 | }: TimeRangePickerProps) { |
| 21 | const { t } = useTranslation(); |
| 22 | const [open, setOpen] = useState(false); |
| 23 | |
| 24 | const createdScope = params.scopes.find((s) => s.id === "created"); |
| 25 | const [fromTs, toTs] = useMemo(() => { |
| 26 | if (!createdScope) return [undefined, undefined]; |
| 27 | const parts = createdScope.value.split(","); |
| 28 | if (parts.length !== 2) return [undefined, undefined]; |
| 29 | return [parseInt(parts[0], 10), parseInt(parts[1], 10)]; |
| 30 | }, [createdScope]); |
| 31 | |
| 32 | const hasRange = fromTs !== undefined && toTs !== undefined; |
| 33 | |
| 34 | // Local draft state for the pickers inside the dropdown. |
| 35 | const [draftFrom, setDraftFrom] = useState<Date | undefined>(undefined); |
| 36 | const [draftTo, setDraftTo] = useState<Date | undefined>(undefined); |
| 37 | |
| 38 | // Sync drafts when the external range changes. |
| 39 | useEffect(() => { |
| 40 | setDraftFrom(fromTs ? new Date(fromTs) : undefined); |
| 41 | setDraftTo(toTs ? new Date(toTs) : undefined); |
| 42 | }, [fromTs, toTs]); |
| 43 | |
| 44 | const displayFrom = fromTs ? dayjs(fromTs).format(DISPLAY_FORMAT) : ""; |
| 45 | const displayTo = toTs ? dayjs(toTs).format(DISPLAY_FORMAT) : ""; |
| 46 | |
| 47 | const applyRange = useCallback(() => { |
| 48 | const fromVal = draftFrom ? draftFrom.getTime() : undefined; |
| 49 | const toVal = draftTo ? draftTo.getTime() : undefined; |
| 50 | const scopes = params.scopes.filter((s) => s.id !== "created"); |
| 51 | if (fromVal !== undefined && toVal !== undefined) { |
| 52 | scopes.push({ |
| 53 | id: "created", |
| 54 | value: `${fromVal},${toVal}`, |
| 55 | readonly: true, |
| 56 | }); |
| 57 | } |
| 58 | onParamsChange({ ...params, scopes }); |
| 59 | setOpen(false); |
| 60 | }, [draftFrom, draftTo, params, onParamsChange]); |
| 61 | |
| 62 | const clearRange = useCallback(() => { |
| 63 | const scopes = params.scopes.filter((s) => s.id !== "created"); |
| 64 | onParamsChange({ ...params, scopes }); |
| 65 | setDraftFrom(undefined); |
| 66 | setDraftTo(undefined); |
| 67 | setOpen(false); |
| 68 | }, [params, onParamsChange]); |
| 69 | |
| 70 | return ( |
| 71 | <Popover open={open} onOpenChange={setOpen}> |
| 72 | <PopoverTrigger className="h-9 flex items-center gap-x-2 border border-control-border rounded-xs px-3 text-sm hover:bg-control-bg whitespace-nowrap shrink-0"> |
| 73 | {displayFrom && displayTo ? ( |
| 74 | <> |