diff --git a/pledge-now-pay-later/src/app/(auth)/signup/page.tsx b/pledge-now-pay-later/src/app/(auth)/signup/page.tsx index 39dac8a..a7dfe72 100644 --- a/pledge-now-pay-later/src/app/(auth)/signup/page.tsx +++ b/pledge-now-pay-later/src/app/(auth)/signup/page.tsx @@ -6,137 +6,115 @@ import { useRouter } from "next/navigation" import Link from "next/link" export default function SignupPage() { + const [step, setStep] = useState<"form" | "loading">("form") const [charityName, setCharityName] = useState("") - const [name, setName] = useState("") const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [error, setError] = useState("") - const [loading, setLoading] = useState(false) const router = useRouter() const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() + if (!charityName.trim() || !email.trim() || !password) return setError("") - setLoading(true) + setStep("loading") try { const res = await fetch("/api/auth/signup", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email, password, name, charityName }), + body: JSON.stringify({ email, password, charityName, name: "" }), }) - const data = await res.json() if (!res.ok) { - setError(data.error || "Failed to create account") - setLoading(false) + setError(data.error || "Something went wrong") + setStep("form") return } - // Auto sign in - const result = await signIn("credentials", { - email, - password, - redirect: false, - }) - + // Auto sign in and go straight to dashboard + const result = await signIn("credentials", { email, password, redirect: false }) if (result?.error) { - setError("Account created but couldn't sign in. Try logging in.") - setLoading(false) + setError("Account created — please sign in") + setStep("form") } else { - router.push("/dashboard/setup") + router.push("/dashboard") } } catch { - setError("Something went wrong. Please try again.") - setLoading(false) + setError("Connection error. Try again.") + setStep("form") } } + if (step === "loading") { + return ( +
+
+
+ 🤲 +
+

Setting up your charity...

+
+
+ ) + } + return (
-
+
-
+
🤲
-

Get Started Free

-

Set up your charity in 2 minutes

+

Start collecting pledges

+

Free. 30 seconds. No card.

-
+ {error && ( -
- {error} -
+
{error}
)} -
- - setCharityName(e.target.value)} - className="mt-1 w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" - placeholder="e.g. Islamic Relief UK" - required - /> -
+ setCharityName(e.target.value)} + className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" + placeholder="Your charity or mosque name" + required + autoFocus + /> -
- - setName(e.target.value)} - className="mt-1 w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" - placeholder="e.g. Fatima Khan" - /> -
+ setEmail(e.target.value)} + className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" + placeholder="Your email" + required + /> -
- - setEmail(e.target.value)} - className="mt-1 w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" - placeholder="you@charity.org" - required - /> -
- -
- - setPassword(e.target.value)} - className="mt-1 w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" - placeholder="Min 8 characters" - required - minLength={8} - /> -
+ setPassword(e.target.value)} + className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all" + placeholder="Pick a password (8+ chars)" + required + minLength={8} + /> - -

- Free forever. No credit card needed. Takes 2 minutes. -

-

- Already have an account?{" "} - - Sign In - +

+ Already have an account? Sign in

diff --git a/pledge-now-pay-later/src/app/api/onboarding/route.ts b/pledge-now-pay-later/src/app/api/onboarding/route.ts new file mode 100644 index 0000000..1797793 --- /dev/null +++ b/pledge-now-pay-later/src/app/api/onboarding/route.ts @@ -0,0 +1,38 @@ +import { NextResponse } from "next/server" +import prisma from "@/lib/prisma" +import { getOrgId } from "@/lib/session" + +/** + * GET /api/onboarding — check setup progress for current org + */ +export async function GET() { + const orgId = await getOrgId(null) + if (!orgId || !prisma) return NextResponse.json({ steps: [] }) + + const [org, eventCount, qrCount, pledgeCount] = await Promise.all([ + prisma.organization.findUnique({ + where: { id: orgId }, + select: { name: true, bankSortCode: true, bankAccountNo: true, bankAccountName: true }, + }), + prisma.event.count({ where: { organizationId: orgId } }), + prisma.qrSource.count({ where: { event: { organizationId: orgId } } }), + prisma.pledge.count({ where: { organizationId: orgId } }), + ]) + + const hasBank = !!(org?.bankSortCode && org?.bankAccountNo) + const hasEvent = eventCount > 0 + const hasQr = qrCount > 0 + const hasPledge = pledgeCount > 0 + + const steps = [ + { id: "bank", label: "Add bank details", desc: "So donors know where to send money", done: hasBank, href: "/dashboard/settings" }, + { id: "event", label: "Create an event", desc: "Give your fundraiser a name", done: hasEvent, href: "/dashboard/events" }, + { id: "qr", label: "Generate a QR code", desc: "One per table or volunteer", done: hasQr, href: hasEvent ? "/dashboard/events" : "/dashboard/events" }, + { id: "pledge", label: "Get your first pledge", desc: "Share the link or scan the QR", done: hasPledge, href: "/dashboard/pledges" }, + ] + + const completed = steps.filter(s => s.done).length + const allDone = completed === steps.length + + return NextResponse.json({ steps, completed, total: steps.length, allDone, orgName: org?.name }) +} diff --git a/pledge-now-pay-later/src/app/dashboard/page.tsx b/pledge-now-pay-later/src/app/dashboard/page.tsx index ac6f546..861cec7 100644 --- a/pledge-now-pay-later/src/app/dashboard/page.tsx +++ b/pledge-now-pay-later/src/app/dashboard/page.tsx @@ -6,7 +6,7 @@ import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Progress } from "@/components/ui/progress" import { formatPence } from "@/lib/utils" -import { TrendingUp, Users, Banknote, AlertTriangle, Calendar, Clock, CheckCircle2, ArrowRight, Loader2, MessageCircle, ExternalLink } from "lucide-react" +import { TrendingUp, Users, Banknote, AlertTriangle, Calendar, Clock, CheckCircle2, ArrowRight, Loader2, MessageCircle, ExternalLink, Circle } from "lucide-react" import Link from "next/link" interface DashboardData { @@ -31,6 +31,7 @@ export default function DashboardPage() { const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [whatsappStatus, setWhatsappStatus] = useState(null) + const [onboarding, setOnboarding] = useState<{ steps: Array<{ id: string; label: string; desc: string; done: boolean; href: string }>; completed: number; total: number; allDone: boolean } | null>(null) const fetchData = useCallback(() => { fetch("/api/dashboard") @@ -43,6 +44,7 @@ export default function DashboardPage() { useEffect(() => { fetchData() fetch("/api/whatsapp/send").then(r => r.json()).then(d => setWhatsappStatus(d.connected)).catch(() => {}) + fetch("/api/onboarding").then(r => r.json()).then(d => { if (d.steps) setOnboarding(d) }).catch(() => {}) const interval = setInterval(fetchData, 15000) return () => clearInterval(interval) }, [fetchData]) @@ -55,22 +57,61 @@ export default function DashboardPage() { ) } - if (!data) { + if (!data || (data.summary.totalPledges === 0 && onboarding && !onboarding.allDone)) { + // Show getting-started checklist + const ob = onboarding return ( -
- -

Welcome to Pledge Now, Pay Later

-

- Start by configuring your organisation's bank details, then create your first event. -

-
- - - - - - +
+
+
+ 🤲 +
+

Let's get you set up

+

4 quick steps, then you're collecting pledges

+ + {ob && ( + <> + +

{ob.completed} of {ob.total} done

+ +
+ {ob.steps.map((step, i) => { + const isNext = !step.done && ob.steps.slice(0, i).every(s => s.done) + return ( + +
+ {step.done ? ( + + ) : isNext ? ( +
{i + 1}
+ ) : ( + + )} +
+

{step.label}

+

{step.desc}

+
+ {isNext && } +
+ + ) + })} +
+ + )} + + {(!ob || ob.completed === 0) && ( +
+

+ 💡 Tip: Add your bank details first — that's the only thing you need before donors can pledge. +

+
+ )}
) }