insanely simple onboarding: 1-screen signup → dashboard checklist
OLD FLOW (8+ screens): signup (4 fields) → auto-login → setup wizard step 1 → step 2 → step 3 → step 4 → dashboard NEW FLOW (2 screens): signup (3 fields) → dashboard with inline checklist - Signup page: just charity name + email + password. No 'your name' field. One button. - Dashboard: shows getting-started checklist when org has no pledges yet - /api/onboarding: returns setup progress (bank, event, qr, pledge) - Checklist: progress bar, next-step highlighting, done states with strikethrough - Each step links directly to the right page (settings, events, pledges) - Tip shown for brand new orgs: 'Add bank details first' - No more separate setup wizard — guidance is inline on the dashboard - Signup loading state: pulsing emoji while account creates
This commit is contained in:
@@ -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<DashboardData | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [whatsappStatus, setWhatsappStatus] = useState<boolean | null>(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 (
|
||||
<div className="text-center py-20 space-y-4">
|
||||
<Calendar className="h-12 w-12 text-muted-foreground mx-auto" />
|
||||
<h2 className="text-xl font-bold">Welcome to Pledge Now, Pay Later</h2>
|
||||
<p className="text-muted-foreground max-w-md mx-auto">
|
||||
Start by configuring your organisation's bank details, then create your first event.
|
||||
</p>
|
||||
<div className="flex gap-3 justify-center">
|
||||
<Link href="/dashboard/settings">
|
||||
<Button variant="outline">Configure Bank Details</Button>
|
||||
</Link>
|
||||
<Link href="/dashboard/events">
|
||||
<Button>Create First Event →</Button>
|
||||
</Link>
|
||||
<div className="max-w-lg mx-auto space-y-6 py-4">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="inline-flex h-14 w-14 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 items-center justify-center shadow-lg shadow-trust-blue/20">
|
||||
<span className="text-white text-2xl">🤲</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-black text-gray-900">Let's get you set up</h1>
|
||||
<p className="text-sm text-muted-foreground">4 quick steps, then you're collecting pledges</p>
|
||||
</div>
|
||||
|
||||
{ob && (
|
||||
<>
|
||||
<Progress value={(ob.completed / ob.total) * 100} className="h-2" indicatorClassName="bg-gradient-to-r from-trust-blue to-success-green" />
|
||||
<p className="text-xs text-center text-muted-foreground">{ob.completed} of {ob.total} done</p>
|
||||
|
||||
<div className="space-y-2">
|
||||
{ob.steps.map((step, i) => {
|
||||
const isNext = !step.done && ob.steps.slice(0, i).every(s => s.done)
|
||||
return (
|
||||
<Link key={step.id} href={step.href}>
|
||||
<div className={`flex items-center gap-3 rounded-xl border p-4 transition-all ${
|
||||
step.done ? "bg-success-green/5 border-success-green/20" :
|
||||
isNext ? "bg-trust-blue/5 border-trust-blue/20 shadow-sm" :
|
||||
"bg-white border-gray-100"
|
||||
}`}>
|
||||
{step.done ? (
|
||||
<CheckCircle2 className="h-5 w-5 text-success-green flex-shrink-0" />
|
||||
) : isNext ? (
|
||||
<div className="h-5 w-5 rounded-full bg-trust-blue text-white text-xs font-bold flex items-center justify-center flex-shrink-0">{i + 1}</div>
|
||||
) : (
|
||||
<Circle className="h-5 w-5 text-gray-300 flex-shrink-0" />
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className={`text-sm font-medium ${step.done ? "text-success-green line-through" : isNext ? "text-gray-900" : "text-gray-400"}`}>{step.label}</p>
|
||||
<p className="text-xs text-muted-foreground">{step.desc}</p>
|
||||
</div>
|
||||
{isNext && <ArrowRight className="h-4 w-4 text-trust-blue flex-shrink-0" />}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(!ob || ob.completed === 0) && (
|
||||
<div className="bg-warm-amber/5 rounded-xl border border-warm-amber/20 p-4 text-center">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
💡 <strong>Tip:</strong> Add your bank details first — that's the only thing you need before donors can pledge.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user