# Documentacion Tecnica - Water Project GRH Documentacion tecnica detallada del Sistema de Gestion de Recursos Hidricos. --- ## Tabla de Contenidos 1. [Arquitectura del Sistema](#arquitectura-del-sistema) 2. [Componentes Principales](#componentes-principales) 3. [Capa de API](#capa-de-api) 4. [Hooks Personalizados](#hooks-personalizados) 5. [Sistema de Autenticacion](#sistema-de-autenticacion) 6. [Gestion de Estado](#gestion-de-estado) 7. [Sistema de Temas](#sistema-de-temas) 8. [Guia de Desarrollo](#guia-de-desarrollo) --- ## Arquitectura del Sistema ### Vision General El proyecto sigue una arquitectura **frontend SPA (Single Page Application)** con las siguientes capas: ``` ┌─────────────────────────────────────────────────────────┐ │ Capa de Presentacion │ │ (React Components, Pages, Layout) │ ├─────────────────────────────────────────────────────────┤ │ Capa de Logica │ │ (Custom Hooks, State Management) │ ├─────────────────────────────────────────────────────────┤ │ Capa de Datos │ │ (API Services, localStorage) │ ├─────────────────────────────────────────────────────────┤ │ API Externa │ │ (REST API Backend) │ └─────────────────────────────────────────────────────────┘ ``` ### Patron de Diseno El proyecto utiliza varios patrones de diseno: 1. **Container/Presentational Pattern**: Separacion entre componentes con logica (pages) y componentes de UI puros (components) 2. **Custom Hooks Pattern**: Encapsulacion de logica reutilizable en hooks (`useMeters`, `useConcentrators`) 3. **Module Pattern**: Organizacion de codigo relacionado en modulos (meters/, concentrators/) --- ## Componentes Principales ### App.tsx - Componente Raiz El componente raiz maneja: - Autenticacion global - Routing interno (sin react-router) - Estado de pagina actual - Modales globales (perfil, configuracion, logout) ```typescript // Estados principales const [isAuth, setIsAuth] = useState(() => { return Boolean(localStorage.getItem(AUTH_KEY)); }); const [currentPage, setCurrentPage] = useState("home"); const [currentSubpage, setCurrentSubpage] = useState(null); ``` ### Sidebar.tsx - Menu Lateral Caracteristicas: - Estados: colapsado/expandido - Hover expansion con delay - Pin/unpin para mantener expandido - Menu jerarquico con submenus ```typescript interface SidebarProps { currentPage: string; setCurrentPage: (page: string) => void; currentSubpage: string | null; setCurrentSubpage: (subpage: string | null) => void; } ``` ### TopMenu.tsx - Barra Superior Funcionalidades: - Breadcrumb de navegacion - Notificaciones (placeholder) - Menu de usuario con dropdown - Acciones: perfil, configuracion, logout --- ## Capa de API ### Estructura de Archivos ``` src/api/ ├── me.ts # Perfil de usuario ├── meters.ts # Operaciones CRUD de medidores ├── concentrators.ts # Operaciones CRUD de concentradores └── projects.ts # Operaciones CRUD de proyectos ``` ### Configuracion ```typescript const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; const API_TOKEN = import.meta.env.VITE_API_TOKEN; ``` ### Funciones de API - Medidores ```typescript // meters.ts // Obtener todos los medidores export async function fetchMeters(): Promise // Crear medidor export async function createMeter(meterData: Partial): Promise // Actualizar medidor export async function updateMeter( id: string, meterData: Partial ): Promise // Eliminar medidor export async function deleteMeter(id: string): Promise ``` ### Funciones de API - Concentradores ```typescript // concentrators.ts export async function fetchConcentrators(): Promise export async function createConcentrator(data: Partial): Promise export async function updateConcentrator(id: string, data: Partial): Promise export async function deleteConcentrator(id: string): Promise ``` ### Funciones de API - Proyectos ```typescript // projects.ts export async function fetchProjects(): Promise export async function fetchProjectNames(): Promise export async function createProject(data: Partial): Promise export async function updateProject(id: string, data: Partial): Promise export async function deleteProject(id: string): Promise ``` ### Manejo de Errores Todas las funciones de API siguen el patron: ```typescript try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`Error: ${response.status}`); } return await response.json(); } catch (error) { console.error("API Error:", error); throw error; } ``` --- ## Hooks Personalizados ### useMeters.ts Hook para gestion completa del modulo de medidores. ```typescript interface UseMetersReturn { // Data meters: Meter[]; filteredMeters: Meter[]; projectNames: string[]; // State loading: boolean; selectedMeter: Meter | null; selectedProject: string | null; searchTerm: string; // Actions setSelectedMeter: (meter: Meter | null) => void; setSelectedProject: (project: string | null) => void; setSearchTerm: (term: string) => void; refreshData: () => Promise; // CRUD handleCreate: (data: Partial) => Promise; handleUpdate: (id: string, data: Partial) => Promise; handleDelete: (id: string) => Promise; } export function useMeters(): UseMetersReturn ``` ### useConcentrators.ts Hook similar para concentradores con estructura equivalente. ```typescript interface UseConcentratorsReturn { concentrators: Concentrator[]; filteredConcentrators: Concentrator[]; projectNames: string[]; loading: boolean; selectedConcentrator: Concentrator | null; // ... similar a useMeters } export function useConcentrators(): UseConcentratorsReturn ``` --- ## Sistema de Autenticacion ### Flujo de Autenticacion ``` ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │ LoginPage │────>│ Validacion │────>│ localStorage│ └──────────────┘ └─────────────┘ └──────────────┘ │ ▼ ┌─────────────┐ │ App.tsx │ │ (isAuth) │ └─────────────┘ ``` ### Almacenamiento de Token ```typescript const AUTH_KEY = "grh_auth"; interface AuthData { token: string; ts: number; // timestamp de login } // Guardar localStorage.setItem(AUTH_KEY, JSON.stringify({ token: "demo", ts: Date.now() })); // Verificar const isAuth = Boolean(localStorage.getItem(AUTH_KEY)); // Eliminar (logout) localStorage.removeItem(AUTH_KEY); ``` ### Proteccion de Rutas ```typescript // App.tsx if (!isAuth) { return ; } // Si esta autenticado, renderiza la aplicacion return (
{renderPage()}
); ``` --- ## Gestion de Estado ### Estado Local (useState) La aplicacion utiliza principalmente `useState` de React para gestion de estado local: ```typescript // Ejemplo en MeterPage.tsx const [meters, setMeters] = useState([]); const [selectedMeter, setSelectedMeter] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [modalMode, setModalMode] = useState<"add" | "edit">("add"); ``` ### Estado Persistente (localStorage) Para datos que deben persistir entre sesiones: ```typescript // Configuraciones de usuario const SETTINGS_KEY = "water_project_settings_v1"; interface Settings { theme: "system" | "light" | "dark"; compactMode: boolean; } // Guardar localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings)); // Cargar const settings = JSON.parse(localStorage.getItem(SETTINGS_KEY) || "{}"); ``` ### Flujo de Datos en Modulos ``` ┌─────────────┐ │ API Layer │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Custom Hook │ (useMeters, useConcentrators) └──────┬──────┘ │ ▼ ┌─────────────┐ │ Page │ (MeterPage, ConcentratorsPage) └──────┬──────┘ │ ├────────────────┬────────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Sidebar │ │ Table │ │ Modal │ └───────────┘ └───────────┘ └───────────┘ ``` --- ## Sistema de Temas ### Configuracion de Tema ```typescript type Theme = "system" | "light" | "dark"; const applyTheme = (theme: Theme) => { if (theme === "system") { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; document.documentElement.classList.toggle("dark", prefersDark); } else { document.documentElement.classList.toggle("dark", theme === "dark"); } }; ``` ### Clases CSS con Tailwind ```css /* Ejemplo de clases con soporte dark mode */ .card { @apply bg-white dark:bg-gray-800; @apply text-gray-900 dark:text-white; @apply border-gray-200 dark:border-gray-700; } ``` ### Modal de Configuracion ```typescript // SettingsModals.tsx interface SettingsModalsProps { open: boolean; onClose: () => void; } // Opciones de tema const themeOptions = [ { value: "system", label: "Sistema" }, { value: "light", label: "Claro" }, { value: "dark", label: "Oscuro" }, ]; ``` --- ## Guia de Desarrollo ### Agregar una Nueva Pagina 1. **Crear el archivo de pagina** ```typescript // src/pages/nueva/NuevaPage.tsx export default function NuevaPage() { return (

Nueva Pagina

); } ``` 2. **Agregar al Sidebar** ```typescript // Sidebar.tsx - agregar al menuItems { label: "Nueva Pagina", page: "nueva", icon: , } ``` 3. **Agregar al renderizado en App.tsx** ```typescript // App.tsx - agregar case en renderPage() case "nueva": return ; ``` ### Agregar un Nuevo Endpoint de API 1. **Crear archivo de API** ```typescript // src/api/nuevo.ts const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; const API_TOKEN = import.meta.env.VITE_API_TOKEN; export interface NuevoItem { id: string; // ... campos } export async function fetchNuevos(): Promise { const response = await fetch(`${API_BASE_URL}/endpoint`, { headers: { Authorization: `Bearer ${API_TOKEN}`, "Content-Type": "application/json", }, }); if (!response.ok) throw new Error("Error fetching data"); const data = await response.json(); return data.records.map((r: any) => ({ id: r.id, ...r.fields })); } ``` 2. **Crear hook personalizado (opcional)** ```typescript // src/pages/nuevo/useNuevo.ts export function useNuevo() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchNuevos() .then(setItems) .finally(() => setLoading(false)); }, []); return { items, loading }; } ``` ### Agregar un Nuevo Componente Reutilizable ```typescript // src/components/layout/common/NuevoComponente.tsx interface NuevoComponenteProps { title: string; onAction: () => void; children?: React.ReactNode; } export default function NuevoComponente({ title, onAction, children, }: NuevoComponenteProps) { return (

{title}

{children}
); } ``` ### Convenios de Codigo 1. **Nombres de archivos**: PascalCase para componentes, camelCase para utilidades 2. **Interfaces**: Prefijo descriptivo (ej: `MeterFormData`, `UserSettings`) 3. **Hooks**: Prefijo `use` (ej: `useMeters`, `useAuth`) 4. **Constantes**: UPPER_SNAKE_CASE (ej: `API_BASE_URL`) 5. **CSS**: Utilizar Tailwind CSS, evitar CSS custom ### Testing (Pendiente de Implementacion) El proyecto actualmente no tiene tests configurados. Para agregar: ```bash npm install -D vitest @testing-library/react @testing-library/jest-dom ``` ```typescript // vitest.config.ts import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "jsdom", globals: true, }, }); ``` --- ## Variables de Entorno | Variable | Descripcion | Requerida | |----------|-------------|-----------| | `VITE_API_BASE_URL` | URL base de la API | Si | | `VITE_API_TOKEN` | Token de autenticacion API | Si | ### Ejemplo .env ```env VITE_API_BASE_URL=https://api.example.com VITE_API_TOKEN=your-api-token-here ``` --- ## Dependencias Principales | Paquete | Version | Uso | |---------|---------|-----| | react | 18.2.0 | Framework UI | | react-dom | 18.2.0 | Renderizado DOM | | typescript | 5.2.2 | Type safety | | vite | 5.2.0 | Build tool | | tailwindcss | 4.1.18 | Estilos | | @mui/material | 7.3.6 | Componentes UI | | @material-table/core | 6.5.2 | Tablas avanzadas | | recharts | 3.6.0 | Graficas | | lucide-react | 0.559.0 | Iconos | --- ## Comandos Utiles ```bash # Desarrollo npm run dev # Build produccion npm run build # Preview del build npm run preview # Linting npm run lint # Type check npx tsc --noEmit ``` --- ## Troubleshooting ### Error: CORS al conectar con API Verificar que el backend tenga configurados los headers CORS correctos o usar un proxy en desarrollo. ### Error: Module not found ```bash rm -rf node_modules npm install ``` ### Error: TypeScript type errors ```bash npx tsc --noEmit ``` Revisar los errores y corregir los tipos. ### La aplicacion no carga 1. Verificar que las variables de entorno estan configuradas 2. Verificar la consola del navegador por errores 3. Verificar que la API esta accesible