Add booking window management and locomotive scheduling features

- Implemented migration to release stuck assigned locomotives.
- Added schedule window phases to train schedules.
- Created booking batch offers table for partial capacity bookings.
- Developed BookingSplitService to handle partial booking offers and splits.
- Introduced BookingWindowService to manage booking window lifecycle and transitions.
- Added BookingBatchOffer entity to represent offers made during booking splits.
- Enhanced locomotive options with warnings for scheduling.
- Created UpcomingWindowsSection component to display upcoming booking windows.
This commit is contained in:
Marshal
2026-07-03 03:36:06 +00:00
parent 18c158fb61
commit 56de90892d
41 changed files with 2480 additions and 124 deletions

View File

@@ -1,6 +1,8 @@
import type { ReactNode } from "react";
import { Box, Group, Progress, Text, Tooltip } from "@mantine/core";
import type { BookingWindowPhase } from "@/types/trainScheduling";
import "./batchVisuals.css";
/**
@@ -213,3 +215,69 @@ export function HeroChip({
</Group>
);
}
const PHASE_META: Record<
BookingWindowPhase,
{ color: string; label: string; pulse: boolean }
> = {
PRE_WINDOW: { color: "gray", label: "Pre-window", pulse: false },
OPEN: { color: "edr-green", label: "Booking open", pulse: true },
DOC_REVIEW: { color: "yellow", label: "Doc review", pulse: true },
PAYMENT: { color: "blue", label: "Payment", pulse: true },
CLOSED_FOR_DAY: { color: "dark", label: "Closed for day", pulse: false },
DONE: { color: "dark", label: "Done", pulse: false },
};
/**
* Import booking-cycle phase pill (OPEN → DOC_REVIEW → PAYMENT → …) with an
* optional cycle number. Same visual language as `WindowStatusPill`.
*/
export function WindowPhasePill({
phase,
cycleNo,
size = "md",
}: {
phase: BookingWindowPhase;
cycleNo?: number;
size?: "sm" | "md";
}) {
const meta = PHASE_META[phase] ?? {
color: "gray",
label: phase,
pulse: false,
};
const compact = size === "sm";
return (
<Group
gap={6}
wrap="nowrap"
style={{
display: "inline-flex",
padding: compact ? "2px 8px" : "4px 11px",
borderRadius: 999,
background: `var(--mantine-color-${meta.color}-0)`,
border: `1px solid var(--mantine-color-${meta.color}-2)`,
}}
>
<Box
w={compact ? 6 : 7}
h={compact ? 6 : 7}
className={meta.pulse ? "bb-pulse-dot" : undefined}
style={{
borderRadius: 999,
flexShrink: 0,
background: `var(--mantine-color-${meta.color}-6)`,
}}
/>
<Text
size="xs"
fw={700}
c={`${meta.color}.8`}
style={{ letterSpacing: 0.3, lineHeight: 1, whiteSpace: "nowrap" }}
>
{meta.label}
{cycleNo && cycleNo > 1 ? ` · cycle ${cycleNo}` : ""}
</Text>
</Group>
);
}