import { useMemo, useState } from "react"; import { Badge, Button, Group, Modal, Paper, Select, Stack, Table, Text, ThemeIcon, } from "@mantine/core"; import { CheckCircle2, Layers, Lock, LockOpen, PlayCircle, Repeat, XCircle } from "lucide-react"; import { useMutation, useQuery } from "@tanstack/react-query"; import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge"; import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { TrainScheduleDetail } from "@/types/trainScheduling"; import { useAuth } from "@/auth/useAuth"; import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions"; interface ScheduleBatchPanelProps { schedule: TrainScheduleDetail; } const windowColor: Record = { OPEN: "edr-green", FULL: "orange", CLOSED: "gray", }; export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) { const { user } = useAuth(); const canMarkPaid = hasPermission(user, FREIGHT_PERMS.trainScheduling.markPaid); const { toast } = useToast(); const actions = { runBatch: useMutation(api.trainScheduling.runBatch.mutationOptions()), setWindow: useMutation(api.trainScheduling.setBookingWindow.mutationOptions()), markPaid: useMutation(api.trainScheduling.markBookingPaid.mutationOptions()), expire: useMutation(api.trainScheduling.expireBooking.mutationOptions()), moveSchedule: useMutation( api.trainScheduling.moveBookingSchedule.mutationOptions(), ), }; const windowStatus = (schedule as { bookingWindowStatus?: string }).bookingWindowStatus ?? "OPEN"; const locked = schedule.status === "DISPATCHED" || schedule.status === "ARRIVED"; const [moveBookingId, setMoveBookingId] = useState(null); const [moveTarget, setMoveTarget] = useState(null); const { data: targets } = useQuery( api.trainScheduling.bookableSchedules.queryOptions({ input: { originYardId: schedule.originStation?.id, destinationYardId: schedule.destinationStation?.id, }, enabled: Boolean( schedule.originStation?.id && schedule.destinationStation?.id, ), }), ); const moveOptions = useMemo( () => (targets ?? []) .filter((s) => s.id !== schedule.id) .map((s) => ({ value: s.id, label: `${s.routeName ?? `${s.origin} → ${s.destination}`} · ${new Date( s.scheduleDate, ).toLocaleString()} · ${s.remainingWagons}/${s.maxWagons} free`, })), [targets, schedule.id], ); const bookings = schedule.bookings ?? []; const run = (fn: Promise, ok: string) => fn .then(() => toast({ title: ok })) .catch(() => toast({ title: "Action failed", variant: "destructive" })); return (
Batch allocation {bookings.length} allocated · {schedule.trainSet?.wagonCount ?? 0} wagons used
Window: {windowStatus}
{!locked && ( {windowStatus === "CLOSED" ? ( ) : ( )} )} {bookings.length === 0 ? ( No bookings allocated yet. The batch cron fills this schedule by priority; paid bookings are assigned automatically. ) : ( Booking Customer Status Actions {bookings.map((b) => ( {b.reference ?? b.id.slice(0, 8)} {b.customer ?? "—"} {!locked && ( {canMarkPaid && b.status !== "PAID" && ( )} )} ))}
)} setMoveBookingId(null)} title="Move booking to another schedule" centered radius="lg" >