import { Calendar, CalendarOff } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import Button from "../components/Button"; import DismissibleAlert from "../components/DismissibleAlert"; import FormField from "../components/FormField"; import SegmentedControl from "../components/SegmentedControl"; import { useUnsavedChanges } from "../components/UnsavedChangesGuard"; import { usePlatformLanguage } from "../i18n/LanguageContext"; import { useTemporalDataContext } from "../platform/TemporalContext"; import { normalizeTemporalDataSelection, type TemporalDataSelection, type TemporalValidityMode } from "../platform/temporal"; export default function TemporalDataMenu() { const { selection, applySelection, isDefault } = useTemporalDataContext(); const { translateText } = usePlatformLanguage(); const { requestNavigation } = useUnsavedChanges(); const [open, setOpen] = useState(false); const [draft, setDraft] = useState(selection); const [error, setError] = useState(""); const menuRef = useRef(null); useEffect(() => { function onPointerDown(event: MouseEvent) { const target = event.target as Node; if (menuRef.current && !menuRef.current.contains(target)) setOpen(false); } window.addEventListener("mousedown", onPointerDown); return () => window.removeEventListener("mousedown", onPointerDown); }, []); useEffect(() => { if (!open) setDraft(selection); }, [open, selection]); function toggleOpen() { if (!open) { setDraft(selection); setError(""); } setOpen(!open); } function selectValidityMode(validityMode: TemporalValidityMode) { setDraft((current) => ({ ...current, validityMode, validAt: validityMode === "at" ? current.validAt ?? new Date().toISOString() : null })); } function selectRecordedMode(mode: "latest" | "at") { setDraft((current) => ({ ...current, recordedAt: mode === "at" ? current.recordedAt ?? new Date().toISOString() : null })); } function apply() { try { const normalized = normalizeTemporalDataSelection(draft); requestNavigation(() => { applySelection(normalized); setOpen(false); setError(""); }); } catch (caught) { setError( caught instanceof Error ? caught.message : "i18n:govoplan-core.temporal_selection_invalid" ); } } const iconTitle = selection.validityMode === "all" ? "i18n:govoplan-core.all_validity_periods" : selection.validityMode === "at" ? "i18n:govoplan-core.historical_data_state" : selection.recordedAt ? "i18n:govoplan-core.historical_recorded_state" : "i18n:govoplan-core.current_data"; const Icon = selection.validityMode === "all" ? CalendarOff : Calendar; const validDateMissing = draft.validityMode === "at" && !draft.validAt; return (
{open && (
i18n:govoplan-core.data_state
className="temporal-validity-control" value={draft.validityMode} width="fill" size="equal" ariaLabel={translateText("i18n:govoplan-core.validity")} onChange={selectValidityMode} options={[ { id: "current", label: "i18n:govoplan-core.current" }, { id: "at", label: "i18n:govoplan-core.at_time" }, { id: "all", label: "i18n:govoplan-core.all" } ]} /> {draft.validityMode === "at" && ( setDraft((current) => ({ ...current, validAt: fromLocalInput(event.target.value) }))} /> )}
{draft.recordedAt && ( setDraft((current) => ({ ...current, recordedAt: fromLocalInput(event.target.value) }))} /> )}

i18n:govoplan-core.temporal_data_explanation

{error && ( {error} )}
)}
); } function toLocalInput(value: string | null): string { if (!value) return ""; const instant = new Date(value); if (!Number.isFinite(instant.getTime())) return ""; const local = new Date(instant.getTime() - instant.getTimezoneOffset() * 60_000); return local.toISOString().slice(0, 16); } function fromLocalInput(value: string): string | null { if (!value) return null; const instant = new Date(value); return Number.isFinite(instant.getTime()) ? instant.toISOString() : null; }