Files
suivicpt/app/Livewire/Sankey/Index.php
T
jeremy bayseandClaude Sonnet 5 d8dc57a5df 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>
2026-07-25 22:39:42 +02:00

83 lines
2.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Livewire\Sankey;
use App\Enums\CategoryType;
use App\Models\Category;
use Livewire\Attributes\Layout;
use Livewire\Component;
#[Layout('layouts.app')]
class Index extends Component
{
public int $year;
public string $period = 'year'; // 'year' or '1'..'12'
public function mount(): void
{
$this->year = (int) now()->year;
$this->period = (string) now()->month;
}
private function trackedFor(Category $category): float
{
$query = $category->transactions()->whereYear('date_effective', $this->year);
if ($this->period !== 'year') {
$query->whereMonth('date_effective', (int) $this->period);
}
return (float) $query->sum('amount');
}
public function render()
{
$household = auth()->user()->currentHousehold;
$revenueCategories = $household->categories()->where('type', CategoryType::Revenu)->orderBy('position')->get();
$totalRevenue = $revenueCategories->sum(fn (Category $c) => $this->trackedFor($c));
$outflowCategories = $household->categories()
->whereIn('type', [CategoryType::Depense, CategoryType::Epargne])
->orderBy('type')
->orderBy('position')
->get()
->map(fn (Category $category) => [
'category' => $category,
'tracked' => $this->trackedFor($category),
])
->filter(fn ($row) => $row['tracked'] > 0)
->values();
$totalOutflow = $outflowCategories->sum('tracked');
$unallocated = max(0, $totalRevenue - $totalOutflow);
$flows = $outflowCategories->map(fn ($row) => [
'from' => 'Revenus',
'to' => $row['category']->name,
'flow' => (float) $row['tracked'],
'type' => $row['category']->type->value,
]);
if ($unallocated > 0) {
$flows->push([
'from' => 'Revenus',
'to' => 'Non affecté',
'flow' => $unallocated,
'type' => 'unallocated',
]);
}
return view('livewire.sankey.index', [
'flows' => $flows->values(),
'totalRevenue' => $totalRevenue,
'totalOutflow' => $totalOutflow,
'unallocated' => $unallocated,
'hasData' => $totalRevenue > 0,
]);
}
}