Initial commit
This commit is contained in:
206
app/(main)/notifications/recentes/page.tsx
Normal file
206
app/(main)/notifications/recentes/page.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
'use client';
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user