A1. Notifiche per-pasto (PRD §6.3)
- NotificationSettings.mealReminders (default off)
- 3 ScheduledTask aggiuntivi: 13:00 pranzo, 16:00 spuntino, 19:00 cena
Europe/Rome
- runMealReminderJob: skip se utente in pausa, altrimenti push "Ora di
pranzo: vedi cosa c'è nel piano di oggi" → /plan
- Toggle in /profile sezione notifiche
A2. Auto-start digiuno a fine cena (PRD §6.3)
- POST /me/meal-plans/slots/:id/consumed: se è il "pasto finale" del
giorno (CENA o ultimo dello schedule IF) e l'utente ha preferences.
fastingProtocol senza FastEvent IN_PROGRESS → crea automaticamente
un nuovo digiuno col protocollo preferito
- isMealLastOfDay helper (rank-based: COL=0, PRA=1, SPU=2, CENA=3)
- Risposta include autoStartedFast: { id } | null
A3. Riassunto piano nel done step onboarding (PRD §6.1.9)
- StepDone fetcha il profilo dopo regenerate
- Card 4-colonne con Fase corrente + durata, BMR, TDEE, aderenza target
- i18n: phaseLabel + phaseDuration
A4. "% del percorso" in /tracking (PRD §6.4.5)
- WeightHistory accetta startKg, calcola (start-current)/(start-goal)
clampato 0-100
- 4° card nel summary: "Del percorso · 65%"
A5. Lista spesa: spuntati in fondo + totale rimanente (PRD §6.5.4-5)
- ShoppingChecklist sort items con consumed=true in fondo,
alfabetico tra non-spuntati
- "Restano €X" mostrato accanto al "n di total" — solo non-spuntati
i18n: namespace Notifications, Onboarding, Tracking, Shopping estesi.
81/81 unit test verdi. Lint, typecheck verdi.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
28 lines
862 B
TypeScript
28 lines
862 B
TypeScript
// Side-effect import: initializes Sentry before any other module is evaluated.
|
|
// Must stay first — moving it later means errors thrown during early boot
|
|
// (env validation, Prisma adapter, etc.) wouldn't reach Sentry.
|
|
import './lib/sentry.js';
|
|
import { buildApp } from './app.js';
|
|
import { env } from './config/env.js';
|
|
import { startNotificationScheduler } from './modules/notifications/scheduler.js';
|
|
|
|
async function start(): Promise<void> {
|
|
const app = await buildApp();
|
|
|
|
const cronHandles = startNotificationScheduler(app.prisma, app.log);
|
|
app.addHook('onClose', async () => {
|
|
cronHandles?.weekly.stop();
|
|
cronHandles?.fasting.stop();
|
|
cronHandles?.meals.forEach((m) => m.stop());
|
|
});
|
|
|
|
try {
|
|
await app.listen({ port: env.PORT, host: env.HOST });
|
|
} catch (err) {
|
|
app.log.error(err);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
void start();
|