Se mejoro estetica y estandarizaron estilos

This commit is contained in:
2025-08-05 10:30:25 -06:00
parent c3d800ba48
commit aa515c1d01
12 changed files with 3530 additions and 1611 deletions

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useUser } from '../context/UserContext';
export default function Sidebar() {
export default function Sidebar({ isMobileOpen, onMobileClose }) {
// Leer si el usuario es importador desde localStorage
const isImportador = typeof window !== 'undefined' && localStorage.getItem('user_is_importador') === 'true';
// Leer grupos del usuario desde localStorage
@@ -18,7 +18,15 @@ export default function Sidebar() {
const isGroup35 = Array.isArray(userGroups) && userGroups.length === 2 && userGroups.includes(3) && userGroups.includes(5);
// Leer DEBUG_MODE desde variables de entorno
const isDebugMode = import.meta.env.VITE_DEBUG_MODE === 'true';
// Estados para responsividad
const [isCollapsed, setIsCollapsed] = useState(false);
const [internalMobileOpen, setInternalMobileOpen] = useState(false);
// Usar estado interno si no se pasan props
const mobileOpen = isMobileOpen !== undefined ? isMobileOpen : internalMobileOpen;
const handleMobileClose = onMobileClose || (() => setInternalMobileOpen(false));
const handleMobileOpen = () => setInternalMobileOpen(true);
const location = useLocation();
const navigate = useNavigate();
const { user: currentUser, loading } = useUser();
@@ -30,6 +38,23 @@ export default function Sidebar() {
navigate('/login');
};
// Cerrar menú móvil cuando se navega o cuando la pantalla es grande
useEffect(() => {
const handleResize = () => {
if (window.innerWidth >= 1024) { // lg breakpoint
handleMobileClose();
}
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
// Cerrar menú móvil cuando cambia la ubicación
useEffect(() => {
handleMobileClose();
}, [location.pathname]);
// El usuario y loading ahora vienen del contexto global
// Definir todas las secciones
@@ -196,35 +221,79 @@ export default function Sidebar() {
.filter(Boolean);
return (
<div className={`bg-slate-900 text-white transition-all duration-300 ${isCollapsed ? 'w-16' : 'w-64'} h-screen flex flex-col shadow-xl`}>
{/* Header - Logo y colapsar */}
<div className="p-4 border-b border-slate-700 flex-shrink-0">
<div className="flex items-center justify-between">
{!isCollapsed && (
<div className="flex items-center">
{/* Logo de la organización */}
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center mr-3 shadow-lg">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
<>
{/* Botón flotante para abrir menú en móvil - solo cuando se usa standalone */}
{!mobileOpen && isMobileOpen === undefined && (
<button
onClick={handleMobileOpen}
className="lg:hidden fixed top-4 left-4 z-30 p-2.5 bg-slate-900/95 backdrop-blur-sm text-white rounded-xl shadow-lg hover:bg-slate-800/95 transition-all duration-200 border border-slate-700/50"
aria-label="Abrir menú"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
)}
{/* Overlay para móviles */}
{mobileOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden"
onClick={handleMobileClose}
/>
)}
{/* Sidebar */}
<div className={`
bg-slate-900 text-white transition-all duration-300 flex flex-col shadow-xl
${isCollapsed ? 'w-16' : 'w-64'}
fixed lg:relative inset-y-0 left-0 z-50
${mobileOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'}
h-screen
`}>
{/* Header - Logo y colapsar */}
<div className="p-4 border-b border-slate-700 flex-shrink-0">
<div className="flex items-center justify-between">
{!isCollapsed && (
<div className="flex items-center">
{/* Logo de la organización */}
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center mr-3 shadow-lg">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
</div>
<h1 className="text-lg font-bold text-white">EFC Dashboard</h1>
</div>
<h1 className="text-lg font-bold text-white">EFC Dashboard</h1>
)}
{/* Botones de control */}
<div className="flex items-center space-x-2">
{/* Botón cerrar en móvil */}
<button
onClick={handleMobileClose}
className="lg:hidden p-1.5 rounded-lg hover:bg-slate-700 transition-all duration-200"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
{/* Botón colapsar en desktop */}
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="hidden lg:block p-1.5 rounded-lg hover:bg-slate-700 transition-all duration-200 hover:shadow-md"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{isCollapsed ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
)}
</svg>
</button>
</div>
)}
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="p-1.5 rounded-lg hover:bg-slate-700 transition-all duration-200 hover:shadow-md"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{isCollapsed ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
)}
</svg>
</button>
</div>
</div>
</div>
{/* Navigation */}
<nav className="flex-1 p-3 space-y-4 overflow-y-auto overflow-x-hidden">
@@ -404,6 +473,32 @@ export default function Sidebar() {
</div>
)}
</div>
</div>
</div>
</>
);
}
// Hook personalizado para manejar el menú móvil desde otros componentes
export function useMobileSidebar() {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prev => !prev);
const close = () => setIsOpen(false);
return { isOpen, toggle, close };
}
// Componente botón para abrir menú móvil
export function MobileMenuButton({ onClick }) {
return (
<button
onClick={onClick}
className="lg:hidden p-2 rounded-lg text-gray-600 hover:text-gray-900 hover:bg-gray-100 transition-colors duration-200"
aria-label="Abrir menú"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
);
}