Files
SAE-BUT2/src/components/Header/Header.tsx
T

198 lines
8.1 KiB
TypeScript

import { Link, NavLink } from "react-router";
import { useContext, useEffect, useRef, useState } from "react";
import styles from "./Header.module.css"
import getUserNotifications from "../../utils/notifications/getUserNotifications.js";
import deleteNotificationUser from "../../utils/notifications/deleteNotificationUser.js";
import initEcho from "../../utils/echo/initEcho.js"
import readNotifications from "../../utils/notifications/readNotifications.js";
import { userNotificationsListener } from "../../utils/echo/listeners/userNotificationsListener.js";
import NotificationCard from "../NotificationCard/NotificationCard";
import { EventContext } from "../../contexts/Events/EventContext.js";
import { AuthContext } from "../../contexts/Auth/AuthContext.js";
import { PendingMembersContext } from "../../contexts/PendingMembers/PendingMembersContext";
import Notification from "../../interfaces/notification.interface";
function Header() {
const { update, user } = useContext(AuthContext)!;
const { updateEvent } = useContext(EventContext)!;
const { updatePendingMembers } = useContext(PendingMembersContext) as unknown as { updatePendingMembers: () => void };
const [notificationMenu, setnotificationMenu] = useState<boolean>(false);
const [unreadNotification, setunreadNotification] = useState<boolean>(false);
const [mobileMenu, setMobileMenu] = useState<boolean>(false);
const [notifications, setNotifications] = useState<Notification[]>([]);
const notificationRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!user) return;
updateEvent();
let echoInstance: ReturnType<typeof initEcho> | null = null;
async function initializeHeader() {
try {
const echo = initEcho();
echoInstance = echo;
userNotificationsListener(echo, user, setNotifications, setunreadNotification, updatePendingMembers);
const notifData = await getUserNotifications();
const data: Notification[] = notifData || [];
setNotifications(data);
const hasUnread = data.some(n => n.read_at === null);
setunreadNotification(hasUnread);
} catch (err) {
console.error("Erreur initialisation Header:", err);
}
}
initializeHeader();
const handleClickOutside = (event: MouseEvent) => {
if (
notificationRef.current &&
!notificationRef.current.contains(event.target as Node) &&
!(event.target as Element).closest(`.${styles.bellBtn}`)
) {
setnotificationMenu(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
if (echoInstance) {
echoInstance.disconnect();
}
};
}, []);
const toggleNotificationMenu = async () => {
try {
setnotificationMenu(!notificationMenu);
if (unreadNotification) {
const result = await readNotifications();
if (result) {
setunreadNotification(false);
update();
}
}
} catch (err) {
console.error('Erreur pour ouvrir/fermer le menu de notifications :', err);
}
}
async function deleteNotification(notificationId: string) {
try {
const result = await deleteNotificationUser(notificationId);
if (result) {
setNotifications(prev => prev.filter(n => n.id !== notificationId));
update();
}
} catch (err) {
console.error('Erreur suppression notification :', err);
}
}
return (
<div className={styles.headerContainer}>
<header className={`${styles.header} glassCard`}>
<nav className={`${mobileMenu ? styles.inactiveHeaderContent : styles.activeHeaderContent}`}>
<div className={styles.headerLeftContent}>
<NavLink className={styles.logoLink} to="/">
<img src="/logo.png" alt="logo" className={styles.logo} />
</NavLink>
<div className={styles.navLinks}>
<NavLink to="/" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Accueil
</NavLink>
<NavLink to="/events" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Evènements
</NavLink>
<NavLink to="/volunteers" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Bénévoles
</NavLink>
{user?.isAdmin && (
<NavLink to="/admin" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Administration
</NavLink>
)}
</div>
</div>
<div className={styles.headerRightContent}>
<button
className={`${styles.bellBtn} ${notificationMenu ? styles.activeBellBtn : ""}`}
onClick={() => { toggleNotificationMenu(); }}
>
<img className={styles.notificationsImg} src="/bell.svg" alt="notifications" />
{unreadNotification && <span className={styles.notificationBadge}></span>}
</button>
<Link to="/profile" className={styles.profileLink}>
<img
className={styles.profileImg}
src={user?.profile_photo_path || "/person.svg"}
alt="profile"
/>
</Link>
</div>
</nav>
<nav className={`${mobileMenu ? styles.activeMobileMenu : styles.inactiveMobileMenu}`}>
<NavLink to="/" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Accueil
</NavLink>
<NavLink to="/events" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Evènements
</NavLink>
<NavLink to="/volunteers" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Bénévoles
</NavLink>
{user?.isAdmin && (
<NavLink to="/admin" className={({ isActive }) => isActive ? styles.activeLink : styles.link}>
Administration
</NavLink>
)}
</nav>
<button
className={`${styles.burgerBtn} ${mobileMenu ? styles.activeBurgerBtn : ""}`}
onClick={() => setMobileMenu(!mobileMenu)}
>
<div className={styles.burgerBar}></div>
<div className={styles.burgerBar}></div>
<div className={styles.burgerBar}></div>
</button>
</header>
<div
ref={notificationRef}
className={`${styles.notificationDiv} glassCard ${notificationMenu ? styles.activeNotificationDiv : ""}`}
>
{notifications.length === 0 ? (
<p className={styles.notification}>Vous n'avez aucune notification !</p>
) : (
<NotificationCard notifications={notifications} deleteNotification={deleteNotification} />
)}
</div>
</div>
);
}
export default Header;