diff --git a/apps/edr-freight-web/portal/src/App.tsx b/apps/edr-freight-web/portal/src/App.tsx index 6c979eaa0..c398eccb4 100644 --- a/apps/edr-freight-web/portal/src/App.tsx +++ b/apps/edr-freight-web/portal/src/App.tsx @@ -64,7 +64,6 @@ const App = () => { if (user && location.pathname === "/") navigate("/portal"); else if (!customer && !!isInProtectedRoutes) navigate("/onboarding"); - else if (customer && !isInProtectedRoutes) return navigate("/portal"); }, [user, location, customer]); if (isPending) { @@ -109,7 +108,10 @@ const App = () => { } /> } /> } /> - } /> + } + /> } /> } /> } /> diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx index 2dcecfaac..92643ccef 100644 --- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage.tsx @@ -436,20 +436,6 @@ function DraftBookingView({ )} - {pricingQuery.isError && ( -
- -
-

Pricing failed

-

- {pricingQuery.error instanceof Error - ? pricingQuery.error.message - : "An unexpected error occurred."} -

-
-
- )} - {uploadMutation.isError && (
@@ -776,11 +762,28 @@ function DraftBookingView({ function ReadonlyBookingView({ booking }: { booking: Freight.IBooking }) { const navigate = useNavigate(); + const queryClient = useQueryClient(); + + const payMutation = useMutation({ + mutationFn: () => api.bookings.pay.call({ id: booking.id }), + onSuccess: (data) => { + if (data.redirectUrl) { + window.location.href = data.redirectUrl; + } + }, + }); const normalizedStatus = booking.status as keyof typeof STATUS_MAP; const statusConfig = STATUS_MAP[normalizedStatus] || STATUS_MAP.DRAFT; const currentStageIndex = statusConfig.stage; + const pricing = booking.pricingBreakdown; + + const uploadedCodes = useMemo( + () => new Set(booking.files?.map((f) => f.code) ?? []), + [booking.files], + ); + return (
@@ -797,7 +800,7 @@ function ReadonlyBookingView({ booking }: { booking: Freight.IBooking }) {
-
+

{booking.reference} @@ -814,11 +817,100 @@ function ReadonlyBookingView({ booking }: { booking: Freight.IBooking }) {

+ {normalizedStatus === "FULLY_EXECUTED" && booking.paymentStatus !== "PAID" && ( + + )}
- {renderContractCard(booking, navigate)} + {renderContractCard(booking, navigate, payMutation)} + + {pricing && ( + + + + + Pricing Breakdown + + + +
+ + + + + + + + + {pricing.lineItems.map((item, i) => ( + + + + + ))} + + + + + +
DescriptionAmount
{item.description} + {item.amount.toLocaleString()} {item.currency} +
Total Estimated Cost + {pricing.totalAmount.toLocaleString()} {pricing.currency} +
+
+
+
+ )} + + {booking.files && booking.files.length > 0 && ( + + + + + Uploaded Documents ({booking.files.length}) + + + + + + + )} @@ -1174,6 +1266,7 @@ function ReadonlyBookingView({ booking }: { booking: Freight.IBooking }) { function renderContractCard( booking: Freight.IBooking, navigate: ReturnType, + payMutation: { mutate: () => void; isPending: boolean }, ) { const s = booking.status; if ( diff --git a/apps/edr-freight-web/portal/src/services/api.ts b/apps/edr-freight-web/portal/src/services/api.ts index c49ebad0f..ced534965 100644 --- a/apps/edr-freight-web/portal/src/services/api.ts +++ b/apps/edr-freight-web/portal/src/services/api.ts @@ -173,6 +173,12 @@ export const api = { >("bookings", "uploadDocuments", ({ id, files }) => bookingsService.uploadDocuments(id, files), ), + + pay: endpoint<{ id: string }, { redirectUrl: string }>( + "bookings", + "pay", + ({ id }) => bookingsService.pay(id), + ), }, consignments: { diff --git a/apps/edr-freight-web/portal/src/services/bookings.service.ts b/apps/edr-freight-web/portal/src/services/bookings.service.ts index d9481d59e..44ccf1e05 100644 --- a/apps/edr-freight-web/portal/src/services/bookings.service.ts +++ b/apps/edr-freight-web/portal/src/services/bookings.service.ts @@ -134,6 +134,11 @@ export const bookingsService = { return data; }, + pay: async (id: string): Promise<{ redirectUrl: string }> => { + const { data } = await client.post(`/api/bookings/${id}/payment/pay`); + return data.data ?? data; + }, + signContract: async ( id: string, payload: SignContractPayload,