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>
57 lines
3.2 KiB
PHP
57 lines
3.2 KiB
PHP
<div class="max-w-3xl mx-auto py-6 space-y-6">
|
|
<div class="bg-surface shadow rounded-lg p-6">
|
|
<h2 class="text-lg font-semibold text-text">{{ $household->name }}</h2>
|
|
|
|
<h3 class="mt-6 text-sm font-medium text-text-muted">Membres</h3>
|
|
<ul class="mt-2 divide-y divide-border">
|
|
@foreach ($household->members as $member)
|
|
<li class="py-2 flex items-center justify-between">
|
|
<div>
|
|
<span class="text-sm text-text">{{ $member->name }}</span>
|
|
<span class="text-xs text-text-muted ml-2">{{ $member->pivot->role }}</span>
|
|
</div>
|
|
@if ($member->id !== $household->owner_id && $member->id !== auth()->id())
|
|
<button wire:click="removeMember({{ $member->id }})" wire:confirm="Retirer ce membre du foyer ?" aria-label="Retirer {{ $member->name }} du foyer" class="text-xs text-danger hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">
|
|
Retirer
|
|
</button>
|
|
@endif
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
|
|
@if ($household->invites->isNotEmpty())
|
|
<h3 class="mt-6 text-sm font-medium text-text-muted">Invitations en attente</h3>
|
|
<ul class="mt-2 divide-y divide-border">
|
|
@foreach ($household->invites as $pendingInvite)
|
|
<li class="py-2 flex items-center justify-between">
|
|
<span class="text-sm text-text-muted">{{ $pendingInvite->email }}</span>
|
|
<button wire:click="revokeInvite({{ $pendingInvite->id }})" aria-label="Annuler l'invitation de {{ $pendingInvite->email }}" class="text-xs text-danger hover:underline rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">
|
|
Annuler
|
|
</button>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
@endif
|
|
|
|
<form wire:submit="invite" class="mt-6 flex items-end gap-3">
|
|
<div class="flex-1">
|
|
<x-input-label for="inviteEmail" value="Inviter par email" />
|
|
<x-text-input wire:model="inviteEmail" id="inviteEmail" type="email" class="block mt-1 w-full" />
|
|
<x-input-error :messages="$errors->get('inviteEmail')" class="mt-2" />
|
|
</div>
|
|
<div>
|
|
<x-input-label for="inviteRole" value="Rôle" />
|
|
<select wire:model="inviteRole" id="inviteRole" class="block mt-1 w-full rounded-md bg-surface border-border-strong text-text text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring">
|
|
<option value="member">Membre</option>
|
|
<option value="owner">Propriétaire</option>
|
|
</select>
|
|
</div>
|
|
<x-primary-button>Envoyer</x-primary-button>
|
|
</form>
|
|
|
|
<div x-data="{ shown: false }" x-on:invite-sent.window="shown = true; setTimeout(() => shown = false, 3000)" x-show="shown" x-cloak role="status" class="mt-3 text-sm text-positive">
|
|
Invitation envoyée.
|
|
</div>
|
|
</div>
|
|
</div>
|