Add mobile bottom tab bar and redesign mobile dashboard

Import the Finlio Mobile design: a fixed bottom tab bar
(Accueil/Plan/Suivi/Flux) replaces the duplicated links in the mobile
hamburger menu, and the dashboard gets a mobile-first layout — hero
"reste à dépenser" card, revenus/épargne tiles, and a spending-by-
category list with progress bars — built from the same $sections data
the desktop tables use. Desktop dashboard is untouched.

The hero card uses a fixed dark background (independent of the
light/dark token swap) so its white text holds contrast in both
themes, matching how the existing -fill tokens are documented.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
jeremy bayse
2026-08-02 11:16:44 +02:00
co-authored by Claude Sonnet 5
parent a77be2dfba
commit d97cc41ee2
5 changed files with 142 additions and 40 deletions
@@ -1,4 +1,84 @@
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 space-y-6">
@php
$colors = [
'revenu' => ['bg' => 'bg-positive-fill', 'row' => 'bg-positive-surface/40'],
'depense' => ['bg' => 'bg-negative-fill', 'row' => 'bg-negative-surface/40'],
'epargne' => ['bg' => 'bg-info-fill', 'row' => 'bg-info-surface/40'],
];
// Ordered to alternate light/dark shades so adjacent donut slices stay distinguishable
// even without relying on the border stroke (WCAG 1.4.11 graphical-object contrast).
$chartPalettes = [
'revenu' => ['#15803d', '#4ade80', '#166534', '#86efac', '#16a34a', '#22c55e'],
'depense' => ['#991b1b', '#fb923c', '#c2410c', '#fdba74', '#dc2626', '#f97316'],
'epargne' => ['#1e40af', '#93c5fd', '#1d4ed8', '#bfdbfe', '#2563eb', '#60a5fa'],
];
// Mobile category dots cycle through a same-hue tonal ramp so adjacent rows
// stay distinguishable (WCAG 1.4.11), independent of the desktop chart palette.
$mobileDotRamp = ['#8c491a', '#b2622d', '#d67f48', '#f6a06b'];
$depenseRows = $sections['depense']['rows']->sortByDesc('tracked')->values();
@endphp
<div>
{{-- Mobile: hero "reste à dépenser" card + tiles + spending-by-category list --}}
<div class="sm:hidden max-w-md mx-auto px-4 py-6 space-y-5">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-display text-text">Bonjour {{ Str::of(auth()->user()->name)->before(' ') }}</h1>
<span class="w-9 h-9 rounded-full bg-positive-fill text-surface flex items-center justify-center text-xs font-bold shrink-0">
{{ Str::of(auth()->user()->name)->explode(' ')->map(fn ($p) => mb_substr($p, 0, 1))->take(2)->implode('') }}
</span>
</div>
<div class="rounded-3xl shadow p-5 flex flex-col gap-2 text-white" style="background-color: #643312">
<span class="text-xs uppercase tracking-wide text-white/75">Reste à dépenser · {{ \Illuminate\Support\Carbon::create($year, (int) ($period === 'year' ? now()->month : $period))->translatedFormat('F') }}</span>
<span class="font-display text-3xl">{{ number_format($sections['depense']['totals']['reste'], 2, ',', ' ') }} </span>
@php
$depTotals = $sections['depense']['totals'];
$spentPct = $depTotals['budget'] > 0 ? min(100, round($depTotals['tracked'] / $depTotals['budget'] * 100)) : 0;
@endphp
<div class="h-2 rounded-full bg-white/20 overflow-hidden" role="img" aria-label="{{ $spentPct }}% du budget dépenses utilisé">
<div class="h-full rounded-full bg-white/80" style="width: {{ $spentPct }}%"></div>
</div>
<span class="text-xs text-white/70">{{ number_format($depTotals['tracked'], 2, ',', ' ') }} dépensés sur {{ number_format($depTotals['budget'], 2, ',', ' ') }} prévus</span>
</div>
<div class="flex gap-2.5">
<div class="flex-1 rounded-2xl shadow-sm p-4 flex flex-col gap-1 bg-positive-surface">
<span class="text-[10px] uppercase tracking-wide text-positive">Revenus</span>
<span class="font-display text-lg text-positive">{{ number_format($sections['revenu']['totals']['tracked'], 2, ',', ' ') }} </span>
</div>
<div class="flex-1 rounded-2xl shadow-sm p-4 flex flex-col gap-1 bg-surface">
<span class="text-[10px] uppercase tracking-wide text-text-muted">Épargne</span>
<span class="font-display text-lg text-text">{{ number_format($sections['epargne']['totals']['tracked'], 2, ',', ' ') }} </span>
</div>
</div>
<div class="flex items-center justify-between">
<h4 class="text-base font-semibold text-text">Dépenses par catégorie</h4>
</div>
@if ($depenseRows->isEmpty())
<p class="text-xs text-text-muted py-8 text-center">Aucune catégorie de dépenses.</p>
@else
<div class="flex flex-col gap-2.5">
@foreach ($depenseRows as $i => $row)
<div class="rounded-2xl shadow-sm p-3 flex items-center gap-3 bg-surface">
<span class="w-10 h-10 rounded-full shrink-0" style="background-color: {{ $mobileDotRamp[$i % count($mobileDotRamp)] }}" aria-hidden="true"></span>
<div class="flex-1 min-w-0">
<div class="font-semibold text-sm text-text truncate">{{ $row['category']->name }}</div>
<div class="h-1.5 rounded-full bg-surface-alt overflow-hidden mt-1.5" role="img" aria-label="{{ $row['pct'] }}% du budget utilisé">
<div class="h-full rounded-full" style="width: {{ $row['pct'] }}%; background-color: {{ $mobileDotRamp[$i % count($mobileDotRamp)] }}"></div>
</div>
</div>
<span class="font-bold text-sm text-text shrink-0">{{ number_format($row['tracked'], 0, ',', ' ') }} </span>
</div>
@endforeach
</div>
@endif
</div>
{{-- Desktop / tablet: full analysis tables + donut charts --}}
<div class="hidden sm:block max-w-7xl mx-auto py-6 px-4 sm:px-6 space-y-6">
<div class="flex flex-wrap items-center justify-between gap-4">
<h1 class="text-3xl font-display text-text">Tableau de bord</h1>
<div class="flex items-center gap-2">
@@ -27,22 +107,6 @@
<p class="font-display text-2xl text-negative">{{ number_format($sections['depense']['totals']['reste'], 2, ',', ' ') }} </p>
</div>
@php
$colors = [
'revenu' => ['bg' => 'bg-positive-fill', 'row' => 'bg-positive-surface/40'],
'depense' => ['bg' => 'bg-negative-fill', 'row' => 'bg-negative-surface/40'],
'epargne' => ['bg' => 'bg-info-fill', 'row' => 'bg-info-surface/40'],
];
// Ordered to alternate light/dark shades so adjacent donut slices stay distinguishable
// even without relying on the border stroke (WCAG 1.4.11 graphical-object contrast).
$chartPalettes = [
'revenu' => ['#15803d', '#4ade80', '#166534', '#86efac', '#16a34a', '#22c55e'],
'depense' => ['#991b1b', '#fb923c', '#c2410c', '#fdba74', '#dc2626', '#f97316'],
'epargne' => ['#1e40af', '#93c5fd', '#1d4ed8', '#bfdbfe', '#2563eb', '#60a5fa'],
];
@endphp
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="bg-surface shadow rounded-3xl overflow-hidden">
<div class="px-5 pt-4 pb-1">