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,68 @@
import { Component, EventEmitter, Input, Output, inject } from '@angular/core';
import { I18nService } from '../../core/i18n.service';
import { TaskView } from '../../core/models';
import { TaskCardComponent, ToggleEvent } from './task-card.component';
/** Vista TABLERO: dos columnas (cole / tarde) con las tareas del día. */
@Component({
selector: 'app-board-view',
imports: [TaskCardComponent],
template: `
<div class="board">
<section class="col">
<h2 class="col__head">🎒 {{ i18n.t('school') }}</h2>
@if (morning.length) {
@for (task of morning; track task.id) {
<app-task-card [task]="task" [ttsEnabled]="ttsEnabled" (toggle)="toggle.emit($event)" />
}
} @else {
<div class="empty">🏖️ {{ i18n.t('noSchool') }}</div>
}
</section>
<section class="col">
<h2 class="col__head">🌙 {{ i18n.t('afternoon') }}</h2>
@for (task of afternoon; track task.id) {
<app-task-card [task]="task" [ttsEnabled]="ttsEnabled" (toggle)="toggle.emit($event)" />
}
</section>
</div>
`,
styles: [
`
.board {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-5);
}
@media (max-width: 760px) {
.board { grid-template-columns: 1fr; }
}
.col { display: flex; flex-direction: column; gap: var(--space-3); }
.col__head {
margin: 0 0 var(--space-2);
font-size: 1.4rem;
color: var(--text-1);
}
.empty {
padding: var(--space-6) var(--space-4);
text-align: center;
font-family: var(--font-display);
font-weight: 700;
font-size: 1.3rem;
color: var(--text-2);
background: var(--surface);
border: 2px dashed var(--border-2);
border-radius: var(--radius-card);
}
`,
],
})
export class BoardViewComponent {
@Input() morning: TaskView[] = [];
@Input() afternoon: TaskView[] = [];
@Input() ttsEnabled = true;
@Output() toggle = new EventEmitter<ToggleEvent>();
protected readonly i18n = inject(I18nService);
}