live: connect ship-log extension to /live page, fix repo link

This commit is contained in:
2026-03-02 19:10:39 +08:00
parent 3bc412320b
commit d08c6a0f3e
173 changed files with 30458 additions and 43 deletions

View File

@@ -0,0 +1,152 @@
"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>
)
}