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>
This commit is contained in:
611
DOCUMENTATION.md
Normal file
611
DOCUMENTATION.md
Normal file
@@ -0,0 +1,611 @@
|
||||
# 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<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
|
||||
|
||||
```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<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
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```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<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.
|
||||
|
||||
```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 <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:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
|
||||
```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 (
|
||||
<div className="p-6">
|
||||
<h1>Nueva Pagina</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
2. **Agregar al Sidebar**
|
||||
|
||||
```typescript
|
||||
// Sidebar.tsx - agregar al menuItems
|
||||
{
|
||||
label: "Nueva Pagina",
|
||||
page: "nueva",
|
||||
icon: <IconComponent className="w-5 h-5" />,
|
||||
}
|
||||
```
|
||||
|
||||
3. **Agregar al renderizado en App.tsx**
|
||||
|
||||
```typescript
|
||||
// App.tsx - agregar case en renderPage()
|
||||
case "nueva":
|
||||
return <NuevaPage />;
|
||||
```
|
||||
|
||||
### 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<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 }));
|
||||
}
|
||||
```
|
||||
|
||||
2. **Crear hook personalizado (opcional)**
|
||||
|
||||
```typescript
|
||||
// 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
|
||||
|
||||
```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 (
|
||||
<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:
|
||||
|
||||
```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
|
||||
Reference in New Issue
Block a user