# Handoff — Encuestapp (encuesta de viabilidad de punto de venta)

Fecha: 2026-07-16 · Proyecto: `/Users/osanchez/Developer/encuestapp`

## Qué es

Encuesta móvil fija en CodeIgniter 4.7.4 (PHP nativo, CSS/JS sin dependencias) para medir la viabilidad de un punto de venta en la nube en México (español, MXN). Incluye dashboard administrativo con métricas, prospectos y exportación CSV. El plan funcional completo vive en la conversación original (generado por otra IA); la fuente de verdad visual es `DESIGN.md` en la raíz del proyecto — **leerlo antes de tocar cualquier vista pública**.

## Estado: COMPLETO Y VERIFICADO

Todo el plan está implementado. `composer test` en verde (24 pruebas, 60 aserciones). Verificado en vivo contra el contenedor Docker: encuesta carga, un envío completo por curl persistió en MySQL (dato de prueba luego borrado), login → dashboard funciona de punta a punta.

### Backend (ya existía al iniciar la sesión, hecho por la IA anterior)
- `app/Config/Routes.php` — GET `/`, POST `/respuestas`, POST `/respuestas/descarte`, grupo `/admin` con filtros `csrf` y `adminauth`.
- `app/Database/Migrations/2026-07-16-170000_CreateSurveyTables.php` — `survey_responses`, `survey_leads` (PII separada, FK cascade), `survey_screenouts`.
- `app/Models/` — SurveyResponseModel, SurveyLeadModel, SurveyScreenoutModel.
- `app/Controllers/SurveyController.php` — validación condicional por rama, throttling, cookie aleatoria hasheada para detectar repeticiones (409 + `confirm_repeat`), transacción respuesta+lead.
- `app/Controllers/AdminController.php` — login con hash y throttling, dashboard, cambio de estado de prospectos, CSV con protección contra inyección de fórmulas.
- `app/Filters/AdminAuth.php`, `app/Commands/PurgeExpiredLeads.php` (`php spark survey:purge-leads`, retención 12 meses).

### Hecho en esta sesión
- **Vistas**: `app/Views/survey/index.php` (encuesta completa, 1 pregunta por pantalla, máx. 11 pasos), `app/Views/admin/login.php`, `app/Views/admin/dashboard.php` (métricas, distribuciones con barras, filtros fecha/origen/giro/sistema/intención/estado, tabla de prospectos con select de estado, tabla de respuestas, export CSV).
- **Assets**: `public/assets/css/survey.css` (tokens de DESIGN.md), `public/assets/js/survey.js` (motor de pasos: ramas, validación por paso, foco/aria-live, fetch con manejo de 201/409/422/419/red, limpieza de campos al cambiar de rama, límite de 3 chips), `public/assets/css/admin.css`, `public/assets/js/admin.js` (estado de prospecto vía fetch con rotación de CSRF).
- **Ajustes backend**: rama `not_open` ya no exige `current_issues`; endurecimiento `?? ''` en submit; frase de razón para HTTP 419 (CI4 lanza excepción sin ella); filtros de fecha/giro y métricas extra (rol, giro, origen, funciones, rangos) en AdminController; `fputcsv` con argumentos explícitos (PHP 8.4 en Docker).
- **Config**: `.env` → host BD corregido a `damp-db`, `app.indexPage = ''`, credenciales admin por entorno (`survey.adminEmail`, `survey.adminPasswordHash`).
- **Pruebas**: `tests/feature/SurveyFlowTest.php`, `AdminDashboardTest.php`, `PurgeLeadsTest.php` (19 pruebas nuevas).
- `composer.json`: script test → `phpunit --no-coverage` (no hay driver de cobertura local).

## Entorno (no obvio)

- Docker DAMP: contenedor `encuestapp-app` (FrankenPHP/PHP 8.4), MySQL en host **`damp-db`** (guion, no guion bajo), sitio en https://encuestapp.test. PHP local: 8.2.
- Migraciones se corren DENTRO del contenedor: `docker exec encuestapp-app php spark migrate` (ya ejecutadas).
- **Login admin dev**: `admin@encuestapp.test` / `ff97252080b4b11a` (documentado en `.env`; cambiar en producción con `php -r "echo password_hash('clave', PASSWORD_DEFAULT);"`).
- Pruebas: SQLite en memoria (grupo `tests`, prefijo `db_`). Los POST evitan el filtro CSRF con `withRoutes()`; la cookie del respondente se inyecta con `service('superglobals')->setCookie()` porque el servicio Superglobals toma snapshot de `$_COOKIE` (asignar la superglobal no basta). Sesión con `$this->mockSession()`; `cache()->clean()` en setUp de admin para no acumular throttling entre corridas.

## Decisiones de producto tomadas

- El rol (dueño/encargado/etc.) se pregunta pero **no filtra**: todos completan y el dashboard segmenta por rol. Única exclusión: "sólo exploro una idea" en la rama por-abrir → descarte (`survey_screenouts`, sin cuestionario).
- Cuestionario fijo en código (no editable desde dashboard). Sin ubicación, sin offline, sin constructor genérico.
- Valores permitidos de cada campo: ver `SurveyController::validatePayload()` — el frontend y el dashboard usan exactamente esos códigos.

## Pendiente (para la próxima sesión)

1. **Prueba manual en móvil** — 320/390/768 px y escritorio: sin scroll horizontal, teclado completo, lector de pantalla, `prefers-reduced-motion`. El CSS está preparado pero no se validó con ojos humanos.
2. **Marca y logotipo** — hoy identidad neutral ("Encuesta de punto de venta"); configurar en `app/Config/Survey.php` o vía `.env` cuando exista.
3. Opcional: programar `survey:purge-leads` en cron del contenedor; revisar textos comerciales finales antes de publicar.

## Skills sugeridas para la próxima sesión

- `adapt` o `audit` — si se hace la pasada responsive/accesibilidad manual con ajustes.
- `verify` — para validar cambios end-to-end contra el contenedor.
- `polish` — pasada final de detalles visuales antes de publicar.
