add dispute functionality for contract duty and implement collection dates

This commit is contained in:
Marshal
2026-07-26 17:30:00 +00:00
parent 5e10c97294
commit bfea9de660
4 changed files with 185 additions and 30 deletions

View File

@@ -5,6 +5,7 @@ import {
Container as ContainerIcon,
Fuel,
Gauge,
GripVertical,
Package,
TrainFront,
Weight,
@@ -202,11 +203,16 @@ function WagonCar({
const containerNumbers = items.map((c) => c.containerNumber?.trim() || "—");
// The whole load drags as one unit (a 20ft pair never splits). Any OTHER
// wagon is a drop target: empty → move (a consist-only wagon repins), loaded
// → the two loads swap. The API validates wagon type + payload weight.
// wagon is a drop target: empty → the load moves onto it, loaded → the two
// loads swap. Either way the wagons stay exactly where they are coupled —
// only the cargo changes wagon. The API validates type + payload weight.
const draggable = canRearrange && !isEmpty;
const beingDragged = drag?.sourceWagonId === wagon.id;
const dropEligible = Boolean(drag && !beingDragged);
// Say which of the two it will be BEFORE the drop — a swap displaces this
// wagon's own load, so it should never come as a surprise.
const dropIntent = dropEligible ? (isEmpty ? "move" : "swap") : null;
const dropColor = dropIntent === "swap" ? "orange" : "teal";
const endDrag = () => {
onDragChange(null);
setDropHover(false);
@@ -258,9 +264,9 @@ function WagonCar({
? "none"
: "0 3px 10px rgba(15,41,27,0.08)",
outline: dropHover
? "2px solid var(--mantine-color-cyan-6)"
? `2px solid var(--mantine-color-${dropColor}-6)`
: dropEligible
? "2px dashed var(--mantine-color-cyan-4)"
? `2px dashed var(--mantine-color-${dropColor}-4)`
: "none",
outlineOffset: 2,
overflow: "hidden",
@@ -269,6 +275,35 @@ function WagonCar({
transition: "box-shadow 120ms ease, outline-color 120ms ease",
}}
>
{/* Drop intent — states the outcome while the load hovers here, so
a swap is never mistaken for dropping onto a free wagon. */}
{dropHover && dropIntent ? (
<Box
style={{
position: "absolute",
inset: 0,
zIndex: 2,
display: "flex",
alignItems: "center",
justifyContent: "center",
background:
dropIntent === "swap"
? "rgba(253,126,20,0.14)"
: "rgba(18,184,134,0.14)",
pointerEvents: "none",
}}
>
<Text
size="9px"
fw={800}
c={`${dropColor}.9`}
style={{ letterSpacing: 0.6 }}
>
{dropIntent === "swap" ? "SWAP LOADS" : "MOVE HERE"}
</Text>
</Box>
) : null}
{/* top accent strip */}
<Box
style={{
@@ -541,6 +576,18 @@ export const InteractiveTrainConsist = ({
overflowX: "auto",
}}
>
{/* Staff kept reading a move as the train re-ordering itself, so say the
invariant out loud: the coupling order never changes, only the cargo. */}
{canRearrange ? (
<Group gap={6} wrap="nowrap" mb={6} pl={2}>
<GripVertical size={12} color="var(--mantine-color-gray-6)" />
<Text size="10px" c="dimmed">
Drag a wagon's load onto another wagon to move or swap it the
wagons themselves keep their position in the train.
</Text>
</Group>
) : null}
<Group gap={0} wrap="nowrap" align="flex-start" style={{ minWidth: "min-content" }}>
{locomotive ? <LocomotiveCar locomotive={locomotive} /> : null}
{wagons.length === 0 ? (