Files
recordalexia/frontend/src/app/features/parents/events-tab.component.ts
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

86 lines
2.8 KiB
TypeScript

import { Component, Input, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ParentApiService } from '../../core/parent-api.service';
import { I18nService } from '../../core/i18n.service';
import { EventAdminView } from '../../core/models';
/** Pestaña Eventos: exámenes y deberes con fecha, por niño. */
@Component({
selector: 'app-events-tab',
imports: [FormsModule],
template: `
<div class="adm-card">
<p class="adm-label">Nuevo evento</p>
<div class="adm-row">
<select class="adm-input" [(ngModel)]="type">
<option value="EXAM">📋 {{ i18n.t('exam') }}</option>
<option value="HOMEWORK">📎 {{ i18n.t('homework') }}</option>
</select>
<input class="adm-input" type="date" [(ngModel)]="date" />
<input class="adm-input" [(ngModel)]="titleEs" placeholder="Título (ES)" />
<input class="adm-input" [(ngModel)]="titleCa" placeholder="Títol (CA)" />
<button class="adm-btn" [disabled]="!date || !titleEs || !titleCa" (click)="add()">+ {{ i18n.t('add') }}</button>
</div>
</div>
<div class="adm-card">
<div class="adm-list">
@for (e of events(); track e.id) {
<div class="adm-item">
<span>{{ e.type === 'EXAM' ? '📋' : '📎' }}</span>
<span class="adm-item__grow">
<strong>{{ i18n.label(e.titleEs, e.titleCa) }}</strong> · {{ e.date }}
</span>
<button class="adm-del" (click)="remove(e)">✕</button>
</div>
} @empty {
<p class="adm-empty">{{ i18n.t('none') }}</p>
}
</div>
</div>
`,
})
export class EventsTabComponent {
@Input({ required: true }) set childId(value: number) {
this._childId = value;
this.reload();
}
private _childId!: number;
private readonly api = inject(ParentApiService);
protected readonly i18n = inject(I18nService);
protected readonly events = signal<EventAdminView[]>([]);
protected type: 'EXAM' | 'HOMEWORK' = 'EXAM';
protected date = '';
protected titleEs = '';
protected titleCa = '';
private reload(): void {
this.api.listEvents(this._childId).subscribe((l) => this.events.set(l));
}
add(): void {
const icon = this.type === 'EXAM' ? '📋' : '📎';
const color = this.type === 'EXAM' ? '#EC8FA4' : '#5B8DEF';
this.api
.createEvent({
childId: this._childId,
date: this.date,
type: this.type,
titleEs: this.titleEs,
titleCa: this.titleCa,
icon,
color,
})
.subscribe(() => {
this.titleEs = this.titleCa = this.date = '';
this.reload();
});
}
remove(e: EventAdminView): void {
this.api.deleteEvent(e.id).subscribe(() => this.reload());
}
}