Files
portail-gise/src/layouts/AppLayout.jsx
T
2026-07-12 10:28:02 +02:00

107 lines
4.2 KiB
React

import { useState, useEffect } from 'react';
import { Outlet, NavLink, useNavigate } from 'react-router-dom';
import { getClientTickets } from '../services/api';
import { LayoutDashboard, Globe, Settings, FileText } from 'lucide-react';
import ConfirmLogoutModal from '../components/ui/ConfirmLogoutModal'; // Le modal extrait !
export default function AppLayout() {
const navigate = useNavigate();
const [showLogoutModal, setShowLogoutModal] = useState(false);
const [unreadCount, setUnreadCount] = useState(0);
const checkUnreadTickets = async () => {
try {
const data = await getClientTickets();
if (data && data.list) {
const unread = data.list.filter(t => t.status === 'on_hold' || t.unread).length;
setUnreadCount(unread);
}
} catch (err) { }
};
useEffect(() => {
checkUnreadTickets();
const intervalId = setInterval(checkUnreadTickets, 120000);
return () => clearInterval(intervalId);
}, []);
const confirmLogout = () => {
setShowLogoutModal(false);
navigate('/login');
};
// 🌟 La fonction magique Tailwind pour les liens du menu
const navLinkClass = ({ isActive }) =>
`block px-6 py-4 no-underline border-l-4 transition-all uppercase tracking-widest text-sm font-mono ${isActive
? 'text-cyan-400 bg-cyan-400/5 border-cyan-400'
: 'text-[#888] border-transparent hover:text-gray-300 hover:bg-white/5'
}`;
return (
<div className="flex min-h-screen bg-[#050505] text-[#E0E0E0] font-sans">
{/* SIDEBAR */}
<aside className="w-65 bg-[#121212] border-r border-[#222222] h-screen flex flex-col fixed top-0 left-0">
<div className="px-6 py-8 border-b border-[#222]">
<h1 className="text-white m-0 text-3xl font-black tracking-widest flex items-baseline gap-2">
NEXUS
<span className="text-cyan-400 text-[10px] tracking-widest font-normal">by GISE</span>
</h1>
<p className="text-[#555] text-[10px] font-mono tracking-wider mt-2 uppercase">
Console d'infrastructure v1.0
</p>
</div>
<nav className="flex-1 mt-6">
<div className="px-6 mb-2 text-[#444] text-[10px] font-bold font-mono tracking-widest">
// SUPERVISION
</div>
<NavLink className={navLinkClass} to="/dashboard">Tableau de bord</NavLink>
<NavLink className={navLinkClass} to="/services">Inventaire Réseau</NavLink>
<div className="px-6 mt-8 mb-2 text-[#444] text-[10px] font-bold font-mono tracking-widest">
// GESTION
</div>
<NavLink className={navLinkClass} to="/support">
<div className="flex justify-between items-center w-full">
<span>Tickets Support</span>
{unreadCount > 0 && (
<span className="bg-cyan-400 text-black text-[10px] font-bold px-2 py-0.5 rounded-full shadow-[0_0_10px_rgba(0,229,255,0.6)] animate-pulse">
{unreadCount} NEW
</span>
)}
</div>
</NavLink>
<NavLink className={navLinkClass} to="/facturation">Facturation</NavLink>
<div className="flex justify-between items-center px-6 py-4 text-[#444] border-l-4 border-transparent uppercase tracking-widest text-sm font-mono cursor-not-allowed select-none">
<span>Profil & Sécurité</span>
<span className="text-[9px] text-[#333] border border-[#333] px-1.5 py-0.5 rounded font-bold">WIP</span>
</div>
</nav>
<div className="p-5 border-t border-[#222]">
<button
onClick={() => setShowLogoutModal(true)}
className="w-full p-3 bg-transparent text-[#ff003c] hover:bg-[#ff003c]/10 border border-[#ff003c] cursor-pointer font-mono text-sm tracking-widest uppercase transition-colors rounded-sm"
>
[ Déconnexion ]
</button>
</div>
</aside>
{/* ZONE DE CONTENU */}
<main className="ml-65 p-10 flex-1 min-h-screen">
<Outlet context={{ refreshNavbarTickets: checkUnreadTickets }} />
</main>
<ConfirmLogoutModal
isOpen={showLogoutModal}
onClose={() => setShowLogoutModal(false)}
onConfirm={confirmLogout}
/>
</div>
);
}