# Progreso del Proyecto

> Última actualización: 2026-08-17 (sesión de reconstrucción completa)

## ✅ Fase 0 — Infraestructura

- Monorepo `cordami-platform/` con git (`main`), `.gitignore` y `Makefile`.
- `docker-compose.yml`: `db` (postgis/postgis:16-3.4, host :5434), `backend` (php:8.4-fpm + pdo_pgsql + gd + composer), `nginx` (:8085).
- Frontend React 19 + TS strict + Vite 8 + Tailwind v4 + PWA (SW + manifest generados por `vite-plugin-pwa`).

## ✅ Fase 1 — Datos y catálogos

- Migraciones: `users` (+`rol`, `cedula_tipo/cedula`, `documento_validado_at`), `municipios`, `parroquias`, Sanctum, PostGIS.
- Seeders: **21 municipios + 54 parroquias de Miranda** (INE/Wikipedia) y **catálogo de producción** (9 categorías ganaderas con subtipos/utilidades/vacunas, 8 de siembra, apicultura, pesca).
- Endpoints públicos: `/health`, `/catalogos/municipios`, `/catalogos/produccion`.

## ✅ Fase 2 — Modelo del censo (PostGIS)

- Migraciones completas: `productores`, `ubicacion_habitacion`, `predios` (**geometry Polygon/Point + GiST**), `predio_agua_riego`, `predio_equipamiento`, `predio_infraestructura`, `datos_socio_organizativos`, `censos` (UUID + soft delete + serial), `actividades` (detalles jsonb: etapas ganaderas + cacao), `censo_historial` (snapshot jsonb), `documentos_verificacion`, `email_verification_tokens`.
- `CensoService` con **upsert idempotente** (por UUID del cliente), serial `MI-26-<cedula>`, fechas de emisión/vencimiento, snapshot inmutable, normalización municipio/parroquia (por id o nombre), cálculo de hectáreas vía `ST_Area` cuando el cliente no envía superficie.
- `CarnetService` (serial, fechas, rubro principal, estado para imprimir), `FotoService` (disco público).

## ✅ Fase 2b — API completa

- Auth: `register` (con **vinculación automática** de censos anónimos por cédula), `login`, `logout`, `me`.
- Email: `verification-notification` + `verify` (tokens con expiración; en dev devuelve el link).
- Documentos: `POST /documentos/cedula` (pendiente) → admin `PATCH /admin/documentos/{id}/verificar` (aprobado/rechazado; aprueba → `documento_validado_at`).
- Censos: `POST /censos` **público** (captura anónima), fotos multipart `foto-perfil`/`foto-cedula`, `GET/DELETE` por rol.
- Panel: listado con filtros (q, municipio, estado_sync, fechas), exportación CSV, expediente PDF.
- Export productor: `export/censo/{id}/pdf|xlsx` **bloqueado por validaciones** (email + documento + propiedad).
- Carnet público: `GET /carnet/{serial}` (solo lectura).

## ✅ Fase 3-4 — Wizard React (8 pasos)

- Store Zustand persistido (localStorage) con `formData` + paso actual; fotos comprimidas (canvas).
- Pasos: 1) Productor (foto, edad automática, formatos) · 2) Habitación (condicionales) · 3) Predio con **Leaflet + Geoman** (satélite Esri, polígono, marcador, GPS, elevación open-meteo, área y centro automáticos) · 4) Agua/riego/equipo · 5) Actividades · 6) Detalle dinámico por tipo (categorías/razas del catálogo, **desglose ganadero por etapas con vacunas** y **cuestionario de Cacao**) · 7) Infraestructura/mano de obra · 8) Resumen y envío.
- Validación por paso (`validation.ts`) con mensajes claros.

## ✅ Fase 5 — Sincronización offline

- IndexedDB (Dexie): `borradores`, `censosLocales`, `colaSync` (outbox), `fotos`.
- `submitCenso`: online → POST directo + fotos multipart + resultado inmediato (serial); offline → todo a la cola.
- `drainQueue`: orden por creación, idempotencia por UUID, intentos con límite, evita bloqueos en cadena.
- Hooks: evento `online/offline`, reintento cada 30s (fallback a Background Sync), contador de pendientes en la UI (banner en header).

## ✅ Fase 6 — Carnet digital + validaciones

- `CarnetCard` anverso/reverso con QR (`/carnet/:serial`).
- Flujo de habilitación: cuenta + email verificado + documento validado → botones PDF/XLSX/Imprimir (server-side gated).
- Páginas: login (con subida de cédula `?modo=documento`), registro (con link de verificación en dev), verificación de email.

## ✅ Fase 7 — Panel admin + verificación pública

- `/admin`: pestañas Censos (filtros + CSV + PDF por censo) y Documentos (aprobar/rechazar).
- `/carnet/:serial` consulta el endpoint público y renderiza el carnet sin datos sensibles extra.

## 🔍 Revisión 1 (feedback del equipo) — corregido

- **Campo Parroquia → autocomplete**: al elegir un municipio (pasos 2 y 3) el campo parroquia ahora filtra/autocompleta con las parroquias reales de ese municipio (nuevo componente `Autocomplete`). Permite texto libre como respaldo (el backend resuelve parroquias no catalogadas). Al cambiar de municipio se limpia la parroquia para evitar desajustes.
- **Catálogos offline**: los catálogos (municipios y producción) ahora se cachean en `localStorage` y caen a esa copia si el dispositivo está sin red, para que el formulario siga operativo en zonas sin cobertura tras la primera visita.

## 🔍 Revisión 2 (feedback del equipo) — combos largos a autocomplete

- El componente `Autocomplete` se generalizó para soportar `{label, value}` (municipios con id) y **todos los combos con más de 3 opciones del censo ahora son autocomplete**: tipo de cédula (V/E/J/G), municipio (habitación y predio), **nivel educativo**, tipo de tenencia, tipo de riego, categoría/raza de actividades, dónde fermentation / tipo de madera (cacao) y utilidad (ganadería).
- Se mantienen como `<select>` los combos de 2-3 opciones (Sí/No, sexo, vía de acceso, unidad de proyección) por ser más ergonómicos así.

## 🔍 Revisión 3 (feedback del equipo) — direcciones identificadas

- Se distingue la **Dirección de Residencia del Productor** (nuevo campo en el Paso 2, persistido en `ubicacion_habitacion.direccion_residencia`) de la **Dirección del Predio / Unidad de Producción** (Paso 3), que ya tenía etiqueta clara.
- En el Paso 3 se agregó un **switch “La dirección del predio es la misma que la de la residencia”**: al activarlo copia automáticamente la dirección de residencia al predio (y deja el campo de solo lectura); al desactivarlo se puede editar libremente. El flag `predioIgualResidencia` se guarda en el payload (y en el snapshot/censo).

## 🔍 Revisión 4 (feedback del equipo) — switch de dirección al inicio y duplicación completa

- El switch **“La dirección del predio es la misma que la de la residencia”** se movió al **inicio del Paso 3** (antes de los campos), con un banner explicativo.
- Al activarlo **duplica los 7 campos de la dirección** (Municipio, Parroquia, Sector/Caserío, Comuna, Consejo Comunal, Consejo Campesino y Dirección de Residencia) en la ubicación del predio, en vivo (si se edita la residencia en el Paso 2, el predio se actualiza). Los campos duplicados quedan en solo lectura; al desactivarlo se vuelven editables.
- Componentes `Input` y `Autocomplete` ahora soportan `disabled`.

## 🔍 Revisión 5 (feedback del equipo) — dirección del predio siempre editable

- Con el switch de "misma dirección de residencia" activo, la **Dirección del Predio / Unidad de Producción** queda **habilitada** (se siembra con la de la residencia al activar, pero no se sobreescribe luego) para que el encuestador pueda completar con puntos de referencia incluso si es más extensa que la del productor. Los demás campos de ubicación (municipio, parroquia, sector, comuna, consejos) siguen bloqueados y sincronizados con la residencia.

## 🔍 Revisión 6 (feedback del equipo) — tipo de depósito como autocomplete

- El campo **"Tipo de depósito"** (Paso 4) ahora es un **autocomplete** con 22 tipos de depósitos de agua para uso agrícola (tanques elevados/polietileno/metal/fibra/hormigón, tinaco, cisterna, aljibe, pozo/noria, pipote, tobo, estanques de geomembrana, laguna artificial, jagüey, tanque australiano, flexi tank, bateas, albercas, etc.), con texto libre como respaldo.

## 🔍 Revisión 7 (auditoría exhaustiva del original + responsive)

Rellenadas etiquetas y funcionalidades que faltaban frente al formulario legado:
- **Paso 1**: tarjeta de instrucciones de la foto (selfie de frente, fondo blanco, sin gorras/lentes/accesorios).
- **Paso 3**: **métricas en vivo** del mapa (Latitud, Longitud, Altitud, Superficie) sobre el mapa, como el original.
- **Paso 6**: cuestionario de cacao completo con **condicionales** (deja conchas → ¿la transformas? → ¿en qué?; realiza fermentación → ¿dónde? → tipo de madera → cuál madera; recolecta baba → ¿en qué la usa?), subtítulo "En la Poscosecha", y botón **"➕ Agregar otra [tipo]"**. La validación también cubre esos condicionales.
- **Paso 8**: resumen **detallado por secciones** (foto, datos personales, habitación, predio con coordenadas/superficies, agua y equipamiento, actividades, infraestructura y mano de obra, prácticas y organización) con nombres de municipio en lugar de ids.
- **Responsive**: header con menú que envuelve en móvil (estado offline/sync en fila inferior), mapa de 320px en móvil / 420px en desktop, padding del card responsive, `overflow-x` controlado y `-webkit-text-size-adjust`.

## 🔍 Revisión 8 (feedback del equipo) — etiquetas de sección con descripción

Nuevo componente `Section` (título + descripción breve de qué se registra) aplicado en todos los pasos, replicando las etiquetas del formulario original:
- **Paso 1**: Datos del Productor.
- **Paso 2**: Ubicación Político-Territorial de Habitación · Nivel Educativo y Experiencia Agropecuaria.
- **Paso 3**: Ubicación del Predio / Finca / Parcela / Conuco · 🛰️ Ubicación Satelital / Trazado de Poligonal · Coordenadas de Ubicación · Superficie del Predio · Titularidad de la Tierra.
- **Paso 4**: Fuentes de Agua Disponibles · Sistema de Riego · Maquinaria · Herramientas · Vialidad y Observaciones.
- **Paso 5**: Actividad Productiva.
- **Paso 6**: Detalle de Actividades Agropecuarias.
- **Paso 7**: Infraestructura de la Unidad de Producción · Mano de Obra.
- **Paso 8**: Conocimientos y Prácticas Agroecológicas · Registros Agrícolas · Pertenencia a Organización Popular · Asistencia Técnica · Resumen del Censo (con nota de revisión).

## 🔍 Revisión 9 (feedback del equipo) — landing institucional como inicio

- La landing entregada por el equipo (antes en `frontend/public/index.html`) se integró como **página de inicio (`/`) del proyecto** convertida a componente React (`src/features/landing/LandingPage.tsx`): hero con collage rotativo, franja de valor, beneficios, CTA final y footer con contacto real de CORDAMI.
- **Rutas actualizadas a la versión actual**: "Iniciar Registro (Agrícola)" → `/censo`, "Carnet del Productor" / "Consultar mi carnet" → `/carnet`, "Ingresar" → `/login`; los anclajes internos (#beneficios, #registro, #contacto) se conservan.
- Tema de la landing integrado en Tailwind v4 (`@theme`): paleta `brand-*`, `gold-*`, tipografía `display` (Plus Jakarta Sans) y sombras `soft`/`card`; fuentes cargadas desde el `index.html` de la app.
- El layout de la app detecta `pathname === '/'` y renderiza la landing **a página completa** (sin el header del portal).
- Se eliminó la antigua `HomePage` y el `public/index.html` estático (conflictúa con el `index.html` que genera el build de Vite); sus imágenes (`1..4.jpeg`, `logo.png`) se mantienen en `public/`.

## 🔍 Revisión 10 (feedback del equipo) — campos de fecha amigables

- Nuevo componente `DateInput` basado en el selector nativo del navegador: **spinner de rueda en iOS y calendario en Android/desktop**, con botón de limpiar opcional. El valor se guarda/expone siempre en formato `DD/MM/AAAA` (compatible con el backend y el carnet).
- Aplicado en: fecha de nacimiento del Paso 1 (con límite "hoy" y cálculo automático de edad) y todas las fechas del Paso 6 (inicio/recolección de cada actividad y fecha de nacimiento de cada etapa ganadera).

## 🔍 Revisión 11 (feedback del equipo) — rueda de fecha en TODOS los dispositivos

- `DateInput` ahora es un **wheel picker propio (estilo iOS)**: tres columnas giratorias (Día, Mes con nombre, Año) que funcionan con el dedo **y con el ratón/rueda del mouse en PC**, idéntico en cualquier dispositivo. Se abre como **hoja inferior (bottom sheet)** al tocar el campo, con Cancelar/Listo, franja resaltada y máscara de desvanecimiento.
- Comportamiento inteligente: el día se ajusta al máximo del mes/año (31→30/28), rangos de años según `min/max` (nacimiento: hasta hoy), e hidratación del valor existente.
- Aplicado en fecha de nacimiento (Paso 1) y todas las fechas de actividades/etapas (Paso 6). Formato interno DD/MM/AAAA intacto.

## 🔍 Revisión 12 (feedback del equipo) — navegación del wizard siempre visible en móvil

- La barra de **Anterior / Siguiente / Enviar** del censo ahora es **sticky en la parte inferior** en móvil: se mantiene fija y visible mientras se hace scroll en el paso (fondo blanco translúcido + sombra + respeto del `safe-area-inset-bottom`). En desktop vuelve a ser estática e integrada al card. El botón principal ocupa todo el ancho en móvil.

## 🆕 Panel de administración completo (usuarios + roles/permisos dinámicos)

**Backend**
- Tablas `roles`, `permisos` y pivote `rol_permiso`; `users.activo` (activar/desactivar cuentas).
- Permisos del catálogo: dashboard.ver, censos.ver, censos.expediente, censos.exportar, documentos.ver, documentos.validar, usuarios.gestionar, roles.gestionar.
- Perfiles sembrados: `admin` (8 permisos + acceso implícito total), `encuestador` (5), `productor` (0).
- Middleware `permiso:clave` (alias en bootstrap) que bloquea cada ruta de admin según el permiso; el perfil admin siempre pasa.
- Endpoints: `resumen` (dashboard), CRUD de usuarios (con protección: no auto-eliminación, no quitar el último admin, login rechaza usuarios desactivados, bloqueo por intentos fallidos ya existente), CRUD de roles/permisos (no borrar perfil admin ni perfiles con usuarios).
- `/me`, login y registro devuelven `permisos[]` para la UI dinámica.
- Tests: 29/29 (incluye AdminPanelTest con 5 casos de permisos).

**Frontend**
- `usePermiso` / `Permiso` (gate de UI) y `usePermisos`; el panel solo muestra las pantallas permitidas por el perfil.
- **Resumen** (tarjetas: censos, pendientes de sync, documentos, productores, usuarios).
- **Censos** (filtros + PDF y CSV visibles solo con permiso).
- **Documentos** (ver; botones Aprobar/Rechazar solo con `documentos.validar`).
- **Usuarios** (nuevo/editar/activar/desactivar/eliminar con protección de sí mismo).
- **Roles y Permisos** (perfiles dinámicos: crear, editar, asignar permisos por grupos de pantalla, eliminar).

## 🆕 Dashboard dinámico + constructor de reportes por usuario

- **Menú desplegable** (☰) en el panel: muestra solo las pantallas permitidas por el perfil (dashboard, censos, documentos, usuarios, roles).
- **Dashboard inicial** con KPIs y **gráficos dinámicos (recharts)**: censos por municipio, por sexo y por actividad, con **filtros del censo** globales (municipio, sexo, actividad, correo registrado, riego, semillas, agroecología, asistencia técnica y rango de fechas).
- **Constructor de reportes** (`ReporteService` + endpoints): cada usuario define **métrica** (cantidad de censos, superficies, cantidades/proyección de actividades) + **agrupación** (municipio, parroquia, sexo, actividad, rubro, riego, semillas, asistencia, sync, vínculo) + **filtros**; genera la consulta agregada en PostgreSQL, la previsualiza (pastel si pocos grupos, barras si son muchos) y la **guarda como "su" reporte** (tabla `user_reportes` por usuario, con permisos `reportes.ver`/`reportes.gestionar`).
- Ejemplos que ya se pueden construir: "productores con correo + cacao + asistencia técnica", "superficie cultivada por municipio", "productores que no producen semillas", etc.
- Tests: 32/32 (incluye consultas agregadas, filtros, CRUD con propiedad por usuario y gating por permisos).

## ✅ Fase 8 — Verificación final

- Backend: **13 tests / 66 assertions** (flujo de censo completo con PostGIS, idempotencia, auth, email, documentos, admin, carnet público).
- Frontend: build de producción OK + lint (0 errores).
- Smoke E2E: login admin, censo anónimo con polígono → serial, carnet público, listado admin con filtros, CSV, expediente PDF (16 KB), rutas del SPA (home/censo/login/carnet público) responden 200 vía dev server con proxy.

## 🔧 Lecciones / decisiones técnicas del camino

1. Laravel 13 exige PHP ≥ 8.4 → imagen `php:8.4-fpm`.
2. Artefactos de artisan: ejecutar con `-u $(id -u):$(id -g)` para que queden editables desde el host.
3. **Aislamiento de tests**: el servicio `backend` del compose no debe inyectar `APP_ENV`/`DB_*` (Dotenv inmutable no los sobreescribe). Los tests se corren con `make test` (`-e APP_ENV=testing` + `.env.testing` → BD `cordami_testing`). Si PHPUnit no logra forzar variables, agregar `force="true"` en `phpunit.xml`.
4. El guard de Sanctum **cachea el usuario entre requests del mismo test** → `$this->app['auth']->forgetGuards()` antes de cambiar de token en tests.
5. `HasUuids` genera id si el atributo no está asignado; como `id` no está en `$fillable`, asignarlo con `setAttribute` (idempotencia del censo).
6. Pluralización de Eloquent (`Productor`→`productors`, `Actividad`→`actividads`): fijar `$table` en los modelos.
7. `pkill -f "vite"` mata el propio shell (el patrón aparece en su command line) → usar `[v]ite` o matar por puerto; el dev server debe lanzarse como subshell desacoplado `(npm run dev &)`.

## ⏭️ Siguientes pasos sugeridos

- **OCR de la cédula** (validación automática de documento: extraer números y comparar con el registro) — columna `documentos_verificacion.ocr` ya reservada.
- Panel de **mapa de cobertura** (densidad por municipio) usando PostGIS.
- Despliegue en el hosting compartido de pruebas (verificar Postgres/SSL) y luego VPS con HTTPS.
- Code-splitting del bundle frontend (Leaflet y librerías en chunks) y tests Vitest del motor de sync.
- CI (GitHub Actions) con los pasos: pint, phpunit, oxlint, tsc, build.
