Files
GRH/src/components/layout/Sidebar.tsx
Exteban08 c81a18987f Migrar backend a PostgreSQL + Node.js/Express con nuevas funcionalidades
Backend (water-api/):
- Crear API REST completa con Express + TypeScript
- Implementar autenticación JWT con refresh tokens
- CRUD completo para: projects, concentrators, meters, gateways, devices, users, roles
- Agregar validación con Zod para todas las entidades
- Implementar webhooks para The Things Stack (LoRaWAN)
- Agregar endpoint de lecturas con filtros y resumen de consumo
- Implementar carga masiva de medidores via Excel (.xlsx)

Frontend:
- Crear cliente HTTP con manejo automático de JWT y refresh
- Actualizar todas las APIs para usar nuevo backend
- Agregar sistema de autenticación real (login, logout, me)
- Agregar selector de tipo (LORA, LoRaWAN, Grandes) en concentradores y medidores
- Agregar campo Meter ID en medidores
- Crear modal de carga masiva para medidores
- Agregar página de consumo con gráficas y filtros
- Corregir carga de proyectos independiente de datos existentes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 10:13:26 +00:00

164 lines
5.0 KiB
TypeScript

import { useState } from "react";
import {
Home,
Settings,
ExpandMore,
ExpandLess,
Menu,
People,
} from "@mui/icons-material";
import { Page } from "../../App";
interface SidebarProps {
setPage: (page: Page) => void;
}
export default function Sidebar({ setPage }: SidebarProps) {
const [systemOpen, setSystemOpen] = useState(true);
const [usersOpen, setUsersOpen] = useState(true);
const [pinned, setPinned] = useState(false);
const [hovered, setHovered] = useState(false);
const isExpanded = pinned || hovered;
return (
<aside
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={`
h-full bg-[#1f2a48] border-r border-white/10
transition-all duration-300 flex flex-col overflow-hidden
${isExpanded ? "w-72" : "w-16"}
`}
>
{/* HEADER */}
<div className="border-b border-white/10 px-4 py-3 flex items-center gap-3">
<button
onClick={() => setPinned(!pinned)}
className="text-white opacity-90 hover:opacity-100"
>
<Menu />
</button>
{isExpanded && (
<span className="text-lg font-bold text-white whitespace-nowrap">
Water System
</span>
)}
</div>
{/* MENU */}
<div className="flex-1 py-4 px-2 overflow-y-auto">
<ul className="space-y-1 text-white text-sm">
{/* DASHBOARD */}
<li>
<button
onClick={() => setPage("home")}
className="flex items-center w-full px-2 py-2 rounded-md hover:bg-white/10 font-bold"
>
<Home className="w-5 h-5 shrink-0" />
{isExpanded && <span className="ml-3">Dashboard</span>}
</button>
</li>
{/* PROJECT MANAGEMENT */}
<li>
<button
onClick={() => isExpanded && setSystemOpen(!systemOpen)}
className="flex items-center w-full px-2 py-2 rounded-md hover:bg-white/10 font-bold"
>
<Settings className="w-5 h-5 shrink-0" />
{isExpanded && (
<>
<span className="ml-3 flex-1 text-left">
Project Management
</span>
{systemOpen ? <ExpandLess /> : <ExpandMore />}
</>
)}
</button>
{isExpanded && systemOpen && (
<ul className="mt-1 space-y-1 text-xs">
<li>
<button
onClick={() => setPage("projects")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Projects
</button>
</li>
<li>
<button
onClick={() => setPage("concentrators")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Concentrators
</button>
</li>
<li>
<button
onClick={() => setPage("meters")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Meters
</button>
</li>
<li>
<button
onClick={() => setPage("consumption")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Consumo
</button>
</li>
</ul>
)}
</li>
{/* USERS MANAGEMENT */}
<li>
<button
onClick={() => isExpanded && setUsersOpen(!usersOpen)}
className="flex items-center w-full px-2 py-2 rounded-md hover:bg-white/10 font-bold"
>
<People className="w-5 h-5 shrink-0" />
{isExpanded && (
<>
<span className="ml-3 flex-1 text-left">
Users Management
</span>
{usersOpen ? <ExpandLess /> : <ExpandMore />}
</>
)}
</button>
{isExpanded && usersOpen && (
<ul className="mt-1 space-y-1 text-xs">
<li>
<button
onClick={() => setPage("users")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Users
</button>
</li>
<li>
<button
onClick={() => setPage("roles")}
className="pl-10 w-full text-left px-2 py-1.5 rounded-md hover:bg-white/10"
>
Roles
</button>
</li>
</ul>
)}
</li>
</ul>
</div>
</aside>
);
}