feat: add DeadlineReminder component for urgent task notifications
- Introduced `DeadlineReminder` component to display urgent tasks based on deadlines. - Integrated the component into `DailyPageClient` for desktop view, enhancing user awareness of critical tasks. - Implemented logic to fetch and sort urgent tasks by urgency level and remaining days.
This commit is contained in:
@@ -11,6 +11,7 @@ import { DailySection } from '@/components/daily/DailySection';
|
||||
import { PendingTasksSection } from '@/components/daily/PendingTasksSection';
|
||||
import { dailyClient } from '@/clients/daily-client';
|
||||
import { Header } from '@/components/ui/Header';
|
||||
import { DeadlineReminder } from '@/components/daily/DeadlineReminder';
|
||||
import { getPreviousWorkday, formatDateLong, isToday, generateDateTitle, formatDateShort, isYesterday } from '@/lib/date-utils';
|
||||
|
||||
interface DailyPageClientProps {
|
||||
@@ -211,8 +212,13 @@ export function DailyPageClient({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rappel des échéances urgentes - Desktop uniquement */}
|
||||
<div className="hidden sm:block container mx-auto px-4 pt-4 pb-2">
|
||||
<DeadlineReminder />
|
||||
</div>
|
||||
|
||||
{/* Contenu principal */}
|
||||
<main className="container mx-auto px-4 py-8">
|
||||
<main className="container mx-auto px-4 py-6 sm:py-4">
|
||||
{/* Layout Mobile uniquement - Section Aujourd'hui en premier */}
|
||||
<div className="block sm:hidden">
|
||||
{dailyView && (
|
||||
|
||||
120
src/components/daily/DeadlineReminder.tsx
Normal file
120
src/components/daily/DeadlineReminder.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useTransition } from 'react';
|
||||
import { DeadlineTask } from '@/services/analytics/deadline-analytics';
|
||||
import { getDeadlineMetrics } from '@/actions/deadline-analytics';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
|
||||
export function DeadlineReminder() {
|
||||
const [urgentTasks, setUrgentTasks] = useState<DeadlineTask[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
useEffect(() => {
|
||||
const loadUrgentTasks = () => {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
setError(null);
|
||||
const response = await getDeadlineMetrics();
|
||||
|
||||
if (response.success && response.data) {
|
||||
// Combiner toutes les tâches urgentes et trier par urgence
|
||||
const combinedTasks = [
|
||||
...response.data.overdue,
|
||||
...response.data.critical,
|
||||
...response.data.warning
|
||||
].sort((a, b) => {
|
||||
// En retard d'abord, puis critique, puis attention
|
||||
const urgencyOrder: Record<string, number> = { 'overdue': 0, 'critical': 1, 'warning': 2 };
|
||||
if (urgencyOrder[a.urgencyLevel] !== urgencyOrder[b.urgencyLevel]) {
|
||||
return urgencyOrder[a.urgencyLevel] - urgencyOrder[b.urgencyLevel];
|
||||
}
|
||||
// Si même urgence, trier par jours restants
|
||||
return a.daysRemaining - b.daysRemaining;
|
||||
});
|
||||
|
||||
setUrgentTasks(combinedTasks);
|
||||
} else {
|
||||
setError(response.error || 'Erreur lors du chargement des échéances');
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Erreur lors du chargement des échéances');
|
||||
console.error('Erreur échéances:', err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadUrgentTasks();
|
||||
}, []);
|
||||
|
||||
const getUrgencyIcon = (task: DeadlineTask) => {
|
||||
if (task.urgencyLevel === 'overdue') return '🔴';
|
||||
if (task.urgencyLevel === 'critical') return '🟠';
|
||||
return '🟡';
|
||||
};
|
||||
|
||||
const getUrgencyText = (task: DeadlineTask) => {
|
||||
if (task.urgencyLevel === 'overdue') {
|
||||
return task.daysRemaining === -1 ? 'En retard de 1 jour' : `En retard de ${Math.abs(task.daysRemaining)} jours`;
|
||||
} else if (task.urgencyLevel === 'critical') {
|
||||
return task.daysRemaining === 0 ? 'Échéance aujourd\'hui' :
|
||||
task.daysRemaining === 1 ? 'Échéance demain' :
|
||||
`Dans ${task.daysRemaining} jours`;
|
||||
} else {
|
||||
return `Dans ${task.daysRemaining} jours`;
|
||||
}
|
||||
};
|
||||
|
||||
const getSourceIcon = (source: string) => {
|
||||
switch (source) {
|
||||
case 'jira': return '🔗';
|
||||
case 'reminder': return '📱';
|
||||
default: return '📋';
|
||||
}
|
||||
};
|
||||
|
||||
// Ne rien afficher si pas de tâches urgentes ou si en cours de chargement
|
||||
if (isPending || error || urgentTasks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="mb-2 p-4 bg-gradient-to-r from-amber-50/50 to-orange-50/50 dark:from-amber-950/20 dark:to-orange-950/20 border-amber-200/50 dark:border-amber-800/50">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="text-2xl">⚠️</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-sm font-semibold text-amber-800 dark:text-amber-200 mb-2">
|
||||
Rappel - Tâches urgentes ({urgentTasks.length})
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{urgentTasks.map((task, index) => (
|
||||
<div
|
||||
key={task.id}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-medium bg-white/60 dark:bg-gray-800/60 border border-amber-200/60 dark:border-amber-700/60"
|
||||
>
|
||||
<span>{getUrgencyIcon(task)}</span>
|
||||
<span className="truncate max-w-[200px]" title={task.title}>
|
||||
{task.title}
|
||||
</span>
|
||||
<span className="text-[10px] text-amber-700 dark:text-amber-300 opacity-75">
|
||||
({getUrgencyText(task)})
|
||||
</span>
|
||||
<span className="text-[10px] opacity-60">
|
||||
{getSourceIcon(task.source)}
|
||||
</span>
|
||||
{index < urgentTasks.length - 1 && (
|
||||
<span className="text-amber-400 dark:text-amber-500 opacity-50">•</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-2 text-xs text-amber-700 dark:text-amber-300 opacity-75">
|
||||
Consultez la page d'accueil pour plus de détails sur les échéances
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user