fix(train-scheduling): move loose wagons and locomotives on train merge

This commit is contained in:
Marshal
2026-08-12 11:23:10 +00:00
parent 3dbda745dd
commit f882104ed3
4 changed files with 123 additions and 45 deletions

View File

@@ -1019,14 +1019,11 @@ export default function TrainScheduleV2ListPage() {
/**
* The row's wagon chips, matching the detail page's wagon plan: used is slots
* carrying a booking allocation (never the coupled consist size), and remaining
* excludes wagons reserved by bookings that have not paid yet — that space is
* claimed, so it is not bookable.
*
* Schedules whose train set has not been built yet have no consist to measure,
* so both figures fall back to the schedule's planned `maxWagons` ceiling.
* Without that fallback an unbuilt 37-wagon schedule reads "0 bookable" even
* though every one of its wagons is still free.
* carrying a booking allocation, the denominator is the schedule's capacity
* (API-computed: the larger of coupled consist and planned `maxWagons`, since
* wagons are coupled on demand), and remaining excludes wagons reserved by
* bookings that have not paid yet — that space is claimed, so it is not
* bookable.
*/
function WagonChips({ schedule }: { schedule: TrainScheduleListItem }) {
// Pre-deploy API rows carry only wagonCount; fall back so the chip still
@@ -1034,17 +1031,7 @@ function WagonChips({ schedule }: { schedule: TrainScheduleListItem }) {
const total = schedule.wagonsTotal ?? schedule.wagonCount;
const used = schedule.wagonsUsed;
const reserved = schedule.wagonsReserved ?? 0;
// Until the train set is built there is no consist to measure against, so
// `wagonsRemaining` (consist minus claimed) is 0 on every unbuilt schedule —
// which reads as "fully booked" when in fact nothing is booked at all. Before
// a consist exists, capacity is the planned ceiling minus what bookings have
// already claimed.
const planCeiling = schedule.maxWagons ?? 0;
const remaining =
total === 0 && planCeiling > 0
? Math.max(0, planCeiling - Math.max(used ?? 0, reserved))
: schedule.wagonsRemaining;
const remaining = schedule.wagonsRemaining;
if (used == null) {
return <MetricChip value={total} label="wgn" subtle />;
@@ -1052,11 +1039,9 @@ function WagonChips({ schedule }: { schedule: TrainScheduleListItem }) {
return (
<>
{/* An unbuilt consist has no "used out of coupled" to show; the plan
ceiling is the only meaningful denominator at that point. */}
<MetricChip
value={total === 0 && planCeiling > 0 ? `${used}/${planCeiling}` : `${used}/${total}`}
label={total === 0 && planCeiling > 0 ? "wgn planned" : "wgn used"}
value={`${used}/${total}`}
label={schedule.wagonCount === 0 ? "wgn planned" : "wgn used"}
/>
{reserved > used ? (
<MetricChip value={reserved} label="reserved" subtle />