diff --git a/apps/edr-freight-web/portal/src/pages/bookings/NewBookingPage.tsx b/apps/edr-freight-web/portal/src/pages/bookings/NewBookingPage.tsx index 52cbb46f8..5ada990d1 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/NewBookingPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/NewBookingPage.tsx @@ -22,6 +22,7 @@ import { Step2ServiceType, Step4Route, Step5CargoDetails, + StepDocuments, Step8Review, } from "./new-booking-form/steps"; @@ -34,8 +35,25 @@ export default function NewBookingPage() { ); const createMutation = useMutation({ - mutationFn: (payload: CreateBookingPayload) => - api.bookings.create.call(payload), + mutationFn: async (payload: CreateBookingPayload) => { + const booking = await api.bookings.create.call(payload); + + // Documents can't ride along with creation — upload them against the + // new booking id once it exists. Optional here; the booking detail page + // remains the catch-all for any docs the user skips. + const documents = form.getValues("documents") ?? {}; + const hasDocuments = Object.values(documents).some((value) => + Array.isArray(value) ? value.length > 0 : Boolean(value), + ); + if (hasDocuments) { + await api.bookings.uploadDocuments.call({ + id: booking.id, + files: documents, + }); + } + + return booking; + }, onSuccess: (booking) => { queryClient.invalidateQueries({ queryKey: api.bookings.list.queryKey() }); navigate(`/bookings/${booking.id}`); @@ -282,7 +300,8 @@ export default function NewBookingPage() { isLoading={refDataLoading} /> )} - {step === 5 && ( + {step === 5 && } + {step === 6 && ( ; + export const bookingFormSchema = z .object({ contractType: z.enum(["new", "renewal"], "Select a contract type."), @@ -78,6 +131,7 @@ export const bookingFormSchema = z }), ), consolidationEnabled: z.boolean(), + documents: z.record(z.string(), z.any()).default({}), notes: z.string(), termsAccepted: z.boolean(), }) @@ -187,6 +241,7 @@ export const initialBookingFormValues: DeepPartial = { isRefrigerated: false, containers: [{ type: "20ft", containerType: "", qty: "1", vgm: "" }], consolidationEnabled: false, + documents: {}, notes: "", termsAccepted: false, }; @@ -215,7 +270,8 @@ export const stepFields: Record>> = { "containers", "consolidationEnabled", ], - 5: ["notes", "termsAccepted"], + 5: ["documents"], + 6: ["notes", "termsAccepted"], }; export type RouteDirection = "import" | "export" | "domestic" | null; diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-documents.tsx b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-documents.tsx new file mode 100644 index 000000000..cd7db7b85 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/step-documents.tsx @@ -0,0 +1,82 @@ +import { Box, Group, Text } from "@mantine/core"; +import { SmartFileInput } from "@edr/ui-common"; +import { CheckCircle2 } from "lucide-react"; +import { Controller, type UseFormReturn } from "react-hook-form"; + +import { + BOOKING_DOCS_SETTING, + type BookingDocuments, + type BookingFormValues, +} from "./schema"; +import { StepHeader } from "./shared"; + +type BookingForm = UseFormReturn; + +function countAttached(documents: BookingDocuments): number { + return BOOKING_DOCS_SETTING.fields.filter((f) => { + const value = documents[f.fileKey]; + return Array.isArray(value) ? value.length > 0 : Boolean(value); + }).length; +} + +export function StepDocuments({ form }: { form: BookingForm }) { + const documents = (form.watch("documents") ?? {}) as BookingDocuments; + const attached = countAttached(documents); + const total = BOOKING_DOCS_SETTING.fields.length; + + return ( +
+ + + + + {attached === total ? : `${attached}/${total}`} + + + {attached === 0 + ? "All documents are optional here — you can upload them later from the booking page." + : `${attached} of ${total} attached. You can finish the rest later from the booking page.`} + + + + ( + field.onChange(value)} + /> + )} + /> +
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/steps.tsx b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/steps.tsx index 7e47282aa..9f1445f91 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/steps.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/new-booking-form/steps.tsx @@ -2,4 +2,5 @@ export { Step1ContractType } from "./step1-contract-type"; export { Step2ServiceType } from "./step2-service-type"; export { Step4Route } from "./step4-route"; export { Step5CargoDetails } from "./step5-cargo-details"; +export { StepDocuments } from "./step-documents"; export { Step8Review } from "./step8-review";