fix(bookings): gate export carriage acceptance sheet on GRN receipt

The sheet attests EDR has taken custody. Export custody happens at
warehouse receipt, so the shared received-with-GRN gate now runs even
when wagons are already allocated. Receipt-row query also accepts the
legacy notes GRN fallback, matching the loading gate.
This commit is contained in:
Hagernesh
2026-08-04 07:59:03 +00:00
parent 19ab3af1cb
commit 87b04973d8
5 changed files with 83 additions and 14 deletions

View File

@@ -17,7 +17,7 @@ import {
Select,
Checkbox,
} from "@mantine/core";
import { ChevronDown, ChevronRight } from "lucide-react";
import { ChevronDown, ChevronRight, History } from "lucide-react";
import { PageContainer, PageHeader } from "@/components/page";
import RuleEngineListFooter from "@/components/ruleEngine/RuleEngineListFooter";
@@ -82,6 +82,7 @@ export default function ContainerReturnsPage() {
const [returnModalOpen, setReturnModalOpen] = useState(false);
const [standaloneModalOpen, setStandaloneModalOpen] = useState(false);
const [activeKey, setActiveKey] = useState<string | null>(null);
const [historyRow, setHistoryRow] = useState<any | null>(null);
const { data: unloadedQueue = [], isLoading: queueLoading } = useQuery({
queryKey: ["import-unloaded-queue"],
@@ -346,18 +347,23 @@ export default function ContainerReturnsPage() {
<Badge size="sm">{RETURN_STATUS_LABEL[ret.status as EmptyContainerReturnStatus] ?? ret.status}</Badge>
</Table.Td>
<Table.Td ta="right">
{nextStatus ? (
<Button
size="xs"
variant="light"
loading={advanceStatusMutation.isPending && advanceStatusMutation.variables === ret.id}
onClick={() => advanceStatusMutation.mutate(ret.id)}
>
Advance to {RETURN_STATUS_LABEL[nextStatus]}
</Button>
) : (
<Text size="xs" c="dimmed">Done</Text>
)}
<Group gap="xs" justify="flex-end" wrap="nowrap">
<ActionIcon variant="subtle" color="gray" onClick={() => setHistoryRow(ret)} title="View status history">
<History size={14} />
</ActionIcon>
{nextStatus ? (
<Button
size="xs"
variant="light"
loading={advanceStatusMutation.isPending && advanceStatusMutation.variables === ret.id}
onClick={() => advanceStatusMutation.mutate(ret.id)}
>
Advance to {RETURN_STATUS_LABEL[nextStatus]}
</Button>
) : (
<Text size="xs" c="dimmed">Done</Text>
)}
</Group>
</Table.Td>
</Table.Tr>
);
@@ -479,6 +485,23 @@ export default function ContainerReturnsPage() {
onSubmit={(payload) => createReturnsMutation.mutate(payload)}
loading={createReturnsMutation.isPending}
/>
<Modal opened={!!historyRow} onClose={() => setHistoryRow(null)} title="Status History" size="sm">
{historyRow && (
<Stack gap="sm">
<Text fw={600}>{historyRow.containerNumber}</Text>
{(historyRow.statusHistory ?? []).map((entry: any, idx: number) => (
<Group key={idx} justify="space-between">
<Badge size="sm">{RETURN_STATUS_LABEL[entry.status as EmptyContainerReturnStatus] ?? entry.status}</Badge>
<Text size="sm" c="dimmed">{new Date(entry.changedAt).toLocaleString()}</Text>
</Group>
))}
{!(historyRow.statusHistory ?? []).length && (
<Text size="sm" c="dimmed">No history recorded.</Text>
)}
</Stack>
)}
</Modal>
</PageContainer>
);
}

View File

@@ -103,6 +103,11 @@ export interface EmptyContainerReturn {
wagonAllocationReference: string | null;
performedBy: string | null;
returnedBy: 'EDR' | 'CUSTOMER' | null;
statusHistory: Array<{
status: EmptyContainerReturnStatus;
changedAt: string;
performedBy: string | null;
}>;
}
export interface CreateEmptyContainerReturnPayload {