Commit Graph

24 Commits

Author SHA1 Message Date
Jaume Garriga Maestre
6b655634ca feat(saas): infraestructura de producción para la VM
Bloque C de la fase SaaS (ficheros; el despliegue se hace con el runbook):
- docker-compose.prod.yml: superficie mínima (solo el frontend en
  127.0.0.1:8089 para el nginx nativo), límites de memoria, sin seeder.
- deploy/nginx-recordalexia.conf: server block con cabeceras de seguridad,
  X-Forwarded-For para el rate-limiter y preparado para certbot.
- deploy/backup.sh: pg_dump diario comprimido, retención 14, copia externa
  vía rclone y detección de backups vacíos.
- .env.prod.example (y .env.prod en .gitignore).
- docs/operacion-saas.md: runbook con primer despliegue, checklist,
  restore ENSAYADO paso a paso, actualización y problemas conocidos.
2026-07-13 22:33:12 +02:00
Jaume Garriga Maestre
ddd1a96af9 feat(saas): PWA instalable y pantallas de recuperación y privacidad
Bloque B de la fase SaaS:
- @angular/pwa: service worker (solo assets; /api siempre fresco), manifest
  con identidad recordaLexia (standalone, tema de la paleta) e iconos
  propios 72-512px (mascota sobre azul de marca).
- Pantallas /forgot y /reset (?code=... del email): mensajes neutros que
  no revelan si una cuenta existe; validación de contraseña repetida.
- Política de privacidad ES/CA en /privacidad (pública) y casilla de
  consentimiento obligatoria en el registro (no premarcada).
- AuthService: forgotPassword/resetPassword y registro con privacyAccepted.
Verificado E2E en navegador: registro con gating del checkbox, enlace del
log, reset, login con la nueva contraseña (la vieja 401), SW 'activated',
y familia de prueba borrada con el endpoint RGPD (dogfooding).
2026-07-13 22:27:29 +02:00
Jaume Garriga Maestre
203d9b0b47 feat(saas): recuperación de contraseña, freno de fuerza bruta y hardening
Bloque A de la fase SaaS:
- Recuperación por email: código de un solo uso (solo su SHA-256 en BD),
  caducidad 30 min, emitir uno nuevo invalida los anteriores, respuesta
  idéntica exista o no la cuenta (anti-enumeración) y email asíncrono;
  restablecer cierra todas las sesiones de la familia. MailService cae a
  modo log sin SMTP configurado.
- LoginAttemptService: 5 fallos -> bloqueo con backoff exponencial (429 +
  Retry-After) en login, PIN del panel y solicitudes de reset, comprobado
  ANTES de evaluar credenciales.
- Registro exige aceptar la política de privacidad (privacy_accepted_at).
- Borrado definitivo de cuenta (RGPD) con confirmación por contraseña.
- CORS por configuración; seeder demo fail-safe (matchIfMissing=false,
  activado explícito en el compose local); application-prod.yml.
- Liquibase 005 con rollback. 44 tests verdes incl. migración en Postgres.
2026-07-13 22:07:06 +02:00
Jaume Garriga Maestre
4606f71bdc docs(manual): sección de la vista de mañana con captura real 2026-07-12 22:50:54 +02:00
Jaume Garriga Maestre
e5385f7ddd feat(hoy): vista previa de mañana en el kiosko del niño
Botón "Mañana ›" en la cabecera: muestra el día siguiente en solo
lectura para preparar la mochila la noche antes. La vista se calcula al
vuelo desde las plantillas SIN materializar DailyTask (los cambios del
panel de esa noche siguen entrando al generarse el día de verdad), con
ids sintéticos negativos no accionables, sin temporizador ni progreso.
Banner "Así será mañana" y estados vacíos propios. Test de integración:
la vista previa no persiste tareas del día siguiente.
2026-07-12 22:37:39 +02:00
Jaume Garriga Maestre
4eccee9b05 feat(demo): horario real de 4t A para Leo en el seeder
La familia demo siembra el horario escolar real de 4º de primaria para
Leo (11 asignaturas nuevas bilingües + 29 entradas semanales en orden
cronológico; Piscina lleva la Toalla asociada). Nora y Mía conservan el
horario genérico. Test que fija el lunes completo de Leo.
2026-07-12 22:29:17 +02:00
Jaume Garriga Maestre
1db262f8ea fix(hoy): el temporizador de salida desaparece sin cole o pasada la hora
buildTimer clavaba el contador en "0 min" el resto del día (Math.max(0,..)),
incluso en días sin cole. Ahora es función pura: sin tareas de mañana o con
la hora de salida ya pasada devuelve minutos null y el kiosko no lo pinta.
Test unitario determinista de los cinco casos.
2026-07-12 22:12:44 +02:00
Jaume Garriga Maestre
0e8bfbcc9c docs(adr): decisiones de auth, catálogo de rutinas y provisión inicial 2026-07-12 22:08:16 +02:00
Jaume Garriga Maestre
5c79480aa4 feat(home): estado vacío amable y vibración háptica al ganar monedas
Un día sin tareas muestra una tarjeta con la mascota del niño en vez de
una pantalla en blanco; la columna de tarde vacía tiene su propio mensaje.
El feedback de recompensa suma una vibración corta (Vibration API) que se
dispara junto al sonido sobre el mismo evento.
2026-07-12 22:06:03 +02:00
Jaume Garriga Maestre
3ec611d373 fix(rutinas): cascade al borrar del catálogo, orden por día y test de migración real
- Changeset 004: ON DELETE CASCADE en fk_routine_assignment_task; borrar
  una rutina del catálogo arrastra sus asignaciones en vez de fallar (test).
- El orderIndex de la asignación multi-día lo calcula el backend al final
  de CADA día destino (antes lo enviaba el front desde el día visible).
- MigrationIT con Testcontainers: el changelog completo y el mapeo JPA se
  verifican contra Postgres 16 real (H2 no detecta su tipado estricto).
- DataSeeder delega en InitialDatasetService: una sola fuente de verdad
  del dataset inicial; la demo solo añade sus extras.
- Eliminado .gitkeep de changes/ (WARN de Liquibase en cada arranque).
2026-07-12 22:02:22 +02:00
Jaume Garriga Maestre
5421080b04 feat(ui): respuesta táctil y áreas de toque según guía de fluidez
Tokens de movimiento centralizados (duraciones y curvas únicas), feedback
de pulsación visible e inmediato en tarjeta de tarea, TTS, chips, teclado
y navegación; áreas táctiles mínimas de 44-48px en controles pequeños;
tracking positivo en etiquetas en mayúsculas; touch-action manipulation
para eliminar el retardo de doble-tap en la tablet kiosko.
2026-07-12 21:35:28 +02:00
Jaume Garriga Maestre
08c7289f25 docs(manual): exportar manual de usuario a PDF 2026-06-23 14:49:25 +02:00
Jaume Garriga Maestre
c165b759b7 docs(manual): manual de usuario con capturas de la app 2026-06-23 14:32:21 +02:00
Jaume Garriga Maestre
242282412c fix(rutinas): castear NULL a INTEGER en migración 003
Postgres tipa el NULL pelado del SELECT como text y rechaza insertarlo
en routine_task.coins_reward (integer). H2 lo toleraba; Postgres no.
Se castea con CAST(NULL AS INTEGER). Validado arrancando contra
Postgres 16 real: los 5 changesets de 003 aplican y la app arranca.
2026-06-23 12:32:09 +02:00
Jaume Garriga Maestre
1f968e82cd feat(alta): dataset inicial al crear familia y niño
Al registrar una familia se siembra el catálogo de materiales escolares
típicos y el catálogo de rutinas. Al dar de alta un niño se le asignan
las rutinas de tarde de L-V escaladas por su edad (tramos 8-9 / 10-11 /
12-14), reutilizando el catálogo de la familia.
2026-06-22 23:14:35 +02:00
Jaume Garriga Maestre
b0069a0c9e feat(rutinas): panel de padres con catálogo y selección de días
routines-tab permite definir rutinas en el catálogo y asignarlas a
varios días marcando casillas, reutilizando emoji y color pickers.
2026-06-22 22:49:51 +02:00
Jaume Garriga Maestre
d0f3975c42 feat(rutinas): API de catálogo y asignación multi-día
CRUD del catálogo en /api/parents/routine-tasks y alta de una rutina
a varios días de una vez en ParentScheduleController. RoutineView
deriva la definición del catálogo e incluye routineTaskId.
2026-06-22 22:49:51 +02:00
Jaume Garriga Maestre
a58d7bda78 feat(rutinas): catálogo de rutinas reutilizable por familia
Normaliza las rutinas de tarde replicando el patrón de las mañanas:
RoutineTask es el catálogo (gemela de Activity) y AfternoonRoutine
pasa a ser pura asignación niño+día. Incluye migración Liquibase 003
con consolidación de duplicados y rollback, y la cascada de monedas
(override del día -> catálogo -> coinsPerTask) en DayGenerationService.
2026-06-22 22:41:19 +02:00
Jaume Garriga Maestre
fd184f769f feat(panel): permitir asignar tareas en fin de semana
Añade sábado y domingo a los selectores de día de Horario y Rutinas. El
backend ya generaba el día para cualquier día de la semana; solo faltaba
ofrecer el fin de semana en el panel.
2026-06-21 19:07:37 +02:00
Jaume Garriga Maestre
b8925d83dd refactor(panel): selector de emoji y color reutilizable
Extrae app-emoji-picker y app-color-picker a shared/ y los usa en las
pestañas de niños, materiales, rutinas y recompensas, que tenían el mismo
problema: input de color a pelo y emoji escrito a mano. Ahora se elige el
icono de una rejilla de emojis y el color de la paleta del handoff.
2026-06-21 15:40:38 +02:00
Jaume Garriga Maestre
cb6db53920 fix(panel): formulario de alta de niños usable
Rediseña el alta de niño que era confusa: selector de mascota por emojis,
paleta de colores clicable, campos con etiqueta legible (la edad antes se
truncaba e impedía activar 'Añadir'), la hora de salida explicada como
opcional y tooltips en cada campo.
2026-06-21 13:31:13 +02:00
Jaume Garriga Maestre
aa42f0cb0b feat(panel): pestaña de alta y baja de niños
Añade la gestión de perfiles de hijo en el panel de padres (crear con
mascota/nombre/edad/color/hora de salida y borrar), que faltaba: una familia
recién registrada empieza sin niños y necesita poder añadirlos. El backend
(CRUD de Child) ya existía; faltaba la UI.
2026-06-21 13:19:54 +02:00
Jaume Garriga Maestre
24a0c8a0dd feat: cuentas de familia (multi-tenant), registro/login y preferencias
Convierte recordaLexia de una sola familia a multi-familia, con cuentas
propias y persistencia de preferencias.

Backend:
- Tenant Family (email único + contraseña BCrypt + PIN + prefs de cuenta);
  family_id en child/activity/material_item/reward; aislamiento por familia
  (acceso cruzado responde 404).
- Auth propia (sin Keycloak): registro/login email+contraseña, sesiones de
  familia persistidas en BD (sobreviven a reinicios), panel de padres tras PIN.
- Liquibase 002-multitenant; seeder crea una familia demo.
- Tests de aislamiento entre familias, registro/login y gate del panel.

Frontend:
- Login, registro y pantalla de cuenta; guards (sesion + PIN) e interceptor
  de sesion global; perfiles scopeados a la familia.

Preferencias:
- OpenDyslexic persistida por nino (child.dyslexiaFont) y default de cuenta.

Decisiones en docs/adr/0004.
2026-06-21 13:11:34 +02:00
Jaume Garriga Maestre
52e559a159 feat: app completa recordaLexia (fases 1-5)
App web familiar de rutinas visuales para niños con TDAH: muestra cada día el
material del cole y las rutinas de tarde, con gamificación por monedas y tienda
de recompensas. Multi-niño y bilingüe ES/CA. Uso doméstico/homelab.

Backend (Spring Boot 3.5 / Java 21 / Gradle):
- Dominio por capas, PostgreSQL + Liquibase, datos semilla.
- API REST con DTOs: /today, toggle con monedas y bonos de bloque/día, monedero,
  tienda/canje, ajustes y CRUD del panel de padres.
- Seguridad ligera por PIN (BCrypt + sesion en memoria), sin Keycloak.
- Tests JUnit: generacion del dia, monedas/bonos con reversion, canje, seguridad.

Frontend (Angular 19, standalone + signals):
- Perfiles, Home (Tablero y Foco), Tienda y panel de padres (5 pestañas).
- Tipografia OpenDyslexic conmutable (accesibilidad), i18n ES/CA, TTS y sonido.
- Tokens de diseño fieles al handoff (paleta, animaciones, monedas voladoras).

Empaquetado:
- Docker multi-stage + docker-compose (PostgreSQL + backend + Nginx).
- Decisiones de arquitectura documentadas en docs/adr.
2026-06-21 10:48:57 +02:00