Landing page philosophy across ALL dashboard pages

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
This commit is contained in:
2026-03-05 03:35:08 +08:00
parent 8366054bd7
commit e852250ce0
9 changed files with 1402 additions and 278 deletions

View File

@@ -0,0 +1,96 @@
<?php
namespace App\Filament\Resources\ScheduledGivingDonationResource\Pages;
use App\Filament\Resources\ScheduledGivingDonationResource;
use App\Models\ScheduledGivingCampaign;
use App\Models\ScheduledGivingDonation;
use Filament\Resources\Components\Tab;
use Filament\Resources\Pages\ListRecords;
use Illuminate\Database\Eloquent\Builder;
class ListScheduledGivingDonations extends ListRecords
{
protected static string $resource = ScheduledGivingDonationResource::class;
public function getHeading(): string
{
return 'Scheduled Giving Subscribers';
}
public function getSubheading(): string
{
$active = ScheduledGivingDonation::where('is_active', true)->count();
return "{$active} active subscribers across all campaigns.";
}
public function getTabs(): array
{
$campaigns = ScheduledGivingCampaign::withCount([
'donations as active_count' => fn ($q) => $q->where('is_active', true),
])->get();
$tabs = [];
// All active tab first
$totalActive = ScheduledGivingDonation::where('is_active', true)->count();
$tabs['active'] = Tab::make('All Active')
->icon('heroicon-o-check-circle')
->badge($totalActive)
->badgeColor('success')
->modifyQueryUsing(fn (Builder $q) => $q->where('is_active', true));
// One tab per campaign
foreach ($campaigns as $c) {
$slug = str($c->title)->slug()->toString();
$tabs[$slug] = Tab::make($c->title)
->icon('heroicon-o-calendar')
->badge($c->active_count > 0 ? $c->active_count : null)
->badgeColor($c->active ? 'primary' : 'gray')
->modifyQueryUsing(fn (Builder $q) => $q
->where('scheduled_giving_campaign_id', $c->id)
->where('is_active', true));
}
// Failed payments tab
$failedCount = ScheduledGivingDonation::where('is_active', true)
->whereHas('payments', fn ($q) => $q
->where('is_paid', false)
->where('attempts', '>', 0))
->count();
$tabs['failed'] = Tab::make('Failed Payments')
->icon('heroicon-o-exclamation-triangle')
->badge($failedCount > 0 ? $failedCount : null)
->badgeColor('danger')
->modifyQueryUsing(fn (Builder $q) => $q
->where('is_active', true)
->whereHas('payments', fn ($sub) => $sub
->where('is_paid', false)
->where('attempts', '>', 0)));
// Cancelled
$cancelled = ScheduledGivingDonation::where('is_active', false)->count();
$tabs['cancelled'] = Tab::make('Cancelled')
->icon('heroicon-o-x-circle')
->badge($cancelled > 0 ? $cancelled : null)
->badgeColor('gray')
->modifyQueryUsing(fn (Builder $q) => $q->where('is_active', false));
// Everything
$tabs['all'] = Tab::make('Everything')
->icon('heroicon-o-squares-2x2');
return $tabs;
}
protected function getHeaderActions(): array
{
return [];
}
public function getDefaultActiveTab(): string | int | null
{
return 'active';
}
}