Fix PayPal billing flow and mobile admin UX
This commit is contained in:
@@ -4,7 +4,7 @@ import { useCheckoutWizard } from "../WizardContext";
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { CalendarDays, CheckCircle2, ClipboardList, LoaderCircle, MailCheck, QrCode, ShieldCheck, Smartphone, Sparkles, XCircle } from "lucide-react";
|
||||
import { AlertTriangle, CalendarDays, CheckCircle2, ClipboardList, LoaderCircle, MailCheck, QrCode, ShieldCheck, Smartphone, Sparkles, XCircle } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface ConfirmationStepProps {
|
||||
@@ -25,8 +25,10 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
checkoutSessionId,
|
||||
setPaymentCompleted,
|
||||
clearCheckoutSessionId,
|
||||
checkoutActionUrl,
|
||||
goToStep,
|
||||
} = useCheckoutWizard();
|
||||
const [status, setStatus] = useState<'processing' | 'completed' | 'failed'>(
|
||||
const [status, setStatus] = useState<'processing' | 'completed' | 'failed' | 'action_required'>(
|
||||
checkoutSessionId ? 'processing' : 'completed',
|
||||
);
|
||||
const [elapsedMs, setElapsedMs] = useState(0);
|
||||
@@ -79,6 +81,15 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
badge: 'bg-rose-50 text-rose-700 border-rose-200',
|
||||
};
|
||||
}
|
||||
if (status === 'action_required') {
|
||||
return {
|
||||
label: t('checkout.confirmation_step.status_state.action_required'),
|
||||
body: t('checkout.confirmation_step.status_body_action_required'),
|
||||
tone: 'text-amber-600',
|
||||
icon: AlertTriangle,
|
||||
badge: 'bg-amber-50 text-amber-700 border-amber-200',
|
||||
};
|
||||
}
|
||||
return {
|
||||
label: t('checkout.confirmation_step.status_state.processing'),
|
||||
body: t('checkout.confirmation_step.status_body_processing'),
|
||||
@@ -88,7 +99,7 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
};
|
||||
}, [status, t]);
|
||||
|
||||
const checkSessionStatus = useCallback(async (): Promise<'processing' | 'completed' | 'failed'> => {
|
||||
const checkSessionStatus = useCallback(async (): Promise<'processing' | 'completed' | 'failed' | 'action_required'> => {
|
||||
if (!checkoutSessionId) {
|
||||
return 'completed';
|
||||
}
|
||||
@@ -114,6 +125,10 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
if (remoteStatus === 'requires_customer_action') {
|
||||
return 'action_required';
|
||||
}
|
||||
|
||||
if (remoteStatus === 'failed' || remoteStatus === 'cancelled') {
|
||||
clearCheckoutSessionId();
|
||||
return 'failed';
|
||||
@@ -204,10 +219,15 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
if (status === 'failed') {
|
||||
return { payment: false, email: false, access: false };
|
||||
}
|
||||
if (status === 'action_required') {
|
||||
return { payment: false, email: false, access: false };
|
||||
}
|
||||
return { payment: true, email: false, access: false };
|
||||
}, [status]);
|
||||
|
||||
const showManualActions = status === 'processing' && elapsedMs >= 30000;
|
||||
const showActionRequired = status === 'action_required';
|
||||
const showFailedActions = status === 'failed';
|
||||
const StatusIcon = statusCopy.icon;
|
||||
|
||||
const handleStatusRetry = useCallback(async () => {
|
||||
@@ -227,6 +247,18 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleContinueCheckout = useCallback(() => {
|
||||
if (checkoutActionUrl && typeof window !== 'undefined') {
|
||||
window.open(checkoutActionUrl, '_blank', 'noopener');
|
||||
return;
|
||||
}
|
||||
goToStep('payment');
|
||||
}, [checkoutActionUrl, goToStep]);
|
||||
|
||||
const handleBackToPayment = useCallback(() => {
|
||||
goToStep('payment');
|
||||
}, [goToStep]);
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 lg:grid-cols-[2fr_1fr]">
|
||||
<div className="space-y-6">
|
||||
@@ -314,6 +346,29 @@ export const ConfirmationStep: React.FC<ConfirmationStepProps> = ({ onViewProfil
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showActionRequired && (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50/60 p-4 text-sm text-amber-900">
|
||||
<p>{t('checkout.confirmation_step.status_action_hint')}</p>
|
||||
<div className="mt-3 flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<Button type="button" onClick={handleContinueCheckout}>
|
||||
{t('checkout.confirmation_step.status_action_button')}
|
||||
</Button>
|
||||
<Button type="button" variant="ghost" onClick={handleBackToPayment}>
|
||||
{t('checkout.confirmation_step.status_action_back')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showFailedActions && (
|
||||
<div className="rounded-lg border border-rose-200 bg-rose-50/60 p-4 text-sm text-rose-900">
|
||||
<p>{t('checkout.confirmation_step.status_failed_hint')}</p>
|
||||
<div className="mt-3 flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<Button type="button" onClick={handleBackToPayment}>
|
||||
{t('checkout.confirmation_step.status_failed_back')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user