feat: add standalone container return modal

Add "Record Return" button at top of Container Returns page for
standalone/unboooked returns. Modal accepts container number, return
date, warehouse, condition, handover notes. No booking association
required — supports returns that arrive without clear booking context.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-07-30 14:34:59 +00:00
parent 63aa2c7920
commit c74b9320a2

View File

@@ -12,6 +12,7 @@ import {
Stack,
Table,
Text,
TextInput,
Textarea,
Select,
Checkbox,
@@ -57,6 +58,7 @@ export default function ContainerReturnsPage() {
const [expanded, setExpanded] = useState<string | null>(null);
const [filterType, setFilterType] = useState<ReturnType>("all");
const [returnModalOpen, setReturnModalOpen] = useState(false);
const [standaloneModalOpen, setStandaloneModalOpen] = useState(false);
const [activeKey, setActiveKey] = useState<string | null>(null);
const { data: unloadedQueue = [], isLoading: queueLoading } = useQuery({
@@ -232,7 +234,7 @@ export default function ContainerReturnsPage() {
subtitle="Empty containers returned by last-mile trucks (EDR or customer self-haul)"
/>
<Group mb="lg">
<Group mb="lg" justify="space-between">
<SegmentedControl
value={filterType}
onChange={(val) => setFilterType(val as ReturnType)}
@@ -242,6 +244,9 @@ export default function ContainerReturnsPage() {
{ label: "Customer Self-Haul", value: "customer" },
]}
/>
<Button onClick={() => setStandaloneModalOpen(true)}>
Record Return
</Button>
</Group>
{filteredGroups.length === 0 ? (
@@ -348,6 +353,13 @@ export default function ContainerReturnsPage() {
onSubmit={(payload) => createReturnsMutation.mutate(payload)}
loading={createReturnsMutation.isPending}
/>
<StandaloneReturnModal
opened={standaloneModalOpen}
onClose={() => setStandaloneModalOpen(false)}
onSubmit={(payload) => createReturnsMutation.mutate(payload)}
loading={createReturnsMutation.isPending}
/>
</PageContainer>
);
}
@@ -493,3 +505,130 @@ function ContainerReturnModal({ opened, onClose, group, onSubmit, loading }: Con
</Modal>
);
}
interface StandaloneReturnModalProps {
opened: boolean;
onClose: () => void;
onSubmit: (payload: any) => void;
loading: boolean;
}
function StandaloneReturnModal({ opened, onClose, onSubmit, loading }: StandaloneReturnModalProps) {
const [containerNumber, setContainerNumber] = useState<string>("");
const [returnDate, setReturnDate] = useState<string>(new Date().toISOString().split("T")[0]);
const [warehouse, setWarehouse] = useState<string | null>(null);
const [condition, setCondition] = useState<string>("");
const [handoverNote, setHandoverNote] = useState<string>("");
const { data: warehousesResponse } = useQuery({
queryKey: ["warehouses-list"],
queryFn: async () => {
return await warehouseService.list({});
},
});
const warehouses = (warehousesResponse as any)?.data ?? warehousesResponse ?? [];
const warehouseOptions = Array.isArray(warehouses)
? warehouses.map((wh: any) => ({
value: wh.id,
label: `${wh.name} ${wh.code ? `(${wh.code})` : ""}`,
}))
: [];
const handleSubmit = () => {
if (!containerNumber || !warehouse) return;
const selectedWarehouse = Array.isArray(warehouses) ? warehouses.find((wh: any) => wh.id === warehouse) : null;
onSubmit({
trucks: [
{
bookingId: null,
customerId: null,
returnType: "CUSTOMER",
containers: [
{
containerNumber,
returnDate,
warehouse: selectedWarehouse?.name || warehouse,
condition: condition || undefined,
handoverNote: handoverNote || undefined,
},
],
},
],
});
setContainerNumber("");
setReturnDate(new Date().toISOString().split("T")[0]);
setWarehouse(null);
setCondition("");
setHandoverNote("");
onClose();
};
return (
<Modal opened={opened} onClose={onClose} title="Record Container Return (Standalone)" size="lg">
<Stack gap="md">
<Text size="sm" c="dimmed">
Record container return without booking association
</Text>
<TextInput
label="Container Number"
placeholder="e.g., TEMU1234567"
value={containerNumber}
onChange={(e) => setContainerNumber(e.currentTarget.value)}
required
/>
<Select
label="Return Warehouse"
placeholder="Select warehouse for container return"
value={warehouse}
onChange={setWarehouse}
data={warehouseOptions}
required
searchable
/>
<input
type="date"
value={returnDate}
onChange={(e) => setReturnDate(e.target.value)}
style={{ padding: "8px", borderRadius: "4px", border: "1px solid #ccc" }}
required
/>
<Textarea
label="Condition"
placeholder="Damage, residue, or cleanliness notes"
value={condition}
onChange={(e) => setCondition(e.currentTarget.value)}
rows={3}
/>
<Textarea
label="Handover Note"
placeholder="Consignee, trucker, or authorization notes"
value={handoverNote}
onChange={(e) => setHandoverNote(e.currentTarget.value)}
rows={3}
/>
<Group justify="flex-end" gap="sm">
<Button variant="default" onClick={onClose} disabled={loading}>
Cancel
</Button>
<Button
onClick={handleSubmit}
disabled={!containerNumber || !warehouse}
loading={loading}
>
Record Return
</Button>
</Group>
</Stack>
</Modal>
);
}