Merge pull request #433 from Tria-plc/freight_feature/usermanagement

feat: enhance contract clearance process with linked booking details
This commit is contained in:
marshal
2026-07-04 00:05:34 +03:00
committed by GitHub
22 changed files with 537 additions and 204 deletions

View File

@@ -1,7 +1,6 @@
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import {
ArrowLeft,
FileSignature,
Layers,
LayoutGrid,
Milestone,
@@ -36,31 +35,19 @@ import {
BookingCargoCard,
BookingCompanyCard,
BookingContractSummaryCard,
BookingDocumentsCard,
BookingContainerUnitsCard,
ClearanceReviewSection,
ContractOrdersPanel,
type BookingFileView,
} from "@/components/bookings/detail";
import { WarehouseInfoCard } from "@/components/warehouses";
import { getStatusMeta } from "@/features/bookings/booking-status.config";
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
import type { BookingDetail } from "@/types/booking";
import { downloadBookingFile } from "@/services/files.service";
import {
useBookingDetail,
useBookingMutations,
} from "@/hooks/bookings/useBookings";
import { useScrollToHash } from "@/hooks/useScrollToHash";
import toast from "react-hot-toast";
// Signature / generated-contract files are surfaced on the contract page, not
// in the booking's Documents list.
const SIGNATURE_FILE_CODES = new Set([
"signature",
"signature_customer",
"signature_staff",
"contract",
]);
export default function BookingRequestDetailPage() {
const { id } = useParams<{ id: string }>();
@@ -77,14 +64,6 @@ export default function BookingRequestDetailPage() {
} = useBookingDetail(id);
const mutations = useBookingMutations(id ?? "");
const handleDownloadFile = async (file: BookingFileView) => {
try {
await downloadBookingFile(file.id, file.name);
} catch {
toast.error("Could not download file.");
}
};
if (isLoading) {
return (
<PageContainer>
@@ -149,11 +128,6 @@ export default function BookingRequestDetailPage() {
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";
@@ -246,11 +220,7 @@ export default function BookingRequestDetailPage() {
</Tabs.List>
<Tabs.Panel value="overview">
<OverviewPanel
booking={booking}
row={row}
onDownload={handleDownloadFile}
/>
<OverviewPanel booking={booking} row={row} />
</Tabs.Panel>
{isGeneralContract && (
<Tabs.Panel value="orders">
@@ -270,11 +240,7 @@ export default function BookingRequestDetailPage() {
)}
</Tabs>
) : (
<OverviewPanel
booking={booking}
row={row}
onDownload={handleDownloadFile}
/>
<OverviewPanel booking={booking} row={row} />
)}
</Grid.Col>
@@ -306,20 +272,6 @@ export default function BookingRequestDetailPage() {
View document clearance
</Button>
)}
{showContractButton && (
<Button
fullWidth
color="edr-green"
leftSection={<FileSignature size={16} />}
onClick={() =>
navigate(
`/dashboard/booking-requests/${booking.id}/contract`,
)
}
>
View & sign contract
</Button>
)}
{showApprovalCard && (
<ApprovalStepsCard booking={booking} mutations={mutations} />
)}
@@ -332,15 +284,13 @@ export default function BookingRequestDetailPage() {
);
}
/** The booking's primary detail cards — route, services, cargo, contract, docs. */
/** The booking's primary detail cards — route, services, cargo, containers. */
function OverviewPanel({
booking,
row,
onDownload,
}: {
booking: BookingDetail;
row: ReturnType<typeof toBookingListRow>;
onDownload: (file: BookingFileView) => void;
}) {
return (
<Stack gap="lg">
@@ -351,15 +301,10 @@ function OverviewPanel({
/>
<BookingMileServicesCard booking={booking} />
<BookingCargoCard booking={booking} />
<BookingContainerUnitsCard booking={booking} />
{booking.contractSummary && (
<BookingContractSummaryCard summary={booking.contractSummary} />
)}
<BookingDocumentsCard
files={(booking.files ?? []).filter(
(f) => !SIGNATURE_FILE_CODES.has(f.code ?? ""),
)}
onDownload={onDownload}
/>
</Stack>
);
}