upgrade ticket system
Deploy Nexus Portal to HestiaCP (FTP) / build-and-deploy (push) Successful in 14s
Deploy Nexus Portal to HestiaCP (FTP) / build-and-deploy (push) Successful in 14s
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// src/layouts/AppLayout.jsx
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Outlet, NavLink, useNavigate } from 'react-router-dom';
|
||||
import { getClientTickets } from '../services/api';
|
||||
|
||||
// ============================================================================
|
||||
// COMPOSANT : MODAL DE CONFIRMATION DE DÉCONNEXION
|
||||
@@ -60,6 +61,25 @@ const ConfirmLogoutModal = ({ isOpen, onClose, onConfirm }) => {
|
||||
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) {
|
||||
// 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
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
checkUnreadTickets(); // Premier check immédiat
|
||||
const intervalId = setInterval(checkUnreadTickets, 120000); // Check toutes les 2 minutes
|
||||
return () => clearInterval(intervalId);
|
||||
}, []);
|
||||
|
||||
// Ouvre simplement le modal
|
||||
const handleLogoutClick = () => {
|
||||
@@ -154,9 +174,48 @@ export default function AppLayout() {
|
||||
<div style={{ padding: '0 25px', marginTop: '25px', marginBottom: '10px', color: '#444', fontSize: '0.7rem', fontWeight: 'bold' }}>
|
||||
// GESTION
|
||||
</div>
|
||||
<NavLink style={getNavLinkStyle} to="/billing">Facturation</NavLink>
|
||||
<NavLink style={getNavLinkStyle} to="/support">Tickets Support</NavLink>
|
||||
<NavLink style={getNavLinkStyle} to="/settings">Profil & Sécurité</NavLink>
|
||||
{/*<NavLink style={getNavLinkStyle} to="/billing" locked>Facturation</NavLink>*/}
|
||||
<NavLink style={getNavLinkStyle} to="/support">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
||||
<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)'
|
||||
}}>
|
||||
{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'
|
||||
}}>
|
||||
<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>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Bas de la Sidebar (Déconnexion) */}
|
||||
@@ -179,7 +238,7 @@ export default function AppLayout() {
|
||||
{/* ========================================== */}
|
||||
<main style={mainContentStyle}>
|
||||
{/* Le composant <Outlet /> injecte le contenu de la page demandée sans recharger la sidebar */}
|
||||
<Outlet />
|
||||
<Outlet context={{ refreshNavbarTickets: checkUnreadTickets }} />
|
||||
</main>
|
||||
|
||||
{/* MODAL DE DÉCONNEXION */}
|
||||
|
||||
Reference in New Issue
Block a user