feat: Implement consolidated booking functionality

- Added support for viewing and managing consolidated bookings in BookingRequestDetailPage.
- Enhanced BookingRequestsPage to display paired bookings in a single row.
- Introduced pairedDecision method in bookings service to handle decisions for both halves of a consolidated pair.
- Updated contracts service to include methods for manual consolidation of odd-20ft bookings.
- Created new components for selecting and editing consolidation partners.
- Added tests for paired decision logic and manual consolidation scenarios.
- Updated UI to reflect changes in booking handling and provide user feedback for odd container counts.
This commit is contained in:
Marshal
2026-08-18 12:50:35 +00:00
parent c723b660e2
commit 22a3fb98ee
25 changed files with 2121 additions and 34 deletions

View File

@@ -121,7 +121,38 @@ export function useBookingMutations(bookingId: string) {
onError: (error) => toast.error(parseApiError(error, "Failed to cancel booking")),
});
/**
* One staff decision applied to both halves of a consolidated pair. Both
* bookings are invalidated on success so whichever tab is open reflects the
* new state immediately.
*/
const pairedDecision = useMutation({
mutationFn: (payload: {
decision: "accept" | "cancel" | "operationAccept" | "requestChanges";
reason?: string;
note?: string;
validityDays?: number;
}) => {
const { decision, ...options } = payload;
return bookingsService.pairedDecision(bookingId, decision, options);
},
onSuccess: (data) => {
toast.success("Applied to both bookings on the shared wagon");
void invalidateBookingDetail(qc, data.booking.id);
void invalidateBookingDetail(qc, data.partner.id);
},
onError: (error) => {
toast.error(
parseApiError(error, "Failed to apply the decision to both bookings"),
);
// Nothing should have committed (the server runs both halves in one
// transaction), but refetch so the UI never shows a stale guess.
void invalidateBookingDetail(qc, bookingId);
},
});
const isPending =
pairedDecision.isPending ||
staffAccept.isPending ||
requestChanges.isPending ||
staffReject.isPending ||
@@ -134,6 +165,7 @@ export function useBookingMutations(bookingId: string) {
cancel.isPending;
return {
pairedDecision,
staffAccept,
requestChanges,
staffReject,