From aa42f0cb0bfd78aebc4f9c495ff376a052fa91a8 Mon Sep 17 00:00:00 2001 From: Jaume Garriga Maestre Date: Sun, 21 Jun 2026 13:19:54 +0200 Subject: [PATCH] =?UTF-8?q?feat(panel):=20pesta=C3=B1a=20de=20alta=20y=20b?= =?UTF-8?q?aja=20de=20ni=C3=B1os?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- frontend/src/app/core/i18n.service.ts | 1 + .../parents/children-tab.component.ts | 101 ++++++++++++++++++ .../app/features/parents/parents.component.ts | 20 +++- 3 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 frontend/src/app/features/parents/children-tab.component.ts diff --git a/frontend/src/app/core/i18n.service.ts b/frontend/src/app/core/i18n.service.ts index 06fd9e1..c29a8c8 100644 --- a/frontend/src/app/core/i18n.service.ts +++ b/frontend/src/app/core/i18n.service.ts @@ -40,6 +40,7 @@ export class I18nService { wrongPin: { ES: 'PIN incorrecto', CA: 'PIN incorrecte' }, // Panel de padres logout: { ES: 'Salir', CA: 'Sortir' }, + tabChildren: { ES: 'Niños', CA: 'Nens' }, tabSchedule: { ES: 'Horario', CA: 'Horari' }, tabMaterials: { ES: 'Materiales', CA: 'Materials' }, tabEvents: { ES: 'Eventos', CA: 'Esdeveniments' }, diff --git a/frontend/src/app/features/parents/children-tab.component.ts b/frontend/src/app/features/parents/children-tab.component.ts new file mode 100644 index 0000000..b3b0cf7 --- /dev/null +++ b/frontend/src/app/features/parents/children-tab.component.ts @@ -0,0 +1,101 @@ +import { Component, EventEmitter, Output, inject, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ParentApiService } from '../../core/parent-api.service'; +import { I18nService } from '../../core/i18n.service'; +import { ChildSummary } from '../../core/models'; + +/** Pestaña Niños: dar de alta y de baja perfiles de hijo en la familia. */ +@Component({ + selector: 'app-children-tab', + imports: [FormsModule], + template: ` + +
+

Nuevo niño/a

+
+ + + + + + +
+

+ La mascota es un emoji y el color identifica al niño en sus tarjetas. +

+
+ + +
+

Perfiles

+
+ @for (c of children(); track c.id) { +
+ {{ c.mascot }} + {{ c.name }} · {{ c.age }} años · 🪙 {{ c.coins }} + +
+ } @empty { +

Aún no hay niños. Añade el primero arriba ✨

+ } +
+
+ `, +}) +export class ChildrenTabComponent { + /** Avisa al panel de que la lista de niños cambió (para refrescar el selector). */ + @Output() changed = new EventEmitter(); + + private readonly api = inject(ParentApiService); + protected readonly i18n = inject(I18nService); + + protected readonly children = signal([]); + + protected mascot = ''; + protected name = ''; + protected age: number | null = null; + protected departureTime = ''; + protected accentColor = '#F2A65A'; + + constructor() { + this.reload(); + } + + private reload(): void { + this.api.listChildren().subscribe((list) => this.children.set(list)); + } + + add(): void { + if (!this.name || !this.mascot || !this.age) { + return; + } + this.api + .createChild({ + name: this.name, + mascot: this.mascot, + accentColor: this.accentColor, + age: this.age, + departureTime: this.departureTime || undefined, + }) + .subscribe(() => { + this.name = this.mascot = this.departureTime = ''; + this.age = null; + this.reload(); + this.changed.emit(); + }); + } + + remove(c: ChildSummary): void { + // Borra el niño y todo lo suyo (horario, rutinas, tareas, monedero...). + this.api.deleteChild(c.id).subscribe(() => { + this.reload(); + this.changed.emit(); + }); + } +} diff --git a/frontend/src/app/features/parents/parents.component.ts b/frontend/src/app/features/parents/parents.component.ts index 457e69e..797a3df 100644 --- a/frontend/src/app/features/parents/parents.component.ts +++ b/frontend/src/app/features/parents/parents.component.ts @@ -5,19 +5,21 @@ import { ParentApiService } from '../../core/parent-api.service'; import { AuthService } from '../../core/auth.service'; import { I18nService } from '../../core/i18n.service'; import { ChildSummary } from '../../core/models'; +import { ChildrenTabComponent } from './children-tab.component'; import { ScheduleTabComponent } from './schedule-tab.component'; import { MaterialsTabComponent } from './materials-tab.component'; import { EventsTabComponent } from './events-tab.component'; import { RoutinesTabComponent } from './routines-tab.component'; import { RewardsTabComponent } from './rewards-tab.component'; -type Tab = 'schedule' | 'materials' | 'events' | 'routines' | 'rewards'; +type Tab = 'children' | 'schedule' | 'materials' | 'events' | 'routines' | 'rewards'; /** Panel de padres: barra de pestañas, selector de niño y salida. */ @Component({ selector: 'app-parents', imports: [ FormsModule, + ChildrenTabComponent, ScheduleTabComponent, MaterialsTabComponent, EventsTabComponent, @@ -27,6 +29,7 @@ type Tab = 'schedule' | 'materials' | 'events' | 'routines' | 'rewards'; template: `
+ @@ -37,7 +40,7 @@ type Tab = 'schedule' | 'materials' | 'events' | 'routines' | 'rewards';
- @if (tab() !== 'materials') { + @if (tab() !== 'materials' && tab() !== 'children') {