mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Fix waiting time loading
This commit is contained in:
@@ -7,7 +7,7 @@ import { useBookingStore } from "@/lib/booking-store";
|
||||
import { usePaymentStore } from "@/lib/payment-store";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiClient } from "@/lib/api-client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { CheckCircle, Clock, Copy, Train, FileText } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
import { isChild, isFirstChild } from "@/utils/fare-utils";
|
||||
@@ -60,6 +60,27 @@ export default function ConfirmationPage() {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isGeneratingVoucher, setIsGeneratingVoucher] = useState(false);
|
||||
|
||||
// Grace period after landing on this page: keep showing the generic "processing"
|
||||
// spinner instead of the "payment pending" screen, and poll the payment intent
|
||||
// frequently — booking.status and paymentIntent.status flip to CONFIRMED/SUCCEEDED
|
||||
// together (see finalizePaymentSuccess in payments.service.ts), so a payment that
|
||||
// already succeeded at the provider often just needs a few more seconds for its
|
||||
// webhook to reach us. Once the grace period elapses, fall back to the normal
|
||||
// pending screen with slower background polling.
|
||||
const CONFIRMATION_GRACE_PERIOD_MS = 10_000;
|
||||
const FAST_POLL_INTERVAL_MS = 2_500;
|
||||
const SLOW_POLL_INTERVAL_MS = 10_000;
|
||||
const mountTimeRef = useRef(Date.now());
|
||||
const [withinGracePeriod, setWithinGracePeriod] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(
|
||||
() => setWithinGracePeriod(false),
|
||||
CONFIRMATION_GRACE_PERIOD_MS,
|
||||
);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// Warms the code-split voucher module ahead of the click so the handler's own
|
||||
// `await import(...)` resolves near-instantly — on iOS Safari, a file save triggered
|
||||
// too long after the originating click's synchronous execution window is silently
|
||||
@@ -92,15 +113,20 @@ export default function ConfirmationPage() {
|
||||
enabled: !!bookingId,
|
||||
});
|
||||
|
||||
// Poll the payment intent every 10 s while the booking is PENDING_PAYMENT.
|
||||
// The backend auto-confirms (and generates tickets) when the payment-api reports
|
||||
// SUCCEEDED, so detecting that here means the booking is now CONFIRMED — refetch
|
||||
// to update the UI without requiring the user to refresh.
|
||||
// Poll the payment intent while the booking is PENDING_PAYMENT — fast during the
|
||||
// grace period (catches a webhook that's just a few seconds behind), then slower
|
||||
// in the background afterward. The backend auto-confirms (and generates tickets)
|
||||
// when the payment-api reports SUCCEEDED, so detecting that here means the
|
||||
// booking is now CONFIRMED — refetch to update the UI without requiring the user
|
||||
// to refresh.
|
||||
const { data: intentStatus } = useQuery<any>({
|
||||
queryKey: ["payment-intent-status", bookingId],
|
||||
queryFn: () => apiClient.get(`/payments/intents/${bookingId}`),
|
||||
enabled: _booking?.status === "PENDING_PAYMENT" && !!bookingId,
|
||||
refetchInterval: 10_000,
|
||||
refetchInterval: () =>
|
||||
Date.now() - mountTimeRef.current < CONFIRMATION_GRACE_PERIOD_MS
|
||||
? FAST_POLL_INTERVAL_MS
|
||||
: SLOW_POLL_INTERVAL_MS,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -279,8 +305,11 @@ export default function ConfirmationPage() {
|
||||
// without this, _booking is briefly undefined on first load, isConfirmed reads
|
||||
// as false, and the page flashes "payment pending" before flipping to
|
||||
// "confirmed" once the fetch resolves (common, since the payment webhook has
|
||||
// often already completed by the time the user lands here).
|
||||
if (isBookingLoading) {
|
||||
// often already completed by the time the user lands here). Also keep showing
|
||||
// this same spinner through the grace period above if the booking is still
|
||||
// PENDING_PAYMENT — most of the time the webhook lands within that window, so
|
||||
// the user goes straight to "confirmed" without ever seeing "pending" at all.
|
||||
if (isBookingLoading || (withinGracePeriod && _booking?.status === "PENDING_PAYMENT")) {
|
||||
return (
|
||||
<div className="booking-page">
|
||||
<div className="container mx-auto px-4">
|
||||
|
||||
Reference in New Issue
Block a user