mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
booking operations and trains scheduling also allocations
This commit is contained in:
@@ -1,53 +1,90 @@
|
||||
import { useState } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
// import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useWagons, useAssignWagonToTrain } from '@/hooks/useWagons';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@edr/ui-common';
|
||||
import { useState } from "react";
|
||||
import { Plus } from "lucide-react";
|
||||
import { Button, Group, Modal, NumberInput, Select, Stack, Text } from "@mantine/core";
|
||||
|
||||
import { Freight } from "@edr/types";
|
||||
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useAssignWagonToTrain, useWagons } from "@/hooks/useWagons";
|
||||
|
||||
export function AssignWagonDialog({ trainId }: { trainId: string }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [wagonId, setWagonId] = useState('');
|
||||
const [sequence, setSequence] = useState<number>();
|
||||
const [wagonId, setWagonId] = useState<string | null>(null);
|
||||
const [sequence, setSequence] = useState<number | "">("");
|
||||
const { data: wagons } = useWagons();
|
||||
const assign = useAssignWagonToTrain();
|
||||
const { toast } = useToast();
|
||||
|
||||
const available = wagons?.filter((w:any) => w.status === 'AVAILABLE' || !w.trainId);
|
||||
const available = (wagons ?? []).filter(
|
||||
(w) => w.status === Freight.WagonStatus.Available || !w.trainId,
|
||||
);
|
||||
|
||||
const wagonOptions = available.map((w) => ({
|
||||
value: w.id,
|
||||
label: `${w.wagonNumber} (${w.readiness.replace("_", " ").toLowerCase()})`,
|
||||
}));
|
||||
|
||||
const handleAssign = async () => {
|
||||
if (!wagonId) return;
|
||||
await assign.mutateAsync({ wagonId, trainId, sequenceNumber: sequence });
|
||||
toast({ title: 'Assigned', description: 'Wagon attached to train.' });
|
||||
setOpen(false);
|
||||
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 (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild><Button size="sm"><Plus className="mr-2 h-4 w-4" />Assign Wagon</Button></DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader><DialogTitle>Assign Wagon to Train</DialogTitle></DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>Wagon</Label>
|
||||
<Select value={wagonId} onValueChange={setWagonId}>
|
||||
<SelectTrigger><SelectValue placeholder="Select wagon" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
{available?.map((w:any) => <SelectItem key={w.id} value={w.id}>{w.wagonNumber}</SelectItem>)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Sequence (optional)</Label>
|
||||
<Input type="number" value={sequence ?? ''} onChange={e => setSequence(parseInt(e.target.value) || undefined)} />
|
||||
</div>
|
||||
<Button onClick={handleAssign} disabled={assign.isPending}>Assign</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<>
|
||||
<Button
|
||||
color="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="green" loading={assign.isPending} onClick={handleAssign}>
|
||||
Assign
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
// src/components/wagons/WagonFormDialog.tsx
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@edr/ui-common';
|
||||
import { useWagonTypes } from '@/hooks/use-wagon-types';
|
||||
import { useCreateWagon, useUpdateWagon } from '@/hooks/useWagons';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
interface WagonFormDialogProps {
|
||||
trigger?: React.ReactNode;
|
||||
wagon?: any;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
export function WagonFormDialog({ trigger, wagon, onSuccess }: WagonFormDialogProps) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [form, setForm] = useState({
|
||||
wagonNumber: '',
|
||||
wagonTypeId: '',
|
||||
tareWeight: 0,
|
||||
maxPayloadWeight: 0,
|
||||
status: 'AVAILABLE',
|
||||
notes: ''
|
||||
});
|
||||
const createWagon = useCreateWagon();
|
||||
const updateWagon = useUpdateWagon();
|
||||
const { data: wagonTypes = [], isLoading: wagonTypesLoading } = useWagonTypes();
|
||||
const { toast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (wagon) setForm({
|
||||
wagonNumber: wagon.wagonNumber,
|
||||
wagonTypeId: wagon.wagonTypeId,
|
||||
tareWeight: wagon.tareWeight,
|
||||
maxPayloadWeight: wagon.maxPayloadWeight,
|
||||
status: wagon.status,
|
||||
notes: wagon.notes || ''
|
||||
});
|
||||
}, [wagon]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!form.wagonNumber || !form.wagonTypeId) {
|
||||
toast({ title: 'Missing required field', description: 'Please select a wagon type.', variant: 'destructive' });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (wagon) await updateWagon.mutateAsync({ id: wagon.id, data: form });
|
||||
else await createWagon.mutateAsync(form);
|
||||
toast({ title: wagon ? 'Wagon updated' : 'Wagon created', description: `${form.wagonNumber} saved.` });
|
||||
setOpen(false);
|
||||
onSuccess?.();
|
||||
} catch {
|
||||
toast({ title: 'Error', description: `Failed to ${wagon ? 'update' : 'create'} wagon.`, variant: 'destructive' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>{trigger || <Button>New Wagon</Button>}</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader><DialogTitle>{wagon ? 'Edit Wagon' : 'Create Wagon'}</DialogTitle></DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div><Label>Wagon Number*</Label><Input value={form.wagonNumber} onChange={e => setForm({...form, wagonNumber: e.target.value})} /></div>
|
||||
<div>
|
||||
<Label>Wagon Type*</Label>
|
||||
<Select
|
||||
value={form.wagonTypeId}
|
||||
disabled={wagonTypesLoading}
|
||||
onValueChange={(value) => {
|
||||
const selectedType = wagonTypes.find((type: any) => type.id === value);
|
||||
setForm((current) => ({
|
||||
...current,
|
||||
wagonTypeId: value,
|
||||
maxPayloadWeight: current.maxPayloadWeight > 0
|
||||
? current.maxPayloadWeight
|
||||
: Number(selectedType?.capacityTons ?? current.maxPayloadWeight),
|
||||
}));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select wagon type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{wagonTypes.map((type: any) => (
|
||||
<SelectItem key={type.id} value={type.id}>
|
||||
{type.code} - {type.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div><Label>Tare Weight (kg)*</Label><Input type="number" value={form.tareWeight} onChange={e => setForm({...form, tareWeight: Number(e.target.value)})} /></div>
|
||||
<div><Label>Max Payload (kg)*</Label><Input type="number" value={form.maxPayloadWeight} onChange={e => setForm({...form, maxPayloadWeight: Number(e.target.value)})} /></div>
|
||||
<div><Label>Status</Label><Input value={form.status} onChange={e => setForm({...form, status: e.target.value})} /></div>
|
||||
<div><Label>Notes</Label><Input value={form.notes} onChange={e => setForm({...form, notes: e.target.value})} /></div>
|
||||
<Button type="submit" disabled={createWagon.isPending || updateWagon.isPending}>Save</Button>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'sonner';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@edr/ui-common';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useWagonTypes } from '@/hooks/use-wagon-types';
|
||||
|
||||
interface Wagon {
|
||||
id: string;
|
||||
wagonNumber: string;
|
||||
wagonTypeId: string;
|
||||
trainId?: string;
|
||||
status: 'AVAILABLE' | 'IN_USE' | 'MAINTENANCE' | 'RETIRED';
|
||||
capacity: number;
|
||||
emptyWeight: number;
|
||||
remarks?: string;
|
||||
}
|
||||
|
||||
interface Train {
|
||||
id: string;
|
||||
trainNumber: string;
|
||||
}
|
||||
|
||||
interface WagonFormDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
wagon?: Wagon | null;
|
||||
trains: Train[];
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
|
||||
|
||||
export default function WagonFormDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
wagon,
|
||||
trains = [],
|
||||
onSuccess,
|
||||
}: WagonFormDialogProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const { data: wagonTypes = [], isLoading: wagonTypesLoading } = useWagonTypes();
|
||||
const [formData, setFormData] = useState<Partial<Wagon>>({
|
||||
wagonNumber: '',
|
||||
wagonTypeId: '',
|
||||
status: 'AVAILABLE',
|
||||
capacity: 0,
|
||||
emptyWeight: 0,
|
||||
remarks: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (wagon) {
|
||||
setFormData(wagon);
|
||||
} else {
|
||||
setFormData({
|
||||
wagonNumber: '',
|
||||
wagonTypeId: '',
|
||||
status: 'AVAILABLE',
|
||||
capacity: 0,
|
||||
emptyWeight: 0,
|
||||
remarks: '',
|
||||
});
|
||||
}
|
||||
}, [wagon, open]);
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: (data: Partial<Wagon>) =>
|
||||
axios.post(`${API_BASE_URL}/api/wagons`, data),
|
||||
onSuccess: () => {
|
||||
toast.success('Wagon created successfully');
|
||||
onOpenChange(false);
|
||||
queryClient.invalidateQueries({ queryKey: ['wagons'] });
|
||||
onSuccess?.();
|
||||
},
|
||||
onError: (error) => {
|
||||
const message = axios.isAxiosError(error)
|
||||
? error.response?.data?.message || 'Failed to create wagon'
|
||||
: 'Failed to create wagon';
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: (data: Partial<Wagon>) =>
|
||||
axios.patch(`${API_BASE_URL}/api/wagons/${wagon?.id}`, data),
|
||||
onSuccess: () => {
|
||||
toast.success('Wagon updated successfully');
|
||||
onOpenChange(false);
|
||||
queryClient.invalidateQueries({ queryKey: ['wagons'] });
|
||||
onSuccess?.();
|
||||
},
|
||||
onError: (error) => {
|
||||
const message = axios.isAxiosError(error)
|
||||
? error.response?.data?.message || 'Failed to update wagon'
|
||||
: 'Failed to update wagon';
|
||||
toast.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!formData.wagonNumber || !formData.wagonTypeId) {
|
||||
toast.error('Please fill in all required fields');
|
||||
return;
|
||||
}
|
||||
if (wagon?.id) {
|
||||
updateMutation.mutate(formData);
|
||||
} else {
|
||||
createMutation.mutate(formData);
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = createMutation.isPending || updateMutation.isPending;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-[500px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{wagon ? 'Edit Wagon' : 'Create New Wagon'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="wagonNumber">Wagon Number *</Label>
|
||||
<Input
|
||||
id="wagonNumber"
|
||||
value={formData.wagonNumber || ''}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, wagonNumber: e.target.value })
|
||||
}
|
||||
placeholder="e.g., W001"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="wagonTypeId">Type *</Label>
|
||||
<Select
|
||||
value={formData.wagonTypeId || ''}
|
||||
disabled={wagonTypesLoading}
|
||||
onValueChange={(value) => {
|
||||
const selectedType = wagonTypes.find((type: any) => type.id === value);
|
||||
setFormData({
|
||||
...formData,
|
||||
wagonTypeId: value,
|
||||
capacity: formData.capacity && formData.capacity > 0
|
||||
? formData.capacity
|
||||
: Number(selectedType?.capacityTons ?? 0),
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger id="wagonTypeId">
|
||||
<SelectValue placeholder="Select wagon type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{wagonTypes.map((type: any) => (
|
||||
<SelectItem key={type.id} value={type.id}>
|
||||
{type.code} - {type.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="trainId">Train (Optional)</Label>
|
||||
<select
|
||||
id="trainId"
|
||||
value={formData.trainId || ''}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, trainId: e.target.value || undefined })
|
||||
}
|
||||
className="flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="">Select a train...</option>
|
||||
{trains.map(train => (
|
||||
<option key={train.id} value={train.id}>
|
||||
{train.trainNumber}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="status">Status</Label>
|
||||
<select
|
||||
id="status"
|
||||
value={formData.status || 'AVAILABLE'}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, status: e.target.value as any })
|
||||
}
|
||||
className="flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="AVAILABLE">Available</option>
|
||||
<option value="IN_USE">In Use</option>
|
||||
<option value="MAINTENANCE">Maintenance</option>
|
||||
<option value="RETIRED">Retired</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="capacity">Capacity *</Label>
|
||||
<Input
|
||||
id="capacity"
|
||||
type="number"
|
||||
value={formData.capacity || ''}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
capacity: parseFloat(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
placeholder="0"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="emptyWeight">Empty Weight (kg)</Label>
|
||||
<Input
|
||||
id="emptyWeight"
|
||||
type="number"
|
||||
value={formData.emptyWeight || ''}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
emptyWeight: parseFloat(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="remarks">Remarks</Label>
|
||||
<Textarea
|
||||
id="remarks"
|
||||
value={formData.remarks || ''}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, remarks: e.target.value })
|
||||
}
|
||||
placeholder="Add any additional notes..."
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{wagon ? 'Update' : 'Create'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1,64 +1,96 @@
|
||||
import { useWagonsByTrain, useUnassignWagon, useReorderWagons } from '@/hooks/useWagons';
|
||||
//import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
//import { Button } from '@/components/ui/button';
|
||||
//import { Trash2, GripVertical } from 'lucide-react';
|
||||
// import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
|
||||
import { useMemo } from "react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import type { ColumnDef } from "@edr/ui-common";
|
||||
import { ActionIcon, Badge, Group, Text, Tooltip } from "@mantine/core";
|
||||
|
||||
import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useUnassignWagon, useWagonsByTrain } from "@/hooks/useWagons";
|
||||
import type { Wagon } from "@/services/wagon.service";
|
||||
import { DataTable } from "@edr/ui-common";
|
||||
|
||||
export function WagonsTable({ trainId }: { trainId: string }) {
|
||||
const { data: wagons, refetch } = useWagonsByTrain(trainId);
|
||||
const { data: wagons = [], isLoading, refetch } = useWagonsByTrain(trainId);
|
||||
const unassign = useUnassignWagon();
|
||||
const reorder = useReorderWagons();
|
||||
const { toast } = useToast();
|
||||
|
||||
const onDragEnd = (result: any) => {
|
||||
if (!result.destination) return;
|
||||
const items = Array.from(wagons || []);
|
||||
const [removed] = items.splice(result.source.index, 1);
|
||||
items.splice(result.destination.index, 0, removed);
|
||||
reorder.mutate({ trainId, wagonIds: items.map((w:any) => w.id) });
|
||||
};
|
||||
const columns = useMemo((): ColumnDef<Wagon>[] => {
|
||||
const headerClassName = ruleEngineTable.headerCell;
|
||||
const cellClassName = ruleEngineTable.bodyCell;
|
||||
return [
|
||||
{
|
||||
id: "wagonNumber",
|
||||
header: "Number",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => row.original.wagonNumber,
|
||||
},
|
||||
{
|
||||
id: "wagonTypeId",
|
||||
header: "Type",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => row.original.wagonTypeId,
|
||||
},
|
||||
{
|
||||
id: "sequenceNumber",
|
||||
header: "Sequence",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => row.original.sequenceNumber ?? "—",
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
header: "Status",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => (
|
||||
<Badge variant="light" color="gray" size="sm">
|
||||
{row.original.status}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "Actions",
|
||||
meta: { headerClassName, cellClassName },
|
||||
cell: ({ row }) => (
|
||||
<Group justify="flex-end">
|
||||
<Tooltip label="Unassign">
|
||||
<ActionIcon
|
||||
variant="subtle"
|
||||
color="red"
|
||||
loading={unassign.isPending}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await unassign.mutateAsync(row.original.id);
|
||||
await refetch();
|
||||
toast({ title: "Wagon unassigned" });
|
||||
} catch {
|
||||
toast({ title: "Failed to unassign wagon", variant: "destructive" });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [unassign.isPending, refetch, toast]);
|
||||
|
||||
if (!wagons?.length) return <div className="text-muted-foreground">No wagons assigned.</div>;
|
||||
if (!isLoading && !wagons.length) {
|
||||
return (
|
||||
<Text size="sm" c="dimmed" py="md">
|
||||
No wagons assigned to this train.
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div></div>
|
||||
// <DragDropContext onDragEnd={onDragEnd}>
|
||||
// <Droppable droppableId="wagons">
|
||||
// {(provided) => (
|
||||
// <Table {...provided.droppableProps} ref={provided.innerRef}>
|
||||
// <TableHeader>
|
||||
// <TableRow>
|
||||
// <TableHead className="w-10"></TableHead>
|
||||
// <TableHead>Number</TableHead>
|
||||
// <TableHead>Type</TableHead>
|
||||
// <TableHead>Sequence</TableHead>
|
||||
// <TableHead>Status</TableHead>
|
||||
// <TableHead>Actions</TableHead>
|
||||
// </TableRow>
|
||||
// </TableHeader>
|
||||
// <TableBody>
|
||||
// {wagons.map((wagon, idx) => (
|
||||
// <Draggable key={wagon.id} draggableId={wagon.id} index={idx}>
|
||||
// {(provided) => (
|
||||
// <TableRow ref={provided.innerRef} {...provided.draggableProps}>
|
||||
// <TableCell {...provided.dragHandleProps}><GripVertical className="h-4 w-4 cursor-grab" /></TableCell>
|
||||
// <TableCell>{wagon.wagonNumber}</TableCell>
|
||||
// <TableCell>{wagon.wagonTypeId}</TableCell>
|
||||
// <TableCell>{wagon.sequenceNumber}</TableCell>
|
||||
// <TableCell>{wagon.status}</TableCell>
|
||||
// <TableCell>
|
||||
// <Button variant="ghost" size="icon" onClick={() => unassign.mutateAsync(wagon.id).then(() => refetch())}>
|
||||
// <Trash2 className="h-4 w-4" />
|
||||
// </Button>
|
||||
// </TableCell>
|
||||
// </TableRow>
|
||||
// )}
|
||||
// </Draggable>
|
||||
// ))}
|
||||
// {provided.placeholder}
|
||||
// </TableBody>
|
||||
// </Table>
|
||||
// )}
|
||||
// </Droppable>
|
||||
// </DragDropContext>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={wagons}
|
||||
status={isLoading ? "loading" : "success"}
|
||||
emptyMessage="No wagons assigned"
|
||||
containerClassName="border-0 shadow-none bg-transparent"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user