Files
L-Ami-Fiduciaire/resources/js/components/dashboard/PriorityAlertsPanel.vue
Saad Ibn-Ezzoubayr 4807376c49 feat: implement Story 2.2 — Priority Alerts Panel with UI fixes
Add PriorityAlertsPanel component to the dashboard, update DashboardController
with alert logic, and apply misc UI fixes across sidebar, forms, and pages.
Includes epic-1 retrospective and sprint status update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 12:33:27 +00:00

155 lines
5.4 KiB
Vue

<script setup lang="ts">
import { Link, router } from '@inertiajs/vue3';
import {
AlertCircle,
AlertTriangle,
CheckCircle,
ChevronRight,
Info,
} from 'lucide-vue-next';
import { computed } from 'vue';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import type { DashboardAlert } from '@/types';
type Props = {
alerts: DashboardAlert[];
viewAllUrl: string | null;
};
const props = defineProps<Props>();
const alertCount = computed(() => props.alerts.length);
const severityConfig = {
critical: {
icon: AlertCircle,
colorClass: 'text-red-600',
bgClass: 'bg-red-500/10',
pulseClass: 'animate-pulse',
},
warning: {
icon: AlertTriangle,
colorClass: 'text-amber-600',
bgClass: 'bg-amber-500/10',
pulseClass: '',
},
info: {
icon: Info,
colorClass: 'text-blue-600',
bgClass: 'bg-blue-500/10',
pulseClass: '',
},
} as const;
function daysText(alert: DashboardAlert): string {
return `${alert.daysValue} ${alert.daysLabel}`;
}
function navigateToAlert(alert: DashboardAlert): void {
router.get(alert.showUrl);
}
</script>
<template>
<div class="space-y-4">
<div class="flex items-center gap-2">
<h2 class="text-lg font-semibold">Alertes prioritaires</h2>
<Badge v-if="alertCount > 0" variant="secondary">
{{ alertCount }}
</Badge>
</div>
<!-- Empty state -->
<Card v-if="alertCount === 0">
<CardContent
class="flex flex-col items-center justify-center py-12"
>
<CheckCircle class="mb-3 h-12 w-12 text-green-500" />
<p class="text-muted-foreground">
Aucune alerte &mdash; tout est en ordre
</p>
</CardContent>
</Card>
<!-- Alerts list -->
<Card v-else class="overflow-hidden">
<CardContent class="p-0">
<ul class="divide-y">
<li
v-for="alert in alerts"
:key="alert.id"
role="link"
tabindex="0"
class="flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
@click="navigateToAlert(alert)"
@keydown.enter="navigateToAlert(alert)"
>
<!-- Severity icon -->
<div
:class="[
'flex h-8 w-8 shrink-0 items-center justify-center rounded-full',
severityConfig[alert.severity].bgClass,
]"
>
<component
:is="severityConfig[alert.severity].icon"
:class="[
'h-4 w-4',
severityConfig[alert.severity].colorClass,
alert.severity === 'critical'
? severityConfig[alert.severity]
.pulseClass
: '',
]"
/>
</div>
<!-- Alert content -->
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<span class="truncate font-medium">
{{ alert.clientName }}
</span>
<span class="text-sm text-muted-foreground">
{{ alert.typeLabel }}
</span>
</div>
<p
:class="[
'text-sm',
severityConfig[alert.severity].colorClass,
alert.severity === 'critical'
? severityConfig[alert.severity]
.pulseClass
: '',
]"
>
{{ daysText(alert) }}
</p>
</div>
<!-- Arrow -->
<ChevronRight
class="h-4 w-4 shrink-0 text-muted-foreground"
/>
</li>
</ul>
<!-- View all link -->
<div
v-if="alertCount === 20 && viewAllUrl"
class="border-t px-4 py-3 text-center"
>
<Link
:href="viewAllUrl"
class="text-sm font-medium text-primary hover:underline"
>
Voir tout
</Link>
</div>
</CardContent>
</Card>
</div>
</template>