Files
btpxpress-frontend/app/(main)/notifications/recentes/page.tsx
dahoud a8825a058b Fix: Corriger toutes les erreurs de build du frontend
- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts
- Ajout des propriétés manquantes aux objets User mockés
- Conversion des dates de string vers objets Date
- Correction des appels asynchrones et des types incompatibles
- Ajout de dynamic rendering pour résoudre les erreurs useSearchParams
- Enveloppement de useSearchParams dans Suspense boundary
- Configuration de force-dynamic au niveau du layout principal

Build réussi: 126 pages générées avec succès

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 13:23:08 +00:00

209 lines
8.4 KiB
TypeScript

'use client';
export const dynamic = 'force-dynamic';
import React, { useState, useEffect, useRef } from 'react';
import { Card } from 'primereact/card';
import { Toast } from 'primereact/toast';
import { Button } from 'primereact/button';
import { Timeline } from 'primereact/timeline';
import { Tag } from 'primereact/tag';
import { Badge } from 'primereact/badge';
import notificationService, { Notification } from '../../../../services/notificationService';
import { formatDistanceToNow, format } from 'date-fns';
import { fr } from 'date-fns/locale';
const NotificationsRecentesPage = () => {
const [notifications, setNotifications] = useState<Notification[]>([]);
const [loading, setLoading] = useState(true);
const toast = useRef<Toast>(null);
useEffect(() => {
loadNotifications();
}, []);
const loadNotifications = async () => {
try {
setLoading(true);
const data = await notificationService.getNotifications();
// Filtrer les notifications des 7 derniers jours
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const recentNotifications = data.filter(n => new Date(n.date) >= sevenDaysAgo);
// Trier par date décroissante
recentNotifications.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
setNotifications(recentNotifications);
} catch (error) {
console.error('Erreur lors du chargement des notifications:', error);
toast.current?.show({
severity: 'error',
summary: 'Erreur',
detail: 'Impossible de charger les notifications récentes',
life: 3000
});
} finally {
setLoading(false);
}
};
const markAsRead = async (notification: Notification) => {
try {
await notificationService.markAsRead(notification.id);
setNotifications(notifications.map(n =>
n.id === notification.id ? { ...n, lu: true } : n
));
toast.current?.show({
severity: 'success',
summary: 'Succès',
detail: 'Notification marquée comme lue',
life: 2000
});
} catch (error) {
console.error('Erreur:', error);
}
};
const getTypeIcon = (type: string) => {
switch (type) {
case 'info': return 'pi-info-circle';
case 'warning': return 'pi-exclamation-triangle';
case 'success': return 'pi-check-circle';
case 'error': return 'pi-times-circle';
default: return 'pi-info-circle';
}
};
const getTypeColor = (type: string) => {
switch (type) {
case 'info': return '#3B82F6';
case 'warning': return '#F59E0B';
case 'success': return '#10B981';
case 'error': return '#EF4444';
default: return '#3B82F6';
}
};
const customizedMarker = (item: Notification) => {
return (
<span
className="flex w-2rem h-2rem align-items-center justify-content-center text-white border-circle z-1 shadow-1"
style={{ backgroundColor: getTypeColor(item.type) }}
>
<i className={`pi ${getTypeIcon(item.type)}`}></i>
</span>
);
};
const customizedContent = (item: Notification) => {
return (
<Card className={!item.lu ? 'bg-blue-50' : ''}>
<div className="flex justify-content-between align-items-start mb-3">
<div className="flex-1">
<div className="flex align-items-center gap-2 mb-2">
<h4 className="m-0">{item.titre}</h4>
{!item.lu && <Badge value="Nouveau" severity="info" />}
</div>
<p className="text-color-secondary m-0">{item.message}</p>
</div>
{!item.lu && (
<Button
icon="pi pi-check"
className="p-button-rounded p-button-text p-button-success"
tooltip="Marquer comme lue"
onClick={() => markAsRead(item)}
/>
)}
</div>
<div className="flex align-items-center gap-3 text-sm text-color-secondary">
<span>
<i className="pi pi-clock mr-2"></i>
{formatDistanceToNow(new Date(item.date), { addSuffix: true, locale: fr })}
</span>
<span>
<i className="pi pi-calendar mr-2"></i>
{format(new Date(item.date), 'dd/MM/yyyy HH:mm', { locale: fr })}
</span>
</div>
{item.metadata && (
<div className="mt-3 pt-3 border-top-1 border-200">
{item.metadata.chantierNom && (
<div className="flex align-items-center gap-2">
<Tag icon="pi pi-building" value={item.metadata.chantierNom} />
</div>
)}
{item.metadata.clientNom && (
<div className="flex align-items-center gap-2 mt-2">
<Tag icon="pi pi-user" value={item.metadata.clientNom} severity="info" />
</div>
)}
</div>
)}
</Card>
);
};
const unreadCount = notifications.filter(n => !n.lu).length;
return (
<div className="grid">
<div className="col-12">
<div className="flex justify-content-between align-items-center mb-4">
<div className="flex align-items-center gap-2">
<h2 className="m-0">Notifications Récentes</h2>
{unreadCount > 0 && (
<Badge value={unreadCount} severity="danger" />
)}
</div>
<Button
label="Actualiser"
icon="pi pi-refresh"
className="p-button-text"
onClick={loadNotifications}
/>
</div>
</div>
<div className="col-12">
<Toast ref={toast} />
{loading ? (
<Card>
<div className="text-center p-5">
<i className="pi pi-spin pi-spinner text-4xl"></i>
<p>Chargement des notifications...</p>
</div>
</Card>
) : notifications.length === 0 ? (
<Card>
<div className="text-center p-5">
<i className="pi pi-inbox text-6xl text-gray-400 mb-3"></i>
<h3>Aucune notification récente</h3>
<p className="text-color-secondary">Aucune notification au cours des 7 derniers jours</p>
</div>
</Card>
) : (
<Card>
<div className="mb-3 p-3 bg-blue-50 border-round">
<div className="flex align-items-center gap-2">
<i className="pi pi-info-circle text-blue-500"></i>
<span className="text-sm">
Affichage des notifications des 7 derniers jours ({notifications.length} notification{notifications.length > 1 ? 's' : ''})
</span>
</div>
</div>
<Timeline
value={notifications}
align="alternate"
className="customized-timeline"
marker={customizedMarker}
content={customizedContent}
/>
</Card>
)}
</div>
</div>
);
};
export default NotificationsRecentesPage;