Skip to main content

Admin P1 Sprint Plan — Structural IA Collapse (All Products)

Seven ordered slices (was six — added Train AI shell). Each slice is independently shippable and testable. No slice breaks the admin dashboard if the next slices don't ship — we're additive, not rewrite.

Scope correction — 2026-04-18 (second pass)

The first version of this plan treated Pro Website as the primary use-case and proposed retiring "Training" as a top-level concept. That was wrong. The admin dashboard at /admin/[token] is a SINGLE unified dashboard serving all ChurchWiseAI product customers:

  • cwa_starter_chat / cwa_pro_chat — chatbot only
  • cwa_starter_voice / cwa_pro_voice — voice agent only
  • cwa_starter_both / cwa_pro_both / cwa_suite_both / cwa_bundle — chat + voice bundle
  • cwa_pro_website — Pro Website (includes built-in chatbot per pricing.yaml)
  • Legacy: ps_pro_website, pro_website, ps_premium

Every paying customer has a chatbot (Pro Website bundles one). Training the AI takes significant real estate — knowledge base, FAQs, agent personality, safety rules, per-tradition vocabulary, simulator. Collapsing it into a Settings slide-over would be a 25-section nightmare.

The correct IA is 4 top-level tabs, universal, gated per plan + capability:

Home · Inbox · Train AI · Website ⚙️ 🔔 [Upgrade]
  • Home — universal (all customers).
  • Inbox — universal; content adapts per plan (calls chip only if voice, prayer chip only if chat, etc.).
  • Train AI — universal (every customer trains a chatbot at minimum).
  • Website — gated: visible only if plan predicate hasProWebsite(plan) is true.
  • Settings / Upgrade — header controls, not tabs.

All slices below now include cross-cutting acceptance criteria for plan + capability gating (see end of doc).


Slice 1 — Extract Settings to a header-triggered slide-over (4 hrs)

Why first: Settings is the easiest to extract (least cross-dependency) and surfaces the header pattern we'll use for everything else.

Scope:

  • Move SettingsTab.tsx rendering into a slide-over panel triggered by a gear icon in the admin header.
  • Slide-over uses the existing SetupSlideOver.tsx component but with sub-tabs inside: Account / Team / Notifications / Integrations (+ Billing sub-tab for admin-only, gated on billing:view).
  • Remove settings from ALL_TABS in AdminDashboard.tsx.
  • Add SettingsPanel component that renders inside the slide-over, lazy-loaded.
  • Keep all existing hash-based deep links working (e.g., #notifications) — open the slide-over to the matching sub-tab.

Files touched:

  • src/app/admin/[token]/components/AdminDashboard.tsx (remove tab, add gear button + state)
  • src/app/admin/[token]/components/SettingsTab.tsx (already exists, wrap for slide-over)
  • src/app/admin/[token]/components/SettingsPanel.tsx (new — wraps sub-tabs)

Cross-cutting acceptance: Settings gear icon visibility gated on hasAnySettingsCap(member) — hidden if user has no settings-namespace capabilities. Billing sub-tab gated on billing:view.

Test (Playwright): Clicking gear opens Settings slide-over; URL hash #notifications opens to Notifications sub-tab; treasurer without settings:church_profile:edit sees Billing-only sub-tab; care_team without any settings cap sees no gear icon.

Rollback: revert the tab hide + gear button; SettingsTab continues to work as a full tab until rollback reverts.


Slice 2 — Merge Calls + Requests + Care into unified Inbox tab with adaptive filters (7 hrs — was 6)

Why second: Largest UX consolidation. Best next step once Settings is out of the way (clears tab budget).

Scope:

  • New InboxTab.tsx component with filter chips conditioned on plan:
    • All — always
    • Calls — only if hasVoice(plan)
    • Prayer Requests — only if hasChat(plan) || hasVoice(plan) (effectively all paying customers)
    • Visitors — only if hasChat(plan)
    • Callbacks — only if hasVoice(plan)
    • Safety — only if user has inbox:safety:read AND any chat/voice channel
  • Unified chronological stream (most-recent first) from all four sources, joined in memory on the client.
  • Role-gated filters: Prayer Team sees only Prayer Requests chip; Treasurer sees Inbox empty-state with "You don't have Inbox permissions" message; care_team sees prayer + visitor only.
  • Plan-gated empty states: chat-only customer sees "No calls — your plan is chat-only. [Learn about voice →]" instead of a blank Calls pane.
  • Existing per-item drill-downs (call transcript modal, prayer detail, visitor card) preserved.
  • Remove calls, requests, care from ALL_TABS.
  • Hash deep links (#calls, #requests/prayer, etc.) redirect to Inbox with the matching filter active.

Files touched:

  • src/app/admin/[token]/components/InboxTab.tsx (new, ~500 LOC with adaptive filter logic)
  • src/app/admin/[token]/components/AdminDashboard.tsx (remove 3 tabs, add Inbox, rewire hash routing)
  • src/lib/premium-queries.ts (add getInboxStream({ planPredicate, roleCaps }) that queries enabled tables + merges)
  • Existing CallsTab.tsx, RequestsTab.tsx, CareTab.tsx kept initially but unmounted from nav; mark for deletion after 1 release cycle.

Cross-cutting acceptance:

  • Chat-only customer → no Calls/Callback chips rendered.
  • Voice-only customer → no Visitors chip rendered.
  • Bundle customer → all 5 chips rendered.
  • Prayer Team role → only Prayer chip visible regardless of plan.
  • Treasurer role → Inbox shows "No Inbox permissions" empty state.

Test: Playwright covers 6 permutations: {chat-only, voice-only, bundle} × {admin, prayer_team}, plus treasurer-sees-empty.

Rollback: re-add the 3 tabs to ALL_TABS; Inbox stays as a parallel path for opt-in.


Slice 2.5 — Train AI tab shell (4 hrs — NEW)

Why added: Train AI is universal (every customer trains a chatbot), but it never had a first-class tab. Previously spread across Training tab + Settings sub-panels + knowledge-base modal. Collapse into one tab with left-rail sub-navigation.

Scope:

  • New TrainAITab.tsx with left-rail section navigation (no live preview — Train is an editor, not a renderer).
  • 5 sub-sections minimum:
    1. Church Knowledge — knowledge base CRUD (existing KnowledgeBaseEditor.tsx reused), document upload, chunk viewer, re-index button
    2. Theology & Tradition — TheoLens picker (existing) + per-tradition vocabulary boost list
    3. Agent Personality — tone, pastor voice template, greeting script, escalation rules
    4. FAQs — curated Q&A list (existing FAQ editor)
    5. Safety Rules — crisis keywords, escalation contacts, moderation thresholds (existing ModerationDashboard.tsx adapted)
  • Sixth sub-section optional: Chat Simulator — test the agent with test prompts, see which knowledge chunks are retrieved
  • Left-rail pattern mirrors Website editor visually (active = gold dot, stone-100 hover).
  • Each sub-section lazy-loads.

Files touched:

  • src/app/admin/[token]/components/TrainAITab.tsx (new)
  • src/app/admin/[token]/components/train/{ChurchKnowledge,Theology,Personality,FAQs,Safety,Simulator}.tsx (new — mostly wrappers over existing editors)
  • src/app/admin/[token]/components/AdminDashboard.tsx (add Train AI to ALL_TABS)
  • src/lib/premium-queries.ts (reuse existing KB + FAQ + moderation queries; no new DB)

Cross-cutting acceptance:

  • Every paying customer sees Train AI tab (no plan gate — Pro Website includes chat).
  • Sub-sections gated per capability: train:church_knowledge:edit, train:theology:edit, train:agents:edit, train:faqs:edit, train:safety:edit, train:simulator:use, train:documents:upload.
  • Volunteer coordinator without any train:* caps → Train AI tab hidden.
  • Admin → all sub-sections visible.

Test: Playwright covers 4 roles × Train AI tab visibility; for admin, verify all 5 sub-sections load.

Rollback: revert the tab addition; Training sub-content remains accessible via existing modals/panels.


Slice 3 — Pre-loaded sample data on fresh accounts (3 hrs)

Why third: Small scope but massive first-run impact. Independent of structural changes.

Scope:

  • On /admin/[token] first load (detected by premium.created_at < 24 hours AND voice_prayer_requests.count === 0), seed an in-memory exampleData array with:
    • 1 sample prayer request
    • 1 sample visitor contact
    • 1 sample call log summary (gated — only if hasVoice(plan))
  • Each example renders with a visible ribbon Example · tap ✕ to remove.
  • "Remove all examples" button at top of Inbox stream.
  • Examples are client-side only — never written to DB, never real data.

Files touched:

  • src/app/admin/[token]/components/InboxTab.tsx (conditional example injection)
  • src/app/admin/[token]/components/examples/ExampleRibbon.tsx (new small component)
  • src/lib/admin-examples.ts (new — exports sample data arrays, one per plan variant)

Cross-cutting acceptance: example set varies by plan — chat-only sees prayer + visitor; voice-only sees prayer + call; bundle sees all 3; Pro Website-only sees prayer + visitor.

Test: Playwright: fresh admin token → inbox shows plan-appropriate example items tagged "Example"; click ✕ on one removes it locally; clicking Remove All clears all examples.

Rollback: remove the <ExampleRibbon> injection; inbox goes back to empty state for new customers.


Slice 4 — Persistent setup checklist rail on Home (5 hrs — was 4)

Why fourth: Once Inbox + Train AI are live and empty state is solved, Home becomes the obvious place for the checklist.

Scope:

  • Convert DashboardOverview.tsx to use a 2-column grid: left = activity summary + share links, right = setup checklist rail.
  • Rail uses existing SetupGuide.tsx steps but renders as a dense vertical list — no gold cards, no slide-over triggers from the rail itself (those still open from clicking).
  • Progress bar at top of rail: "3 of N done · 43%" — N varies by plan.
  • Plan-specific checklist steps:
    • Universal: Church name, Pastor tone, Theology tradition, Write 3 FAQs, Send share link, Invite team
    • Chat-only addition: Upload chatbot logo, Configure widget colors
    • Voice addition: Record greeting, Set business hours, Test call
    • Pro Website addition: Upload hero photo, Add service times, Pick template, Publish site
  • "Hide this checklist" button collapses to a thin badge on the header ("Setup 43% ▸ ") that re-expands.
  • Rail dismissal state persists in localStorage (cwai-setup-rail-collapsed=true).
  • When all required steps complete, rail auto-collapses with a "Setup complete" pill.

Files touched:

  • src/app/admin/[token]/components/DashboardOverview.tsx (2-column layout)
  • src/components/admin/SetupChecklistRail.tsx (new — condensed, plan-aware version of SetupGuide)
  • src/lib/setup-checklist-steps.ts (new — maps plan key to step list)
  • src/components/admin/SetupGuide.tsx (keep for legacy, but the rail becomes the primary surface)

Cross-cutting acceptance: step count varies per plan; chat-only customer never sees "Record greeting"; Pro Website-only never sees "Set business hours" unless their plan also has voice.

Test: Playwright: rail renders with correct steps per plan, clicking step opens slide-over, progress updates on save, collapse/expand persists across reloads, bundle customer sees union of steps.

Rollback: remove 2-column layout; DashboardOverview reverts to single-column with old SetupGuide.


Slice 5 — 3-question onboarding shortcut (6-8 hrs)

Why fifth: High-impact but requires Slices 1-4 to land first — otherwise there's nowhere for the "generated defaults" to live intelligibly.

Scope:

  • New route /onboard/quickstart?plan=<plan_key> (accessed from post-Stripe-payment redirect instead of the current /onboard/return).
  • Three questions in sequence (not a multi-step wizard — one screen, three dropdowns/radios):
    1. Denomination (re-use the cascade from OnboardForm)
    2. Tone: Warm / Professional / Casual (radio group)
    3. Primary goal: Prayer Support / First-Time Visitors / Staying in Touch (radio group)
  • Defaults generated vary by plan:
    • Chat-only: sample FAQ seeded, widget config filled, 3 example Q&As populated
    • Voice-only: greeting script template written, business hours pre-filled (Sun 9-12, Mon-Fri 9-5), crisis keywords seeded
    • Bundle: all of the above
    • Pro Website: above + hero video per denomination + section stubs (About, Services, Beliefs, Contact)
  • Denomination → theological lens (already wired via webhook in P0)
  • Tone → agent personality default (warm + 1-2 paragraphs of pastor voice template)
  • Primary goal → enabled tools set + sample FAQ seeded
  • Redirect to /admin/[token]?from=quickstart with a celebratory toast.
  • Pastor lands in a working dashboard — checklist items show as 'auto-filled — tap to customize' state instead of 'not done'.

Files touched:

  • src/app/onboard/quickstart/page.tsx (new)
  • src/app/onboard/quickstart/QuickstartForm.tsx (new client component)
  • src/app/api/onboard/quickstart/route.ts (new — receives 3 answers + plan, writes plan-specific defaults)
  • src/lib/onboarding-defaults.ts (new — maps 3 answers + plan to default state)
  • src/components/admin/SetupStepCard.tsx (add 'auto-filled' status with amber ring)
  • src/app/onboard/return/ReturnContent.tsx (redirect to /onboard/quickstart instead of /thank-you for first-time activations)

Cross-cutting acceptance:

  • Plan-appropriate defaults populated; chat-only customer does NOT get business hours seeded.
  • Quickstart skipped for existing subscriptions (gate on premium.created_at < 5 minutes).

Test: Playwright: submit quickstart with Baptist + Warm + Prayer × 4 plans → verify plan-specific default fields populated.

Rollback: revert the redirect in ReturnContent.tsx — new customers go back to /thank-you → admin. Quickstart page stays live as an opt-in path.


Slice 6 — Mobile bottom tab bar with conditional Website tab (3 hrs)

Why last: Once IA is collapsed to 4 tabs (Home / Inbox / Train AI / Website) + header Settings, mobile becomes trivial. Doing this earlier would require changing it again as slices 1-2.5 land.

Scope:

  • New MobileBottomNav.tsx — renders only on sm: breakpoint (<640px).
  • Up to 4 icon+label items — Website conditional on hasProWebsite(plan):
    • Always: Home / Inbox / Train AI
    • If Pro Website: + Website (4-tab mobile nav)
  • Active tab has gold top-border + gold icon; inactive tabs are stone-500.
  • Fixed to bottom with env(safe-area-inset-bottom) padding.
  • Desktop nav hidden on sm:; mobile nav hidden on md: and up.
  • Settings gear moves to the top-right corner (sticky mobile header, unchanged).
  • Upgrade CTA becomes a contextual prompt on Home (removable toast), not nav-persistent.

Files touched:

  • src/app/admin/[token]/components/MobileBottomNav.tsx (new)
  • src/app/admin/[token]/components/AdminDashboard.tsx (conditional render, pass plan to mobile nav)
  • src/app/admin/[token]/components/AdminHeader.tsx (mobile layout tweaks — new small component if not already separated)

Cross-cutting acceptance:

  • Chat-only customer → 3-tab bottom nav (Home / Inbox / Train AI).
  • Pro Website customer → 4-tab bottom nav.
  • Bundle customer without Pro Website add-on → 3-tab bottom nav.

Test: Playwright --project=mobile (iPhone 12 viewport): bottom nav renders with correct tab count per plan, tap Inbox switches tab, active state visible, Settings accessible from header gear.

Rollback: remove the conditional render — mobile goes back to horizontal tabs.


Cross-cutting acceptance criteria (ALL slices)

Every slice above MUST, as part of its Playwright spec:

  1. Capability-gate every interactive element. If a cap is missing, the element is absent from the DOM (not visually hidden — absent). Test matrix: for each element, at least one passing role + one failing role.
  2. Plan-gate every tab and plan-dependent feature. Website tab absent for non-Pro-Website plans. Calls chip absent for chat-only plans. Etc.
  3. Fallback to legacyRoleToCapabilities() for pre-RBAC users — verify admin role works even with empty group_ids.
  4. No broken deep links. Every legacy hash (#settings, #calls, #care) must redirect correctly.
  5. Test across 4 representative plan permutations: cwa_starter_chat, cwa_starter_voice, cwa_pro_both, cwa_pro_website.
  6. Test across 4 representative roles: admin (all caps), care_team (pastoral + care caps), prayer_team (prayer-only), treasurer (financial only).
  7. Visual regression: every new/changed screen gets a Playwright screenshot baseline.

Sprint execution order + time

DaySliceHoursCumulative
Mon AMSlice 1 (Settings slide-over)44
Mon PMSlice 2 part A (Inbox skeleton + adaptive filter chips)48
Tue AMSlice 2 part B (role-gating + hash redirects + plan permutations)311
Tue PMSlice 2.5 (Train AI tab shell)415
Wed AMSlice 3 (plan-adaptive sample data)318
Wed PMSlice 4 (plan-aware checklist rail)523
Thu AMSlice 5 part A (quickstart form + per-plan defaults lib)427
Thu PMSlice 5 part B (webhook hooks + redirect + 4-plan tests)431
Fri AMSlice 6 (mobile bottom nav with conditional Website)334
Fri PMPlaywright test pass across plan × role matrix + visual QA438

Total: ~38 hrs across 5.5 working days.

Branch strategy

  • One long-lived branch feat/admin-p1-ia-collapse off main.
  • Each slice lands as a commit + self-contained Playwright test.
  • PR stays draft until Slice 6 is green.
  • Before merge: run full admin regression (existing admin.spec.ts) against preview URL × 4 plan permutations.
  • Single squash-merge on Friday end-of-day.

Pre-flight checklist (do before Monday AM)

  • P0 PR #60 merged to main (or critical-path-override applied)
  • RBAC DB migration applied to Supabase (pewsearch/migrations/20260418_admin_rbac_groups_and_capabilities.sql + seed DO block)
  • 4 test admin tokens exist covering plan matrix: one each for chat-only, voice-only, bundle, Pro Website (all with directory_visible=false)
  • Test team_members exist for each test token covering 4 representative roles
  • knowledge/architecture/admin-nav-capability-map.md exists and is the source of truth for visibility rules (see companion doc produced same day)
  • Verify Supabase schema matches what each slice assumes
  • Read the canonical acceptance spec knowledge/acceptance/pro-website-setup-wizard.md end-to-end

Out of scope for P1 (deferred to P2)

  • Strikingly-style Website editor redesign (section-based left panel + explicit Publish). Bigger scope, benefits from customer usage data from P0/P1 before committing.
  • Role-gated Inbox filters extending to call transcripts (privacy-pass over the unified stream). Requires policy decisions with founder.
  • Checklist rail persistence across devices (server-side rather than localStorage). Nice-to-have.
  • Self-serve custom domain flow (FA-### filed from earlier). Unblocked by Vercel Domains API work, not IA.
  • Train AI Simulator as interactive feature (not shell only). Shell lands in Slice 2.5; interactive simulator with real RAG retrieval in P2.
  • Dashboard branding per product (SermonWise dashboard integration). Separate project.

Risk register

RiskImpactMitigation
Slice 2 merges calls+care — if founder wants to split these later, unwinding is expensivemedKeep CallsTab.tsx + CareTab.tsx in codebase as "not routed but importable" so re-adding a tab is 1-line
Slice 2.5 Train AI shell proves insufficient for power users (ChMS admins used to drilling into FAQs)medLeft-rail nav + sub-section lazy-load preserves depth; add breadcrumbs if needed
Slice 5 pre-fills data users may want explicit control overlowEvery auto-filled value shows an "edit" link on the checklist card
Mobile bottom nav with 4 tabs too tight on small screens (iPhone SE 320px)medFall back to 3-tab nav + Website in overflow menu on viewports <360px
Plan downgrade mid-session (customer cancels) — tabs disappear abruptlylowPoll premium.status every 60s; soft-redirect to Home if user is on a now-gated tab
3-question quickstart confuses existing-subscription customers hitting /admin post-P1lowGate on premium.created_at < 5 minutes — existing customers bypass quickstart entirely
Multi-product dashboard feels bloated to single-product customersmedPlan gates hide unused tabs; upsell CTAs offer path to expand, not clutter the nav

Dependencies on other work streams

  • admin-nav-capability-map.md (companion doc, same day) — single source of truth for visibility rules. If that doc and this plan disagree, the nav-map doc wins.
  • DESIGN_SPEC_FOR_CLAUDE_DESIGN.md (design artifact for external UI designer, same day) — visual mockups for all 7 slices. If that spec and this plan disagree, the spec owns pixels, this plan owns code.
  • admin-rbac-2026-04-18.md — RBAC spec. Never violate it; slices must gate with can() from rbac.ts.