import { Button, Group, Modal, Stack, Text, Textarea } from "@mantine/core"; import { DateTimePicker } from "@mantine/dates"; import { useMediaQuery } from "@mantine/hooks"; import { useEffect, useState } from "react"; import type { CheckpointHandlingTimes } from "@/types/trainScheduling"; /** The four station-work stamps, in the order they happen. */ const HANDLING_FIELDS = [ ["unloadingStartedAt", "Unloading started"], ["unloadingCompletedAt", "Unloading finished"], ["loadingStartedAt", "Loading started"], ["loadingCompletedAt", "Loading finished"], ] as const; type HandlingField = (typeof HANDLING_FIELDS)[number][0]; type HandlingState = Record; const EMPTY_HANDLING: HandlingState = { unloadingStartedAt: null, unloadingCompletedAt: null, loadingStartedAt: null, loadingCompletedAt: null, }; /** * Time, station work and note for one leg of a train's journey — used both to * log a pass (defaults to now) and to correct an already-logged leg * (prefilled). Past times are allowed (staff record after the fact); the future * is not, and the server additionally keeps legs in corridor order. * * The four handling stamps are what the loading-and-unloading reports measure: * total handling is unloading start to loading end, and whatever is left of the * stay is other activity. All four are optional — a stop logged without them * still reports its staying time. */ export function CheckpointTimeModal({ opened, onClose, title, icon, description, initialOccurredAt, initialNote, initialHandling, submitLabel, submitColor = "edr-green", loading, onSubmit, }: { opened: boolean; onClose: () => void; title: string; icon?: React.ReactNode; description?: string; /** ISO; omit to default to now. */ initialOccurredAt?: string | null; initialNote?: string | null; initialHandling?: CheckpointHandlingTimes | null; submitLabel: string; submitColor?: string; loading: boolean; onSubmit: ( values: { occurredAt: string; note: string } & Record, ) => void; }) { const isSmallScreen = useMediaQuery("(max-width: 48em)"); const [at, setAt] = useState(null); const [note, setNote] = useState(""); // The four station-work stamps are no longer edited HERE — the track page's // "Loading & unloading windows" section owns start/end with its own // permissions. The modal still carries any existing stamps through // unchanged on submit, so editing a checkpoint never wipes them. const [handling, setHandling] = useState(EMPTY_HANDLING); useEffect(() => { if (!opened) return; setAt(initialOccurredAt ? new Date(initialOccurredAt) : new Date()); setNote(initialNote ?? ""); setHandling({ unloadingStartedAt: initialHandling?.unloadingStartedAt ? new Date(initialHandling.unloadingStartedAt) : null, unloadingCompletedAt: initialHandling?.unloadingCompletedAt ? new Date(initialHandling.unloadingCompletedAt) : null, loadingStartedAt: initialHandling?.loadingStartedAt ? new Date(initialHandling.loadingStartedAt) : null, loadingCompletedAt: initialHandling?.loadingCompletedAt ? new Date(initialHandling.loadingCompletedAt) : null, }); }, [opened, initialOccurredAt, initialNote, initialHandling]); return ( {icon} {title} } > {description ? ( {description} ) : null} setAt(v ? new Date(v) : null)} maxDate={new Date()} dropdownType={isSmallScreen ? "modal" : "popover"} popoverProps={{ withinPortal: true }} valueFormat="DD MMM YYYY HH:mm" clearable={false} radius="md" />