164 lines
5.4 KiB
React
164 lines
5.4 KiB
React
import { useEffect, useState } from 'react';
|
|
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
|
import { useAuth } from '../../contexts/AuthContext';
|
|
import { FileText, Shield, PenTool, LayoutDashboard, LogOut, Palette, Menu, X, KeyRound, Users, Settings, ScrollText, Scale } from 'lucide-react';
|
|
import ChangePasswordModal from '../ui/ChangePasswordModal';
|
|
|
|
const navSections = [
|
|
{
|
|
label: 'Principal',
|
|
items: [
|
|
{ to: '/dashboard', icon: LayoutDashboard, label: 'Dashboard' },
|
|
{ to: '/documents', icon: FileText, label: 'Mis documentos' },
|
|
{ to: '/signatures', icon: PenTool, label: 'Firmar ahora' },
|
|
{ to: '/certificates', icon: Users, label: 'Firmantes' },
|
|
],
|
|
},
|
|
{
|
|
label: 'Sistema',
|
|
items: [
|
|
{ to: '/signature-style', icon: Settings, label: 'Configuración' },
|
|
{ to: '/audit', icon: ScrollText, label: 'Auditoría' },
|
|
],
|
|
},
|
|
];
|
|
|
|
const topbarTitles = {
|
|
'/dashboard': 'Dashboard',
|
|
'/documents': 'Mis documentos',
|
|
'/certificates': 'Firmantes',
|
|
'/signature-style': 'Configuración',
|
|
'/signatures': 'Firmar ahora',
|
|
'/legal': 'Legal',
|
|
'/audit': 'Auditoría',
|
|
};
|
|
|
|
export default function Layout({ children }) {
|
|
const { user, logout } = useAuth();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
const [showChangePassword, setShowChangePassword] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setMobileMenuOpen(false);
|
|
}, [location.pathname]);
|
|
|
|
const handleLogout = async () => {
|
|
await logout();
|
|
navigate('/login');
|
|
};
|
|
|
|
const currentTitle = topbarTitles[location.pathname] || 'FirmaDoc';
|
|
|
|
return (
|
|
<div className="app-layout">
|
|
{showChangePassword && (
|
|
<ChangePasswordModal onClose={() => setShowChangePassword(false)} />
|
|
)}
|
|
<header className="mobile-topbar">
|
|
<button
|
|
className="mobile-menu-toggle"
|
|
type="button"
|
|
onClick={() => setMobileMenuOpen(true)}
|
|
aria-label="Abrir menú"
|
|
>
|
|
<Menu size={20} />
|
|
</button>
|
|
<div className="mobile-topbar-brand">
|
|
<FileText size={18} />
|
|
<span>FirmaDoc</span>
|
|
</div>
|
|
</header>
|
|
|
|
{mobileMenuOpen && (
|
|
<button
|
|
type="button"
|
|
className="mobile-sidebar-backdrop"
|
|
aria-label="Cerrar menú"
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
/>
|
|
)}
|
|
|
|
<aside className={`sidebar ${mobileMenuOpen ? 'sidebar--mobile-open' : ''}`}>
|
|
<div className="sidebar-brand">
|
|
<div className="sidebar-brand-mark">
|
|
<img src="/app-icon-96.png" alt="LinkedSign" width="32" height="32" style={{ display: 'block' }} />
|
|
</div>
|
|
<div className="sidebar-brand-copy">
|
|
<span>LinkedSign</span>
|
|
<small>Desarrollo de Soluciones</small>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
className="mobile-sidebar-close"
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
aria-label="Cerrar menú"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
<nav className="sidebar-nav">
|
|
{navSections.map((section) => (
|
|
<div key={section.label} className="sidebar-group">
|
|
<div className="nav-group-label">{section.label}</div>
|
|
{section.items.map(({ to, icon: Icon, label }) => (
|
|
<Link
|
|
key={to}
|
|
to={to}
|
|
className={`nav-item ${location.pathname.startsWith(to) ? 'active' : ''}`}
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
>
|
|
<Icon size={15} />
|
|
<span>{label}</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
))}
|
|
<div className="sidebar-group">
|
|
<div className="nav-group-label">Extra</div>
|
|
<Link
|
|
to="/legal"
|
|
className={`nav-item ${location.pathname.startsWith('/legal') ? 'active' : ''}`}
|
|
onClick={() => setMobileMenuOpen(false)}
|
|
>
|
|
<Scale size={15} />
|
|
<span>Legal</span>
|
|
</Link>
|
|
</div>
|
|
</nav>
|
|
<div className="sidebar-footer">
|
|
<button
|
|
type="button"
|
|
className="user-info-button"
|
|
onClick={() => setShowChangePassword(true)}
|
|
title="Cambiar contraseña"
|
|
>
|
|
<div className="user-info">
|
|
<div className="user-avatar">{user?.name?.[0]?.toUpperCase()}</div>
|
|
<div className="user-details">
|
|
<span className="user-name">{user?.name}</span>
|
|
<span className="user-email">{user?.email}</span>
|
|
</div>
|
|
</div>
|
|
<KeyRound size={16} />
|
|
</button>
|
|
<button className="btn-logout" onClick={handleLogout} title="Cerrar sesión">
|
|
<LogOut size={18} />
|
|
</button>
|
|
</div>
|
|
</aside>
|
|
<main className="main-content">
|
|
<div className="topbar">
|
|
<div className="topbar-title">{currentTitle}</div>
|
|
<div className="topbar-brand-logo" aria-hidden="true">
|
|
<span className="topbar-brand-by">by</span>
|
|
<img src="/logoLinkedCorpTxtBlack.png" alt="LinkedCorp" />
|
|
</div>
|
|
</div>
|
|
{children}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|