153 lines
5.7 KiB
TypeScript
153 lines
5.7 KiB
TypeScript
"use client"
|
|
|
|
import { Suspense, useEffect, useState } from "react"
|
|
import { useSearchParams } from "next/navigation"
|
|
import { Check, X, Loader2 } from "lucide-react"
|
|
import { Card, CardContent } from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import Link from "next/link"
|
|
|
|
interface PledgeInfo {
|
|
reference: string
|
|
amountPence: number
|
|
rail: string
|
|
donorName: string | null
|
|
eventName: string
|
|
status: string
|
|
}
|
|
|
|
function SuccessContent() {
|
|
const searchParams = useSearchParams()
|
|
const pledgeId = searchParams.get("pledge_id")
|
|
const rail = searchParams.get("rail") || "card"
|
|
const cancelled = searchParams.get("cancelled") === "true"
|
|
const [pledge, setPledge] = useState<PledgeInfo | null>(null)
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
if (!pledgeId) { setLoading(false); return }
|
|
fetch(`/api/pledges/${pledgeId}`)
|
|
.then((r) => r.json())
|
|
.then((data) => { if (data.reference) setPledge(data) })
|
|
.catch(() => {})
|
|
.finally(() => setLoading(false))
|
|
}, [pledgeId])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="text-center space-y-4">
|
|
<Loader2 className="h-10 w-10 text-trust-blue animate-spin mx-auto" />
|
|
<p className="text-muted-foreground">Confirming your payment...</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (cancelled) {
|
|
return (
|
|
<div className="max-w-md mx-auto text-center space-y-6">
|
|
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-amber-100">
|
|
<X className="h-10 w-10 text-amber-600" />
|
|
</div>
|
|
<h1 className="text-2xl font-extrabold text-gray-900">Payment Cancelled</h1>
|
|
<p className="text-muted-foreground">
|
|
Your payment was not completed. Your pledge has been saved — you can return to complete it anytime.
|
|
</p>
|
|
{pledge && (
|
|
<Card>
|
|
<CardContent className="pt-6 space-y-2 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Reference</span>
|
|
<span className="font-mono font-bold">{pledge.reference}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Amount</span>
|
|
<span className="font-bold">£{(pledge.amountPence / 100).toFixed(2)}</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
<Link href="/">
|
|
<Button variant="outline" size="lg">Return Home</Button>
|
|
</Link>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const railLabels: Record<string, string> = {
|
|
card: "Card Payment",
|
|
gocardless: "Direct Debit",
|
|
fpx: "FPX Online Banking",
|
|
bank: "Bank Transfer",
|
|
}
|
|
|
|
const nextStepMessages: Record<string, string> = {
|
|
card: "Your card payment has been processed. You'll receive a confirmation email shortly.",
|
|
gocardless: "Your Direct Debit mandate has been set up. The payment will be collected automatically in 3-5 working days.",
|
|
fpx: "Your FPX payment has been received and verified.",
|
|
bank: "Please complete the bank transfer using the reference provided.",
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-md mx-auto text-center space-y-6">
|
|
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-success-green/10">
|
|
<Check className="h-10 w-10 text-success-green" />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<h1 className="text-2xl font-extrabold text-gray-900">
|
|
{rail === "gocardless" ? "Mandate Set Up!" : "Payment Successful!"}
|
|
</h1>
|
|
<p className="text-muted-foreground">
|
|
Thank you for your generous donation
|
|
{pledge?.eventName && <> to <span className="font-semibold text-foreground">{pledge.eventName}</span></>}
|
|
</p>
|
|
</div>
|
|
{pledge && (
|
|
<Card>
|
|
<CardContent className="pt-6 space-y-3 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Amount</span>
|
|
<span className="font-bold">£{(pledge.amountPence / 100).toFixed(2)}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Method</span>
|
|
<span>{railLabels[rail] || rail}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Reference</span>
|
|
<span className="font-mono font-bold">{pledge.reference}</span>
|
|
</div>
|
|
{pledge.donorName && (
|
|
<div className="flex justify-between">
|
|
<span className="text-muted-foreground">Donor</span>
|
|
<span>{pledge.donorName}</span>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
<div className="rounded-2xl bg-trust-blue/5 border border-trust-blue/20 p-4 space-y-2">
|
|
<p className="text-sm font-medium text-trust-blue">What happens next?</p>
|
|
<p className="text-sm text-muted-foreground">{nextStepMessages[rail] || nextStepMessages.card}</p>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground">
|
|
Need help? Contact the charity directly.{pledge && <> Ref: {pledge.reference}</>}
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function SuccessPage() {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
|
<Suspense fallback={
|
|
<div className="text-center space-y-4">
|
|
<Loader2 className="h-10 w-10 text-trust-blue animate-spin mx-auto" />
|
|
<p className="text-muted-foreground">Loading...</p>
|
|
</div>
|
|
}>
|
|
<SuccessContent />
|
|
</Suspense>
|
|
</div>
|
|
)
|
|
}
|