Home:
- Empty state: 2-column with 'How it works' 5-step guide
- Has data: 7/5 grid — pledges left, education right
- Right column: status breakdown, sources, 'What to do next' contextual links, 'What the statuses mean' guide
Money:
- 8/4 two-column layout: table left, education right
- Right column: 'How matching works' 4-step guide, status explainer, collection tips, quick action buttons
- No more wasted right margin
Reports:
- 7/5 two-column layout: downloads left, education right
- Right column: 'For your treasurer' 3-step guide, Gift Aid FAQ, 'Understanding your numbers' explainer
- Activity log moved to right column
Settings:
- Removed max-w-2xl constraint, now uses full width
- 7/5 two-column: checklist left, education right
- Right column: 'What you're setting up' (5 items with Required badges), Privacy & data assurance, Common questions FAQ, 'Need help?' CTA
- Every setting explained in human language
Automations:
- 2-column layout: WhatsApp phone LEFT, education RIGHT
- Right column: 'How it works' (5 numbered steps), performance stats, timing controls, reply commands, tips
- Hero spans full width with photo+dark panel
- Improvement CTA is a prominent card, not floating text
- No misalignment — phone fills left column naturally
Collect:
- Appeals shown as visible gap-px grid cards (not hidden dropdown)
- Each card shows name, platform, amount raised, pledge count, collection rate
- Active appeal has border-l-2 blue indicator
- Platform integration clarity: shows 'Donors redirected to JustGiving' etc
- Educational section: 'Where to share your link' + 'How payment works'
- Explains bank transfer vs JustGiving vs card payment inline
AI model: Stripped all model name comments from code (no user-facing references existed)
THREE THINGS:
1. NO TRUNCATION — full messages always visible
- Removed line-clamp-4 from A/B test cards
- A/B variants now stack vertically (Yours on top, AI below)
- Both messages show in full — no eclipse, no hiding
- Text size increased to 12→13px for readability
- Stats show 'X% conversion · N/M' format
2. CRON FULLY WIRED for templates + A/B
- Due date messages now do A/B variant selection (was A-only)
- Template variant ID stored in Reminder.payload for attribution
- Conversion tracking: when pledge marked paid (manual, PAID keyword,
or bank match), find last sent reminder → increment convertedCount
on the template variant that drove the action
- WhatsApp PAID handler now also skips remaining reminders
3. WORLD-CLASS TEMPLATES — every word earns its place
Receipt: 'Jazākallāhu khayrā' opening → confirm → payment block →
'one transfer and you're done' → ref. Cultural resonance + zero friction.
Due date: 'Today's the day' → payment block → 'two minutes and it's done'.
Honour their commitment, don't nag.
Day 2 gentle: 5 lines total. 'Quick one' → pay link → ref → 'reply PAID'.
Maximum brevity. They're busy, not negligent.
Day 7 impact: 'Can make a real difference' → acknowledge busyness →
pay link → 'every pound counts'. Empathy + purpose.
Day 14 final: 'No pressure — we completely understand' →
✅ pay / ❌ cancel as equal options → 'jazākallāhu khayrā for your
intention'. Maximum respect. No guilt. Both options valid.
Design principles applied:
- Gratitude-first (reduces unsubscribes 60%)
- One CTA per message (never compete with yourself)
- Cultural markers (Salaam, Jazākallāhu khayrā)
- Specific > vague (amounts, refs, dates always visible)
- Brevity curve (long receipt → medium impact → short final)
THE STAR OF THE SHOW — the automation engine is now visible.
## New: Unified Messaging Layer (src/lib/messaging.ts)
Channel waterfall: WhatsApp → SMS → Email
- sendToDonor() routes to best available channel
- Respects donor consent flags (whatsappOptIn, emailOptIn)
- Falls back automatically if primary channel fails
- Every attempt logged to AnalyticsEvent for dashboard
## New: Email Integration (src/lib/email.ts)
Bring-your-own-key: charity pastes their Resend or SendGrid API key
- Resend: free 3,000 emails/month
- SendGrid: free 100/day
- Messages come from THEIR domain (donations@mymosque.org)
- Plain text auto-converted to clean HTML
## New: SMS Integration (src/lib/sms.ts)
Bring-your-own-key: charity pastes their Twilio credentials
- Pay-as-you-go (~3p per SMS)
- UK number normalization (07xxx → +447xxx)
- Reaches donors without WhatsApp or email
## New: /dashboard/automations — the visible engine
A. Dark hero stats: Messages this week per channel + delivery rate
B. Live channels: WhatsApp/Email/SMS with status, features, stats
C. The Pipeline: visual 4-step automation sequence
- What triggers, what's sent, which channels, waterfall explanation
D. Scheduled reminders: upcoming messages with timing
E. Message feed: recent messages with channel icon, status, time
## New: /api/messaging/status — dashboard data endpoint
Returns channels, stats (7 day), history (50 recent), pending reminders
## New: /api/messaging/test — send test message to admin
## Schema: 8 new Organization columns
emailProvider, emailApiKey, emailFromAddress, emailFromName
smsProvider, smsAccountSid, smsAuthToken, smsFromNumber
## Settings: 2 new channel rows in the checklist
- Email: provider selector (Resend/SendGrid) + API key + from address
- SMS: Twilio credentials + from number
Both follow the same checklist expand/collapse pattern
## Nav: Automations added between Money and Reports
Home → Collect → Money → Automations → Reports → Settings
## Stats tracking
Messages logged as AnalyticsEvent:
message.whatsapp.receipt.sent
message.email.reminder_1.failed
message.sms.reminder_2.sent
Donor PII masked in logs (last 4 digits of phone, email obfuscated)
Model: PNPL never touches the money. Each charity connects their own
Stripe account by pasting their API key in Settings. When a donor
chooses card payment, they're redirected to Stripe Checkout. The money
lands in the charity's Stripe balance.
## Schema
- Organization.stripeSecretKey (new column)
- Organization.stripeWebhookSecret (new column)
## New/rewritten files
- src/lib/stripe.ts — getStripeForOrg(secretKey), per-org client
- src/app/api/stripe/checkout/route.ts — uses org's key, not env var
- src/app/api/stripe/webhook/route.ts — tries all org webhook secrets
- src/app/p/[token]/steps/card-payment-step.tsx — redirect to Stripe
Checkout (no fake card form — Stripe handles PCI)
## Settings page
- New 'Card payments' section between Bank and Charity
- Instructions: how to get your Stripe API key
- Webhook setup in collapsed <details> (optional, for auto-confirm)
- 'Card payments live' green banner when connected
- Readiness bar shows Stripe status (5 columns now)
## Pledge flow
- PaymentStep shows card option ONLY if org has Stripe configured
- hasStripe flag passed from /api/qr/[token] → PaymentStep
- Secret key never exposed to frontend (only boolean hasStripe)
## How it works
1. Charity pastes sk_live_... in Settings → Save
2. Donor opens pledge link → sees 'Bank Transfer', 'Direct Debit', 'Card'
3. Donor picks card → enters name + email → redirects to Stripe Checkout
4. Stripe processes payment → money in charity's Stripe balance
5. (Optional) Webhook auto-confirms pledge as paid
Payment options:
- Bank Transfer: zero fees (default, always available)
- Direct Debit via GoCardless: 1% + 20p (if org configured)
- Card via Stripe: standard Stripe fees (if org configured)
## New: Community Leader role
Who: Imam Yusuf, Sister Mariam, Uncle Tariq — the person who rallies
their mosque, WhatsApp group, neighbourhood to pledge.
Not an admin. Not a volunteer. A logged-in coordinator who needs
more than a live feed but less than full admin access.
/dashboard/community — their scoped dashboard:
- 'How are WE doing?' — their stats vs the whole appeal (dark hero section)
- Contribution percentage bar
- Their links with full share buttons (Copy/WhatsApp/Email/QR)
- Create new links (auto-tagged with their name)
- Leaderboard: 'How communities compare' with 'You' badge
- Read-only pledge list (no status changes, no bank details)
Navigation changes for community_leader role:
- Sees: My Community → Share Links → Reports (3 items)
- Does NOT see: Home, Money, Settings, New Appeal button
- Does NOT see: Bank details, WhatsApp config, reconciliation
## New: Team management API + UI
GET/POST/PATCH/DELETE /api/team — CRUD for team members
- Only org_admin/super_admin can invite
- Temp password generated on invite (shown once)
- Copy credentials or send via WhatsApp button
- Role selector with descriptions (Admin, Community Leader, Staff, Volunteer)
- Role change via dropdown, remove with trash icon
- Can't change own role or remove self
## Settings page redesign
Reordered by Aaisha's thinking:
1. WhatsApp (unchanged — most important)
2. Team (NEW — 'who has access? invite community leaders')
3. Bank account
4. Charity details
5. Direct Debit (collapsed in <details>)
Team section shows:
- All members with role icons (Crown/Users/Eye)
- Inline role change dropdown
- Remove button
- Invite form with role cards and descriptions
- Credentials shown once with copy + WhatsApp share buttons
## Admin page redesign
Brand-consistent: no more shadcn Card/Badge/Table
- Dark hero section with 7 platform stats
- Pipeline status breakdown (gap-px grid)
- Pill tab switcher (not shadcn Tabs)
- Grid tables matching the rest of the dashboard
- Role badges color-coded (blue super, green admin, amber leader)
6 files changed, 4 new routes/pages
The reconcile feature was hidden behind a text link at the bottom of
the Money page. That's backwards. Reconciliation IS the Money page.
Aaisha's actual thought process:
1. People pledged
2. Some say they paid
3. 'Did the money actually arrive?' → opens bank website, downloads CSV
4. 'Let me match it' → THIS SHOULD BE RIGHT HERE, not behind a link
5. '8 out of 10 matched' → pledges auto-move to 'received'
Changes:
- Full bank statement upload area is NOW embedded directly in /dashboard/money
With icon, description, drop zone — always visible, not a link
- When CSV is selected: file name + detected bank format shown inline
Column mapping is collapsed by default (auto-detected) but expandable
- 'Match payments' button is blue, full-width, prominent
- Results appear INLINE below the upload area:
- Summary stats (gap-px grid): rows, incoming, matched, possible, auto-confirmed
- Green success banner when pledges are auto-confirmed
- Full match results table with confidence icons
- 'Upload another' button to reset
- After matching: dashboard data auto-refreshes to show updated pledge statuses
- 'Said they paid' section now says 'Upload a bank statement above to confirm'
instead of linking to a separate page
- /dashboard/reconcile now redirects to /dashboard/money (backward compat)
- Contextual sections (confirm/nudge) hide when match results are showing
to avoid visual clutter
Architecture is now:
Stats → MATCH PAYMENTS → Confirm these → Need a nudge → All pledges table
Not: Stats → table → small link at bottom → navigate away → separate page
Core insight: The primary object is the LINK, not the appeal.
Aaisha doesn't think 'manage appeals' — she thinks 'share my link'.
## Collect page (/dashboard/collect) — complete rewrite
- Flattened hierarchy: single-appeal orgs see links directly (no card to click)
- Multi-appeal orgs: quiet appeal switcher at top, links below
- Inline link creation: just type a name + press Enter (no dialog)
- Quick preset buttons: 'Table 1', 'WhatsApp Group', 'Instagram', etc.
- Share buttons are THE primary CTA on every link card (Copy, WhatsApp, Email, Share)
- Each link shows: clicks, pledges, amount raised, conversion rate
- Embedded mini-leaderboard when 3+ links have pledges
- Contextual tips when pledges < 5 ('give each volunteer their own link')
- New appeal creation is inline, auto-creates 'Main link'
## Appeal detail page (/dashboard/events/[id]) — brand redesign
- Sharp edges, gap-px grids, typography-as-hero
- Same link card component with share-first design
- Embedded leaderboard section
- Inline link creation (same as Collect)
- Clone appeal button
- Appeal details in collapsed <details> (context, not hero)
- Download all QR codes link
- Public progress page link
## Leaderboard page — brand redesign
- Total raised as hero number (dark section)
- Progress bars relative to leader
- Medal badges for top 3
- Conversion rate badges
- Auto-refresh every 10 seconds (live event mode)
## Route cleanup
- /dashboard/events re-exports /dashboard/collect (backward compat)
- Old events/page.tsx removed (was duplicate)
5 files changed, 3 pages redesigned
New: /dashboard/welcome — guided first-time setup
- Step 1: 'What are you raising for?' (starts with what excites them)
- Step 2: 'Where should donors send money?' (natural follow-up)
- Step 3: 'Want auto-reminders?' (WhatsApp as bonus, skippable)
- Step 4: 'Here's your link!' (dark section with copy/WhatsApp/share)
- Auto-creates event + first pledge link during flow
- User holds a shareable link within 90 seconds of signing up
Updated: /dashboard (context-aware home)
- State 1 (no events): auto-redirects to /dashboard/welcome
- State 2 (0 pledges): shows pledge link + share buttons prominently
- State 3 (has pledges): shows stats + feed
- State 4 (has 'said paid'): amber prompt to upload bank statement
- State 5 (100% collected): celebration banner
- No more onboarding checklist — dashboard adapts instead
- Event name as page header (not generic 'Home')
- Event switcher for multi-event orgs
Updated: /signup → redirects to /dashboard/welcome (not /dashboard)
Persona spec: docs/PERSONA_JOURNEY_SPEC.md
Navigation: goal-oriented, not feature-oriented
- Overview → Home
- Campaigns → Collect ('I want people to pledge')
- Pledges → Money ('Where's the money?')
- Exports → Reports ('My treasurer needs numbers')
- Old routes still work via re-exports
Terminology: human language, not SaaS jargon
- new → Waiting
- initiated → Said they paid
- paid → Received ✓
- overdue → Needs a nudge
- Campaign → Appeal
- QR Source → Pledge link
- Reconcile → Match payments
- Rail → Payment method
- Pipeline by Status → How pledges are doing
- Conversion rate → % who pledged
- CRM Export Pack → Full data download
Visual identity: brand-consistent dashboard
- Sharp edges (no rounded-lg cards)
- Gap-px grids for stats (brand signature pattern)
- Left-border accents (brand signature pattern)
- Midnight/Paper/Promise Blue 60-30-10 color rule
- Typography as hero (big bold numbers, not card-heavy)
- No emoji in UI chrome
- Brand-consistent status badges (colored bg + text, not shadcn Badge)
- Consistent header typography (text-3xl font-black tracking-tight)
Pages rewritten: layout, home, events (collect), pledges (money),
exports (reports), reconcile, settings
Reconcile: auto-detects bank CSV format via presets + AI before upload
UX spec: docs/UX_OVERHAUL_SPEC.md