From 772712e405fc86bcfe1131d1ec9964e88648594e Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 22:31:02 +0200 Subject: [PATCH] courrier --- components/announcement/announcement-form.tsx | 6 +- .../announcement/announcements-list.tsx | 3 + components/main-nav.tsx | 9 ++- hooks/use-local-storage.ts | 56 +++++++++++++ hooks/use-unread-announcements.ts | 79 +++++++++++++++++++ 5 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 hooks/use-local-storage.ts create mode 100644 hooks/use-unread-announcements.ts diff --git a/components/announcement/announcement-form.tsx b/components/announcement/announcement-form.tsx index 04738bf0..5f9e25ce 100644 --- a/components/announcement/announcement-form.tsx +++ b/components/announcement/announcement-form.tsx @@ -219,11 +219,11 @@ export function AnnouncementForm({ userRole }: AnnouncementFormProps) { {availableRoles.map(role => ( handleRoleToggle(role.id)} > diff --git a/components/announcement/announcements-list.tsx b/components/announcement/announcements-list.tsx index f7aa64b4..150398e8 100644 --- a/components/announcement/announcements-list.tsx +++ b/components/announcement/announcements-list.tsx @@ -34,6 +34,7 @@ import { } from "@/components/ui/dialog"; import { Announcement } from "@/app/types/announcement"; import { useToast } from "@/components/ui/use-toast"; +import { useUnreadAnnouncements } from '@/hooks/use-unread-announcements'; interface AnnouncementsListProps { userRole: string[]; @@ -47,6 +48,7 @@ export function AnnouncementsList({ userRole }: AnnouncementsListProps) { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const { toast } = useToast(); + const { markAsRead } = useUnreadAnnouncements(); // Fetch announcements const fetchAnnouncements = async () => { @@ -77,6 +79,7 @@ export function AnnouncementsList({ userRole }: AnnouncementsListProps) { const handleViewAnnouncement = (announcement: Announcement) => { setSelectedAnnouncement(announcement); setIsViewDialogOpen(true); + markAsRead(announcement.id); }; // Handle deleting an announcement diff --git a/components/main-nav.tsx b/components/main-nav.tsx index 09bdc03c..cecd8ca8 100644 --- a/components/main-nav.tsx +++ b/components/main-nav.tsx @@ -38,6 +38,7 @@ import { format } from 'date-fns'; import { fr } from 'date-fns/locale'; import { NotificationBadge } from './notification-badge'; import { NotesDialog } from './notes-dialog'; +import { useUnreadAnnouncements } from '@/hooks/use-unread-announcements'; const requestNotificationPermission = async () => { try { @@ -55,6 +56,9 @@ export function MainNav() { const [userStatus, setUserStatus] = useState<'online' | 'busy' | 'away'>('online'); const [isNotesDialogOpen, setIsNotesDialogOpen] = useState(false); + // Use the unread announcements hook + const { hasUnread } = useUnreadAnnouncements(); + console.log("Session:", session); console.log("Status:", status); @@ -272,8 +276,11 @@ export function MainNav() { Radio - + + {hasUnread && ( + + )} Announcement diff --git a/hooks/use-local-storage.ts b/hooks/use-local-storage.ts new file mode 100644 index 00000000..320bf14b --- /dev/null +++ b/hooks/use-local-storage.ts @@ -0,0 +1,56 @@ +import { useState, useEffect } from 'react'; + +export function useLocalStorage(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] { + // State to store our value + // Pass initial state function to useState so logic is only executed once + const [storedValue, setStoredValue] = useState(() => { + if (typeof window === 'undefined') { + return initialValue; + } + try { + // Get from local storage by key + const item = window.localStorage.getItem(key); + // Parse stored json or if none return initialValue + return item ? JSON.parse(item) : initialValue; + } catch (error) { + // If error also return initialValue + console.error('Error reading from localStorage:', error); + return initialValue; + } + }); + + // Return a wrapped version of useState's setter function that ... + // ... persists the new value to localStorage. + const setValue = (value: T | ((val: T) => T)) => { + try { + // Allow value to be a function so we have same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value; + // Save state + setStoredValue(valueToStore); + // Save to local storage + if (typeof window !== 'undefined') { + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } + } catch (error) { + // A more advanced implementation would handle the error case + console.error('Error saving to localStorage:', error); + } + }; + + // Update stored value if the key changes + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + try { + const item = window.localStorage.getItem(key); + setStoredValue(item ? JSON.parse(item) : initialValue); + } catch (error) { + console.error('Error updating from localStorage:', error); + setStoredValue(initialValue); + } + }, [key, initialValue]); + + return [storedValue, setValue]; +} \ No newline at end of file diff --git a/hooks/use-unread-announcements.ts b/hooks/use-unread-announcements.ts new file mode 100644 index 00000000..f1a9979c --- /dev/null +++ b/hooks/use-unread-announcements.ts @@ -0,0 +1,79 @@ +import { useState, useEffect } from 'react'; +import { useLocalStorage } from './use-local-storage'; +import { Announcement } from '@/app/types/announcement'; + +type AnnouncementRead = { + [id: string]: boolean; +}; + +export function useUnreadAnnouncements() { + const [hasUnread, setHasUnread] = useState(false); + const [announcements, setAnnouncements] = useState([]); + const [readAnnouncements, setReadAnnouncements] = useLocalStorage('read-announcements', {}); + const [isLoading, setIsLoading] = useState(true); + + // Fetch announcements and check if there are any unread + const checkForUnreadAnnouncements = async () => { + try { + setIsLoading(true); + const response = await fetch('/api/announcements'); + + if (!response.ok) { + throw new Error('Failed to fetch announcements'); + } + + const data = await response.json(); + setAnnouncements(data); + + // Check if there are any unread announcements + const hasUnreadAnnouncements = data.some((announcement: Announcement) => { + return !readAnnouncements[announcement.id]; + }); + + setHasUnread(hasUnreadAnnouncements); + } catch (err) { + console.error('Error checking for unread announcements:', err); + } finally { + setIsLoading(false); + } + }; + + // Mark an announcement as read + const markAsRead = (announcementId: string) => { + setReadAnnouncements((prev: AnnouncementRead) => ({ + ...prev, + [announcementId]: true + })); + + // Check if there are still any unread announcements + const hasUnreadAnnouncements = announcements.some(announcement => { + return !readAnnouncements[announcement.id] && announcement.id !== announcementId; + }); + + setHasUnread(hasUnreadAnnouncements); + }; + + // Mark all announcements as read + const markAllAsRead = () => { + const allRead: AnnouncementRead = {}; + announcements.forEach(announcement => { + allRead[announcement.id] = true; + }); + + setReadAnnouncements(allRead); + setHasUnread(false); + }; + + // Check for unread announcements on mount + useEffect(() => { + checkForUnreadAnnouncements(); + }, []); + + return { + hasUnread, + isLoading, + checkForUnreadAnnouncements, + markAsRead, + markAllAsRead + }; +} \ No newline at end of file