diff --git a/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx index 19bae536b..d60918752 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/confirmation/page.tsx @@ -1,6 +1,7 @@ "use client"; export const dynamic = "force-dynamic"; +export const fetchCache = "force-no-store"; import { useRouter } from "next/navigation"; import { useBookingStore } from "@/lib/booking-store"; @@ -105,15 +106,8 @@ export default function ConfirmationPage() { // common) as normal pending state forever, since a caught error that returns data // looks like a success to React Query and never gets retried. queryFn: (): Promise => apiClient.get(`/bookings/${bookingId}`), - // Payment status must never be served from a stale cache — the app-wide default - // (providers.tsx) is a 60s staleTime, which would otherwise block React Query's - // own refetch-on-window-focus from firing (it only refetches stale data). Without - // this override, a tab left open past a payment completing can sit showing - // "pending" long after it's actually confirmed, even after being refocused, - // until the interval below happens to tick — which browsers throttle heavily in - // backgrounded tabs, so that can take a very long time. staleTime: 0, - enabled: !!bookingId, + enabled: !!bookingId && typeof window !== 'undefined', // Keep polling after CONFIRMED until tickets are issued — ticket generation runs // async after the booking transaction commits (see finalizePaymentSuccess in // payments.service.ts), so the first CONFIRMED fetch often returns an empty @@ -143,7 +137,7 @@ export default function ConfirmationPage() { const { data: intentStatus } = useQuery({ queryKey: ["payment-intent-status", bookingId], queryFn: () => apiClient.get(`/payments/intents/${bookingId}`), - enabled: _booking?.status === "PENDING_PAYMENT" && !!bookingId, + enabled: _booking?.status === "PENDING_PAYMENT" && !!bookingId && typeof window !== 'undefined', staleTime: 0, refetchInterval: () => Date.now() - mountTimeRef.current < CONFIRMATION_GRACE_PERIOD_MS @@ -155,7 +149,7 @@ export default function ConfirmationPage() { if (intentStatus?.status === "SUCCEEDED") { refetchBooking(); } - }, [intentStatus?.status]); + }, [intentStatus?.status, refetchBooking]); // Only trust an actually-confirmed booking to show ticket numbers / a "CONFIRMED" badge — // a gateway redirect back here does not mean payment succeeded (see payment return pages). diff --git a/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx index 789cf20a6..4b9148001 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/review/page.tsx @@ -94,14 +94,6 @@ export default function ReviewPage() { return suffix ? `${base}${suffix}` : base; }; - // The seat class/category is chosen once per leg (coach type selected on /booking/seats), - // so every seat on that leg shares it — no need to look it up per-seat. - const formatSeatClass = (schedule: any): string => { - const raw = schedule?.seatClassName; - if (!raw) return ''; - return String(raw).replace(/_/g, ' '); - }; - useEffect(() => { const fetchSeatDetails = async () => { try {