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
51 lines
1.6 KiB
PHP
51 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Widgets;
|
|
|
|
use App\Models\Donation;
|
|
use Filament\Widgets\ChartWidget;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
class DonationChartWidget extends ChartWidget
|
|
{
|
|
protected static ?string $heading = 'Daily Donations — Last 30 Days';
|
|
|
|
protected static ?int $sort = 2;
|
|
|
|
protected int | string | array $columnSpan = 'full';
|
|
|
|
protected static ?string $maxHeight = '220px';
|
|
|
|
protected function getData(): array
|
|
{
|
|
$confirmedScope = fn (Builder $q) => $q->whereNotNull('confirmed_at');
|
|
$days = collect(range(29, 0))->map(fn ($i) => now()->subDays($i)->format('Y-m-d'));
|
|
|
|
$donations = Donation::whereHas('donationConfirmation', $confirmedScope)
|
|
->where('created_at', '>=', now()->subDays(30)->startOfDay())
|
|
->selectRaw('DATE(created_at) as date, SUM(amount) / 100 as total')
|
|
->groupByRaw('DATE(created_at)')
|
|
->pluck('total', 'date');
|
|
|
|
return [
|
|
'datasets' => [
|
|
[
|
|
'label' => 'Revenue (£)',
|
|
'data' => $days->map(fn ($d) => round($donations[$d] ?? 0, 2))->toArray(),
|
|
'borderColor' => '#10b981',
|
|
'backgroundColor' => 'rgba(16, 185, 129, 0.1)',
|
|
'fill' => true,
|
|
'tension' => 0.3,
|
|
],
|
|
],
|
|
'labels' => $days->map(fn ($d) => Carbon::parse($d)->format('d M'))->toArray(),
|
|
];
|
|
}
|
|
|
|
protected function getType(): string
|
|
{
|
|
return 'line';
|
|
}
|
|
}
|