production auth: signup, login, protected dashboard, landing page, WAHA QR fix

AUTH:
- NextAuth with credentials provider (bcrypt password hashing)
- /api/auth/signup: creates org + user in transaction
- /login, /signup pages with clean minimal UI
- Middleware protects all /dashboard/* routes → redirects to /login
- Session-based org resolution (no more hardcoded 'demo' headers)
- SessionProvider wraps entire app
- Dashboard header shows org name + sign out button

LANDING PAGE:
- Full marketing page at / with hero, problem, how-it-works, features, CTA
- 'Get Started Free' → /signup → auto-login → /dashboard/setup
- Clean responsive design, no auth required for public pages

WAHA QR FIX:
- WAHA CORE doesn't expose QR value via API or webhook
- Now uses /api/screenshot (full browser capture) with CSS crop to QR area
- Settings panel shows cropped screenshot with overflow:hidden
- Auto-polls every 5s, refresh button

MULTI-TENANT:
- getOrgId() tries session first, then header, then first-org fallback
- All dashboard APIs use session-based org
- Signup creates isolated org per charity
This commit is contained in:
2026-03-03 05:37:04 +08:00
parent 6894f091fd
commit 4f23f28873
22 changed files with 708 additions and 221 deletions

View File

@@ -1,123 +1,188 @@
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { CreditCard, Landmark, Building2, QrCode, BarChart3, Bell, Download, Users, Gift, MessageCircle, Share2, Smartphone } from "lucide-react"
export default function Home() {
export default function HomePage() {
return (
<div className="min-h-screen bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
<div className="min-h-screen bg-white">
{/* Nav */}
<header className="sticky top-0 z-40 border-b bg-white/80 backdrop-blur-xl">
<div className="max-w-5xl mx-auto flex h-14 items-center justify-between px-4">
<div className="flex items-center gap-2.5">
<div className="h-8 w-8 rounded-xl bg-gradient-to-br from-trust-blue to-blue-600 flex items-center justify-center">
<span className="text-white text-base">🤲</span>
</div>
<span className="font-black text-sm">Pledge Now, Pay Later</span>
</div>
<div className="flex items-center gap-3">
<Link href="/login" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
Sign In
</Link>
<Link href="/signup" className="rounded-lg bg-trust-blue px-4 py-2 text-sm font-semibold text-white hover:bg-trust-blue/90 transition-colors">
Get Started Free
</Link>
</div>
</div>
</header>
{/* Hero */}
<div className="flex flex-col items-center justify-center px-4 pt-20 pb-16">
<div className="text-center max-w-2xl mx-auto space-y-8">
<div className="space-y-4">
<div className="inline-flex items-center gap-2 rounded-full bg-trust-blue/10 px-4 py-2 text-sm font-medium text-trust-blue">
Free forever for UK charities
</div>
<h1 className="text-5xl md:text-6xl font-extrabold tracking-tight text-gray-900">
Pledge Now,{" "}
<span className="text-trust-blue">Pay Later</span>
</h1>
<p className="text-xl text-muted-foreground max-w-lg mx-auto">
Turn &quot;I&apos;ll donate later&quot; into tracked pledges with automatic follow-up. Built for UK charity fundraising events.
</p>
<section className="py-16 md:py-24 px-4">
<div className="max-w-3xl mx-auto text-center space-y-6">
<div className="inline-flex items-center gap-2 bg-trust-blue/5 border border-trust-blue/20 rounded-full px-4 py-1.5 text-xs font-medium text-trust-blue">
🇬🇧 Built for UK charities · Free to start
</div>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/dashboard">
<Button size="xl">Open Dashboard </Button>
</Link>
<Link href="/p/demo">
<Button size="xl" variant="outline">Try Donor Flow </Button>
<h1 className="text-4xl md:text-5xl font-black text-gray-900 leading-tight">
Turn promises into
<span className="text-trust-blue"> payments</span>
</h1>
<p className="text-lg text-muted-foreground max-w-xl mx-auto">
At your next event, donors pledge what they want to give then pay on their own terms.
You get QR codes, WhatsApp reminders, and a dashboard to track every penny.
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<Link href="/signup" className="rounded-xl bg-trust-blue px-6 py-3.5 text-base font-semibold text-white hover:bg-trust-blue/90 transition-all shadow-lg shadow-trust-blue/20">
Start Collecting Pledges
</Link>
<a href="#how" className="rounded-xl border-2 border-gray-200 px-6 py-3.5 text-base font-semibold text-gray-700 hover:border-gray-300 transition-all">
See How It Works
</a>
</div>
<div className="grid grid-cols-3 gap-6 pt-8 border-t max-w-md mx-auto">
<div className="text-center">
<div className="text-2xl font-bold text-trust-blue">0%</div>
<div className="text-xs text-muted-foreground">Bank transfer fees</div>
<p className="text-xs text-muted-foreground">Free forever · No card needed · 2 minute setup</p>
</div>
</section>
{/* Problem */}
<section className="py-12 bg-gray-50 px-4">
<div className="max-w-4xl mx-auto">
<div className="grid md:grid-cols-3 gap-6">
<div className="bg-white rounded-2xl p-6 border">
<div className="text-3xl mb-3">😤</div>
<h3 className="font-bold mb-1">Pledges go cold</h3>
<p className="text-sm text-muted-foreground">Donors say &quot;I&apos;ll pay £500&quot; at the gala, then forget. You have no way to follow up.</p>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-success-green">15s</div>
<div className="text-xs text-muted-foreground">Pledge time</div>
<div className="bg-white rounded-2xl p-6 border">
<div className="text-3xl mb-3">📝</div>
<h3 className="font-bold mb-1">Paper tracking</h3>
<p className="text-sm text-muted-foreground">Spreadsheets, napkin notes, WhatsApp groups. No system, no references, no proof.</p>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-warm-amber">+25%</div>
<div className="text-xs text-muted-foreground">Gift Aid boost</div>
<div className="bg-white rounded-2xl p-6 border">
<div className="text-3xl mb-3">💸</div>
<h3 className="font-bold mb-1">Money left on the table</h3>
<p className="text-sm text-muted-foreground">UK charities lose 30-50% of pledged amounts because there&apos;s no follow-up system.</p>
</div>
</div>
</div>
</div>
</section>
{/* Who is this for? */}
<div className="bg-white border-y py-16">
<div className="max-w-4xl mx-auto px-4">
<h2 className="text-2xl font-bold text-center mb-2">Built for everyone in your fundraising chain</h2>
<p className="text-center text-muted-foreground mb-8">From the charity manager to the donor&apos;s phone</p>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
{/* How it works */}
<section id="how" className="py-16 px-4">
<div className="max-w-4xl mx-auto space-y-10">
<div className="text-center">
<h2 className="text-3xl font-black text-gray-900">How it works</h2>
<p className="text-muted-foreground mt-2">From pledge to payment in 4 steps</p>
</div>
<div className="grid md:grid-cols-4 gap-6">
{[
{ icon: BarChart3, title: "Charity Managers", desc: "Live dashboard, bank reconciliation, Gift Aid reports. See every pound from pledge to collection.", color: "text-trust-blue" },
{ icon: Users, title: "Volunteers", desc: "Personal QR codes, leaderboard, own pledge tracker. Know exactly who pledged at your table.", color: "text-warm-amber" },
{ icon: Smartphone, title: "Donors", desc: "15-second pledge on your phone. Clear bank details, copy buttons, reminders until paid.", color: "text-success-green" },
{ icon: Share2, title: "Personal Fundraisers", desc: "Share your pledge link on WhatsApp. Track friends and family pledges with a progress bar.", color: "text-purple-600" },
].map((p, i) => (
<div key={i} className="rounded-2xl border bg-white p-5 space-y-3 hover:shadow-md transition-shadow">
<p.icon className={`h-8 w-8 ${p.color}`} />
<h3 className="font-bold">{p.title}</h3>
<p className="text-xs text-muted-foreground leading-relaxed">{p.desc}</p>
{ step: "1", icon: "📱", title: "Donor scans QR", desc: "At your event, each table/volunteer has a unique QR code." },
{ step: "2", icon: "🤲", title: "Pledges amount", desc: "Pick an amount. Choose to pay now, on a date, or monthly instalments." },
{ step: "3", icon: "💬", title: "Gets reminders", desc: "WhatsApp messages with bank details before each due date. They reply PAID when done." },
{ step: "4", icon: "✅", title: "You reconcile", desc: "Dashboard shows who pledged, who paid, who needs a nudge. Upload bank statements to auto-match." },
].map((s) => (
<div key={s.step} className="text-center space-y-2">
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-trust-blue/5 text-2xl">
{s.icon}
</div>
<div className="inline-flex items-center justify-center w-6 h-6 rounded-full bg-trust-blue text-white text-xs font-bold">
{s.step}
</div>
<h3 className="font-bold text-sm">{s.title}</h3>
<p className="text-xs text-muted-foreground">{s.desc}</p>
</div>
))}
</div>
</div>
</div>
{/* Payment Methods */}
<div className="max-w-4xl mx-auto px-4 py-16">
<h2 className="text-2xl font-bold text-center mb-2">3 UK Payment Rails, One Platform</h2>
<p className="text-center text-muted-foreground mb-8">Every method a UK donor expects</p>
<div className="grid sm:grid-cols-3 gap-4">
{[
{ icon: Building2, title: "Bank Transfer", desc: "Zero fees — 100% to charity. Unique reference for auto-matching.", color: "text-success-green", tag: "0% fees" },
{ icon: Landmark, title: "Direct Debit", desc: "GoCardless auto-collection. Protected by the Direct Debit Guarantee.", color: "text-trust-blue", tag: "Set & forget" },
{ icon: CreditCard, title: "Card via Stripe", desc: "Visa, Mastercard, Amex. Instant payment and receipt.", color: "text-purple-600", tag: "Instant" },
].map((m, i) => (
<div key={i} className="rounded-2xl border bg-white p-6 text-center space-y-3 hover:shadow-md transition-shadow">
<m.icon className={`h-10 w-10 mx-auto ${m.color}`} />
<span className={`inline-block text-xs font-bold px-3 py-1 rounded-full ${
i === 0 ? "bg-success-green/10 text-success-green" : i === 1 ? "bg-trust-blue/10 text-trust-blue" : "bg-purple-100 text-purple-700"
}`}>{m.tag}</span>
<h3 className="font-bold">{m.title}</h3>
<p className="text-xs text-muted-foreground">{m.desc}</p>
</div>
))}
</div>
</div>
</section>
{/* Features */}
<div className="bg-white border-y py-16">
<div className="max-w-4xl mx-auto px-4">
<h2 className="text-2xl font-bold text-center mb-8">Everything a UK charity needs</h2>
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-6">
<section className="py-16 bg-gray-50 px-4">
<div className="max-w-4xl mx-auto space-y-10">
<div className="text-center">
<h2 className="text-3xl font-black text-gray-900">Everything you need</h2>
</div>
<div className="grid md:grid-cols-2 gap-4">
{[
{ icon: QrCode, title: "QR Attribution", desc: "Per-table, per-volunteer tracking. Know who raised what." },
{ icon: Gift, title: "Gift Aid Built In", desc: "One-tap declaration. HMRC-ready export. +25% on every eligible pledge." },
{ icon: Bell, title: "Smart Reminders", desc: "Automated follow-up via email and SMS until the pledge is paid." },
{ icon: Download, title: "Bank Reconciliation", desc: "Upload your CSV statement. Auto-match by unique reference." },
{ icon: MessageCircle, title: "WhatsApp Sharing", desc: "Donors share their pledge with friends. Viral fundraising built in." },
{ icon: Users, title: "Volunteer Portal", desc: "Each volunteer sees their own pledges and conversion rate." },
{ icon: BarChart3, title: "Live Dashboard", desc: "Real-time ticker during events. Pipeline from pledge to payment." },
{ icon: Share2, title: "Fundraiser Pages", desc: "Shareable links with progress bars. Perfect for personal campaigns." },
].map((f, i) => (
<div key={i} className="space-y-2">
<f.icon className="h-6 w-6 text-trust-blue" />
<h3 className="font-bold text-sm">{f.title}</h3>
<p className="text-xs text-muted-foreground">{f.desc}</p>
{ icon: "📱", title: "QR Code Generator", desc: "Unique codes per volunteer/table. Track who brings in the most." },
{ icon: "📅", title: "Flexible Scheduling", desc: "Pay now, pick a date, or split into 2-12 monthly instalments." },
{ icon: "💬", title: "WhatsApp Reminders", desc: "Auto-send bank details and reminders. Donors reply PAID, HELP, or CANCEL." },
{ icon: "🎁", title: "Gift Aid", desc: "Collect declarations inline. Export HMRC-ready CSV with one click." },
{ icon: "🏦", title: "UK Bank Transfers", desc: "Unique reference per pledge for easy reconciliation. Tap-to-copy details." },
{ icon: "📊", title: "Live Dashboard", desc: "See pledges come in real-time. Pipeline view: pending → initiated → paid." },
{ icon: "🏆", title: "Volunteer Leaderboard", desc: "Real-time scoreboard. Motivate your team with friendly competition." },
{ icon: "📤", title: "CRM Export", desc: "Download all pledge data as CSV. Gift Aid pack for HMRC." },
].map((f) => (
<div key={f.title} className="bg-white rounded-xl p-4 border flex gap-3 items-start">
<span className="text-xl">{f.icon}</span>
<div>
<h3 className="font-bold text-sm">{f.title}</h3>
<p className="text-xs text-muted-foreground mt-0.5">{f.desc}</p>
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* Donor schedule */}
<section className="py-16 px-4">
<div className="max-w-3xl mx-auto text-center space-y-8">
<h2 className="text-3xl font-black text-gray-900">Donors choose when to pay</h2>
<div className="grid grid-cols-3 gap-4">
<div className="rounded-2xl border-2 border-trust-blue/20 p-5 space-y-2">
<div className="text-3xl"></div>
<h3 className="font-bold">Pay Now</h3>
<p className="text-xs text-muted-foreground">Card, bank transfer, or Direct Debit right away</p>
</div>
<div className="rounded-2xl border-2 border-warm-amber/20 p-5 space-y-2">
<div className="text-3xl">📅</div>
<h3 className="font-bold">Pick a Date</h3>
<p className="text-xs text-muted-foreground">&quot;I&apos;ll pay on payday&quot; reminders sent automatically</p>
</div>
<div className="rounded-2xl border-2 border-success-green/20 p-5 space-y-2">
<div className="text-3xl">📆</div>
<h3 className="font-bold">Monthly</h3>
<p className="text-xs text-muted-foreground">Split into 2-12 instalments. Each one tracked separately</p>
</div>
</div>
</div>
</section>
{/* CTA */}
<section className="py-16 bg-gradient-to-br from-trust-blue to-blue-600 px-4">
<div className="max-w-2xl mx-auto text-center space-y-6">
<h2 className="text-3xl font-black text-white">Start collecting pledges today</h2>
<p className="text-blue-100">
Free to use. Set up in 2 minutes. No technical knowledge needed.
</p>
<Link href="/signup" className="inline-block rounded-xl bg-white px-8 py-4 text-base font-bold text-trust-blue hover:bg-blue-50 transition-all shadow-xl">
Create Your Free Account
</Link>
<p className="text-xs text-blue-200">Used by mosques, churches, schools and charities across the UK</p>
</div>
</section>
{/* Footer */}
<footer className="py-8 px-4 text-center text-xs text-muted-foreground space-y-2">
<p>Pledge Now, Pay Later Built for UK charities by <a href="https://calvana.quikcue.com" className="text-trust-blue hover:underline">QuikCue</a>.</p>
<p>Free forever. No hidden fees. No card required.</p>
<footer className="py-8 px-4 border-t">
<div className="max-w-4xl mx-auto flex flex-col md:flex-row items-center justify-between gap-4 text-xs text-muted-foreground">
<div className="flex items-center gap-2">
<div className="h-6 w-6 rounded-lg bg-trust-blue flex items-center justify-center">
<span className="text-white text-[10px]">🤲</span>
</div>
<span>Pledge Now, Pay Later</span>
</div>
<div className="flex gap-4">
<Link href="/login" className="hover:text-foreground">Sign In</Link>
<Link href="/signup" className="hover:text-foreground">Get Started</Link>
</div>
<span>© {new Date().getFullYear()} QuikCue Ltd</span>
</div>
</footer>
</div>
)