mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 06:28:12 +00:00
178 lines
5.7 KiB
TypeScript
178 lines
5.7 KiB
TypeScript
import { useNavigate, useParams } from "react-router-dom";
|
|
import { ArrowLeft, FileSignature, Package } from "lucide-react";
|
|
import {
|
|
Container,
|
|
Stack,
|
|
Grid,
|
|
Center,
|
|
Loader,
|
|
Text,
|
|
Paper,
|
|
Button,
|
|
Box,
|
|
} from "@mantine/core";
|
|
|
|
import Breadcrumbs from "@/components/ui/Breadcrumbs";
|
|
import { ApprovalStepsCard } from "@/components/bookings/ApprovalStepsCard";
|
|
import { BookingActionsToolbar } from "@/components/bookings/BookingActionsToolbar";
|
|
import { BookingPricingSummary } from "@/components/bookings/BookingPricingSummary";
|
|
import { BookingWorkflowStepper } from "@/components/bookings/BookingWorkflowStepper";
|
|
import {
|
|
detailStyles,
|
|
BookingRequestHero,
|
|
BookingRouteServiceCard,
|
|
BookingMileServicesCard,
|
|
BookingCargoCard,
|
|
BookingContractSummaryCard,
|
|
} from "@/components/bookings/detail";
|
|
import { getStatusMeta } from "@/features/bookings/booking-status.config";
|
|
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
|
|
import { useBookingDetail, useBookingMutations } from "@/hooks/bookings/useBookings";
|
|
|
|
export default function BookingRequestDetailPage() {
|
|
const { id } = useParams<{ id: string }>();
|
|
const navigate = useNavigate();
|
|
const { data: booking, isLoading, isError, refetch, isFetching } = useBookingDetail(id);
|
|
const mutations = useBookingMutations(id ?? "");
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<Box style={detailStyles.page}>
|
|
<Center mih="60vh">
|
|
<Stack align="center" gap="md">
|
|
<Loader color="gray" />
|
|
<Text size="sm" c="dimmed" fw={500}>
|
|
Loading booking…
|
|
</Text>
|
|
</Stack>
|
|
</Center>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (isError || !booking) {
|
|
return (
|
|
<Box style={detailStyles.page}>
|
|
<Container size="sm" py="xl">
|
|
<Paper radius="md" withBorder p="xl" ta="center" style={detailStyles.card}>
|
|
<Center>
|
|
<Box
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: 16,
|
|
background: "var(--mantine-color-gray-1)",
|
|
color: "var(--mantine-color-gray-6)",
|
|
}}
|
|
>
|
|
<Package size={32} />
|
|
</Box>
|
|
</Center>
|
|
<Text fw={700} size="lg" mt="lg">
|
|
Booking not found
|
|
</Text>
|
|
<Text size="sm" c="dimmed" mt={4}>
|
|
This request may have been removed or the link is invalid.
|
|
</Text>
|
|
<Button
|
|
variant="default"
|
|
mt="lg"
|
|
leftSection={<ArrowLeft size={16} />}
|
|
onClick={() => navigate("/dashboard/booking-requests")}
|
|
>
|
|
Back to booking requests
|
|
</Button>
|
|
</Paper>
|
|
</Container>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
const row = toBookingListRow(booking);
|
|
const statusMeta = getStatusMeta(booking.status);
|
|
const showContractButton = [
|
|
"CONTRACT_READY",
|
|
"SIGNED_CUSTOMER",
|
|
"FULLY_EXECUTED",
|
|
].includes(booking.status);
|
|
const showApprovalCard =
|
|
booking.status === "PENDING_APPROVAL" ||
|
|
booking.status === "APPROVED_PENDING_SIGNATURE";
|
|
|
|
return (
|
|
<Box style={detailStyles.page}>
|
|
<Container size="xxl" py="lg">
|
|
<Breadcrumbs
|
|
items={[
|
|
{ label: "Booking requests", href: "/dashboard/booking-requests" },
|
|
{ label: booking.reference },
|
|
]}
|
|
/>
|
|
|
|
<Stack gap="lg" mt="sm">
|
|
<BookingRequestHero
|
|
booking={booking}
|
|
customerLabel={row.customerLabel}
|
|
onBack={() => navigate("/dashboard/booking-requests")}
|
|
onRefresh={() => refetch()}
|
|
isFetching={isFetching}
|
|
/>
|
|
|
|
<BookingWorkflowStepper
|
|
status={booking.status}
|
|
title={statusMeta.title}
|
|
description={statusMeta.description}
|
|
titleColor={statusMeta.color}
|
|
/>
|
|
|
|
<Grid gutter="lg">
|
|
{/* LEFT — primary content */}
|
|
<Grid.Col span={{ base: 12, lg: 8 }}>
|
|
<Stack gap="lg">
|
|
<BookingRouteServiceCard
|
|
booking={booking}
|
|
originLabel={row.originLabel}
|
|
destinationLabel={row.destinationLabel}
|
|
/>
|
|
<BookingMileServicesCard booking={booking} />
|
|
<BookingCargoCard booking={booking} />
|
|
{booking.contractSummary && (
|
|
<BookingContractSummaryCard summary={booking.contractSummary} />
|
|
)}
|
|
</Stack>
|
|
</Grid.Col>
|
|
|
|
{/* RIGHT — sticky action / summary rail */}
|
|
<Grid.Col span={{ base: 12, lg: 4 }}>
|
|
<Box style={{ position: "sticky", top: 24 }}>
|
|
<Stack gap="lg">
|
|
<BookingPricingSummary booking={booking} />
|
|
<BookingActionsToolbar booking={booking} mutations={mutations} />
|
|
{showContractButton && (
|
|
<Button
|
|
fullWidth
|
|
color="green"
|
|
leftSection={<FileSignature size={16} />}
|
|
onClick={() =>
|
|
navigate(`/dashboard/booking-requests/${booking.id}/contract`)
|
|
}
|
|
>
|
|
View & sign contract
|
|
</Button>
|
|
)}
|
|
{showApprovalCard && (
|
|
<ApprovalStepsCard booking={booking} mutations={mutations} />
|
|
)}
|
|
</Stack>
|
|
</Box>
|
|
</Grid.Col>
|
|
</Grid>
|
|
</Stack>
|
|
</Container>
|
|
</Box>
|
|
);
|
|
}
|