mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 05:58:18 +00:00
train
This commit is contained in:
@@ -16,6 +16,7 @@ import { isAxiosError } from "axios";
|
||||
import {
|
||||
AlertTriangle,
|
||||
CalendarClock,
|
||||
MapPin,
|
||||
MoreHorizontal,
|
||||
Replace,
|
||||
Ruler,
|
||||
@@ -29,9 +30,10 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import AvailableWagonsPanel from "@/components/trainBuilder/AvailableWagonsPanel";
|
||||
import ChangeLocomotivesModal from "@/components/trainBuilder/ChangeLocomotivesModal";
|
||||
import ChangeYardModal from "@/components/trainBuilder/ChangeYardModal";
|
||||
import ConsistWagonList from "@/components/trainBuilder/ConsistWagonList";
|
||||
import TrainConsistStrip from "@/components/trainBuilder/TrainConsistStrip";
|
||||
import { trainStatusColor, trainStatusLabel } from "@/components/trainBuilder/trainStatus";
|
||||
import { TrainCompositionDiagram } from "@/components/trainScheduling/TrainCompositionDiagram";
|
||||
import { KpiStrip, PageContainer, PageHeader } from "@/components/page";
|
||||
import { api } from "@/services/api";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@@ -62,6 +64,7 @@ export default function TrainBuilderDetailPage() {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const [locoModalOpen, setLocoModalOpen] = useState(false);
|
||||
const [yardModalOpen, setYardModalOpen] = useState(false);
|
||||
const [disbandOpen, setDisbandOpen] = useState(false);
|
||||
|
||||
const compositionQuery = useQuery(
|
||||
@@ -144,6 +147,13 @@ export default function TrainBuilderDetailPage() {
|
||||
>
|
||||
Change locomotives
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
leftSection={<MapPin size={15} />}
|
||||
disabled={!composition.editable}
|
||||
onClick={() => setYardModalOpen(true)}
|
||||
>
|
||||
Change yard
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
color="red"
|
||||
leftSection={<Trash2 size={15} />}
|
||||
@@ -162,8 +172,8 @@ export default function TrainBuilderDetailPage() {
|
||||
{ label: "Locomotives", value: composition.locomotives.length, icon: TrainFront },
|
||||
{ label: "Wagons", value: totals.wagonCount, icon: TrainIcon },
|
||||
{
|
||||
label: "Max gross / haul limit",
|
||||
value: `${totals.maxGrossTons}T / ${totals.maxPullWeightTons}T`,
|
||||
label: "Payload available",
|
||||
value: `${totals.payloadCapacityTons}T of ${totals.maxPullWeightTons}T`,
|
||||
icon: Weight,
|
||||
},
|
||||
{
|
||||
@@ -180,33 +190,40 @@ export default function TrainBuilderDetailPage() {
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<Card>
|
||||
<Stack gap="md">
|
||||
<Group justify="space-between" align="center">
|
||||
<Text fw={600}>Consist</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{composition.locomotives.length} locomotive
|
||||
{composition.locomotives.length === 1 ? "" : "s"} · {totals.wagonCount} wagon
|
||||
{totals.wagonCount === 1 ? "" : "s"}
|
||||
</Text>
|
||||
</Group>
|
||||
<TrainConsistStrip
|
||||
locomotives={composition.locomotives}
|
||||
wagons={composition.wagons}
|
||||
/>
|
||||
<Stack gap="sm">
|
||||
<TrainCompositionDiagram
|
||||
locomotives={composition.locomotives.map((loco) => ({
|
||||
code: loco.code,
|
||||
name: loco.name,
|
||||
maxPullWeightTons: loco.maxPullWeightTons,
|
||||
}))}
|
||||
wagons={composition.wagons.map((wagon, index) => ({
|
||||
sequenceNo: wagon.sequenceNumber ?? index + 1,
|
||||
capacityTons: wagon.wagonType?.capacityTons ?? 0,
|
||||
// No bookings at build time — wagons ride empty until allocation.
|
||||
assignedWeightTons: 0,
|
||||
tareWeightTons: wagon.wagonType?.tareWeightTons ?? 0,
|
||||
wagonTypeCode: wagon.wagonType?.code ?? null,
|
||||
physicalWagonNumber: wagon.wagonNumber,
|
||||
allocations: [],
|
||||
}))}
|
||||
trainNumber={composition.code}
|
||||
totalLengthMeters={totals.totalLengthMeters}
|
||||
/>
|
||||
<Card>
|
||||
<Grid gap="lg">
|
||||
<Grid.Col span={{ base: 12, sm: 6 }}>
|
||||
<UtilizationBar
|
||||
label="Weight utilization (fully loaded)"
|
||||
pct={totals.weightUtilizationPct}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, sm: 6 }}>
|
||||
<UtilizationBar label="Length utilization" pct={totals.lengthUtilizationPct} />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={{ base: 12, sm: 6 }}>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
The real weight check happens at allocation: booked cargo weight plus
|
||||
wagon tare (gross) must stay within the locomotives' haul limit.
|
||||
</Text>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Card>
|
||||
</Stack>
|
||||
|
||||
<Grid gap="lg" align="stretch">
|
||||
{composition.editable ? (
|
||||
@@ -297,6 +314,12 @@ export default function TrainBuilderDetailPage() {
|
||||
onClose={() => setLocoModalOpen(false)}
|
||||
/>
|
||||
|
||||
<ChangeYardModal
|
||||
composition={composition}
|
||||
opened={yardModalOpen}
|
||||
onClose={() => setYardModalOpen(false)}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
opened={disbandOpen}
|
||||
onClose={() => setDisbandOpen(false)}
|
||||
|
||||
@@ -183,7 +183,7 @@ export default function TrainBuilderListPage() {
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => (
|
||||
<Text size="sm">
|
||||
{row.original.wagonCount} wagons · {row.original.maxGrossTons}T ·{" "}
|
||||
{row.original.wagonCount} wagons · {row.original.totalTareTons}T tare ·{" "}
|
||||
{row.original.totalLengthMeters}m
|
||||
</Text>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user