upgrade design

This commit is contained in:
LathanDevers
2026-06-25 10:50:49 +02:00
parent 323667e835
commit d83b7ad4e7
9 changed files with 226 additions and 442 deletions
+41 -134
View File
@@ -1,12 +1,8 @@
// src/layouts/AppLayout.jsx
import { useState, useEffect } from 'react';
import { Outlet, NavLink, useNavigate } from 'react-router-dom';
import { getClientTickets } from '../services/api';
import ConfirmLogoutModal from '../components/ui/ConfirmLogoutModal';
import ConfirmLogoutModal from '../components/ui/ConfirmLogoutModal'; // Le modal extrait !
// ============================================================================
// COMPOSANT PRINCIPAL : LAYOUT DU PORTAIL
// ============================================================================
export default function AppLayout() {
const navigate = useNavigate();
const [showLogoutModal, setShowLogoutModal] = useState(false);
@@ -16,187 +12,98 @@ export default function AppLayout() {
try {
const data = await getClientTickets();
if (data && data.list) {
// On cherche les tickets qui ont le statut 'on_hold'
const unread = data.list.filter(t => t.status === 'on_hold' || t.unread).length;
setUnreadCount(unread);
}
} catch (err) {
// Erreur silencieuse : on ne pollue pas l'écran si le réseau saute
}
} catch (err) {}
};
useEffect(() => {
checkUnreadTickets(); // Premier check immédiat
const intervalId = setInterval(checkUnreadTickets, 120000); // Check toutes les 2 minutes
checkUnreadTickets();
const intervalId = setInterval(checkUnreadTickets, 120000);
return () => clearInterval(intervalId);
}, []);
// Ouvre simplement le modal
const handleLogoutClick = () => {
setShowLogoutModal(true);
};
// Exécute la vraie déconnexion
const confirmLogout = () => {
// Ici, tu pourras effacer les tokens (ex: localStorage.removeItem('token'))
setShowLogoutModal(false);
navigate('/login');
};
// --- DESIGN SYSTEM DU BUNKER ---
const sidebarStyle = {
width: '260px',
backgroundColor: '#121212', // Gris blindage
borderRight: '1px solid #222222',
height: '100vh',
display: 'flex',
flexDirection: 'column',
position: 'fixed',
top: 0,
left: 0,
fontFamily: 'monospace'
};
const mainContentStyle = {
marginLeft: '260px', // Laisse la place à la sidebar
minHeight: '100vh',
backgroundColor: '#050505', // Fond abyssal
color: '#E0E0E0',
padding: '40px'
};
// Fonction pour styliser le lien actif (en Cyan)
const getNavLinkStyle = ({ isActive }) => ({
display: 'block',
padding: '15px 25px',
color: isActive ? '#00E5FF' : '#888',
backgroundColor: isActive ? 'rgba(0, 229, 255, 0.05)' : 'transparent',
textDecoration: 'none',
borderLeft: isActive ? '4px solid #00E5FF' : '4px solid transparent',
transition: 'all 0.2s',
textTransform: 'uppercase',
letterSpacing: '1px',
fontSize: '0.9rem'
});
// 🌟 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 style={{ display: 'block' }}>
{/* ========================================== */}
{/* BARRE LATÉRALE (SIDEBAR) */}
{/* ========================================== */}
<aside style={sidebarStyle}>
{/* En-tête de la Sidebar (Logo / Marque) */}
<div style={{ padding: '30px 25px', borderBottom: '1px solid #222' }}>
<h1 style={{
color: '#FFF',
margin: 0,
fontSize: '1.8rem',
letterSpacing: '3px',
display: 'flex',
alignItems: 'baseline',
gap: '8px'
}}>
<div className="flex min-h-screen bg-[#050505] text-[#E0E0E0] font-sans">
{/* SIDEBAR */}
<aside className="w-[260px] 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 style={{
color: '#00E5FF',
fontSize: '0.7rem',
letterSpacing: '1px',
fontWeight: 'normal',
}}>
by GISE
</span>
<span className="text-cyan-400 text-[10px] tracking-widest font-normal">by GISE</span>
</h1>
<p style={{ color: '#555', fontSize: '0.7rem', margin: '8px 0 0 0' }}>
CONSOLE D'INFRASTRUCTURE v1.0
<p className="text-[#555] text-[10px] font-mono tracking-wider mt-2 uppercase">
Console d'infrastructure v1.0
</p>
</div>
{/* Menu de Navigation */}
<nav style={{ flex: 1, marginTop: '20px' }}>
<div style={{ padding: '0 25px', marginBottom: '10px', color: '#444', fontSize: '0.7rem', fontWeight: 'bold' }}>
<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 style={getNavLinkStyle} to="/dashboard">Tableau de bord</NavLink>
<NavLink style={getNavLinkStyle} to="/services">Inventaire Réseau</NavLink>
<NavLink className={navLinkClass} to="/dashboard">Tableau de bord</NavLink>
<NavLink className={navLinkClass} to="/services">Inventaire Réseau</NavLink>
<div style={{ padding: '0 25px', marginTop: '25px', marginBottom: '10px', color: '#444', fontSize: '0.7rem', fontWeight: 'bold' }}>
<div className="px-6 mt-8 mb-2 text-[#444] text-[10px] font-bold font-mono tracking-widest">
// GESTION
</div>
{/*<NavLink style={getNavLinkStyle} to="/billing" locked>Facturation</NavLink>*/}
<NavLink style={getNavLinkStyle} to="/support">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
<NavLink className={navLinkClass} to="/support">
<div className="flex justify-between items-center w-full">
<span>Tickets Support</span>
{/* LE BADGE DE NOTIFICATION */}
{unreadCount > 0 && (
<span style={{
backgroundColor: '#00E5FF',
color: '#000',
fontSize: '0.65rem',
fontWeight: 'bold',
padding: '2px 8px',
borderRadius: '12px',
boxShadow: '0 0 10px rgba(0,229,255,0.6)'
}}>
<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 style={getNavLinkStyle} to="/settings">Profil & Sécurité</NavLink>*/}
{/* === MODULES VERROUILLÉS (WORK IN PROGRESS) === */}
<div style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '15px 25px', color: '#444', borderLeft: '4px solid transparent',
textTransform: 'uppercase', letterSpacing: '1px', fontSize: '0.9rem',
cursor: 'not-allowed', userSelect: 'none'
}}>
<span>Facturation</span>
<span style={{ fontSize: '0.6rem', color: '#333', border: '1px solid #333', padding: '2px 5px', borderRadius: '3px', fontWeight: 'bold' }}>PROCHAINEMENT</span>
</div>
<div style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
padding: '15px 25px', color: '#444', borderLeft: '4px solid transparent',
textTransform: 'uppercase', letterSpacing: '1px', fontSize: '0.9rem',
cursor: 'not-allowed', userSelect: 'none'
}}>
<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>Facturation</span>
<span className="text-[9px] text-[#333] border border-[#333] px-1.5 py-0.5 rounded font-bold">WIP</span>
</div>
<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 style={{ fontSize: '0.6rem', color: '#333', border: '1px solid #333', padding: '2px 5px', borderRadius: '3px', fontWeight: 'bold' }}>PROCHAINEMENT</span>
<span className="text-[9px] text-[#333] border border-[#333] px-1.5 py-0.5 rounded font-bold">WIP</span>
</div>
</nav>
{/* Bas de la Sidebar (Déconnexion) */}
<div style={{ padding: '20px', borderTop: '1px solid #222' }}>
<div className="p-5 border-t border-[#222]">
<button
onClick={handleLogoutClick}
style={{
width: '100%', padding: '12px', backgroundColor: 'transparent',
color: '#ff003c', border: '1px solid #ff003c', cursor: 'pointer',
fontFamily: 'monospace', textTransform: 'uppercase'
}}
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 DYNAMIQUE */}
{/* ========================================== */}
<main style={mainContentStyle}>
{/* Le composant <Outlet /> injecte le contenu de la page demandée sans recharger la sidebar */}
{/* ZONE DE CONTENU */}
<main className="ml-[260px] p-10 flex-1 min-h-screen">
<Outlet context={{ refreshNavbarTickets: checkUnreadTickets }} />
</main>
{/* MODAL DE DÉCONNEXION */}
<ConfirmLogoutModal
isOpen={showLogoutModal}
onClose={() => setShowLogoutModal(false)}
onConfirm={confirmLogout}
/>
</div>
);
}