Actualizar frontend/src/components/Sidebar.jsx
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
// src/components/Sidebar.jsx
|
||||
import { useState } from "react";
|
||||
import { useAuth } from "../lib/auth";
|
||||
|
||||
const MENU = [
|
||||
{ id: "ocupabilidad", ico: "📊", label: "Ocupabilidad" },
|
||||
{ id: "ventas", ico: "💰", label: "Ventas" },
|
||||
{ id: "comisiones", ico: "🧮", label: "Comisiones" },
|
||||
{ id: "cobranza", ico: "📋", label: "Cobranza" },
|
||||
// Grupo COBRANZA con submenú (CARTERA = cobranza, RETIROS = retiros)
|
||||
{ id: "grupo-cobranza", ico: "📋", label: "Cobranza", grupo: true,
|
||||
hijos: [
|
||||
{ id: "cobranza", ico: "📁", label: "Cartera" },
|
||||
{ id: "retiros", ico: "🚪", label: "Retiros" },
|
||||
] },
|
||||
{ id: "rentabilidad", ico: "📈", label: "Rentabilidad" },
|
||||
{ id: "asesores", ico: "👥", label: "Asesores" },
|
||||
{ id: "usuarios", ico: "🔐", label: "Usuarios" },
|
||||
@@ -13,25 +19,94 @@ const MENU = [
|
||||
|
||||
export default function Sidebar({ active, onChange }) {
|
||||
const { perfil, permisos, logout } = useAuth();
|
||||
const visibles = MENU.filter((m) => permisos.includes(m.id));
|
||||
const [colapsado, setColapsado] = useState(false);
|
||||
|
||||
// Un grupo se muestra si el usuario tiene permiso a al menos uno de sus hijos.
|
||||
const puede = (m) => m.grupo
|
||||
? m.hijos.some((h) => permisos.includes(h.id))
|
||||
: permisos.includes(m.id);
|
||||
const visibles = MENU.filter(puede);
|
||||
|
||||
// Submenú abierto: por defecto abierto si la vista activa es uno de sus hijos.
|
||||
const [abiertos, setAbiertos] = useState(() => {
|
||||
const ini = {};
|
||||
MENU.forEach((m) => { if (m.grupo && m.hijos.some((h) => h.id === active)) ini[m.id] = true; });
|
||||
return ini;
|
||||
});
|
||||
const toggle = (id) => setAbiertos((p) => ({ ...p, [id]: !p[id] }));
|
||||
|
||||
return (
|
||||
<aside className="sidebar">
|
||||
<div className="sidebar-logo">Escuela <span>Refrigeración</span></div>
|
||||
<aside className={`sidebar ${colapsado ? "collapsed" : ""}`}>
|
||||
<div className="sidebar-logo" style={{ display: "flex", alignItems: "center",
|
||||
justifyContent: colapsado ? "center" : "space-between", gap: 8 }}>
|
||||
<span className="sidebar-logo-text">Escuela <span>Refrigeración</span></span>
|
||||
<button className="sidebar-toggle" onClick={() => setColapsado((v) => !v)}
|
||||
title={colapsado ? "Expandir menú" : "Colapsar menú"}>☰</button>
|
||||
</div>
|
||||
|
||||
<nav className="nav" style={{ flex: 1 }}>
|
||||
{visibles.map((m) => (
|
||||
<button
|
||||
key={m.id}
|
||||
className={`nav-item ${active === m.id ? "active" : ""}`}
|
||||
onClick={() => onChange(m.id)}
|
||||
>
|
||||
<span className="ico">{m.ico}</span> {m.label}
|
||||
</button>
|
||||
))}
|
||||
{visibles.map((m) => {
|
||||
if (!m.grupo) {
|
||||
return (
|
||||
<button
|
||||
key={m.id}
|
||||
className={`nav-item ${active === m.id ? "active" : ""}`}
|
||||
onClick={() => onChange(m.id)}
|
||||
title={colapsado ? m.label : undefined}
|
||||
>
|
||||
<span className="ico">{m.ico}</span> <span className="nav-label">{m.label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
// Grupo con submenú desplegable
|
||||
const hijosVis = m.hijos.filter((h) => permisos.includes(h.id));
|
||||
const abierto = !!abiertos[m.id];
|
||||
const hijoActivo = hijosVis.some((h) => h.id === active);
|
||||
return (
|
||||
<div key={m.id}>
|
||||
<button
|
||||
className={`nav-item ${hijoActivo && !abierto ? "active" : ""}`}
|
||||
onClick={() => { if (colapsado) { setColapsado(false); setAbiertos((p)=>({ ...p, [m.id]: true })); } else { toggle(m.id); } }}
|
||||
title={colapsado ? m.label : undefined}
|
||||
style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}
|
||||
>
|
||||
<span><span className="ico">{m.ico}</span> <span className="nav-label">{m.label}</span></span>
|
||||
<span className="nav-caret" style={{ fontSize: 10, transition: "transform .25s ease",
|
||||
transform: abierto ? "rotate(180deg)" : "rotate(0deg)" }}>▼</span>
|
||||
</button>
|
||||
{/* Submenú con transición suave de despliegue */}
|
||||
<div style={{
|
||||
display: "grid",
|
||||
gridTemplateRows: abierto ? "1fr" : "0fr",
|
||||
transition: "grid-template-rows .28s ease",
|
||||
}}>
|
||||
<div style={{ overflow: "hidden" }}>
|
||||
<div style={{
|
||||
paddingTop: abierto ? 2 : 0,
|
||||
opacity: abierto ? 1 : 0,
|
||||
transform: abierto ? "translateY(0)" : "translateY(-6px)",
|
||||
transition: "opacity .25s ease, transform .25s ease",
|
||||
}}>
|
||||
{hijosVis.map((h) => (
|
||||
<button
|
||||
key={h.id}
|
||||
className={`nav-item ${active === h.id ? "active" : ""}`}
|
||||
onClick={() => onChange(h.id)}
|
||||
tabIndex={abierto ? 0 : -1}
|
||||
style={{ paddingLeft: 34, fontSize: 13.5 }}
|
||||
>
|
||||
<span className="ico">{h.ico}</span> <span className="nav-label">{h.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div style={{ padding: 14, borderTop: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
<div className="sidebar-footer" style={{ padding: 14, borderTop: "1px solid rgba(255,255,255,0.08)" }}>
|
||||
<div style={{ fontSize: 12, color: "#94a3b8", marginBottom: 2 }}>
|
||||
{perfil?.nombre || "Usuario"}
|
||||
</div>
|
||||
@@ -51,4 +126,4 @@ export default function Sidebar({ active, onChange }) {
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user