Fundamental Collect redesign: unified creation, payment clarity, widget embed

THE CORE PROBLEM:
Users didn't understand the appeal→link hierarchy.
Payment method was hidden inside appeal creation.
The widget was a separate, undiscoverable concept.
External platforms (JustGiving, LaunchGood) felt disconnected.

THE FIX:

1. ONE CREATION FLOW for everything:
   Step 1: 'What are you raising for?' → creates the appeal
   Step 2: 'How will donors pay?' → 3 big clear cards:
     - Bank transfer (most popular, free)
     - External platform (JustGiving, LaunchGood, etc.)
     - Card payment (Stripe)
   Step 3: 'Name your link' → shows summary, creates both

2. PAYMENT METHOD VISIBLE ON EVERY LINK:
   Each link card shows a badge: 'Bank' or 'JustGiving' etc.
   External links show 'After pledging, donors are sent to...'
   No confusion about how money flows.

3. WIDGET IS A SHARING TAB, NOT A SEPARATE CONCEPT:
   Every link card expands to show 3 tabs:
     - Link (copy URL, WhatsApp, email, share)
     - QR Code (download PNG for printing)
     - Website Widget (iframe embed code with copy button)
   The widget is just another way to share the same link.

4. FLAT LINK LIST (not appeal→link hierarchy):
   All links shown in one flat list
   Appeal name shown as subtitle when multiple appeals exist
   'New link' adds to existing appeal
   'New appeal' uses the full 3-step wizard

5. EDUCATIONAL RIGHT COLUMN:
   'How it works' 5-step guide
   'Which payment method should I choose?' comparison
   'Can I mix payment methods?' FAQ
   'What's an appeal?' explanation (demystifies the concept)
   Leaderboard when 3+ links have pledges
This commit is contained in:
2026-03-05 04:00:14 +08:00
parent dc8e593849
commit c11bf4bea7
13 changed files with 2203 additions and 567 deletions

View File

@@ -0,0 +1,93 @@
<?php
namespace App\Providers\Filament;
use App\Helpers;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Navigation\MenuItem;
use Filament\Navigation\NavigationGroup;
use Filament\Navigation\NavigationItem;
use Filament\Panel;
use Filament\PanelProvider;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors(['primary' => config('branding.colours')])
->viteTheme('resources/css/filament/admin/theme.css')
->sidebarCollapsibleOnDesktop()
->sidebarWidth('16rem')
->globalSearch(true)
->globalSearchKeyBindings(['command+k', 'ctrl+k'])
->globalSearchDebounce('300ms')
->navigationGroups([
// ── Daily Work (always visible, top of sidebar) ──
NavigationGroup::make('Daily')
->collapsible(false),
// ── Giving (30 Nights, 10 Days, Night of Power) ──
NavigationGroup::make('Giving')
->icon('heroicon-o-calendar-days')
->collapsible(),
// ── Fundraising (appeals, review queue) ──
NavigationGroup::make('Fundraising')
->icon('heroicon-o-megaphone')
->collapsible(),
// ── Setup (rarely touched config) ──
NavigationGroup::make('Setup')
->icon('heroicon-o-cog-6-tooth')
->collapsible()
->collapsed(),
])
->brandLogo(Helpers::getCurrentLogo(true))
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([\Filament\Pages\Dashboard::class])
->userMenuItems([
'profile' => MenuItem::make()
->label('Edit profile')
->url(url('user/profile')),
'back2site' => MenuItem::make()
->label('Return to site')
->icon('heroicon-o-home')
->url(url('/')),
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
])
->login(null)
->registration(null)
->darkMode(false)
->databaseNotifications();
}
}