feat(portal): show warehouse location for arrived cargo

Display cargo warehouse location (warehouse, yard, zone, arrival time) on
the portal booking detail page once the cargo has been received and
assigned to a warehouse location.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-07-21 11:38:57 +00:00
parent f9607a58c3
commit ed503f1e6b
3 changed files with 130 additions and 0 deletions

View File

@@ -34,6 +34,7 @@ import { HeaderButton, PageHeader } from "./components/PageHeader";
import { PaymentMethodModal } from "./components/PaymentMethodModal";
import { ScheduleCard } from "./components/ScheduleCard";
import { WarehousePaymentsSection } from "./components/WarehousePaymentsSection";
import { WarehouseLocationCard } from "./components/WarehouseLocationCard";
import { ShipmentDetailsCard } from "./components/ShipmentDetailsCard";
import { ShipmentTrackingCard } from "./components/ShipmentTrackingCard";
import { StatusHero } from "./components/StatusHero";
@@ -273,6 +274,8 @@ export function ReadonlyBookingView({
<ContainersCard booking={booking} />
<WarehouseLocationCard bookingId={booking.id} />
<ContractInfoCard booking={booking} />
<ShipmentTrackingCard bookingId={booking.id} />

View File

@@ -0,0 +1,71 @@
import { Group, Paper, Stack, Text, Divider } from "@mantine/core";
import { useQuery } from "@tanstack/react-query";
import { Warehouse as WarehouseIcon } from "lucide-react";
import { warehouseService } from "@/services/warehouse.service";
interface WarehouseLocationCardProps {
bookingId: string;
}
function Row({ label, value }: { label: string; value: React.ReactNode }) {
return (
<Group justify="space-between" wrap="nowrap">
<Text size="sm" c="dimmed">
{label}
</Text>
<Text size="sm" fw={500} ta="right">
{value}
</Text>
</Group>
);
}
export function WarehouseLocationCard({ bookingId }: WarehouseLocationCardProps) {
const { data, isLoading } = useQuery({
queryKey: ["warehouse-inventory", bookingId],
queryFn: () => warehouseService.listInventory({ bookingId }),
});
const items = data ?? [];
const latest = items[0];
return (
<Paper withBorder radius="md" padding="lg">
<Stack gap="md">
<Group gap="xs">
<WarehouseIcon size={18} />
<Text fw={700}>Warehouse Location</Text>
</Group>
<Divider />
{isLoading ? (
<Text size="sm" c="dimmed">
Loading
</Text>
) : !latest ? (
<Text size="sm" c="dimmed">
Your cargo will appear here once it arrives at the warehouse.
</Text>
) : (
<Stack gap="xs">
<Row
label="Warehouse"
value={latest.warehouse ? `${latest.warehouse.name} (${latest.warehouse.code})` : "—"}
/>
<Row
label="Yard"
value={latest.yard ? `${latest.yard.name} (${latest.yard.code})` : "—"}
/>
<Row
label="Zone"
value={latest.zone ? `${latest.zone.name} (${latest.zone.code})` : "—"}
/>
<Row label="Arrived At" value={latest.arrivedAt ? new Date(latest.arrivedAt).toLocaleString() : "—"} />
</Stack>
)}
</Stack>
</Paper>
);
}

View File

@@ -0,0 +1,56 @@
import { client } from "@/utils/api";
export interface InventoryFilter {
bookingId?: string;
}
export interface WarehouseInventoryItem {
id: string;
bookingId: string | null;
warehouseId: string;
yardId: string;
zoneId: string;
status: string;
arrivedAt: string | null;
warehouse?: {
id: string;
name: string;
code: string;
} | null;
yard?: {
id: string;
name: string;
code: string;
} | null;
zone?: {
id: string;
name: string;
code: string;
} | null;
}
export interface BookingScheduleView {
schedule: {
status: string;
scheduledDepartureDate: string | null;
scheduledArrivalDate: string | null;
} | null;
wagon?: {
wagonNumber: string | null;
sequenceNo: number | null;
} | null;
}
export const warehouseService = {
listInventory: async (filter?: InventoryFilter): Promise<WarehouseInventoryItem[]> => {
const { data } = await client.get("/warehouse-inventory", {
params: filter,
});
return data?.data ?? data ?? [];
},
bookingSchedule: async (bookingId: string): Promise<BookingScheduleView> => {
const { data } = await client.get(`/warehouse-inventory/booking-schedule/${bookingId}`);
return data?.data ?? data ?? { schedule: null, wagon: null };
},
};