mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge pull request #1375 from Tria-plc/freight_feature/usermanagement
feat(train-scheduling): add showWagonStat prop to control wagon stats…
This commit is contained in:
@@ -18,6 +18,8 @@ interface TrainConsistViewProps {
|
|||||||
scheduleDetail: TrainScheduleDetail;
|
scheduleDetail: TrainScheduleDetail;
|
||||||
scheduleId: string;
|
scheduleId: string;
|
||||||
maxWagons: number;
|
maxWagons: number;
|
||||||
|
/** Hide the consist-wide Wagons stat tile (dispatch shows leg capacity instead). */
|
||||||
|
showWagonStat?: boolean;
|
||||||
/** Booking id selected in the side panel — highlights its wagons in the consist. */
|
/** Booking id selected in the side panel — highlights its wagons in the consist. */
|
||||||
highlightBookingId?: string | null;
|
highlightBookingId?: string | null;
|
||||||
}
|
}
|
||||||
@@ -45,6 +47,7 @@ export const TrainConsistView = ({
|
|||||||
scheduleDetail,
|
scheduleDetail,
|
||||||
scheduleId,
|
scheduleId,
|
||||||
maxWagons,
|
maxWagons,
|
||||||
|
showWagonStat = true,
|
||||||
highlightBookingId,
|
highlightBookingId,
|
||||||
}: TrainConsistViewProps) => {
|
}: TrainConsistViewProps) => {
|
||||||
const [selectedWagonId, setSelectedWagonId] = useState<string | null>(null);
|
const [selectedWagonId, setSelectedWagonId] = useState<string | null>(null);
|
||||||
@@ -172,6 +175,7 @@ export const TrainConsistView = ({
|
|||||||
lengthMax={lengthMax}
|
lengthMax={lengthMax}
|
||||||
wagonCount={wagonsUsed}
|
wagonCount={wagonsUsed}
|
||||||
wagonMax={maxWagons}
|
wagonMax={maxWagons}
|
||||||
|
showWagons={showWagonStat}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Consist panel */}
|
{/* Consist panel */}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ interface TrainStatsBarProps {
|
|||||||
lengthMax: number | null;
|
lengthMax: number | null;
|
||||||
wagonCount: number;
|
wagonCount: number;
|
||||||
wagonMax: number;
|
wagonMax: number;
|
||||||
|
/**
|
||||||
|
* The wagon tile is a consist-wide count, which reads as wrong on a
|
||||||
|
* multi-leg schedule where per-leg capacity is the real number. Dispatch
|
||||||
|
* hides it (leg capacity is shown there instead); the batch board keeps it.
|
||||||
|
*/
|
||||||
|
showWagons?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pctColor(pct: number) {
|
function pctColor(pct: number) {
|
||||||
@@ -83,6 +89,7 @@ export const TrainStatsBar = ({
|
|||||||
lengthMax,
|
lengthMax,
|
||||||
wagonCount,
|
wagonCount,
|
||||||
wagonMax,
|
wagonMax,
|
||||||
|
showWagons = true,
|
||||||
}: TrainStatsBarProps) => {
|
}: TrainStatsBarProps) => {
|
||||||
const weightPct = weightMax ? (weightUsed / weightMax) * 100 : null;
|
const weightPct = weightMax ? (weightUsed / weightMax) * 100 : null;
|
||||||
const lengthPct = lengthMax ? (lengthUsed / lengthMax) * 100 : null;
|
const lengthPct = lengthMax ? (lengthUsed / lengthMax) * 100 : null;
|
||||||
@@ -95,7 +102,7 @@ export const TrainStatsBar = ({
|
|||||||
withBorder
|
withBorder
|
||||||
style={{ borderColor: "var(--mantine-color-gray-2)", background: "white" }}
|
style={{ borderColor: "var(--mantine-color-gray-2)", background: "white" }}
|
||||||
>
|
>
|
||||||
<SimpleGrid cols={{ base: 1, xs: 3 }} spacing="lg">
|
<SimpleGrid cols={{ base: 1, xs: showWagons ? 3 : 2 }} spacing="lg">
|
||||||
<StatTile
|
<StatTile
|
||||||
icon={<Weight size={15} />}
|
icon={<Weight size={15} />}
|
||||||
label="Gross weight"
|
label="Gross weight"
|
||||||
@@ -108,7 +115,7 @@ export const TrainStatsBar = ({
|
|||||||
px={{ base: 0, xs: "lg" }}
|
px={{ base: 0, xs: "lg" }}
|
||||||
style={{
|
style={{
|
||||||
borderLeft: "1px solid var(--mantine-color-gray-2)",
|
borderLeft: "1px solid var(--mantine-color-gray-2)",
|
||||||
borderRight: "1px solid var(--mantine-color-gray-2)",
|
borderRight: showWagons ? "1px solid var(--mantine-color-gray-2)" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StatTile
|
<StatTile
|
||||||
@@ -120,14 +127,16 @@ export const TrainStatsBar = ({
|
|||||||
unit="m"
|
unit="m"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<StatTile
|
{showWagons ? (
|
||||||
icon={<Train size={15} />}
|
<StatTile
|
||||||
label="Wagons"
|
icon={<Train size={15} />}
|
||||||
pct={wagonPct}
|
label="Wagons"
|
||||||
current={String(wagonCount)}
|
pct={wagonPct}
|
||||||
max={String(wagonMax)}
|
current={String(wagonCount)}
|
||||||
unit=""
|
max={String(wagonMax)}
|
||||||
/>
|
unit=""
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -848,6 +848,7 @@ export default function TrainScheduleV2DetailPage() {
|
|||||||
scheduleDetail={schedule}
|
scheduleDetail={schedule}
|
||||||
scheduleId={scheduleId ?? ""}
|
scheduleId={scheduleId ?? ""}
|
||||||
maxWagons={schedule.maxWagons ?? 53}
|
maxWagons={schedule.maxWagons ?? 53}
|
||||||
|
showWagonStat={false}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TrainCompositionDiagram
|
<TrainCompositionDiagram
|
||||||
|
|||||||
@@ -330,17 +330,6 @@ export default function TrainScheduleV2ListPage() {
|
|||||||
meta: { headerClassName, cellClassName },
|
meta: { headerClassName, cellClassName },
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Stack gap={6}>
|
<Stack gap={6}>
|
||||||
<Group gap={6} wrap="nowrap">
|
|
||||||
<Text size="sm" fw={600} lh={1.2}>
|
|
||||||
{row.original.routeName ?? "—"}
|
|
||||||
</Text>
|
|
||||||
{row.original.direction ? (
|
|
||||||
<Badge size="xs" variant="light" color={directionColor(row.original.direction)}>
|
|
||||||
{row.original.direction}
|
|
||||||
</Badge>
|
|
||||||
) : null}
|
|
||||||
<ShippingLineBadge schedule={row.original} />
|
|
||||||
</Group>
|
|
||||||
<Box maw={260}>
|
<Box maw={260}>
|
||||||
<RouteCorridor
|
<RouteCorridor
|
||||||
origin={row.original.origin}
|
origin={row.original.origin}
|
||||||
@@ -349,6 +338,14 @@ export default function TrainScheduleV2ListPage() {
|
|||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Group gap={6} wrap="nowrap">
|
||||||
|
{row.original.direction ? (
|
||||||
|
<Badge size="xs" variant="light" color={directionColor(row.original.direction)}>
|
||||||
|
{row.original.direction}
|
||||||
|
</Badge>
|
||||||
|
) : null}
|
||||||
|
<ShippingLineBadge schedule={row.original} />
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -888,12 +885,12 @@ export default function TrainScheduleV2ListPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The row's wagon chips, matching the detail page's wagon plan: used is slots
|
* The row's wagon chips: used is slots carrying a booking allocation, the
|
||||||
* carrying a booking allocation, the denominator is the schedule's capacity
|
* denominator is the schedule's capacity (API-computed: the larger of coupled
|
||||||
* (API-computed: the larger of coupled consist and planned `maxWagons`, since
|
* consist and planned `maxWagons`, since wagons are coupled on demand). Both
|
||||||
* wagons are coupled on demand), and remaining excludes wagons reserved by
|
* are consist-wide totals, so on a multi-leg schedule they do not describe any
|
||||||
* bookings that have not paid yet — that space is claimed, so it is not
|
* single leg — the bookable/planned counts were dropped for that reason; the
|
||||||
* bookable.
|
* detail page's wagon plan is the per-leg source of truth.
|
||||||
*/
|
*/
|
||||||
/** Green tint for departures dedicated to a shipping line (overrides direction tint). */
|
/** Green tint for departures dedicated to a shipping line (overrides direction tint). */
|
||||||
const SHIPPING_LINE_ROW_STYLE = {
|
const SHIPPING_LINE_ROW_STYLE = {
|
||||||
@@ -965,20 +962,15 @@ function WagonChips({ schedule }: { schedule: TrainScheduleListItem }) {
|
|||||||
const total = schedule.wagonsTotal ?? schedule.wagonCount;
|
const total = schedule.wagonsTotal ?? schedule.wagonCount;
|
||||||
const used = schedule.wagonsUsed;
|
const used = schedule.wagonsUsed;
|
||||||
const reserved = schedule.wagonsReserved ?? 0;
|
const reserved = schedule.wagonsReserved ?? 0;
|
||||||
const remaining = schedule.wagonsRemaining;
|
|
||||||
|
|
||||||
if (used == null) {
|
if (used == null || schedule.wagonCount === 0) {
|
||||||
return <MetricChip value={total} label="wgn" subtle />;
|
return <MetricChip value={total} label="wgn" subtle />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MetricChip
|
<MetricChip value={`${used}/${total}`} label="wgn used" />
|
||||||
value={`${used}/${total}`}
|
|
||||||
label={schedule.wagonCount === 0 ? "wgn planned" : "wgn used"}
|
|
||||||
/>
|
|
||||||
{reserved > used ? <MetricChip value={reserved} label="reserved" subtle /> : null}
|
{reserved > used ? <MetricChip value={reserved} label="reserved" subtle /> : null}
|
||||||
{remaining != null ? <MetricChip value={remaining} label="bookable" subtle /> : null}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1049,9 +1041,6 @@ function ScheduleCard({
|
|||||||
{schedule.reference}
|
{schedule.reference}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
<Text fw={600} size="sm" lineClamp={1}>
|
|
||||||
{schedule.routeName ?? "Train schedule"}
|
|
||||||
</Text>
|
|
||||||
</Group>
|
</Group>
|
||||||
<Text size="xs" c="dimmed">
|
<Text size="xs" c="dimmed">
|
||||||
{day} · {time}
|
{day} · {time}
|
||||||
|
|||||||
Reference in New Issue
Block a user