diff --git a/frontend/src/app/core/i18n.service.ts b/frontend/src/app/core/i18n.service.ts
index c29a8c8..f272eea 100644
--- a/frontend/src/app/core/i18n.service.ts
+++ b/frontend/src/app/core/i18n.service.ts
@@ -21,6 +21,9 @@ export class I18nService {
afternoon: { ES: 'TARDE', CA: 'TARDA' },
ready: { ES: 'listo', CA: 'fet' },
noSchool: { ES: 'HOY NO HAY COLE', CA: 'AVUI NO HI HA COLE' },
+ freeAfternoon: { ES: 'TARDE LIBRE', CA: 'TARDA LLIURE' },
+ emptyDayTitle: { ES: '¡Hoy no hay tareas!', CA: 'Avui no hi ha tasques!' },
+ emptyDaySub: { ES: 'Disfruta del día', CA: 'Gaudeix del dia' },
allDone: { ES: '¡TODO LISTO!', CA: 'TOT FET!' },
great: { ES: '¡GENIAL!', CA: 'GENIAL!' },
done: { ES: '¡HECHO!', CA: 'FET!' },
diff --git a/frontend/src/app/core/sound.service.ts b/frontend/src/app/core/sound.service.ts
index 76a1576..5e33e96 100644
--- a/frontend/src/app/core/sound.service.ts
+++ b/frontend/src/app/core/sound.service.ts
@@ -1,8 +1,10 @@
import { Injectable } from '@angular/core';
/**
- * Sonido de recompensa sintetizado con WebAudio (sin ficheros de audio). Un par
- * de notas alegres al ganar monedas. Silencioso si WebAudio no está disponible.
+ * Feedback de recompensa: sonido sintetizado con WebAudio (sin ficheros de
+ * audio) y vibración háptica sutil en dispositivos que la soportan (tablets).
+ * Ambos se disparan sobre el MISMO evento (causalidad) para que se perciban
+ * como una sola respuesta. Silencioso/inerte si el navegador no lo soporta.
*/
@Injectable({ providedIn: 'root' })
export class SoundService {
@@ -47,4 +49,11 @@ export class SoundService {
osc.stop(start + 0.28);
});
}
+
+ /** Vibración corta al ganar monedas (no-op si el dispositivo no vibra). */
+ buzz(): void {
+ if (typeof navigator !== 'undefined' && 'vibrate' in navigator) {
+ navigator.vibrate(15);
+ }
+ }
}
diff --git a/frontend/src/app/features/home/board-view.component.ts b/frontend/src/app/features/home/board-view.component.ts
index e2020fd..82a95d3 100644
--- a/frontend/src/app/features/home/board-view.component.ts
+++ b/frontend/src/app/features/home/board-view.component.ts
@@ -22,8 +22,12 @@ import { TaskCardComponent, ToggleEvent } from './task-card.component';
🌙 {{ i18n.t('afternoon') }}
- @for (task of afternoon; track task.id) {
-
+ @if (afternoon.length) {
+ @for (task of afternoon; track task.id) {
+
+ }
+ } @else {
+ 🌈 {{ i18n.t('freeAfternoon') }}
}
diff --git a/frontend/src/app/features/home/home.component.html b/frontend/src/app/features/home/home.component.html
index 00b9326..f2cbb50 100644
--- a/frontend/src/app/features/home/home.component.html
+++ b/frontend/src/app/features/home/home.component.html
@@ -17,32 +17,41 @@
-
-
-
-
-
-
-
-
+ @if (t.progress.total > 0) {
+
+
+
+
+
-
- @if (mode() === 'BOARD') {
-
+
+
+
+
+ @if (mode() === 'BOARD') {
+
+ } @else {
+
+ }
} @else {
-
+
+
+
{{ t.child.mascot }}
+
{{ i18n.t('emptyDayTitle') }}
+
{{ i18n.t('emptyDaySub') }} 🎉
+
}
diff --git a/frontend/src/app/features/home/home.component.scss b/frontend/src/app/features/home/home.component.scss
index c4bc3be..84f636a 100644
--- a/frontend/src/app/features/home/home.component.scss
+++ b/frontend/src/app/features/home/home.component.scss
@@ -89,6 +89,38 @@
}
}
+// Día sin tareas: tarjeta amable con la mascota, nunca una pantalla vacía.
+.freeday {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-6) var(--space-4);
+ background: var(--surface);
+ border-radius: var(--radius-card);
+ box-shadow: var(--shadow-card);
+ text-align: center;
+ animation: slideUp 0.4s ease both;
+
+ &__mascot {
+ font-size: 96px;
+ line-height: 1;
+ animation: floatY 3s ease-in-out infinite;
+ }
+ &__title {
+ margin: var(--space-3) 0 0;
+ font-family: var(--font-display);
+ font-weight: 700;
+ font-size: 1.8rem;
+ color: var(--text-strong);
+ }
+ &__sub {
+ margin: 0;
+ font-size: 1.2rem;
+ color: var(--text-1);
+ }
+}
+
// Moneda voladora: parte de la tarea y viaja al monedero.
.flycoin {
position: fixed;
diff --git a/frontend/src/app/features/home/home.component.ts b/frontend/src/app/features/home/home.component.ts
index 4615ccd..5fc06a6 100644
--- a/frontend/src/app/features/home/home.component.ts
+++ b/frontend/src/app/features/home/home.component.ts
@@ -127,12 +127,14 @@ export class HomeComponent implements OnInit {
};
this.today.set(updated);
- // Feedback positivo solo al ganar monedas (marcar, no desmarcar).
+ // Feedback positivo solo al ganar monedas (marcar, no desmarcar). Sonido y
+ // vibración se disparan juntos sobre el mismo evento (armonía multimodal).
if (result.coinsEarned > 0) {
this.flyCoins(ev.x, ev.y, result.coinsEarned);
this.wallet?.bump();
if (current.child.soundEnabled) {
this.sound.playReward();
+ this.sound.buzz();
}
}