diff --git a/src/App.tsx b/src/App.tsx index f7cad5b..8ec6113 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,9 @@ import UsersPage from "./pages/UsersPage"; import RolesPage from "./pages/RolesPage"; import ConsumptionPage from "./pages/consumption/ConsumptionPage"; import AuditoriaPage from "./pages/AuditoriaPage"; +import SHMetersPage from "./pages/conectores/SHMetersPage"; +import XMetersPage from "./pages/conectores/XMetersPage"; +import TTSPage from "./pages/conectores/TTSPage"; import ProfileModal from "./components/layout/common/ProfileModal"; import { updateMyProfile } from "./api/me"; @@ -40,7 +43,10 @@ export type Page = | "consumption" | "auditoria" | "users" - | "roles"; + | "roles" + | "sh-meters" + | "xmeters" + | "tts"; export default function App() { const [isAuth, setIsAuth] = useState(false); @@ -183,6 +189,12 @@ export default function App() { return ; case "roles": return ; + case "sh-meters": + return ; + case "xmeters": + return ; + case "tts": + return ; case "home": default: return ( @@ -242,7 +254,7 @@ export default function App() { {/* ✅ AQUÍ VA LA MARCA DE AGUA */} -
+
{renderPage()}
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 57257f0..c45e325 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -6,6 +6,7 @@ import { ExpandLess, Menu, People, + Cable, } from "@mui/icons-material"; import { Page } from "../../App"; import { getCurrentUserRole } from "../../api/auth"; @@ -17,6 +18,7 @@ interface SidebarProps { export default function Sidebar({ setPage }: SidebarProps) { const [systemOpen, setSystemOpen] = useState(true); const [usersOpen, setUsersOpen] = useState(true); + const [conectoresOpen, setConectoresOpen] = useState(true); const [pinned, setPinned] = useState(false); const [hovered, setHovered] = useState(false); @@ -172,6 +174,55 @@ export default function Sidebar({ setPage }: SidebarProps) { )} )} + + {/* CONECTORES */} + {!isOperator && ( +
  • + + + {isExpanded && conectoresOpen && ( +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    + )} +
  • + )} diff --git a/src/components/layout/TopMenu.tsx b/src/components/layout/TopMenu.tsx index 7d9c66d..d47cfb9 100644 --- a/src/components/layout/TopMenu.tsx +++ b/src/components/layout/TopMenu.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; -import { Bell, User, LogOut } from "lucide-react"; +import { Bell, User, LogOut, Sun, Moon } from "lucide-react"; import NotificationDropdown from "../NotificationDropdown"; import { useNotifications } from "../../hooks/useNotifications"; import ProjectBadge from "./common/ProjectBadge"; @@ -33,11 +33,32 @@ const TopMenu: React.FC = ({ }) => { const [openUserMenu, setOpenUserMenu] = useState(false); const [openNotifications, setOpenNotifications] = useState(false); + const [isDarkMode, setIsDarkMode] = useState(() => { + return document.documentElement.classList.contains("dark"); + }); const menuRef = useRef(null); const notificationRef = useRef(null); - + const { unreadCount } = useNotifications(); + const toggleTheme = () => { + const root = document.documentElement; + const newIsDark = !isDarkMode; + + if (newIsDark) { + root.classList.add("dark"); + } else { + root.classList.remove("dark"); + } + + setIsDarkMode(newIsDark); + + // Save to localStorage + const settings = JSON.parse(localStorage.getItem("water_project_settings_v1") || "{}"); + settings.theme = newIsDark ? "dark" : "light"; + localStorage.setItem("water_project_settings_v1", JSON.stringify(settings)); + }; + const initials = useMemo(() => { const parts = (userName || "").trim().split(/\s+/).filter(Boolean); const a = parts[0]?.[0] ?? "U"; @@ -100,6 +121,17 @@ const TopMenu: React.FC = ({ {/* DERECHA */}
    + {/* Theme Toggle */} + +
    ); diff --git a/src/index.css b/src/index.css index 20f080a..690467e 100644 --- a/src/index.css +++ b/src/index.css @@ -1 +1,23 @@ -@import 'tailwindcss' \ No newline at end of file +@import 'tailwindcss'; + +/* Dark mode configuration */ +@custom-variant dark (&:where(.dark, .dark *)); + +/* Base styles */ +:root { + color-scheme: light; +} + +.dark { + color-scheme: dark; +} + +/* Dark mode body */ +body { + @apply bg-slate-50 text-gray-900; +} + +.dark body, +body:where(.dark *) { + @apply bg-gray-900 text-gray-100; +} \ No newline at end of file diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 373c3b3..8fea2f3 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -392,10 +392,10 @@ export default function Home({ {/* ✅ Título + logo a la derecha */}
    -

    +

    Sistema de Tomas de Agua

    -

    +

    Monitorea, administra y controla tus operaciones en un solo lugar.

    @@ -412,41 +412,44 @@ export default function Home({ {/* Cards de Secciones */}
    setPage("meters")} > - Tomas + Tomas
    -
    +
    setPage("auditoria")} + > - Alertas + Alertas
    -
    setPage("projects")} > - Proyectos + Proyectos
    -
    +
    - Reportes + Reportes
    {isAdmin && ( -
    +
    -

    Organismos Operadores

    -

    +

    Organismos Operadores

    +

    Seleccionado:{" "} - - {selectedOrganism === "Todos" - ? "Todos" + + {selectedOrganism === "Todos" + ? "Todos" : organismsData.find(o => o.id === selectedOrganism)?.name || "Ninguno"}

    @@ -473,21 +476,21 @@ export default function Home({ /> {/* Panel */} -
    +
    {/* Header */} -
    +
    -

    +

    Organismos Operadores

    -

    +

    Selecciona un organismo para filtrar la información del dashboard

    {/* Search */} -
    +
    setOrganismQuery(e.target.value)} placeholder="Buscar organismo…" - className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-200" + className="w-full rounded-lg border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-200 dark:focus:ring-blue-500 dark:placeholder-gray-400" />
    @@ -519,16 +522,16 @@ export default function Home({ className={[ "rounded-xl border p-4 transition", selectedOrganism === "Todos" - ? "border-blue-600 bg-blue-50/40" - : "border-gray-200 bg-white hover:bg-gray-50", + ? "border-blue-600 bg-blue-50/40 dark:bg-blue-900/20" + : "border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600", ].join(" ")} >
    -

    +

    Todos los Organismos

    -

    Ver todos los datos del sistema

    +

    Ver todos los datos del sistema

    @@ -565,16 +568,16 @@ export default function Home({ className={[ "rounded-xl border p-4 transition", active - ? "border-blue-600 bg-blue-50/40" - : "border-gray-200 bg-white hover:bg-gray-50", + ? "border-blue-600 bg-blue-50/40 dark:bg-blue-900/20" + : "border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600", ].join(" ")} >
    -

    +

    {o.name}

    -

    {o.region}

    +

    {o.region}

    - Rol - + Rol + {o.contact}
    - Email - + Email + {o.region}
    - Proyectos - + Proyectos + {o.projects}
    - Medidores - + Medidores + {o.meters}
    - Último acceso - + Último acceso + {o.lastSync}
    @@ -651,14 +654,14 @@ export default function Home({ )} {!loadingUsers && filteredOrganisms.length === 0 && ( -
    +
    No se encontraron organismos.
    )}
    {/* Footer */} -
    +
    Mostrando {filteredOrganisms.length} organismo{filteredOrganisms.length !== 1 ? 's' : ''} de {users.length} total{users.length !== 1 ? 'es' : ''}
    @@ -669,10 +672,10 @@ export default function Home({
    {/* Gráfica */} -
    +
    -

    - Número de Medidores por Proyecto +

    + Numero de Medidores por Proyecto

    Click en barra para ver tomas @@ -681,20 +684,20 @@ export default function Home({ {chartData.length === 0 && selectedOrganism !== "Todos" ? (
    -

    - {selectedUserProjectName - ? "Este organismo no tiene medidores registrados" +

    + {selectedUserProjectName + ? "Este organismo no tiene medidores registrados" : "Este organismo no tiene un proyecto asignado"}

    {selectedUserProjectName && ( -

    - Proyecto asignado: {selectedUserProjectName} +

    + Proyecto asignado: {selectedUserProjectName}

    )}
    ) : chartData.length === 0 ? (
    -

    No hay datos disponibles

    +

    No hay datos disponibles

    ) : ( <> @@ -715,14 +718,14 @@ export default function Home({
    {selectedOrganism !== "Todos" && selectedUserProjectName && ( -
    +
    - Proyecto del organismo: - {selectedUserProjectName} + Proyecto del organismo: + {selectedUserProjectName}
    - Total de medidores: + Total de medidores: {filteredMeters.length}
    @@ -733,8 +736,8 @@ export default function Home({
    {!isOperator && ( -
    -

    Historial Reciente de Auditoría

    +
    +

    Historial Reciente de Auditoria

    {loadingAuditLogs ? (
    @@ -744,12 +747,12 @@ export default function Home({ No hay registros de auditoría disponibles

    ) : ( -
      +
        {history.map((h, i) => (
      • -

        +

        {h.user} {h.action}{" "} {h.target}

        @@ -763,8 +766,8 @@ export default function Home({ )} {!isOperator && ( -
        -

        Últimas Alertas

        +
        +

        Ultimas Alertas

        {loadingNotifications ? (
        @@ -774,11 +777,11 @@ export default function Home({ No hay alertas disponibles

        ) : ( -
          +
            {alerts.map((a, i) => (
          • - + {a.company} - {a.type}
            diff --git a/src/pages/conectores/SHMetersPage.tsx b/src/pages/conectores/SHMetersPage.tsx new file mode 100644 index 0000000..fa300c5 --- /dev/null +++ b/src/pages/conectores/SHMetersPage.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; +import { Radio } from "lucide-react"; + +export default function SHMetersPage() { + const [loading] = useState(false); + + return ( +
            + {/* Header */} +
            +
            + +
            +
            +

            SH-METERS

            +

            Conector para medidores SH

            +
            +
            + + {/* Content */} +
            + {loading ? ( +
            +
            +
            + ) : ( +
            + +

            + Conector SH-METERS +

            +

            + Configuracion e integracion con medidores SH. + Esta seccion esta en desarrollo. +

            +
            + )} +
            +
            + ); +} diff --git a/src/pages/conectores/TTSPage.tsx b/src/pages/conectores/TTSPage.tsx new file mode 100644 index 0000000..9cd6937 --- /dev/null +++ b/src/pages/conectores/TTSPage.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; +import { Wifi } from "lucide-react"; + +export default function TTSPage() { + const [loading] = useState(false); + + return ( +
            + {/* Header */} +
            +
            + +
            +
            +

            TTS

            +

            The Things Stack - Integracion LoRaWAN

            +
            +
            + + {/* Content */} +
            + {loading ? ( +
            +
            +
            + ) : ( +
            + +

            + Conector TTS (The Things Stack) +

            +

            + Configuracion e integracion con The Things Stack para dispositivos LoRaWAN. + Esta seccion esta en desarrollo. +

            +
            + )} +
            +
            + ); +} diff --git a/src/pages/conectores/XMetersPage.tsx b/src/pages/conectores/XMetersPage.tsx new file mode 100644 index 0000000..bab7e83 --- /dev/null +++ b/src/pages/conectores/XMetersPage.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; +import { Gauge } from "lucide-react"; + +export default function XMetersPage() { + const [loading] = useState(false); + + return ( +
            + {/* Header */} +
            +
            + +
            +
            +

            XMETERS

            +

            Conector para medidores X

            +
            +
            + + {/* Content */} +
            + {loading ? ( +
            +
            +
            + ) : ( +
            + +

            + Conector XMETERS +

            +

            + Configuracion e integracion con medidores X. + Esta seccion esta en desarrollo. +

            +
            + )} +
            +
            + ); +}