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.
This commit is contained in:
Jaume Garriga Maestre
2026-06-21 10:48:57 +02:00
commit 52e559a159
160 changed files with 29022 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
import { Component, Input, inject } from '@angular/core';
import { I18nService } from '../../core/i18n.service';
/** Barra de progreso global del día: "{hechas}/{total} listo ✨". */
@Component({
selector: 'app-progress-bar',
imports: [],
template: `
<div class="prog">
<div class="prog__track">
<div class="prog__fill" [style.width.%]="pct"></div>
</div>
<p class="prog__label">{{ done }}/{{ total }} {{ i18n.t('ready') }} ✨</p>
</div>
`,
styles: [
`
.prog { display: flex; flex-direction: column; gap: 6px; }
.prog__track {
height: 16px;
border-radius: var(--radius-pill);
background: var(--surface-softer);
overflow: hidden;
}
.prog__fill {
height: 100%;
border-radius: var(--radius-pill);
background: linear-gradient(90deg, var(--accent-green), var(--accent-teal));
transition: width 0.5s ease;
}
.prog__label {
margin: 0;
font-family: var(--font-display);
font-weight: 600;
color: var(--text-1);
}
`,
],
})
export class ProgressBarComponent {
@Input() done = 0;
@Input() total = 0;
protected readonly i18n = inject(I18nService);
get pct(): number {
return this.total > 0 ? Math.round((this.done / this.total) * 100) : 0;
}
}