feat: implement Story 2.3 — Worker-Scoped Dashboard
Scope stat cards and urgent declarations table to the authenticated worker's own assignments. Add empty state when no declarations are assigned, hide the "Assigné à" column for worker role, and expose isWorker flag through DashboardController and dashboard types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { Head, Link, router } from '@inertiajs/vue3';
|
||||
import {
|
||||
Briefcase,
|
||||
Building2,
|
||||
ClipboardList,
|
||||
EllipsisVertical,
|
||||
Eye,
|
||||
FolderOpen,
|
||||
@@ -54,6 +55,13 @@ const breadcrumbs: BreadcrumbItem[] = [
|
||||
|
||||
const hasWorkspace = computed(() => !!props.workspaceName);
|
||||
|
||||
const isWorkerEmpty = computed(
|
||||
() =>
|
||||
props.isWorker &&
|
||||
props.declarations.length === 0 &&
|
||||
props.statCards.every((c) => c.count === 0),
|
||||
);
|
||||
|
||||
type DeadlineProximity = 'safe' | 'approaching' | 'urgent' | 'overdue' | 'none';
|
||||
|
||||
function deadlineProximity(dueDate: string | null): DeadlineProximity {
|
||||
@@ -150,166 +158,205 @@ function navigateToDeclaration(declaration: DashboardDeclaration): void {
|
||||
|
||||
<!-- Workspace dashboard -->
|
||||
<template v-if="hasWorkspace">
|
||||
<!-- KPI StatCards -->
|
||||
<div
|
||||
class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4"
|
||||
>
|
||||
<StatCard
|
||||
v-for="card in statCards"
|
||||
:key="card.label"
|
||||
:label="card.label"
|
||||
:count="card.count"
|
||||
:status="card.status as StatCardLink['status']"
|
||||
:href="card.href"
|
||||
/>
|
||||
</div>
|
||||
<!-- Worker subtitle -->
|
||||
<p v-if="isWorker" class="text-sm text-muted-foreground">
|
||||
Mes déclarations
|
||||
</p>
|
||||
|
||||
<!-- Priority Alerts Panel -->
|
||||
<PriorityAlertsPanel
|
||||
:alerts="alerts"
|
||||
:view-all-url="viewAllAlertsUrl"
|
||||
/>
|
||||
<!-- Worker empty state -->
|
||||
<Card v-if="isWorkerEmpty">
|
||||
<CardContent
|
||||
class="flex flex-col items-center justify-center py-16"
|
||||
>
|
||||
<ClipboardList
|
||||
class="mb-3 h-12 w-12 text-muted-foreground"
|
||||
/>
|
||||
<p class="text-lg font-medium">
|
||||
Aucune déclaration assignée
|
||||
</p>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
Contactez votre responsable pour recevoir des
|
||||
déclarations
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Urgent Declarations Table -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">
|
||||
Déclarations urgentes
|
||||
</h2>
|
||||
<Button
|
||||
v-if="declarationsUrl"
|
||||
variant="outline"
|
||||
as-child
|
||||
>
|
||||
<Link :href="declarationsUrl">
|
||||
Toutes les déclarations
|
||||
</Link>
|
||||
</Button>
|
||||
<template v-if="!isWorkerEmpty">
|
||||
<!-- KPI StatCards -->
|
||||
<div
|
||||
class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4"
|
||||
>
|
||||
<StatCard
|
||||
v-for="card in statCards"
|
||||
:key="card.label"
|
||||
:label="card.label"
|
||||
:count="card.count"
|
||||
:status="card.status as StatCardLink['status']"
|
||||
:href="card.href"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card
|
||||
v-if="declarations.length > 0"
|
||||
class="overflow-hidden"
|
||||
>
|
||||
<div class="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Client</TableHead>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead>Date limite</TableHead>
|
||||
<TableHead>Assigné à</TableHead>
|
||||
<TableHead>Statut</TableHead>
|
||||
<TableHead class="w-10" />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow
|
||||
v-for="declaration in declarations"
|
||||
:key="declaration.id"
|
||||
class="cursor-pointer"
|
||||
@click="
|
||||
navigateToDeclaration(declaration)
|
||||
"
|
||||
>
|
||||
<TableCell class="font-medium">
|
||||
{{ declaration.clientName }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{ declaration.typeLabel }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span
|
||||
:class="
|
||||
deadlineClass(
|
||||
declaration.dueDate,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ declaration.dueDate ?? '—' }}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{
|
||||
declaration.assigneeName ?? '—'
|
||||
}}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
:variant="
|
||||
statusBadgeVariant(
|
||||
declaration.status,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ declaration.statusLabel }}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
as-child
|
||||
@click.stop
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
>
|
||||
<EllipsisVertical
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
@click.stop="
|
||||
navigateToDeclaration(
|
||||
declaration,
|
||||
)
|
||||
"
|
||||
>
|
||||
<Eye
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Voir
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem disabled>
|
||||
<Send
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Relancer
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem disabled>
|
||||
<UserRoundCog
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Réassigner
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</Card>
|
||||
<!-- Priority Alerts Panel -->
|
||||
<PriorityAlertsPanel
|
||||
:alerts="alerts"
|
||||
:view-all-url="viewAllAlertsUrl"
|
||||
/>
|
||||
|
||||
<Card v-else>
|
||||
<CardContent
|
||||
class="flex flex-col items-center justify-center py-12"
|
||||
<!-- Urgent Declarations Table -->
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">
|
||||
Déclarations urgentes
|
||||
</h2>
|
||||
<Button
|
||||
v-if="declarationsUrl"
|
||||
variant="outline"
|
||||
as-child
|
||||
>
|
||||
<Link :href="declarationsUrl">
|
||||
Toutes les déclarations
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card
|
||||
v-if="declarations.length > 0"
|
||||
class="overflow-hidden"
|
||||
>
|
||||
<FolderOpen
|
||||
class="mb-3 h-12 w-12 text-muted-foreground"
|
||||
/>
|
||||
<p class="text-muted-foreground">
|
||||
Aucune déclaration urgente pour le moment.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Client</TableHead>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead>Date limite</TableHead>
|
||||
<TableHead v-if="!isWorker"
|
||||
>Assigné à</TableHead
|
||||
>
|
||||
<TableHead>Statut</TableHead>
|
||||
<TableHead class="w-10" />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow
|
||||
v-for="declaration in declarations"
|
||||
:key="declaration.id"
|
||||
class="cursor-pointer"
|
||||
@click="
|
||||
navigateToDeclaration(
|
||||
declaration,
|
||||
)
|
||||
"
|
||||
>
|
||||
<TableCell class="font-medium">
|
||||
{{ declaration.clientName }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{{ declaration.typeLabel }}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span
|
||||
:class="
|
||||
deadlineClass(
|
||||
declaration.dueDate,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
declaration.dueDate ??
|
||||
'—'
|
||||
}}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="!isWorker">
|
||||
{{
|
||||
declaration.assigneeName ??
|
||||
'—'
|
||||
}}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
:variant="
|
||||
statusBadgeVariant(
|
||||
declaration.status,
|
||||
)
|
||||
"
|
||||
>
|
||||
{{
|
||||
declaration.statusLabel
|
||||
}}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
as-child
|
||||
@click.stop
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
>
|
||||
<EllipsisVertical
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
>
|
||||
<DropdownMenuItem
|
||||
@click.stop="
|
||||
navigateToDeclaration(
|
||||
declaration,
|
||||
)
|
||||
"
|
||||
>
|
||||
<Eye
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Voir
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
disabled
|
||||
>
|
||||
<Send
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Relancer
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
disabled
|
||||
>
|
||||
<UserRoundCog
|
||||
class="mr-2 h-4 w-4"
|
||||
/>
|
||||
Réassigner
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card v-else>
|
||||
<CardContent
|
||||
class="flex flex-col items-center justify-center py-12"
|
||||
>
|
||||
<FolderOpen
|
||||
class="mb-3 h-12 w-12 text-muted-foreground"
|
||||
/>
|
||||
<p class="text-muted-foreground">
|
||||
Aucune déclaration urgente pour le moment.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</AppLayout>
|
||||
|
||||
@@ -43,6 +43,7 @@ export type DashboardProps = {
|
||||
alerts: DashboardAlert[];
|
||||
workspaceName: string | null;
|
||||
roleLabel: string | null;
|
||||
isWorker: boolean;
|
||||
declarationsUrl: string | null;
|
||||
clientsUrl: string | null;
|
||||
viewAllAlertsUrl: string | null;
|
||||
|
||||
Reference in New Issue
Block a user