This commit is contained in:
alma 2025-05-04 22:31:02 +02:00
parent b3569f80f7
commit 772712e405
5 changed files with 149 additions and 4 deletions

View File

@ -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)}
>

View File

@ -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

View File

@ -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>

View 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];
}

View 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
};
}