Convert Header to Typescript
This commit is contained in:
1 parent
5b4078e759
commit
5787f941fd
2 files changed
+29
-21
No files matched your search
@@ -0,0 +1,204 @@
|
||||
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 { userCreatedListener } from "../../utils/echo/listeners/userCreatedListener";
|
||||
import { userNotificationsListener } from "../../utils/echo/listeners/userNotificationsListener.js";
|
||||
import NotificationCard from "../NotificationCard/NotificationCard.jsx";
|
||||
import { EventContext } from "../../contexts/Events/EventContext.js";
|
||||
import { AuthContext } from "../../contexts/Auth/AuthContext.js";
|
||||
import { PendingMembersContext } from "../../contexts/PendingMembers/PendingMembersContext";
|
||||
|
||||
interface Notification {
|
||||
id: number;
|
||||
content: string;
|
||||
created_at: string | number;
|
||||
pivot: { unread: number };
|
||||
}
|
||||
|
||||
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(() => {
|
||||
|
||||
updateEvent();
|
||||
|
||||
let echoInstance: ReturnType<typeof initEcho> | null = null;
|
||||
let channelsToLeave: string[] = [];
|
||||
|
||||
async function initializeHeader() {
|
||||
try {
|
||||
const notifData = await getUserNotifications();
|
||||
|
||||
const data: Notification[] = notifData || [];
|
||||
setNotifications(data);
|
||||
const hasUnread = data.some(n => n.pivot && n.pivot.unread === 1);
|
||||
setunreadNotification(hasUnread);
|
||||
|
||||
const echo = initEcho();
|
||||
echoInstance = echo;
|
||||
|
||||
const activeChannels: (string | null)[] = [
|
||||
userCreatedListener(echo, user, setNotifications, setunreadNotification, updatePendingMembers),
|
||||
userNotificationsListener(echo, user, setNotifications, setunreadNotification),
|
||||
];
|
||||
channelsToLeave = activeChannels.filter((name): name is string => name !== null);
|
||||
|
||||
} 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) {
|
||||
channelsToLeave.forEach(chan => echoInstance!.leave(chan));
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
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: number) {
|
||||
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="/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;
|
||||
Reference in new issue
Block a user