feat: complete Epic 0 — foundation migration & infrastructure setup
Stories 0.2-0.5: rename folders→declarations (backend+frontend), configure Redis for cache/queue/sessions, add foundation database migrations (permissions, archived_at), replace DeclarationStatus enum with architecture lifecycle values, create DeclarationObserver for status transition validation and auto-archive, fix controller status transitions to respect observer rules. 93 tests pass (240 assertions). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { Head, Link } from '@inertiajs/vue3';
|
||||
import { Building2, FileText, FolderOpen } from 'lucide-vue-next';
|
||||
import { computed } from 'vue';
|
||||
import DeclarationCalendar from '@/components/clients/DeclarationCalendar.vue';
|
||||
import Heading from '@/components/Heading.vue';
|
||||
import AppLayout from '@/layouts/AppLayout.vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import FolderCalendar from '@/components/clients/FolderCalendar.vue';
|
||||
import { Building2, FileText, FolderOpen } from 'lucide-vue-next';
|
||||
import AppLayout from '@/layouts/AppLayout.vue';
|
||||
|
||||
type ClientContact = {
|
||||
id: number;
|
||||
@@ -34,7 +34,7 @@ type Client = {
|
||||
internal_notes: string | null;
|
||||
};
|
||||
|
||||
type Folder = {
|
||||
type Declaration = {
|
||||
id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
@@ -52,11 +52,11 @@ type Stats = {
|
||||
|
||||
type Props = {
|
||||
client: Client;
|
||||
folders: Folder[];
|
||||
declarations: Declaration[];
|
||||
stats: Stats;
|
||||
indexUrl: string;
|
||||
editUrl: string;
|
||||
createFolderUrl: string;
|
||||
createDeclarationUrl: string;
|
||||
};
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -78,7 +78,7 @@ const typeLabels: Record<string, string> = {
|
||||
other: 'Autre',
|
||||
};
|
||||
|
||||
const folderStatusLabels: Record<string, string> = {
|
||||
const declarationStatusLabels: Record<string, string> = {
|
||||
draft: 'Brouillon',
|
||||
waiting_documents: 'En attente documents',
|
||||
documents_received: 'Documents reçus',
|
||||
@@ -139,7 +139,9 @@ function getFieldValue(fieldKey: string): string {
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="outline" as-child>
|
||||
<Link :href="createFolderUrl">Nouveau dossier</Link>
|
||||
<Link :href="createDeclarationUrl"
|
||||
>Nouvelle déclaration</Link
|
||||
>
|
||||
</Button>
|
||||
<Button variant="outline" as-child>
|
||||
<Link :href="editUrl">Modifier le client</Link>
|
||||
@@ -154,7 +156,7 @@ function getFieldValue(fieldKey: string): string {
|
||||
class="flex flex-row items-center justify-between space-y-0 pb-2"
|
||||
>
|
||||
<CardTitle class="text-sm font-medium"
|
||||
>Total dossiers</CardTitle
|
||||
>Total déclarations</CardTitle
|
||||
>
|
||||
<FolderOpen class="size-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
@@ -181,8 +183,8 @@ function getFieldValue(fieldKey: string): string {
|
||||
?.additional_documents_requested ?? 0) +
|
||||
(stats.by_status?.waiting_documents ?? 0) +
|
||||
(stats.by_status?.documents_received ?? 0) +
|
||||
(stats.by_status
|
||||
?.waiting_client_validation ?? 0)
|
||||
(stats.by_status?.waiting_client_validation ??
|
||||
0)
|
||||
}}
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -384,7 +386,7 @@ function getFieldValue(fieldKey: string): string {
|
||||
>
|
||||
Notes
|
||||
</p>
|
||||
<p class="whitespace-pre-wrap text-sm">
|
||||
<p class="text-sm whitespace-pre-wrap">
|
||||
{{ client.internal_notes }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -398,55 +400,45 @@ function getFieldValue(fieldKey: string): string {
|
||||
<CardHeader>
|
||||
<CardTitle>Calendrier des échéances</CardTitle>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Dossiers par date limite
|
||||
Déclarations par date limite
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FolderCalendar :folders="folders" />
|
||||
<DeclarationCalendar :declarations="declarations" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Folders history -->
|
||||
<!-- Declarations history -->
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Historique des dossiers</CardTitle>
|
||||
<CardTitle>Historique des déclarations</CardTitle>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Derniers dossiers du client
|
||||
Dernières déclarations du client
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div
|
||||
v-if="folders.length"
|
||||
v-if="declarations.length"
|
||||
class="overflow-x-auto rounded-xl border border-sidebar-border/70"
|
||||
>
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-muted/50">
|
||||
<tr>
|
||||
<th
|
||||
class="px-4 py-3 text-left font-medium"
|
||||
>
|
||||
Dossier
|
||||
<th class="px-4 py-3 text-left font-medium">
|
||||
Déclaration
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left font-medium"
|
||||
>
|
||||
<th class="px-4 py-3 text-left font-medium">
|
||||
Type
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left font-medium"
|
||||
>
|
||||
<th class="px-4 py-3 text-left font-medium">
|
||||
Statut
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left font-medium"
|
||||
>
|
||||
<th class="px-4 py-3 text-left font-medium">
|
||||
Échéance
|
||||
</th>
|
||||
<th
|
||||
class="px-4 py-3 text-left font-medium"
|
||||
>
|
||||
<th class="px-4 py-3 text-left font-medium">
|
||||
Créé le
|
||||
</th>
|
||||
<th class="px-4 py-3"></th>
|
||||
@@ -454,31 +446,31 @@ function getFieldValue(fieldKey: string): string {
|
||||
</thead>
|
||||
<tbody class="divide-y divide-sidebar-border/70">
|
||||
<tr
|
||||
v-for="folder in folders"
|
||||
:key="folder.id"
|
||||
v-for="declaration in declarations"
|
||||
:key="declaration.id"
|
||||
class="hover:bg-muted/30"
|
||||
>
|
||||
<td class="px-4 py-3 font-medium">
|
||||
{{ folder.title }}
|
||||
{{ declaration.title }}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{
|
||||
typeLabels[folder.type] ??
|
||||
folder.type
|
||||
typeLabels[declaration.type] ??
|
||||
declaration.type
|
||||
}}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{
|
||||
folderStatusLabels[
|
||||
folder.status
|
||||
] ?? folder.status
|
||||
declarationStatusLabels[
|
||||
declaration.status
|
||||
] ?? declaration.status
|
||||
}}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{ folder.due_date ?? '—' }}
|
||||
{{ declaration.due_date ?? '—' }}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
{{ folder.created_at }}
|
||||
{{ declaration.created_at }}
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<Button
|
||||
@@ -486,7 +478,7 @@ function getFieldValue(fieldKey: string): string {
|
||||
size="sm"
|
||||
as-child
|
||||
>
|
||||
<Link :href="folder.showUrl"
|
||||
<Link :href="declaration.showUrl"
|
||||
>Voir</Link
|
||||
>
|
||||
</Button>
|
||||
@@ -499,11 +491,11 @@ function getFieldValue(fieldKey: string): string {
|
||||
v-else
|
||||
class="rounded-xl border border-sidebar-border/70 p-8 text-center text-muted-foreground"
|
||||
>
|
||||
Aucun dossier.
|
||||
Aucune déclaration.
|
||||
<Link
|
||||
:href="createFolderUrl"
|
||||
:href="createDeclarationUrl"
|
||||
class="text-primary underline"
|
||||
>Créer un dossier</Link
|
||||
>Créer une déclaration</Link
|
||||
>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
Reference in New Issue
Block a user