Added new design to modals
This commit is contained in:
@@ -42,6 +42,29 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
||||
}
|
||||
);
|
||||
|
||||
// Récupérer les notifications
|
||||
const { data: notificationsData, mutate: mutateNotifications } = useSWR<{
|
||||
notifications: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
message: string;
|
||||
read: boolean;
|
||||
link: string | null;
|
||||
createdAt: string;
|
||||
}>;
|
||||
unreadCount: number;
|
||||
}>(
|
||||
'/api/notifications',
|
||||
fetcher,
|
||||
{
|
||||
refreshInterval: 3000, // Rafraîchir toutes les 3 secondes
|
||||
}
|
||||
);
|
||||
|
||||
const notifications = notificationsData?.notifications || [];
|
||||
const unreadNotificationsCount = notificationsData?.unreadCount || 0;
|
||||
|
||||
// Récupérer les pages accessibles pour l'utilisateur
|
||||
const { data: userPagesData } = useSWR<{ pages: string[] }>(
|
||||
'/api/user/pages',
|
||||
@@ -222,7 +245,7 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
||||
{/* Footer */}
|
||||
<div className="p-6 border-t border-gray-200">
|
||||
<p className="text-xs text-gray-500 text-center">
|
||||
© 2025 MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
||||
© {new Date().getFullYear()} MAD - <a href="https://legouix.dev" target="_blank" className="text-lblue hover:text-dblue">Propulsé par LGX</a>
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -244,19 +267,105 @@ export default function DashboardLayout({ user, children }: DashboardLayoutProps
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||
</svg>
|
||||
{/* Badge de notification */}
|
||||
<span className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border-2 border-white"></span>
|
||||
{unreadNotificationsCount > 0 && (
|
||||
<span className="absolute -top-1 -right-1 min-w-[18px] h-[18px] bg-red-500 text-white text-[10px] font-semibold rounded-full border-2 border-white flex items-center justify-center px-1">
|
||||
{unreadNotificationsCount > 99 ? '99+' : unreadNotificationsCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Dropdown Notifications */}
|
||||
{showNotifications && (
|
||||
<div className="absolute right-0 top-12 w-80 bg-white rounded-lg shadow-xl border border-gray-200 z-50 animate-slideUp">
|
||||
<div className="p-4 border-b border-gray-200">
|
||||
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h3 className="text-sm font-semibold text-gray-900">Notifications</h3>
|
||||
{unreadNotificationsCount > 0 && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
await fetch('/api/notifications', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ markAllAsRead: true }),
|
||||
});
|
||||
mutateNotifications();
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du marquage des notifications:', error);
|
||||
}
|
||||
}}
|
||||
className="text-xs text-lblue hover:text-dblue font-medium"
|
||||
>
|
||||
Tout marquer comme lu
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
<div className="p-4 text-center text-sm text-gray-500">
|
||||
Aucune notification
|
||||
</div>
|
||||
{notifications.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-gray-500">
|
||||
Aucune notification
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y divide-gray-100">
|
||||
{notifications.map((notification) => (
|
||||
<button
|
||||
key={notification.id}
|
||||
onClick={async () => {
|
||||
// Marquer comme lue
|
||||
if (!notification.read) {
|
||||
try {
|
||||
await fetch('/api/notifications', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ notificationId: notification.id }),
|
||||
});
|
||||
mutateNotifications();
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du marquage de la notification:', error);
|
||||
}
|
||||
}
|
||||
// Naviguer vers le lien si disponible
|
||||
if (notification.link) {
|
||||
router.push(notification.link);
|
||||
setShowNotifications(false);
|
||||
}
|
||||
}}
|
||||
className={`w-full text-left p-4 hover:bg-gray-50 transition-colors ${
|
||||
!notification.read ? 'bg-blue-50/50' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`flex-shrink-0 w-2 h-2 rounded-full mt-2 ${
|
||||
!notification.read ? 'bg-lblue' : 'bg-transparent'
|
||||
}`}></div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<p className={`text-sm font-medium ${
|
||||
!notification.read ? 'text-gray-900' : 'text-gray-700'
|
||||
}`}>
|
||||
{notification.title}
|
||||
</p>
|
||||
<span className="text-xs text-gray-400 ml-2 flex-shrink-0">
|
||||
{new Date(notification.createdAt).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 line-clamp-2">
|
||||
{notification.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user