Files
calvana/ADMIN_PANEL_AUDIT.md
Omair Saleh fcfae1c1a4 Ship all P0/P1/P2 gaps + 11 AI features
P0 Critical (7):
- STOP/UNSUBSCRIBE keyword → CANCEL (PECR compliance)
- Rate limiting on pledge creation (10/IP/5min)
- Terms of Service + Privacy Policy pages
- WhatsApp onboarding gate (persistent dashboard banner)
- Demo account seeding (demo@pnpl.app)
- Footer legal links
- Basic accessibility (aria labels on donor flow)

P1 Within 2 Weeks (8):
- Pledge editing by staff (PATCH amount, name, email, phone, rail)
- Donor self-cancel page (/p/cancel) + API
- Donor 'My Pledges' lookup page (/p/my-pledges)
- Bulk QR code download (print-ready HTML)
- Public event progress bar (/e/[slug]/progress)
- Email-only donor handling (honest status + WhatsApp fallback)
- Email verification (format + disposable domain blocking)
- Organisations page rewrite (multi-campaign, not multi-org)

P2 Within First Month (10):
- Event cloning with QR sources
- Account deletion (GDPR Article 17)
- Daily digest cron via WhatsApp
- AI-6 Smart reminder timing (due date anchoring, cultural sensitivity)
- H1 Duplicate donor detection (email, phone, Jaro-Winkler name)
- H5 Bank CSV format presets (10 UK banks)
- H16 Partial payment matching (underpay, overpay, instalment)
- H10 Activity logging (audit trail for staff actions)
- AI nudge endpoint + AI column mapping + AI event setup wizard
- AI anomaly detection wired into daily digest

AI Features (11): smart reconciliation, social proof, auto column mapper,
daily digest, impact storyteller, smart timing, nudge composer, event wizard,
NLU concierge, anomaly detection, bank presets

22 new files, 15 modified files, 0 TypeScript errors, clean build.
2026-03-04 20:10:34 +08:00

12 KiB
Raw Blame History

CharityRight Admin Panel — Full Audit & Overhaul Plan

Date: 4 March 2026
Auditor: Claude (via pi)
Stack: Laravel 11 + Filament v3.3.26 + PHP 8.3 + DigitalOcean


1. Current State Inventory

Navigation Groups & Resources

Group Resource Model Records Notes
Management Customers Customer 21,558 Basic CRUD, no search filters
Users User 20,189 Hidden from Admin role, minimal form
Snowdon Registrations SnowdonRegistration 112 Legacy event, still in nav
Campaign Registrations CampaignRegistration 115 T365/Ramadan, hardcoded title
Donations General Donation 30,497 Main donation view, good filters
Scheduled ScheduledGivingDonation 2,841 Separate resource from general
URL Builder (page) Utility page for donation links
Campaigns Appeals Appeal 8,914 Core resource, complex
Approvals ApprovalQueue 1,934 Queue for appeal changes
Scheduled Campaigns ScheduledGivingCampaign 3 Very few records
Words of Hope WOHMessage 817 Form submissions
Allocation Donation Items DonationType 30 Hidden from Admin role
Countries DonationCountry 12 Hidden from Admin role
Fund Dimensions EngageFundDimension N3O Engage sync
Attribution Dimensions EngageAttributionDimension N3O Engage sync
Misc Settings (page) Global settings, hidden from Admin
Event Logs EventLog 2,276,079 ⚠️ 2.2M rows, no cleanup

Roles & Permissions

Role Permissions Can See
Superadmin All (via Gate::before) Everything
Admin Full CRUD on customers, donations, appeals, campaigns Most things, hidden: Users, Settings, DonationType, DonationCountry
Appeal Manager view/edit/delete appeal Appeals only
Finance Manager view/edit/delete donation + donation-item Donations only
User (none) Nothing in admin

2. Critical Issues Found

🔴 A. No Dashboard — Zero At-a-Glance Intelligence

The admin panel has no widgets, no dashboard, no KPIs. When staff log in they see a blank page with navigation. They have no idea:

  • How many donations came in today/this week/this month
  • Total revenue
  • Active campaigns and their progress
  • Pending approvals count
  • Failed/errored donations
  • Scheduled giving health

🔴 B. No Supporter/Care View

There is zero support workflow. A support agent can't:

  • Search for a donor by email/name/phone and see everything about them in one place
  • See a donor's full history (all donations, scheduled giving, appeals, gift aid status)
  • Issue refunds or resend receipts easily
  • Add notes/tags to a customer record
  • Merge duplicate customer records (21K customers vs 20K users = many duplicates)
  • See a timeline/activity log for a customer

🔴 C. Customer ↔ User Confusion

Two separate models (User = login account, Customer = donation record) with no unified view. Staff must:

  1. Search Users to find the login account
  2. Search Customers to find donation records
  3. Mentally link them (via customer.user_id)
  4. No way to see "this person's full picture"

🔴 D. Event Logs — 2.2 Million Rows, No Cleanup

EventLog table has 2.2M rows with no pruning, no auto-archive, no summary view. This:

  • Slows down the admin panel
  • Makes the "Event Logs" page unusable (loads millions of rows)
  • No alerting on errors
  • No way to filter by severity or recent errors only

🔴 E. Hardcoded Emails for Export Access

->visible(function () {
    $authedEmail = auth()->user()->email;
    return $authedEmail == 'omair.saleh@charityright.org.uk' || $authedEmail == 'development@verge.digital';
})

Export functionality is locked to specific email addresses instead of using the permission system.


3. UX Issues

🟠 F. Top Navigation is Overcrowded

->topNavigation() with 5 groups × 3-5 items = 15+ items in a horizontal bar. With long names like "Scheduled Giving Campaigns", this overflows on most screens. Many items are rarely used (Snowdon Registrations, Fund Dimensions, Attribution Dimensions).

🟠 G. Deprecated Code Patterns (Partially Fixed)

  • ->reactive() instead of ->live() — fixed for Appeal resources, but DonationResource, CustomerResource, ScheduledGivingDonationResource still use ->reactive() and callable $get
  • HtmlString used extensively for simple formatting (should use Filament's native badge/formatting)

No ->globalSearch() configured. Staff can't type a donor email in the search bar and find them. They must navigate to Customers, then search.

🟠 I. Donation Edit Form is Read-Only Placeholders

The DonationResource edit form is entirely Placeholder components — nothing is actually editable. The form pretends to be an edit page but is really a view page. Should use ViewAction or a proper view page.

  • Viewing a Donation doesn't link to the Customer
  • Viewing a Customer doesn't link to their User account
  • Viewing an Appeal doesn't link to the parent appeal in a clickable way
  • view_customer action exists but has no URL attached

🟠 K. Inconsistent Form Patterns

  • AppealResource defines the form schema TWICE (in AppealResource::form() AND EditAppeal::form()) — 100% duplicated
  • Some resources use ViewAction, others use EditAction for the same purpose
  • Some tables have bulk actions, others don't
  • Inconsistent use of ->collapsible()->collapsed() vs not

🟠 L. Legacy/Dead Resources in Navigation

  • Snowdon Registrations (112 records, likely a past event)
  • Campaign Registrations with hardcoded "T365 / Ramadan Challenge" title
  • Both clutter the Management group for daily users

4. Functional Gaps

🟡 M. No Refund Workflow

No ability to process or record refunds from the admin panel. Support staff need to go to Stripe dashboard.

🟡 N. No Donation Notes/Comments

No way to add internal notes to a donation (e.g., "Donor called, wants to change allocation" or "Refunded via Stripe on 2024-01-15").

🟡 O. No Customer Merge Tool

With 21K customers and 20K users, there are certainly duplicates. No tool to merge them.

🟡 P. No Scheduled Giving Management

Can't pause, cancel, or modify a scheduled giving subscription from the admin. The edit page exists but fields are placeholder-only.

🟡 Q. No Bulk Operations for Appeals

Can't bulk-approve, bulk-reject, or bulk-publish appeals. Approval queue exists but it's a basic list.

🟡 R. No Communication/Email Log

No record of emails sent to donors (receipts, confirmations). Can't tell if a receipt was sent or if it bounced.

🟡 S. No Gift Aid Reporting

Gift Aid is tracked per donation but there's no aggregated report view for HMRC claims.

🟡 T. Missing Filters on Key Resources

  • Customers: No filters at all (no date range, no donation count, no total donated)
  • Appeals: No status filter, no date filter, no "has donations" filter
  • Users: No role filter, no activity filter

5. Overhaul Plan — Phases

Phase 1: Dashboard & Navigation (High Impact, Quick Win)

  1. Build a Dashboard with widgets:

    • Today's donations (count + total £)
    • This week/month revenue chart
    • Pending approvals badge
    • Active campaigns progress bars
    • Recent errors count (from EventLog)
    • Scheduled giving health (active/paused/failed)
  2. Fix Navigation Structure:

    Dashboard (home)
    ├── Supporter Care (new)
    │   ├── Donors (unified Customer+User view)
    │   └── Donor Lookup (global search page)
    ├── Donations
    │   ├── All Donations
    │   ├── Scheduled Giving
    │   └── URL Builder
    ├── Campaigns
    │   ├── Appeals
    │   ├── Approvals (with badge count)
    │   ├── Scheduled Campaigns
    │   └── Registrations (merge Snowdon + Campaign)
    ├── Configuration (collapsible, for admins)
    │   ├── Donation Items
    │   ├── Countries
    │   ├── Fund Dimensions
    │   ├── Attribution Dimensions
    │   └── Settings
    ├── Logs & System
    │   └── Event Logs (with cleanup)
    
  3. Switch from Top Nav to Sidebar — Too many items for top nav. Sidebar with collapsible groups is standard for admin panels this size.

  4. Enable Global Search across Customers, Donations, Appeals.

Phase 2: Supporter Care Hub (Highest Business Value)

  1. Unified Donor Profile Page — Single page showing:

    • Customer details + linked User account
    • All donations (one-off + scheduled) in a timeline
    • All appeals they've donated to
    • Gift aid status across all donations
    • Address history
    • Internal notes (new feature)
    • Communication log (new feature)
    • Quick actions: Send receipt, Add note, View in Stripe
  2. Donor Search Widget — Search by email, name, phone, donation reference, provider reference. Returns unified results.

  3. Internal Notes System — Polymorphic notes table: add notes to Customers, Donations, Appeals.

Phase 3: Operational Improvements

  1. Fix DonationResource — Make it a proper ViewRecord page, not a fake edit page with Placeholders
  2. Add missing filters to all key resources
  3. Replace hardcoded email checks with proper permissions (can-export permission)
  4. Add cross-resource links (Donation → Customer, Customer → User, Appeal → Parent)
  5. Deduplicate AppealResource form (remove from AppealResource::form(), keep only in EditAppeal::form())
  6. Archive/hide legacy resources (Snowdon, or move to a "Legacy" group)

Phase 4: Reporting & Data Health

  1. Gift Aid Report Page — Aggregate view for HMRC claims
  2. Donation Summary Report — By type, country, period
  3. EventLog Cleanup — Auto-prune entries older than 90 days, add severity indicators
  4. Scheduled Giving Health Dashboard — Failed payments, retries, cancellations

6. Technical Recommendations

Item Current Recommended
Navigation ->topNavigation() ->sidebarCollapsibleOnDesktop()
Global Search None Enable on Customer, Donation, Appeal
Dashboard Blank Custom widgets page
Form pattern ->reactive() + callable $get ->live() + \Filament\Forms\Get $get
Export access Hardcoded emails Permission-based (can-export)
EventLog 2.2M unmanaged rows Pruning schedule + summary widget
Donation form Placeholder-only edit page ViewRecord page
Customer-User Separate resources Unified Donor resource

7. Priority Matrix

Priority Item Effort Impact
🔴 P0 Dashboard widgets Medium Very High
🔴 P0 Fix navigation (sidebar + groups) Low High
🔴 P0 Global search Low High
🔴 P0 Supporter Care donor profile High Very High
🟠 P1 Internal notes system Medium High
🟠 P1 Fix donation view page Low Medium
🟠 P1 Add missing filters Low Medium
🟠 P1 Cross-resource links Low Medium
🟠 P1 Replace hardcoded email checks Low Medium
🟡 P2 Gift Aid report Medium Medium
🟡 P2 EventLog cleanup Medium Medium
🟡 P2 Customer merge tool High Medium
🟡 P2 Communication log Medium Medium
🟡 P2 Archive legacy resources Low Low

Ready to execute. Say "go" with a phase number, or "go all" to start from Phase 1.