Monolito
Última actualización: 06-01-2026 09:01 PM
pAVANTI — Modularidad (Monolito)
Section titled “pAVANTI — Modularidad (Monolito)”Objetivo de este documento: describir cómo está dividido hoy el mini‑gestor de Proyectos → Grupos → Retos (pAVANTI) en términos de módulos/capas, responsabilidades, APIs internas y dependencias. Este es el monolito inicial; más adelante lo partimos en documentos específicos.
Nota de estado: el archivo conceptual
pAVANTI.mdmenciona “sin almacenamiento automático”, pero el código actual sí persiste enlocalStorage. Este documento toma como fuente de verdad la implementación.
1) Estructura actual (carpetas y archivos)
Section titled “1) Estructura actual (carpetas y archivos)”/src/proyectos/ components/ ProgressBar.tsx ProyectosApp.tsx SidebarProyectos.tsx SidebarDerechaProyectos.tsx TablaProyectos.tsx hooks/ useProyectosState.ts lib/ actividad.ts calculoProgreso.ts format.ts ops.ts types/ proyecto.tsLectura rápida por capas:
- types/ → modelo de datos del dominio (sin dependencias).
- lib/ → lógica pura de dominio (operaciones y cálculos).
- hooks/ → estado, persistencia y “sync UX” (cambios no descargados).
- components/ → UI: composición, interacción, rendering y estilos.
2) Mapa de dependencias (dirección permitida)
Section titled “2) Mapa de dependencias (dirección permitida)”Regla práctica actual:
components/*→ pueden importar dehooks/*,lib/*,types/*.hooks/*→ pueden importar detypes/*.lib/*→ pueden importar detypes/*.types/*→ no importan nada interno (base del dominio).
Dependencias externas visibles:
uuid→ generación de IDs enlib/ops.ts.lucide-react→ iconografía en UI.structuredClone→ clonación profunda para mutaciones puras.
Motivo del diseño: mantener el núcleo (types/lib) testable y sin React. La UI solo “orquesta”.
3) Módulo: types/proyecto.ts (Modelo de dominio)
Section titled “3) Módulo: types/proyecto.ts (Modelo de dominio)”3.1 Entidades
Section titled “3.1 Entidades”-
Proyecto
id,nombre,descripcion?
-
nodos: NodoGrupo[](raíz del árbol: solo grupos)creadoEn,actualizadoEn(ISO)
-
NodoBase (base común)
id,tipo: "grupo" | "reto",titulo,estado: "todo" | "done"fecha?: string(ISO YYYY-MM-DD)minutos?: number(reto: editable; grupo: derivado en UI)notas?: string(solo reto)orden?: number(reservado para DnD/orden estable)
-
NodoGrupo
tipo: "grupo"hijos: NodoReto[]- Funciona como carpeta de organización (un proyecto puede tener muchos grupos).
-
NodoReto
tipo: "reto"
3.2 Type guards
Section titled “3.2 Type guards”esGrupo(n): n is NodoGrupoesReto(n): n is NodoReto
3.3 Invariantes (importantes para mantener)
Section titled “3.3 Invariantes (importantes para mantener)”Proyecto.nodoses la única raíz del árbol.- Un
Gruposolo contiene retos (no hay subgrupos). - No existen retos en raíz.
- Un proyecto puede tener muchos grupos (organización).
estadoes binario (no hay “in progress”).ordenexiste pero hoy no gobierna el orden real (aún).
4) Módulo: lib/ops.ts (Operaciones puras del dominio)
Section titled “4) Módulo: lib/ops.ts (Operaciones puras del dominio)”Responsabilidad: mutaciones “puras” sobre el estado de proyectos, siempre devolviendo una nueva lista (clonada) con timestamps actualizados.
4.1 Helpers internos
Section titled “4.1 Helpers internos”-
nowISO()→ timestamp ISO. -
Traversal del árbol:
visitRaiz(nodos, fn)recorre raíz y subárbol.visitGrupo(g, fn)recorre recursivamentehijos.
-
Búsqueda y manipulación:
-
findGroupById(p, id)→ encuentra grupo (raíz).removeNodeByIdFrom(parentArr, id)→ elimina nodo en cualquier profundidad y lo retorna.
Diseño intencional: traversal y remove son internos para no exponer demasiadas primitivas aún.
4.2 API pública (funciones exportadas)
Section titled “4.2 API pública (funciones exportadas)”-
Creación
crearProyecto(prev, nombre) => Proyecto[](inserta al inicio)crearGrupo(titulo, hijos?, fecha?) => NodoGrupocrearReto(titulo, minutos?, fecha?) => NodoReto
-
Mutaciones del árbol
agregarNodoRaiz(prev, proyectoId, nodo) => Proyecto[]editarNodo(prev, proyectoId, nodoId, patch) => Proyecto[]toggleEstado(prev, proyectoId, nodoId) => Proyecto[]
-
moverReto(prev, proyectoId, retoId, destinoGrupoId) => Proyecto[]eliminarNodo(prev, proyectoId, nodoId) => Proyecto[]
-
Proyecto
renombrarProyecto(prev, proyectoId, nuevoNombre) => Proyecto[]
4.3 Reglas operativas implementadas (lo que realmente pasa)
Section titled “4.3 Reglas operativas implementadas (lo que realmente pasa)”-
Todas las mutaciones hacen
structuredClone(prev)y trabajan sobre la copia. -
Cualquier cambio que pase por
ops.*refrescaProyecto.actualizadoEn. -
editarNodo:- no recorta
titulodurante escritura (permite teclear espacios), pero la UI hacetrim()enonBlur. minutossolo se aplica si el nodo esreto.notassolo se aplica si el nodo esreto.
- no recorta
4.4 Limitaciones actuales
Section titled “4.4 Limitaciones actuales”-
moverRetosolo mueve retos (no grupos) y permite destino:destinoGrupoId→ grupo válido (raíz). No se permite mover a raíz.
-
No hay reordenamiento real:
ordenexiste pero no se usa.
5) Módulo: lib/calculoProgreso.ts (Métricas y progreso)
Section titled “5) Módulo: lib/calculoProgreso.ts (Métricas y progreso)”Responsabilidad: calcular progreso y contadores de manera recursiva.
5.1 Tipos
Section titled “5.1 Tipos”-
Contadores:total→ cuenta grupos + retos (cada nodo cuenta 1)done/pendientesminutos→ suma recursiva de minutos (solo retos aportan minutos; grupos “agregan” pero no suman extra)
5.2 API pública
Section titled “5.2 API pública”-
sumarMinutos(nodo)- Si es
reto→minutos ?? 0 - Si es
grupo→ suma desumarMinutos(hijo)
- Si es
-
contarNodos(nodo)- Retorna contadores del subárbol
- Regla: el grupo cuenta como unidad adicional (incrementa
totalen 1)
-
contadoresProyecto(proyecto)- Reduce
contarNodossobreproyecto.nodos
- Reduce
-
calcularAvancePct(proyecto)Math.round(done/total * 100)con guard sitotal===0.
-
contadoresGrupo(g)- Alias para
contarNodos(g)(útil para UI).
- Alias para
5.3 Lectura de negocio
Section titled “5.3 Lectura de negocio”- El “progreso” es una métrica de checklist: todo nodo vale 1.
- Un grupo puede estar
doneaunque tenga hijos pendientes (hoy no hay “auto‑done”).
6) Módulo: lib/format.ts (Helpers de formato)
Section titled “6) Módulo: lib/format.ts (Helpers de formato)”minsToHM(mins?)→ retorna string (1h 30m,2h,15m).
Uso: UI del sidebar y header del proyecto.
7) Módulo: hooks/useProyectosState.ts (Estado + persistencia)
Section titled “7) Módulo: hooks/useProyectosState.ts (Estado + persistencia)”Responsabilidad: mantener la lista de proyectos como estado React y sincronizarla con localStorage.
7.1 API pública
Section titled “7.1 API pública”useProyectosState(initial?) => {
proyectossetProyectos(fn|value)(envuelto para marcar cambios)hasUnsavedChanges(cambios pendientes de descarga)markSynced()(llamar tras export/descarga)resetFromImported(nuevos)(llamar tras importar)
}
7.2 Persistencia real (lo que hace hoy)
Section titled “7.2 Persistencia real (lo que hace hoy)”- Key fija:
pweb_proyectos_v1. - Load (una vez): en mount, lee JSON, si es array lo aplica y marca
hasUnsavedChanges=true. - Save (siempre): cada cambio de
proyectosescribe JSON allocalStorage.
7.3 Implicación UX
Section titled “7.3 Implicación UX”-
El indicador “hay cambios no descargados” no significa “no guardado” (porque ya está guardado en web), sino:
- “hiciste cambios y aún no exportaste una copia JSON”.
Si quieres volver a la regla “sin almacenamiento automático”, este módulo sería el primer candidato a cambiar/eliminar.
8) Módulo UI: components/ProyectosApp.tsx (Orquestador)
Section titled “8) Módulo UI: components/ProyectosApp.tsx (Orquestador)”Responsabilidad: componer la experiencia completa:
- Hook global de proyectos (
useProyectosState). - Selección local del proyecto (
selectedId). - Construcción de handlers para la tabla (toggle/edit/add/delete/move) delegando a
lib/ops.ts. - Import/Export global (JSON) y coordinación con
markSynced/resetFromImported.
8.1 Contratos
Section titled “8.1 Contratos”-
TablaProyectosrecibe{proyecto, handlers}. -
SidebarProyectosrecibe:- lista completa,
selectedId, callbacks hasUnsavedChangespara el highlight del botón “Descargar”.
- lista completa,
-
SidebarDerechaProyectosrecibe{ proyectos }(lista completa) porque la actividad es global (suma por día a través de todos los proyectos).
8.2 Import/Export
Section titled “8.2 Import/Export”Export
Section titled “Export”- Exporta todos los proyectos en formato v2 (versionado) para permitir compatibilidad hacia atrás y transportar preferencias UI.
- Archivo recomendado:
pavanti-export.v2.json. - Luego llama
markSynced().
Formato (v2):
{ "schema": "pavanti-proyectos-export", "v": 2, "exportedAt": "<ISO>", "proyectos": [ ... ], "ui": { "v": 1, "collapsedByProyecto": { "<proyectoId>": ["<grupoId>", "<grupoId>"] } }}Notas:
uies opcional (si no hay nada relevante que persistir, puede omitirse).collapsedByProyectoguarda IDs de grupos colapsados por proyecto.
Import
Section titled “Import”-
Acepta 2 formatos:
- v1 (legacy):
Proyecto[](archivos antiguos sinui). - v2: objeto con
proyectos+ui.
- v1 (legacy):
-
Si viene
ui, se restaura el colapsado antes de montar la tabla (para que el layout quede igual al export). -
Luego llama
resetFromImported(importedProyectos)y selecciona el primer proyecto (si existe).
9) Módulo UI: components/SidebarProyectos.tsx (Selector + acciones globales)
Section titled “9) Módulo UI: components/SidebarProyectos.tsx (Selector + acciones globales)”Responsabilidad:
-
Ordenar proyectos por
actualizadoEndescendente. -
Renderizar cards con:
- nombre
- progreso
% ProgressBar- contadores
done/total, pendientes - tiempo total (minutos del árbol)
- “Actualizado” en local time
-
Acciones globales:
- Importar JSON (file input oculto)
- Descargar JSON
- Crear nuevo proyecto
-
UX de scroll:
- detecta overflow y muestra overlay inferior cuando no estás al final.
Detalle relevante: el botón “Descargar” resalta en ámbar si hasUnsavedChanges.
10) Módulo UI: components/SidebarDerechaProyectos.tsx (Actividad Global)
Section titled “10) Módulo UI: components/SidebarDerechaProyectos.tsx (Actividad Global)”Este módulo ya fue separado en documentación dedicada.
Ver: Sidebar derecha — Actividad Global (Calendario) https://docs.raulnivelazo.com/proyectos/pavanti/Modularidad/sidebar-derecha/
Resumen:
- Calendario global por meses (12 bloques), selector de año.
- Agregación por fecha desde todos los proyectos (
lib/actividad.ts). - Cada celda muestra horas/día (1 decimal) y color en verde degradado.
- Auto-scroll al mes actual (o primer mes con actividad en otros años).
11) Módulo UI: components/ProgressBar.tsx (UI atómica)
Section titled “11) Módulo UI: components/ProgressBar.tsx (UI atómica)”Responsabilidad: barra de progreso accesible.
- Normaliza
valuea 0–100. - Usa
role="progressbar"+aria-*. - Render solo barra (sin texto).
12) Límites actuales y riesgos (para tenerlos a la vista)
Section titled “12) Límites actuales y riesgos (para tenerlos a la vista)”-
Inconsistencia documental:
pAVANTI.mddice “sin almacenamiento automático”, perouseProyectosStatepersiste enlocalStorage. -
Selección al cargar: si hay proyectos persistidos, no se auto‑selecciona uno. Es fricción menor.
-
Mover a… solo permite mover retos entre grupos existentes (sin mover a raíz ni subgrupos).
-
Orden (
orden) está “preparado” pero no se aplica; al implementar DnD hay que decidir:- si el orden se aplica por
ordenen cada lista de hijos, - o si se mantiene por orden del array.
- si el orden se aplica por
-
structuredClone: requiere entornos modernos; en Next/React actual suele ser ok, pero si se apunta a navegadores viejos habría que reemplazarlo.
13) Split futuro: documentos que probablemente separaremos
Section titled “13) Split futuro: documentos que probablemente separaremos”Cuando empecemos a iterar documentación por módulos, estos son candidatos claros:
-
Modelo de dominio (types)
- invariantes, ejemplos de JSON, migraciones de esquema.
-
Operaciones del árbol (ops)
- reglas de mutación, traversal, orden, DnD, edición, performance.
-
Métricas y progreso (calculoProgreso)
- definición formal de “progreso”, variantes posibles.
-
Persistencia y export/import (useProyectosState)
- decisión “localStorage vs no”, versionado de storage, validación y migración.
-
UI: TablaProyectos
- decisiones UX, accesibilidad, navegación teclado, popovers, colapsado.
-
UI: SidebarProyectos / ProyectosApp
- flujo general, selección, acciones globales, estados vacíos.
-
Actividad por fecha (lib/actividad.ts) (YA SEPARADO)
- ver doc:
/src/content/docs/proyectos/pAVANTI/Modularidad/lib-actividad.md
- ver doc:
-
UI: SidebarDerechaProyectos (Actividad Global) (YA SEPARADO)
- ver doc:
/src/content/docs/proyectos/pAVANTI/Modularidad/sidebar-derecha.md
- ver doc:
14) Checklist de coherencia (para próxima iteración)
Section titled “14) Checklist de coherencia (para próxima iteración)”- Alinear
pAVANTI.mdcon la realidad: ¿persistencia automática sí/no? - Decidir auto‑selección del primer proyecto tras load/import.
- Reforzar guards en
opse import: sin subgrupos y sin mover a raíz. - Definir estrategia de orden y preparar DnD.