feat: remove FPX, add UK charity persona features

- Remove FPX payment rail entirely (Malaysian, not UK)
- Add volunteer portal (/v/[code]) with live pledge tracking
- Add public event page (/e/[slug]) with progress bar + social proof
- Add fundraiser leaderboard (/dashboard/events/[id]/leaderboard)
- Add WhatsApp share buttons on confirmation, bank instructions, volunteer view
- Enhanced Gift Aid UX with +25% bonus display and HMRC declaration text
- Gift Aid report export (HMRC-ready CSV filter)
- Volunteer view link + WhatsApp share on QR code cards
- Updated home page: 4 personas, 3 UK payment rails, 8 features
- Public event API endpoint with privacy-safe donor name truncation
- Volunteer API with stats, conversion rate, auto-refresh
This commit is contained in:
2026-03-03 03:47:18 +08:00
parent 1389c848b2
commit 0236867c88
32 changed files with 2293 additions and 494 deletions

View File

@@ -1,9 +1,9 @@
"use client"
import { Building2, CreditCard, Landmark, Globe } from "lucide-react"
import { Building2, CreditCard, Landmark } from "lucide-react"
interface Props {
onSelect: (rail: "bank" | "gocardless" | "card" | "fpx") => void
onSelect: (rail: "bank" | "gocardless" | "card") => void
amount: number
}
@@ -18,34 +18,31 @@ export function PaymentStep({ onSelect, amount }: Props) {
subtitle: "Zero fees — 100% goes to charity",
tag: "Recommended",
tagColor: "bg-success-green text-white",
detail: "Use your banking app to transfer directly",
detail: "Use your banking app to transfer directly. We'll give you the details.",
fee: "No fees",
feeColor: "text-success-green",
},
{
id: "gocardless" as const,
icon: Landmark,
title: "Direct Debit",
subtitle: "Automatic collection — set and forget",
tag: "Low fees",
tag: "Set up once",
tagColor: "bg-trust-blue/10 text-trust-blue",
detail: "We'll collect via GoCardless",
detail: "We'll collect via GoCardless. Protected by the Direct Debit Guarantee.",
fee: "1% + 20p",
feeColor: "text-muted-foreground",
},
{
id: "card" as const,
icon: CreditCard,
title: "Card Payment via Stripe",
subtitle: "Pay now by Visa, Mastercard, Amex",
tag: "Stripe",
title: "Debit or Credit Card",
subtitle: "Pay instantly by Visa, Mastercard, or Amex",
tag: "Instant",
tagColor: "bg-purple-100 text-purple-700",
detail: "Secure payment powered by Stripe",
},
{
id: "fpx" as const,
icon: Globe,
title: "FPX Online Banking",
subtitle: "Pay via Malaysian bank account",
tag: "Malaysia",
tagColor: "bg-amber-500/10 text-amber-700",
detail: "Instant payment from 18 Malaysian banks",
detail: "Secure payment powered by Stripe. Receipt emailed immediately.",
fee: "1.4% + 20p",
feeColor: "text-muted-foreground",
},
]
@@ -56,7 +53,7 @@ export function PaymentStep({ onSelect, amount }: Props) {
How would you like to pay?
</h1>
<p className="text-lg text-muted-foreground">
Pledge: <span className="font-bold text-foreground">£{pounds}</span>
Your pledge: <span className="font-bold text-foreground">£{pounds}</span>
</p>
</div>
@@ -72,16 +69,17 @@ export function PaymentStep({ onSelect, amount }: Props) {
<opt.icon className="h-6 w-6 text-trust-blue" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<div className="flex items-center gap-2 flex-wrap">
<span className="font-bold text-gray-900">{opt.title}</span>
{opt.tag && (
<span className={`text-xs font-semibold px-2 py-0.5 rounded-full ${opt.tagColor}`}>
{opt.tag}
</span>
)}
<span className={`text-xs font-semibold px-2 py-0.5 rounded-full ${opt.tagColor}`}>
{opt.tag}
</span>
</div>
<p className="text-sm text-muted-foreground mt-0.5">{opt.subtitle}</p>
<p className="text-xs text-muted-foreground/70 mt-1">{opt.detail}</p>
<p className={`text-xs font-medium mt-1 ${opt.feeColor}`}>
Fee: {opt.fee}
</p>
</div>
<div className="text-muted-foreground/40 group-hover:text-trust-blue transition-colors text-xl">
@@ -90,6 +88,10 @@ export function PaymentStep({ onSelect, amount }: Props) {
</button>
))}
</div>
<p className="text-center text-xs text-muted-foreground">
All payments are secure. Bank transfers mean 100% reaches the charity.
</p>
</div>
)
}