@php use Illuminate\Support\Carbon; $eur = fn ($n) => '€'.number_format((float) $n, 0); $eur2 = fn ($n) => '€'.number_format((float) $n, 2); $num = fn ($n) => number_format((float) $n); $pct = fn ($n) => number_format((float) $n * 100, 1).'%'; $d = $analysis['donations']; $p = $analysis['profit']; $vw = $analysis['vouchers']; $dist = $analysis['distribution']; $league = $analysis['league']; $ret = $analysis['retention']; $tax = $analysis['tax_relief']; // Standard tier = total − Platinum. Derived here so the panel renders against any // snapshot version (older snapshots predate the standard_* keys in the payload). $stdIssued = $vw['standard_issued'] ?? (($vw['issued'] ?? 0) - ($vw['platinum_issued'] ?? 0)); $stdLiability = $vw['standard_liability'] ?? (($vw['liability'] ?? 0) - ($vw['platinum_liability'] ?? 0)); // Chart row builders from the payload. $dow = collect($d['by_dow'])->map(fn ($r) => ['label' => $r['label'], 'value' => $r['sum'], 'display' => $eur($r['sum']).' · '.$num($r['count']).' gifts', 'x' => $r['label']])->all(); $mon = collect($d['by_month'])->map(fn ($r) => ['label' => $r['label'], 'value' => $r['sum'], 'display' => $eur($r['sum']).' · '.$num($r['count']).' gifts', 'x' => $r['label']])->all(); $hour = collect($d['by_hour'])->map(fn ($r) => ['label' => $r['label'].':00', 'value' => $r['count'], 'display' => $num($r['count']).' gifts · '.$eur($r['sum']), 'x' => ((int) $r['label']) % 3 === 0 ? $r['label'] : ''])->all(); $sizes = collect($d['sizes'])->map(fn ($count, $label) => ['label' => $label, 'value' => $count, 'display' => $num($count).' gifts', 'x' => $label])->values()->all(); $bands = collect($dist['bands'])->map(fn ($count, $label) => ['label' => $label, 'value' => $count, 'display' => $num($count).' fundraisers', 'x' => $label])->values()->all(); $trendSum = collect($d['trend'])->map(fn ($r) => ['label' => $r['label'], 'value' => $r['sum'], 'display' => $eur($r['sum']).' · '.$num($r['count']).' gifts', 'x' => $r['label']])->all(); $trendAvg = collect($d['trend'])->map(fn ($r) => ['label' => $r['label'], 'value' => $r['average'], 'display' => $eur($r['average']).' avg · '.$num($r['count']).' gifts', 'x' => $r['label']])->all(); @endphp
Financial analysis

{{ $title }}

All fundraisers · the charity's donation & voucher position @if ($computedAt) · snapshot {{ Carbon::parse($computedAt)->diffForHumans() }} @else · live @endif

Back
{{-- Interval selector (calendar windows). --}} Timeframe @foreach ($analysis['intervals'] as $key => $label) {{ $label }} @endforeach {{-- Profit & liability against event cost. --}} Profit & liability
{{ $eur($p['raised']) }} {{ $eur($p['realised_cost']) }} {{ $eur($p['realised_profit']) }} {{ $eur($p['outstanding_liability']) }} {{ $eur($p['conservative_net']) }} {{ $pct($p['coverage_ratio']) }}
{{-- Voucher position (issued vs redeemed vs outstanding). --}} Voucher positions (all tiers)
{{ $num($vw['issued']) }} {{ $num($vw['redeemed']) }} ({{ number_format($vw['redemption_rate'] * 100, 0) }}%) {{ $num($vw['outstanding']) }} {{ $num($vw['outstanding_indate']) }} {{ $num($vw['outstanding_expired']) }} {{ $eur($vw['liability']) }}
{{-- Tier split — these are SUBSETS of the totals above (Standard + Platinum = total). --}}
Of which, by tier
TierIssuedLiability
Standard{{ $num($stdIssued) }}{{ $eur($stdLiability) }}
Platinum{{ $num($vw['platinum_issued']) }}{{ $eur($vw['platinum_liability']) }}
Total{{ $num($vw['issued']) }}{{ $eur($vw['liability']) }}
{{-- Fundraiser distribution. --}} Fundraiser distribution
{{ $num($dist['active_fundraisers']) }} {{ $eur($dist['average_raised']) }} {{ $eur($dist['median_raised']) }} {{ $num($dist['cover_cost']) }} ({{ $pct($dist['cover_cost_rate']) }}) {{ $pct($dist['top10_share']) }}
Fundraisers by amount raised
@include('admin.analysis._chart', ['rows' => $bands, 'unit' => 'count', 'aria' => 'Fundraisers by amount raised'])
{{-- League tables. --}}
Top fundraisers
@forelse ($league['top_fundraisers'] as $i => $row) @empty @endforelse
#FundraiserRaised
{{ $i + 1 }}{{ $row['name'] }} {{ $row['u_ident'] }}{{ $eur($row['total']) }}
No data in this timeframe.
Top counties
@forelse ($league['top_counties'] as $i => $row) @empty @endforelse
#CountyRaised
{{ $i + 1 }}{{ $row['county'] }}{{ $eur($row['total']) }}
No data in this timeframe.
{{-- Donor retention + tax relief. --}}
Donor retention
{{ $num($ret['donors']) }} {{ $num($ret['repeat_donors']) }} {{ $pct($ret['repeat_rate']) }} {{ number_format($ret['gifts_per_donor'], 2) }}
Tax relief potential (CDS)
{{ $num($tax['eligible_donor_years']) }} {{ $eur($tax['eligible_amount']) }} {{ $eur($tax['estimated_relief']) }}
{{-- Donation KPIs (selected timeframe). --}}
{{ $num($d['count']) }} {{ $eur($d['total']) }} {{ $eur2($d['average']) }} {{ $eur2($d['median']) }} {{ $eur2($d['min']) }} {{ $eur2($d['max']) }} {{ $num($d['donors']) }} {{ $num($d['anonymous']) }}
@if ($d['count'] === 0)

No donations in this timeframe.

@else
By day of week
@include('admin.analysis._chart', ['rows' => $dow, 'unit' => 'eur', 'aria' => 'Donations by day of week'])
By hour of day (Dublin)
@include('admin.analysis._chart', ['rows' => $hour, 'unit' => 'count', 'aria' => 'Donations by hour of day'])
By month of year
@include('admin.analysis._chart', ['rows' => $mon, 'unit' => 'eur', 'aria' => 'Donations by month of year'])
Gift-size distribution
@include('admin.analysis._chart', ['rows' => $sizes, 'unit' => 'count', 'aria' => 'Donations by size band'])
Total raised over time
@include('admin.analysis._chart', ['rows' => $trendSum, 'unit' => 'eur', 'aria' => 'Total raised by month'])
Average gift over time
@include('admin.analysis._chart', ['rows' => $trendAvg, 'unit' => 'eur', 'aria' => 'Average gift by month'])
@endif @push('scripts') @vite('resources/js/analysis.js') @endpush