courrier
This commit is contained in:
parent
b3569f80f7
commit
772712e405
@ -219,11 +219,11 @@ export function AnnouncementForm({ userRole }: AnnouncementFormProps) {
|
||||
{availableRoles.map(role => (
|
||||
<Badge
|
||||
key={role.id}
|
||||
variant={selectedRoles.includes(role.id) ? "default" : "outline"}
|
||||
variant={selectedRoles.includes(role.id) ? "secondary" : "outline"}
|
||||
className={`cursor-pointer px-3 py-1 ${
|
||||
selectedRoles.includes(role.id)
|
||||
? "bg-blue-600 hover:bg-blue-700"
|
||||
: "hover:bg-gray-100"
|
||||
? "bg-blue-600 hover:bg-blue-700 text-white"
|
||||
: "bg-white hover:bg-gray-100 text-gray-700 border-gray-200"
|
||||
}`}
|
||||
onClick={() => handleRoleToggle(role.id)}
|
||||
>
|
||||
|
||||
@ -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<string | null>(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
|
||||
|
||||
@ -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() {
|
||||
<RadioIcon className='w-5 h-5' />
|
||||
<span className="sr-only">Radio</span>
|
||||
</Link>
|
||||
<Link href='/announcement' className='text-white/80 hover:text-white'>
|
||||
<Link href='/announcement' className='text-white/80 hover:text-white relative'>
|
||||
<Megaphone className='w-5 h-5' />
|
||||
{hasUnread && (
|
||||
<span className="absolute -top-1 -right-1 w-2.5 h-2.5 bg-red-500 rounded-full"></span>
|
||||
)}
|
||||
<span className="sr-only">Announcement</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
56
hooks/use-local-storage.ts
Normal file
56
hooks/use-local-storage.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export function useLocalStorage<T>(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<T>(() => {
|
||||
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];
|
||||
}
|
||||
79
hooks/use-unread-announcements.ts
Normal file
79
hooks/use-unread-announcements.ts
Normal file
@ -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<Announcement[]>([]);
|
||||
const [readAnnouncements, setReadAnnouncements] = useLocalStorage<AnnouncementRead>('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
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user