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>
155 lines
5.4 KiB
Vue
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 — 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>
|