import { useEffect, useMemo, useState } from "react"; import { Alert, Badge, Box, Button, Group, Loader, Modal, Select, Stack, Text, Textarea, ThemeIcon, } from "@mantine/core"; import { DateInput } from "@mantine/dates"; import { CalendarClock, Info } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { api } from "@/services/api"; import { useBookingDetail, useBookingMutations, } from "@/hooks/bookings/useBookings"; export interface OperationRescheduleModalProps { bookingId: string; opened: boolean; onClose: () => void; } /** * Operations moves a pending operation request to another shipment day and, * for export rail, another train — instead of returning it to the customer. * The server re-runs the customer's own gates (open departure that day, wagon * that can carry the cargo, export train with room) and refuses with the * reason if the new day does not work. */ export function OperationRescheduleModal({ bookingId, opened, onClose, }: OperationRescheduleModalProps) { const detailQuery = useBookingDetail(opened ? bookingId : undefined); const booking = detailQuery.data; const mutations = useBookingMutations(bookingId); const isExportRail = booking ? isExportRailBooking(booking) : false; const [day, setDay] = useState(null); const [trainId, setTrainId] = useState(null); const [note, setNote] = useState(""); // Seed from the booking each time the modal opens: the current day and, for // export, the train the customer picked (the detail's requested/allocated train). useEffect(() => { if (!opened || !booking) return; setDay(booking.scheduledDate ? new Date(booking.scheduledDate) : null); setTrainId(booking.trainScheduleSummary?.id ?? null); setNote(""); }, [opened, booking]); const dayKey = day ? eatDay(day) : null; const currentDayKey = booking?.scheduledDate ? eatDay(booking.scheduledDate) : null; // Days with an open departure on the booking's route — a planning hint; the // server still validates the pick. const daysQuery = useQuery({ ...api.trainScheduling.availableDays.queryOptions({ input: { originYardId: booking?.originYard?.id ?? null, destinationYardId: booking?.destinationYard?.id ?? null, }, }), enabled: opened && Boolean(booking?.originYard?.id && booking?.destinationYard?.id), }); const availableDays = useMemo( () => new Set((daysQuery.data ?? []).map((d) => eatDay(d))), [daysQuery.data], ); const dayHasDeparture = dayKey ? availableDays.has(dayKey) : false; // Export rail: the day's export trains with free space, so staff pick one. const trainsQuery = useQuery({ ...api.trainScheduling.exportTrains.queryOptions({ input: { bookingId, date: day ? day.toISOString() : "" }, }), enabled: opened && isExportRail && Boolean(day), }); const trainOptions = useMemo( () => (trainsQuery.data ?? []).map(exportTrainOption), [trainsQuery.data], ); // A train belongs to one day: changing the day drops a pick from another day. useEffect(() => { if (!isExportRail || !trainsQuery.data) return; if (trainId && !trainsQuery.data.some((t) => t.scheduleId === trainId)) { setTrainId(null); } }, [isExportRail, trainsQuery.data, trainId]); const unchanged = dayKey != null && dayKey === currentDayKey && (!isExportRail || trainId === (booking?.trainScheduleSummary?.id ?? null)); const canSave = Boolean(day) && !unchanged && (!isExportRail || Boolean(trainId)); const handleSave = () => { if (!day || !canSave) return; mutations.rescheduleOperation.mutate( { scheduledDate: day.toISOString(), ...(isExportRail && trainId ? { trainScheduleId: trainId } : {}), ...(note.trim() ? { note: note.trim() } : {}), }, { onSuccess: () => onClose() }, ); }; return ( Change train / shipment day {booking?.reference ?? "Booking"} } > {detailQuery.isLoading || !booking ? ( ) : ( }> Sets the shipment day {isExportRail ? " and the export train " : " "} for the customer, so nothing has to go back to them. The request stays under review for the normal accept, and the customer is told the new day. {!isExportRail ? " Import and domestic trains are assigned by the batch engine on the chosen day." : ""} Currently {currentDayKey ?? "no day"} {booking.trainScheduleSummary ? ( {booking.trainScheduleSummary.trainNumber ?? booking.trainScheduleSummary.reference ?? "train"} {booking.trainScheduleSummary.isRequested ? " (requested)" : ""} ) : null} setDay(v ? new Date(v) : null)} minDate={new Date()} excludeDate={ daysQuery.data && daysQuery.data.length ? (d) => !availableDays.has(eatDay(d)) : undefined } popoverProps={{ withinPortal: true }} /> {day && daysQuery.data && daysQuery.data.length && !dayHasDeparture ? ( No open departure on this route for {dayKey}. ) : null} {isExportRail ? (