Initial commit: SuiviCPT personal finance tracker

Laravel 11 + Livewire 3/Volt + Alpine.js + Tailwind app for household
budget planning, transaction tracking, and financial dashboards.

- Multi-tenant households with member invites
- Plan: monthly/yearly budget per category with overrides
- Suivi: transaction log with running balance (desktop + mobile quick-entry)
- Dashboard: budget vs tracked breakdown + Chart.js donuts
- Flux: Sankey diagram of income allocation
- WCAG 2.1 AA color tokens, dark mode, accessible tables/forms
- 50 Pest tests

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
jeremy bayse
2026-07-25 22:39:42 +02:00
co-authored by Claude Sonnet 5
commit d8dc57a5df
164 changed files with 19880 additions and 0 deletions
@@ -0,0 +1,57 @@
<div class="max-w-3xl mx-auto py-6 px-4 sm:px-0 space-y-6">
<h1 class="text-xl font-semibold text-text">Catégories</h1>
@php
$colors = [
'revenu' => 'bg-positive-fill',
'depense' => 'bg-negative-fill',
'epargne' => 'bg-info-fill',
];
@endphp
@foreach ($types as $type)
<div class="bg-surface shadow rounded-lg overflow-hidden">
<div class="{{ $colors[$type->value] }} px-4 py-2">
<h2 class="text-white font-semibold">{{ $type->label() }}</h2>
</div>
<ul class="divide-y divide-border">
@forelse ($categories->get($type->value) ?? [] as $category)
<li class="px-4 py-2 flex items-center justify-between">
@if ($editingId === $category->id)
<form wire:submit="saveEdit" class="flex-1 flex items-center gap-2">
<label for="editing-name-{{ $category->id }}" class="sr-only">Renommer {{ $category->name }}</label>
<input wire:model="editingName" id="editing-name-{{ $category->id }}" type="text" class="flex-1 bg-surface border-border-strong text-text rounded-md text-sm py-1" autofocus />
<button type="submit" class="text-xs text-positive hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">Sauver</button>
<button type="button" wire:click="cancelEdit" class="text-xs text-text-muted hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">Annuler</button>
</form>
@else
<span class="text-sm text-text">{{ $category->name }}</span>
<div class="flex items-center gap-3">
<button wire:click="startEdit({{ $category->id }})" aria-label="Modifier {{ $category->name }}" class="text-xs text-brand hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">Modifier</button>
<button wire:click="delete({{ $category->id }})" wire:confirm="Supprimer cette catégorie ?" aria-label="Supprimer {{ $category->name }}" class="text-xs text-danger hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">Supprimer</button>
</div>
@endif
</li>
@empty
<li class="px-4 py-3 text-sm text-text-muted">Aucune catégorie.</li>
@endforelse
</ul>
<form wire:submit="add('{{ $type->value }}')" class="px-4 py-3 bg-surface-alt flex items-center gap-2">
<label for="new-category-{{ $type->value }}" class="sr-only">Nouvelle catégorie de {{ Str::lower($type->label()) }}</label>
<input
id="new-category-{{ $type->value }}"
type="text"
placeholder="Nouvelle catégorie"
class="flex-1 bg-surface border-border-strong text-text rounded-md text-sm py-1"
wire:model="newName.{{ $type->value }}"
/>
<button type="submit" class="text-sm text-white {{ $colors[$type->value] }} px-3 py-1 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface">
Ajouter
</button>
</form>
@error("newName.{$type->value}") <p class="px-4 pb-2 text-xs text-danger">{{ $message }}</p> @enderror
</div>
@endforeach
</div>