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.
This commit is contained in:
@@ -11,31 +11,47 @@
|
||||
|
||||
<app-morning-timer [minutes]="t.timer.minutesUntilDeparture" />
|
||||
|
||||
<!-- Ver mañana / volver a hoy (la vista de mañana es solo lectura) -->
|
||||
<button type="button" class="daybtn" (click)="toggleDay()">
|
||||
@if (viewingTomorrow()) {
|
||||
‹ {{ i18n.t('today') }}
|
||||
} @else {
|
||||
{{ i18n.t('tomorrow') }} ›
|
||||
}
|
||||
</button>
|
||||
|
||||
<div class="home__wallet">
|
||||
<button type="button" class="iconbtn" (click)="goStore()" aria-label="Tienda">🎁</button>
|
||||
<app-wallet #wallet [coins]="t.wallet.coins" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Aviso de vista previa: que el niño sepa que está mirando mañana -->
|
||||
@if (viewingTomorrow()) {
|
||||
<div class="tomorrow">👀 {{ i18n.t('tomorrowBanner') }}</div>
|
||||
}
|
||||
|
||||
<!-- Eventos del día -->
|
||||
<app-event-banner [events]="t.specialEvents" [ttsEnabled]="t.child.ttsEnabled" />
|
||||
|
||||
@if (t.progress.total > 0) {
|
||||
<!-- Conmutador de modo -->
|
||||
<div class="modes">
|
||||
<button type="button" class="modes__btn" [class.modes__btn--on]="mode() === 'BOARD'" (click)="setMode('BOARD')">
|
||||
🗂️ {{ i18n.t('board') }}
|
||||
</button>
|
||||
<button type="button" class="modes__btn" [class.modes__btn--on]="mode() === 'FOCUS'" (click)="setMode('FOCUS')">
|
||||
🎯 {{ i18n.t('focus') }}
|
||||
</button>
|
||||
</div>
|
||||
@if (!viewingTomorrow()) {
|
||||
<!-- Conmutador de modo -->
|
||||
<div class="modes">
|
||||
<button type="button" class="modes__btn" [class.modes__btn--on]="mode() === 'BOARD'" (click)="setMode('BOARD')">
|
||||
🗂️ {{ i18n.t('board') }}
|
||||
</button>
|
||||
<button type="button" class="modes__btn" [class.modes__btn--on]="mode() === 'FOCUS'" (click)="setMode('FOCUS')">
|
||||
🎯 {{ i18n.t('focus') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Progreso global -->
|
||||
<app-progress-bar [done]="t.progress.totalDone" [total]="t.progress.total" />
|
||||
<!-- Progreso global -->
|
||||
<app-progress-bar [done]="t.progress.totalDone" [total]="t.progress.total" />
|
||||
}
|
||||
|
||||
<!-- Tareas: tablero o foco -->
|
||||
@if (mode() === 'BOARD') {
|
||||
<!-- Tareas: tablero o foco (la vista de mañana siempre en tablero, solo lectura) -->
|
||||
@if (mode() === 'BOARD' || viewingTomorrow()) {
|
||||
<app-board-view
|
||||
[morning]="t.morning"
|
||||
[afternoon]="t.afternoon"
|
||||
@@ -49,7 +65,9 @@
|
||||
<!-- Día sin tareas: mensaje amable, nunca una pantalla en blanco. -->
|
||||
<div class="freeday">
|
||||
<span class="freeday__mascot">{{ t.child.mascot }}</span>
|
||||
<p class="freeday__title">{{ i18n.t('emptyDayTitle') }}</p>
|
||||
<p class="freeday__title">
|
||||
{{ i18n.t(viewingTomorrow() ? 'emptyTomorrowTitle' : 'emptyDayTitle') }}
|
||||
</p>
|
||||
<p class="freeday__sub">{{ i18n.t('emptyDaySub') }} 🎉</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -63,6 +63,36 @@
|
||||
flex: none;
|
||||
}
|
||||
|
||||
// Botón "Mañana ›" / "‹ Hoy" de la cabecera.
|
||||
.daybtn {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
min-height: var(--touch-min);
|
||||
padding: 0 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow-btn);
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
color: var(--text-1);
|
||||
transition: transform var(--dur-press) var(--ease-out);
|
||||
}
|
||||
.daybtn:active { transform: scale(0.95); }
|
||||
|
||||
// Aviso de vista previa de mañana (solo lectura).
|
||||
.tomorrow {
|
||||
align-self: center;
|
||||
padding: 8px 20px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: color-mix(in srgb, var(--accent-purple) 16%, #fff);
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
color: var(--text-strong);
|
||||
animation: slideUp 0.3s ease both;
|
||||
}
|
||||
|
||||
// Conmutador Tablero / Foco.
|
||||
.modes {
|
||||
display: inline-flex;
|
||||
|
||||
@@ -59,6 +59,8 @@ export class HomeComponent implements OnInit {
|
||||
protected readonly coins = signal<FlyingCoin[]>([]);
|
||||
protected readonly celebrating = signal(false);
|
||||
protected readonly lastEarned = signal(0);
|
||||
/** true mientras se muestra la vista previa (solo lectura) de mañana. */
|
||||
protected readonly viewingTomorrow = signal(false);
|
||||
|
||||
private childId!: number;
|
||||
private coinSeq = 0;
|
||||
@@ -69,14 +71,18 @@ export class HomeComponent implements OnInit {
|
||||
return t ? [...t.morning, ...t.afternoon] : [];
|
||||
});
|
||||
|
||||
/** Fecha de hoy formateada en el idioma activo. */
|
||||
/** Fecha mostrada (hoy o mañana, según la vista) formateada en el idioma activo. */
|
||||
protected readonly dateLabel = computed(() => {
|
||||
const locale = this.i18n.lang() === 'CA' ? 'ca-ES' : 'es-ES';
|
||||
const date = new Date();
|
||||
if (this.viewingTomorrow()) {
|
||||
date.setDate(date.getDate() + 1);
|
||||
}
|
||||
return new Intl.DateTimeFormat(locale, {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
}).format(new Date());
|
||||
}).format(date);
|
||||
});
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -107,8 +113,25 @@ export class HomeComponent implements OnInit {
|
||||
this.api.updateSettings(this.childId, { viewMode: mode }).subscribe();
|
||||
}
|
||||
|
||||
/** Alterna entre hoy y la vista previa (solo lectura) de mañana. */
|
||||
toggleDay(): void {
|
||||
if (this.viewingTomorrow()) {
|
||||
this.viewingTomorrow.set(false);
|
||||
this.load();
|
||||
return;
|
||||
}
|
||||
this.api.getTomorrow(this.childId).subscribe((data) => {
|
||||
this.today.set(data);
|
||||
this.viewingTomorrow.set(true);
|
||||
});
|
||||
}
|
||||
|
||||
/** Marca/desmarca una tarea y aplica el feedback (monedas, sonido, celebración). */
|
||||
onToggle(ev: ToggleEvent): void {
|
||||
// La vista de mañana es solo lectura: mirar, no marcar.
|
||||
if (this.viewingTomorrow()) {
|
||||
return;
|
||||
}
|
||||
this.api.toggleTask(ev.taskId).subscribe((result) => {
|
||||
const current = this.today();
|
||||
if (!current) {
|
||||
|
||||
Reference in New Issue
Block a user