@switch($id)
@case('balance')
{{ __('budgets.dashboard_widgets.balance.eyebrow') }}
{{ __('budgets.dashboard_widgets.balance.title') }}
{{ \App\Support\Money::formatEur($totalBalance) }}
{{ __('budgets.dashboard_widgets.balance.tracked', ['count' => count($tx)]) }}
@if (count($tx) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'receipt',
'wrapperClass' => 'mt-3 py-6',
'title' => __('budgets.dashboard_widgets.balance.empty_tx_title'),
'body' => __('budgets.dashboard_widgets.balance.empty_tx_body'),
'href' => url('/transactions'),
'cta' => __('budgets.dashboard_widgets.balance.empty_tx_cta'),
])
@endif
@break
@case('income-expense')
{{ __('budgets.dashboard_widgets.income-expense.eyebrow') }}
{{ __('budgets.dashboard_widgets.income-expense.title') }}
{{ \App\Support\Money::formatEur($net) }}
{{ __('budgets.dashboard_widgets.income-expense.saved_pct', ['pct' => number_format($savingsRate, 0)]) }}
{{ $monthIncome <= 0 && $monthExpenses <= 0
? __('budgets.dashboard_widgets.income-expense.net_hint_empty')
: __('budgets.dashboard_widgets.income-expense.net_hint') }}
@if ($monthIncome > 0 || $monthExpenses > 0)
{{ __('budgets.dashboard_widgets.income-expense.row_income') }}
{{ \App\Support\Money::formatEur($monthIncome) }}
{{ __('budgets.dashboard_widgets.income-expense.row_expenses') }}
{{ \App\Support\Money::formatEur($monthExpenses) }}
@else
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'pie-chart',
'wrapperClass' => 'mt-3 py-6',
'title' => __('budgets.dashboard_widgets.income-expense.empty_flow_title'),
'body' => __('budgets.dashboard_widgets.income-expense.empty_flow_body'),
'href' => url('/transactions'),
'cta' => __('budgets.dashboard_widgets.income-expense.empty_flow_cta'),
])
@endif
@break
@case('safe-to-spend')
@php $monthProgress = ((30 - $daysLeft) / 30) * 100; @endphp
{{ __('budgets.dashboard_widgets.safe-to-spend.eyebrow') }}
{{ __('budgets.dashboard_widgets.safe-to-spend.title') }}
{{ __('budgets.dashboard_widgets.safe-to-spend.days_left_badge', ['days' => $daysLeft]) }}
{{ \App\Support\Money::formatEur($safeAmount) }}
{{ __('budgets.dashboard_widgets.safe-to-spend.per_day', ['amount' => \App\Support\Money::formatEur($daily)]) }}
@if (count($budgets) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'piggy-bank',
'wrapperClass' => 'mt-3 py-6',
'title' => __('budgets.dashboard_widgets.safe-to-spend.empty_budgets_title'),
'body' => __('budgets.dashboard_widgets.safe-to-spend.empty_budgets_body'),
'href' => url('/budgets'),
'cta' => __('budgets.dashboard_widgets.safe-to-spend.empty_budgets_cta'),
])
@endif
{{ __('budgets.dashboard_widgets.safe-to-spend.month_progress') }}
{{ __('budgets.dashboard_widgets.safe-to-spend.days_progress', ['active' => 30 - $daysLeft]) }}
@break
@case('upcoming')
@php
$today = \Carbon\Carbon::now()->startOfDay();
$windowEnd = \Carbon\Carbon::now()->copy()->addDays(14)->endOfDay();
$subRows = collect($subs)->whereIn('status', ['active', 'trial'])->map(function ($subscription) {
return [
'name' => $subscription['name'],
'amount' => (float) ($subscription['cost'] ?? 0),
'date' => \Carbon\Carbon::parse($subscription['nextRenewal'] ?? now()),
'kind' => 'sub',
];
})->filter(function ($row) use ($today, $windowEnd) {
return $row['date']->betweenIncluded($today, $windowEnd);
});
$debtRows = collect($debts)->map(function ($debt) use ($today) {
$day = (int) ($debt['dueDay'] ?? 1);
$dueThisMonth = $today->copy()->day(min($day, $today->daysInMonth))->startOfDay();
$dueNextMonth = $today->copy()->addMonthNoOverflow()->day(min($day, $today->copy()->addMonthNoOverflow()->daysInMonth))->startOfDay();
$d = $dueThisMonth->gte($today) ? $dueThisMonth : $dueNextMonth;
return [
'name' => $debt['name'],
'amount' => (float) ($debt['minimumPayment'] ?? 0),
'date' => $d,
'kind' => 'debt',
'icon' => $debt['icon'] ?? '💳',
];
})->filter(function ($row) use ($today, $windowEnd) {
return $row['date']->betweenIncluded($today, $windowEnd);
});
$upcomingAll = $subRows->concat($debtRows)->sortBy(fn ($r) => $r['date']->timestamp)->values();
$upcomingSlice = $upcomingAll->take(3);
$upcomingCount = $upcomingAll->count();
$upcomingTotal = $upcomingAll->sum('amount');
@endphp
{{ __('budgets.dashboard_widgets.upcoming.eyebrow') }}
{{ __('budgets.dashboard_widgets.upcoming.title') }}
@if ($upcomingSlice->isEmpty())
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'calendar',
'wrapperClass' => 'py-6',
'title' => __('budgets.dashboard_widgets.upcoming.empty_title'),
'body' => __('budgets.dashboard_widgets.upcoming.empty_body'),
'href' => url('/subscriptions'),
'cta' => __('budgets.dashboard_widgets.upcoming.empty_cta_subs'),
'secondaryHref' => url('/debts'),
'secondaryCta' => __('budgets.dashboard_widgets.upcoming.empty_cta_debts'),
])
@else
@foreach ($upcomingSlice as $expense)
@php
$dateStr = \App\Support\RelativeDayLabel::upcomingDay($expense['date'], $locale);
@endphp
@endforeach
@endif
@if ($upcomingCount > 0)
{{ __('budgets.dashboard_widgets.upcoming.footer_label', ['count' => $upcomingCount]) }}
{{ \App\Support\Money::formatEur($upcomingTotal) }}
@endif
@break
@case('recent-transactions')
@if (count($tx) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'list',
'wrapperClass' => 'py-8',
'title' => __('budgets.dashboard_widgets.recent-transactions.empty_title'),
'body' => __('budgets.dashboard_widgets.recent-transactions.empty_body'),
'href' => url('/transactions'),
'cta' => __('budgets.dashboard_widgets.recent-transactions.empty_cta'),
])
@else
@foreach (($txRecentGrouped ?? []) as $rgI => $rg)
{{ $rg['label'] }}
@foreach ($rg['items'] as $t)
@php
$cat = \App\Support\CategoryCatalog::get($t['categoryId'] ?? 'other');
$txType = (string) ($t['type'] ?? '');
$txAmount = (float) ($t['amount'] ?? 0);
$txIsIncome = $txType !== '' ? $txType === 'income' : $txAmount > 0;
@endphp
@endforeach
@endforeach
@endif
@break
@case('budgets')
@if (count($budgets) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'gauge',
'wrapperClass' => 'py-8',
'title' => __('budgets.dashboard_widgets.budgets.empty_title'),
'body' => __('budgets.dashboard_widgets.budgets.empty_body'),
'href' => url('/budgets'),
'cta' => __('budgets.dashboard_widgets.budgets.empty_cta'),
])
@else
@foreach (collect($budgets)->take(4) as $b)
@php $progress = ($b['limit'] ?? 1) > 0 ? (($b['spent'] ?? 0) / $b['limit']) * 100 : 0; @endphp
@endforeach
@endif
@break
@case('goals')
@if (count($goals) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'target',
'wrapperClass' => 'py-8',
'title' => __('budgets.dashboard_widgets.goals.empty_title'),
'body' => __('budgets.dashboard_widgets.goals.empty_body'),
'href' => url('/goals'),
'cta' => __('budgets.dashboard_widgets.goals.empty_cta'),
])
@else
@foreach (collect($goals)->take(3) as $g)
@php $pct = ($g['target'] ?? 1) > 0 ? (($g['saved'] ?? 0) / $g['target']) * 100 : 0; @endphp
@endforeach
@endif
@break
@case('subscriptions')
@php
$activeSubs = collect($subs)->where('status', 'active');
$totalSub = $activeSubs->sum('cost');
$zombie = collect($subs)->where('status', 'zombie');
$zombieFirst = $zombie->first();
@endphp
@if ($zombie->count() > 0)
{{ __('budgets.dashboard_widgets.subscriptions.active_with_zombies', ['count' => $activeSubs->count(), 'zombies' => $zombie->count()]) }}
@else
{{ __('budgets.dashboard_widgets.subscriptions.active_only', ['count' => $activeSubs->count()]) }}
@endif
{{ __('budgets.dashboard_widgets.subscriptions.per_mo', ['amount' => \App\Support\Money::formatEur($totalSub)]) }}
@if ($activeSubs->isEmpty())
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'repeat',
'wrapperClass' => 'py-8',
'title' => __('budgets.dashboard_widgets.subscriptions.empty_active_title'),
'body' => __('budgets.dashboard_widgets.subscriptions.empty_active_body'),
'href' => url('/subscriptions'),
'cta' => __('budgets.dashboard_widgets.subscriptions.empty_active_cta'),
])
@else
@foreach ($activeSubs->sortBy('nextRenewal')->take(4) as $s)
@php
$low = ($s['usageScore'] ?? 100) < 40;
$renewStr = \App\Support\RelativeDayLabel::upcomingDay(\Carbon\Carbon::parse($s['nextRenewal']), $locale);
$shared = ! empty($s['shared']);
$dashSubCat = \App\Support\CategoryCatalog::get($s['categoryId'] ?? 'other');
@endphp
@endforeach
@endif
@if ($zombieFirst)
{{ __('budgets.dashboard_widgets.subscriptions.watchlist_title') }}
{{ __('budgets.dashboard_widgets.subscriptions.watchlist_line', ['name' => $zombieFirst['name']]) }}
{{ __('budgets.dashboard_widgets.subscriptions.watchlist_body', ['amount' => \App\Support\Money::formatEur($zombieFirst['cost'] ?? 0)]) }}
@endif
@break
@case('debts')
@php
$totalDebt = collect($debts)->sum('balance');
$totalOrig = collect($debts)->sum('originalBalance');
$paidOff = $totalOrig > 0 ? (1 - $totalDebt / $totalOrig) * 100 : 0;
$priority = collect($debts)->sortByDesc('interestRate')->first();
@endphp
@if (count($debts) === 0)
@include('livewire.partials.dashboard-widget-empty-state', [
'icon' => 'credit-card',
'wrapperClass' => 'py-8',
'title' => __('budgets.dashboard_widgets.debts.empty_title'),
'body' => __('budgets.dashboard_widgets.debts.empty_body'),
'href' => url('/debts'),
'cta' => __('budgets.dashboard_widgets.debts.empty_cta'),
])
@else
{{ __('budgets.dashboard_widgets.debts.summary', ['count' => count($debts), 'pct' => number_format($paidOff, 0)]) }}
{{ \App\Support\Money::formatEur($totalDebt) }}
@foreach (collect($debts)->sortBy('dueDay') as $d)
@php $isPriorityRow = ($priority['id'] ?? null) === ($d['id'] ?? null); @endphp
@endforeach
@endif
@if ($priority)
{{ __('budgets.dashboard_widgets.debts.priority') }}
{{ __('budgets.dashboard_widgets.debts.priority_line', ['name' => $priority['name'], 'rate' => $priority['interestRate']]) }}
{{ __('budgets.dashboard_widgets.debts.priority_hint') }}
@endif
@break
@case('ai')
@php $monthProgress = ((30 - $daysLeft) / 30) * 100; @endphp
{{ __('budgets.dashboard_widgets.ai.eyebrow') }}
{{ __('budgets.dashboard_widgets.ai.title') }}
{{ __('budgets.dashboard_widgets.ai.live') }}
@if (count($tx) === 0 && count($budgets) === 0)
{{ __('budgets.dashboard_widgets.ai.empty_context') }}
@endif
@forelse ($aiTips ?? [] as $idx => $tip)
@empty
@endforelse
{{ __('budgets.dashboard_widgets.ai.open_advisor') }}
@break
@default
{{ __('budgets.dashboard_widgets.unknown', ['id' => $id]) }}
@endswitch