Files
edr-platform/apps/edr-freight-web/backoffice/src/components/wagons/AssignWagonDialog.tsx

104 lines
2.9 KiB
TypeScript

import { useState, useMemo } from "react";
import { Plus } from "lucide-react";
import { Button, Group, Modal, NumberInput, Select, Stack, Text } from "@mantine/core";
import { Freight } from "@edr/types";
import { useMutation, useQuery } from "@tanstack/react-query";
import { api } from "@/services/api";
import { useToast } from "@/hooks/use-toast";
export function AssignWagonDialog({ trainId }: { trainId: string }) {
const [open, setOpen] = useState(false);
const [wagonId, setWagonId] = useState<string | null>(null);
const [sequence, setSequence] = useState<number | "">("");
const { data: wagons } = useQuery(api.wagons.list.queryOptions({ input: {} }));
const { data: yards = [] } = useQuery(api.routes.yards.queryOptions());
const assign = useMutation(api.wagons.assignToTrain.mutationOptions());
const { toast } = useToast();
const available = (wagons ?? []).filter(
(w) => w.status === Freight.WagonStatus.Available || !w.trainId,
);
const yardLabelById = useMemo(
() =>
new Map(
yards.map((y) => [y.id, y.label ?? y.code ?? y.id]),
),
[yards],
);
const wagonOptions = available.map((w) => ({
value: w.id,
label: w.currentYardId
? `${w.wagonNumber} (${yardLabelById.get(w.currentYardId) ?? "yard"})`
: `${w.wagonNumber} (no yard)`,
}));
const handleAssign = async () => {
if (!wagonId) return;
try {
await assign.mutateAsync({
wagonId,
trainId,
sequenceNumber: sequence === "" ? undefined : Number(sequence),
});
toast({ title: "Wagon attached to train" });
setOpen(false);
setWagonId(null);
setSequence("");
} catch {
toast({ title: "Failed to assign wagon", variant: "destructive" });
}
};
return (
<>
<Button
color="edr-green"
size="sm"
radius="lg"
leftSection={<Plus size={16} />}
onClick={() => setOpen(true)}
>
Assign wagon
</Button>
<Modal
opened={open}
onClose={() => setOpen(false)}
title={<Text fw={600}>Assign wagon to train</Text>}
radius="lg"
centered
>
<Stack gap="md">
<Select
label="Wagon"
placeholder="Select wagon"
data={wagonOptions}
value={wagonId}
onChange={setWagonId}
searchable
/>
<NumberInput
label="Sequence (optional)"
value={sequence}
onChange={(value) => setSequence(value === "" ? "" : Number(value))}
min={1}
/>
<Group justify="flex-end">
<Button variant="default" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button color="edr-green" loading={assign.isPending} onClick={handleAssign}>
Assign
</Button>
</Group>
</Stack>
</Modal>
</>
);
}