Files
GRH/DOCUMENTATION.md
Exteban08 2b5735d78d Agregar documentacion completa del proyecto
- README.md actualizado con descripcion detallada del sistema
- DOCUMENTATION.md con documentacion tecnica exhaustiva

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 04:58:02 +00:00

15 KiB

Documentacion Tecnica - Water Project GRH

Documentacion tecnica detallada del Sistema de Gestion de Recursos Hidricos.


Tabla de Contenidos

  1. Arquitectura del Sistema
  2. Componentes Principales
  3. Capa de API
  4. Hooks Personalizados
  5. Sistema de Autenticacion
  6. Gestion de Estado
  7. Sistema de Temas
  8. 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)
// Estados principales
const [isAuth, setIsAuth] = useState<boolean>(() => {
  return Boolean(localStorage.getItem(AUTH_KEY));
});
const [currentPage, setCurrentPage] = useState("home");
const [currentSubpage, setCurrentSubpage] = useState<string | null>(null);

Sidebar.tsx - Menu Lateral

Caracteristicas:

  • Estados: colapsado/expandido
  • Hover expansion con delay
  • Pin/unpin para mantener expandido
  • Menu jerarquico con submenus
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

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
const API_TOKEN = import.meta.env.VITE_API_TOKEN;

Funciones de API - Medidores

// meters.ts

// Obtener todos los medidores
export async function fetchMeters(): Promise<Meter[]>

// Crear medidor
export async function createMeter(meterData: Partial<Meter>): Promise<Meter>

// Actualizar medidor
export async function updateMeter(
  id: string,
  meterData: Partial<Meter>
): Promise<Meter>

// Eliminar medidor
export async function deleteMeter(id: string): Promise<void>

Funciones de API - Concentradores

// concentrators.ts

export async function fetchConcentrators(): Promise<Concentrator[]>
export async function createConcentrator(data: Partial<Concentrator>): Promise<Concentrator>
export async function updateConcentrator(id: string, data: Partial<Concentrator>): Promise<Concentrator>
export async function deleteConcentrator(id: string): Promise<void>

Funciones de API - Proyectos

// projects.ts

export async function fetchProjects(): Promise<Project[]>
export async function fetchProjectNames(): Promise<string[]>
export async function createProject(data: Partial<Project>): Promise<Project>
export async function updateProject(id: string, data: Partial<Project>): Promise<Project>
export async function deleteProject(id: string): Promise<void>

Manejo de Errores

Todas las funciones de API siguen el patron:

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.

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<void>;

  // CRUD
  handleCreate: (data: Partial<Meter>) => Promise<void>;
  handleUpdate: (id: string, data: Partial<Meter>) => Promise<void>;
  handleDelete: (id: string) => Promise<void>;
}

export function useMeters(): UseMetersReturn

useConcentrators.ts

Hook similar para concentradores con estructura equivalente.

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

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

// App.tsx
if (!isAuth) {
  return <LoginPage onSuccess={handleLogin} />;
}

// Si esta autenticado, renderiza la aplicacion
return (
  <div className="flex h-screen">
    <Sidebar {...props} />
    <main>{renderPage()}</main>
  </div>
);

Gestion de Estado

Estado Local (useState)

La aplicacion utiliza principalmente useState de React para gestion de estado local:

// Ejemplo en MeterPage.tsx
const [meters, setMeters] = useState<Meter[]>([]);
const [selectedMeter, setSelectedMeter] = useState<Meter | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalMode, setModalMode] = useState<"add" | "edit">("add");

Estado Persistente (localStorage)

Para datos que deben persistir entre sesiones:

// 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

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

/* 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

// 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
// src/pages/nueva/NuevaPage.tsx
export default function NuevaPage() {
  return (
    <div className="p-6">
      <h1>Nueva Pagina</h1>
    </div>
  );
}
  1. Agregar al Sidebar
// Sidebar.tsx - agregar al menuItems
{
  label: "Nueva Pagina",
  page: "nueva",
  icon: <IconComponent className="w-5 h-5" />,
}
  1. Agregar al renderizado en App.tsx
// App.tsx - agregar case en renderPage()
case "nueva":
  return <NuevaPage />;

Agregar un Nuevo Endpoint de API

  1. Crear archivo de API
// 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<NuevoItem[]> {
  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 }));
}
  1. Crear hook personalizado (opcional)
// src/pages/nuevo/useNuevo.ts
export function useNuevo() {
  const [items, setItems] = useState<NuevoItem[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchNuevos()
      .then(setItems)
      .finally(() => setLoading(false));
  }, []);

  return { items, loading };
}

Agregar un Nuevo Componente Reutilizable

// src/components/layout/common/NuevoComponente.tsx
interface NuevoComponenteProps {
  title: string;
  onAction: () => void;
  children?: React.ReactNode;
}

export default function NuevoComponente({
  title,
  onAction,
  children,
}: NuevoComponenteProps) {
  return (
    <div className="bg-white dark:bg-gray-800 rounded-lg shadow p-4">
      <h3 className="text-lg font-semibold">{title}</h3>
      {children}
      <button onClick={onAction}>Accion</button>
    </div>
  );
}

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:

npm install -D vitest @testing-library/react @testing-library/jest-dom
// 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

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

# 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

rm -rf node_modules
npm install

Error: TypeScript type errors

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