commit 7fd03a99ba692503f2dfc2c7fa293659ed38bc7e Author: emanuele Date: Thu Apr 16 09:44:35 2026 +0200 chore: initial project setup with documentation and design assets Add project foundation: CLAUDE.md, requirements tracking system, technical architecture docs, Firestore setup guide, device testing guide, and Stitch design mockups for Precision Vitality app. Co-Authored-By: Claude Opus 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..708ffb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Dependencies +node_modules/ +.npm + +# Build +www/ +dist/ + +# Capacitor native projects +android/ +ios/ + +# Environment files (contain secrets) +src/environments/environment.ts +src/environments/environment.prod.ts + +# Firebase +.firebase/ +google-services.json +GoogleService-Info.plist +functions/node_modules/ +functions/lib/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Keystore (signing keys) +*.keystore +*.jks + +# Misc +*.log +npm-debug.log* diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1d7f087 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,96 @@ +# CalorieTracker (Precision Vitality) — CLAUDE.md + +## Progetto +App mobile per il tracciamento calorico giornaliero con stima AI dei pasti. +Nome UI: **Precision Vitality** | Nome progetto: **CalorieTracker** + +## Stack Tecnologico +- **Frontend**: Ionic 7+ / Angular 17+ / TypeScript +- **Capacitor**: 5+ (Android + iOS) +- **Database**: Firebase Firestore (Spark plan gratuito) +- **Auth**: Firebase Anonymous Auth (upgrade Google Sign-In futuro) +- **AI**: Anthropic Claude API (claude-haiku-4-5-20251001) via proxy sicuro +- **Chart**: Chart.js + ng2-charts +- **Storage locale**: @ionic/storage-angular + +## Repository +- **Remote**: git.i-a.run (Gitea) — `emanuele/traccia-calorica` +- **Branch principale**: `main` +- **Lingua codice**: inglese (variabili, commenti tecnici) +- **Lingua UI**: italiano + +## Design System — "Precision Vitality" +- Riferimento design: `stitch_calorietracker_design_brief_brief/` +- **Font**: Lexend (dati/numeri) + Plus Jakarta Sans (UI) +- **Colori primari**: Verde `#006b1b`, Accent arancione `#FF7043`, Errore `#F44336` +- **Background**: `#f6f6f6` (surface), `#f0f1f1` (container-low), `#ffffff` (cards) +- **Testo**: `#2d2f2f` (primario), `on-surface-variant` (secondario) +- **Regola NO-LINE**: niente bordi 1px — separazione solo tramite tonal shifts +- **Elevazione**: tonal layering, no drop shadows su card statiche +- **Icone**: Material Icons (Outlined/Two-Tone, mai Filled pesanti) + +## Struttura Tab (da design) +1. **JOURNAL** (Home) — diario pasti + FAB arancione +2. **STATS** — statistiche giornaliere/settimanali +3. **PLANS** — (futuro) piani alimentari +4. **PROFILE** — (futuro) profilo utente + +## Struttura Cartelle +``` +src/ + app/ + core/ # Servizi singleton, guards, interceptors + services/ + meal.service.ts + calorie-estimator.service.ts + auth.service.ts + shared/ # Componenti, pipe, direttive condivise + features/ + home/ # Tab Journal/Home + modals/ # Manual input + LLM input modals + stats/ # Tab Statistiche (daily + weekly) + search/ # Ricerca pasti + profile/ # Tab Profile (futuro) + assets/ + data/ + food-dictionary.md + environments/ + environment.ts + environment.prod.ts +``` + +## Convenzioni +- Lazy loading per ogni feature module +- Servizi in `core/services/` — singleton con `providedIn: 'root'` +- Componenti condivisi in `shared/` +- Ogni modal è un componente standalone +- Commit messages in inglese, descrittivi +- Branch naming: `feature/REQ-XXX-descrizione`, `fix/REQ-XXX-descrizione` + +## Documenti di Progetto +- `docs/REQUIREMENTS.md` — requisiti funzionali completi +- `docs/TRACKING.md` — tracciamento stato requisiti sessione per sessione +- `docs/TECHNICAL.md` — architettura e decisioni tecniche +- `docs/FIRESTORE_SETUP.md` — guida configurazione Firebase/Firestore +- `docs/DEVICE_TESTING.md` — test su device e build APK senza Android Studio + +## Decisioni Architetturali +- **Proxy API Claude**: Firebase Cloud Function (free tier) — API key mai nel bundle +- **Cancellazione pasti**: hard delete (semplice, niente soft delete per ora) +- **Obiettivo calorico**: salvato in @ionic/storage locale (default 2000 kcal) +- **Ricerca**: client-side su ultimi 30 giorni + +## Comandi Utili +```bash +ionic serve # Dev server +ionic build # Build produzione +npx cap sync # Sync Capacitor +npx cap open android # Apri in Android Studio +``` + +## Note per Claude +- Leggere sempre il design in `stitch_calorietracker_design_brief_brief/` prima di implementare UI +- Seguire il DESIGN.md (Precision Vitality) per colori, tipografia e componenti +- Aggiornare `docs/TRACKING.md` dopo ogni sessione di lavoro +- Non esporre mai API keys nel codice client +- Verificare `docs/TRACKING.md` per sapere cosa è stato completato nelle sessioni precedenti diff --git a/docs/DEVICE_TESTING.md b/docs/DEVICE_TESTING.md new file mode 100644 index 0000000..89b6deb --- /dev/null +++ b/docs/DEVICE_TESTING.md @@ -0,0 +1,416 @@ +# Test su Device e Creazione APK senza Android Studio + +> Guida per testare l'app su dispositivo reale e generare APK usando solo CLI (senza Android Studio). +> Ultimo aggiornamento: 16/04/2026 + +--- + +## 1. Prerequisiti + +### Software Necessario +| Software | Versione | Download | Note | +|----------|----------|----------|------| +| Node.js | 18+ | nodejs.org | Già installato | +| Java JDK | 17+ | adoptium.net | Per Gradle | +| Android SDK (cmdline-tools) | Latest | developer.android.com | Solo CLI, no IDE | +| Gradle | 8+ | Incluso nel progetto | Via wrapper | + +### Installare Android SDK Command Line Tools (senza Android Studio) + +#### Windows +```bash +# 1. Scaricare command line tools da: +# https://developer.android.com/studio#command-line-tools-only + +# 2. Creare struttura directory +mkdir -p C:/Android/cmdline-tools/latest + +# 3. Estrarre lo zip in C:/Android/cmdline-tools/latest/ +# (i file bin/, lib/ etc. devono essere DENTRO latest/) + +# 4. Configurare variabili d'ambiente (aggiungere al profilo bash o System env) +export ANDROID_HOME=C:/Android +export ANDROID_SDK_ROOT=C:/Android +export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin +export PATH=$PATH:$ANDROID_HOME/platform-tools +``` + +#### Installare componenti SDK +```bash +# Accettare licenze +sdkmanager --licenses + +# Installare componenti necessari +sdkmanager "platform-tools" +sdkmanager "platforms;android-34" +sdkmanager "build-tools;34.0.0" + +# Verificare installazione +sdkmanager --list | head -20 +``` + +### Configurare JAVA_HOME +```bash +# Windows — verificare path Java +# Il JDK 17+ deve essere installato +export JAVA_HOME="C:/Program Files/Eclipse Adoptium/jdk-17.x.x-hotspot" +# oppure +export JAVA_HOME="C:/Program Files/Java/jdk-17" + +# Verificare +java -version +javac -version +``` + +--- + +## 2. Preparazione Progetto per Android + +### Build dell'app Ionic +```bash +# Build di produzione +ionic build --prod + +# Oppure build di sviluppo (più veloce, con source maps) +ionic build +``` + +### Aggiungere piattaforma Android (prima volta) +```bash +npx cap add android +``` + +### Sync dopo ogni build +```bash +npx cap sync android +``` + +### Copiare solo i file web (senza sync dipendenze native) +```bash +npx cap copy android +``` + +--- + +## 3. Build APK da Riga di Comando + +### APK di Debug (per test) +```bash +# Navigare nella directory android +cd android + +# Build con Gradle wrapper (non serve Gradle installato globalmente) +./gradlew assembleDebug + +# L'APK si trova in: +# android/app/build/outputs/apk/debug/app-debug.apk +``` + +### APK di Release (per distribuzione) +```bash +# 1. Generare keystore (solo la prima volta) +keytool -genkey -v \ + -keystore calorie-tracker-release.keystore \ + -alias calorie-tracker \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 + +# 2. Configurare signing in android/app/build.gradle +# Aggiungere nella sezione android {}: +``` + +```groovy +// In android/app/build.gradle +android { + // ... configurazione esistente ... + + signingConfigs { + release { + storeFile file('calorie-tracker-release.keystore') + storePassword 'LA_TUA_PASSWORD' + keyAlias 'calorie-tracker' + keyPassword 'LA_TUA_PASSWORD' + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} +``` + +```bash +# 3. Build release APK +cd android +./gradlew assembleRelease + +# L'APK si trova in: +# android/app/build/outputs/apk/release/app-release.apk +``` + +### AAB (Android App Bundle) per Google Play +```bash +cd android +./gradlew bundleRelease + +# L'AAB si trova in: +# android/app/build/outputs/bundle/release/app-release.aab +``` + +--- + +## 4. Installare APK su Device + +### Via USB (ADB) +```bash +# 1. Abilitare "Opzioni sviluppatore" sul telefono: +# Impostazioni → Info telefono → Tocca 7 volte "Numero build" + +# 2. Abilitare "Debug USB" nelle opzioni sviluppatore + +# 3. Collegare il telefono via USB + +# 4. Verificare che il device sia rilevato +adb devices +# Deve mostrare il device con stato "device" + +# 5. Installare l'APK +adb install android/app/build/outputs/apk/debug/app-debug.apk + +# Per reinstallare (sovrascrivere) +adb install -r android/app/build/outputs/apk/debug/app-debug.apk +``` + +### Via WiFi (ADB wireless) +```bash +# 1. Collegare prima via USB +adb devices + +# 2. Abilitare TCP/IP +adb tcpip 5555 + +# 3. Trovare IP del telefono (Impostazioni → WiFi → IP) +adb connect 192.168.1.XXX:5555 + +# 4. Scollegare USB — ora si può installare via WiFi +adb install android/app/build/outputs/apk/debug/app-debug.apk +``` + +### Senza ADB — Trasferimento diretto +1. Copiare l'APK sul telefono (via USB, email, cloud, etc.) +2. Sul telefono: abilitare "Installa da origini sconosciute" +3. Aprire il file APK → Installa + +--- + +## 5. Test in Tempo Reale (Live Reload) + +### Live Reload su Device via WiFi +Il modo più veloce per testare durante lo sviluppo: + +```bash +# 1. Trovare l'IP della macchina di sviluppo +# Windows: +ipconfig | grep "IPv4" +# Es: 192.168.1.100 + +# 2. Avviare Ionic con external host +ionic serve --external --host=0.0.0.0 + +# 3. Modificare capacitor.config.ts per il live reload +``` + +```typescript +// capacitor.config.ts — SOLO PER SVILUPPO +const config: CapacitorConfig = { + appId: 'com.precisionvitality.app', + appName: 'Precision Vitality', + webDir: 'www', + server: { + // SOLO per live reload development! + url: 'http://192.168.1.100:8100', // IP della tua macchina + cleartext: true + } +}; +``` + +```bash +# 4. Sync e reinstallare +npx cap sync android +cd android && ./gradlew assembleDebug && cd .. +adb install -r android/app/build/outputs/apk/debug/app-debug.apk + +# 5. Aprire l'app sul device — le modifiche al codice si rifletteranno in real-time! +``` + +> **IMPORTANTE**: Rimuovere la sezione `server.url` da `capacitor.config.ts` prima del build di produzione! + +--- + +## 6. Debug Remoto + +### Chrome DevTools (per WebView) +1. Collegare device via USB con debug abilitato +2. Aprire Chrome sul PC → `chrome://inspect` +3. Il WebView dell'app apparirà nella lista +4. Cliccare "inspect" → si apre DevTools con console, network, elements + +### Logcat (log nativi Android) +```bash +# Tutti i log +adb logcat + +# Filtrare per l'app +adb logcat | grep -i "capacitor\|precision" + +# Solo errori +adb logcat *:E + +# Pulire e seguire +adb logcat -c && adb logcat +``` + +--- + +## 7. Script Automazione + +### Script build + install completo +Creare `scripts/build-and-install.sh`: + +```bash +#!/bin/bash +set -e + +echo "=== Building Ionic app ===" +ionic build + +echo "=== Syncing Capacitor ===" +npx cap sync android + +echo "=== Building APK debug ===" +cd android +./gradlew assembleDebug +cd .. + +APK_PATH="android/app/build/outputs/apk/debug/app-debug.apk" + +echo "=== Installing on device ===" +adb install -r "$APK_PATH" + +echo "=== Done! APK: $APK_PATH ===" +echo "=== App launched ===" +adb shell am start -n com.precisionvitality.app/.MainActivity +``` + +### Script build release +Creare `scripts/build-release.sh`: + +```bash +#!/bin/bash +set -e + +echo "=== Building Ionic production ===" +ionic build --prod + +echo "=== Syncing Capacitor ===" +npx cap sync android + +echo "=== Building Release APK ===" +cd android +./gradlew assembleRelease +cd .. + +APK_PATH="android/app/build/outputs/apk/release/app-release.apk" +echo "=== Release APK: $APK_PATH ===" +ls -lh "$APK_PATH" +``` + +--- + +## 8. Troubleshooting + +### Errore: "ANDROID_HOME not set" +```bash +# Verificare che le variabili siano settate +echo $ANDROID_HOME +echo $ANDROID_SDK_ROOT + +# Se vuote, settarle nel profilo (~/.bashrc o ~/.bash_profile) +export ANDROID_HOME=C:/Android +export ANDROID_SDK_ROOT=C:/Android +``` + +### Errore: "SDK location not found" +```bash +# Creare/verificare android/local.properties +echo "sdk.dir=C:\\\\Android" > android/local.properties +``` + +### Errore: "No connected devices" +```bash +# Verificare driver USB (Windows) +# Installare Google USB Driver o driver OEM del produttore + +# Verificare che il device sia in modalità debug +adb kill-server +adb start-server +adb devices +``` + +### Errore: "INSTALL_FAILED_UPDATE_INCOMPATIBLE" +```bash +# Disinstallare la versione precedente +adb uninstall com.precisionvitality.app +# Poi reinstallare +adb install android/app/build/outputs/apk/debug/app-debug.apk +``` + +### Build lento +```bash +# Aggiungere a android/gradle.properties: +org.gradle.jvmargs=-Xmx4096m +org.gradle.daemon=true +org.gradle.parallel=true +org.gradle.caching=true +``` + +--- + +## 9. Checklist Test su Device + +### Prima del test +- [ ] Node.js e Ionic CLI installati +- [ ] Android SDK command line tools installati +- [ ] Java JDK 17+ installato +- [ ] ANDROID_HOME e JAVA_HOME configurati +- [ ] Device in modalità debug USB + +### Test funzionali +- [ ] App si avvia correttamente +- [ ] Navigazione tra tab funzionante +- [ ] FAB visibile e cliccabile +- [ ] Modal input numerico funzionante +- [ ] Modal input LLM funzionante +- [ ] Tastiera nativa si apre correttamente +- [ ] Scroll liste fluido +- [ ] Grafico statistiche visibile +- [ ] Ricerca funzionante +- [ ] Connessione a Firestore (online) +- [ ] Funzionamento offline (attivare modalità aereo) + +### Test performance +- [ ] Tempo avvio app < 3 secondi +- [ ] Scroll fluido (60fps) +- [ ] Risposta AI < 5 secondi +- [ ] Nessun crash in 10 minuti di utilizzo + +### Test compatibilità +- [ ] Android 10+ (API 29+) +- [ ] Schermo piccolo (5") e grande (6.5"+) +- [ ] Orientamento portrait +- [ ] Tastiera non copre input diff --git a/docs/FIRESTORE_SETUP.md b/docs/FIRESTORE_SETUP.md new file mode 100644 index 0000000..e3767b6 --- /dev/null +++ b/docs/FIRESTORE_SETUP.md @@ -0,0 +1,349 @@ +# Guida Configurazione Firebase & Firestore + +> Guida passo-passo per configurare Firebase, Firestore e Cloud Functions per CalorieTracker. +> Ultimo aggiornamento: 16/04/2026 + +--- + +## 1. Creazione Progetto Firebase + +### Passo 1 — Console Firebase +1. Andare su [console.firebase.google.com](https://console.firebase.google.com) +2. Cliccare "Aggiungi progetto" +3. Nome progetto: `calorie-tracker` (o simile disponibile) +4. **Disabilitare** Google Analytics (non necessario per ora) +5. Cliccare "Crea progetto" + +### Passo 2 — Aggiungere App Web +1. Nella dashboard progetto, cliccare l'icona **Web** (``) +2. Nome app: `CalorieTracker Web` +3. **Non** abilitare Firebase Hosting (non necessario) +4. Copiare la configurazione Firebase: +```javascript +const firebaseConfig = { + apiKey: "AIza...", + authDomain: "calorie-tracker-XXXXX.firebaseapp.com", + projectId: "calorie-tracker-XXXXX", + storageBucket: "calorie-tracker-XXXXX.appspot.com", + messagingSenderId: "123456789", + appId: "1:123456789:web:abcdef" +}; +``` +5. Salvare questi valori in `environment.ts` e `environment.prod.ts` + +--- + +## 2. Abilitare Firestore + +### Passo 1 — Creare Database +1. Nel menu laterale Firebase: **Build → Firestore Database** +2. Cliccare "Crea database" +3. Selezionare località: **europe-west1 (Belgio)** — più vicino all'Italia +4. Selezionare "Inizia in modalità produzione" +5. Cliccare "Abilita" + +### Passo 2 — Regole di Sicurezza +Nella tab **Regole** di Firestore, incollare: + +``` +rules_version = '2'; + +service cloud.firestore { + match /databases/{database}/documents { + + // Nessun accesso di default + match /{document=**} { + allow read, write: if false; + } + + // Accesso pasti: solo utente proprietario + match /users/{userId}/meals/{mealId} { + allow read, write: if request.auth != null + && request.auth.uid == userId; + + // Validazione scrittura + allow create: if request.auth != null + && request.auth.uid == userId + && request.resource.data.calories is number + && request.resource.data.calories > 0 + && request.resource.data.calories <= 9999 + && request.resource.data.inputType in ['manual', 'llm'] + && request.resource.data.timestamp is timestamp + && request.resource.data.createdAt is timestamp; + + allow update: if request.auth != null + && request.auth.uid == userId; + + allow delete: if request.auth != null + && request.auth.uid == userId; + } + } +} +``` + +### Passo 3 — Indici +Nella tab **Indici** di Firestore, creare indice composto: + +| Collection | Campi | Query Scope | +|------------|-------|-------------| +| `users/{userId}/meals` | `timestamp` DESC | Collection | + +> **Nota**: Firestore creerà automaticamente gli indici necessari quando l'app esegue le prime query. In alternativa, il file `firestore.indexes.json` nel progetto definisce gli indici programmaticamente. + +--- + +## 3. Abilitare Authentication + +### Passo 1 — Anonymous Auth +1. Nel menu laterale: **Build → Authentication** +2. Cliccare "Inizia" +3. Tab **Metodo di accesso** +4. Abilitare **Anonimo** → switch ON → Salva + +### Passo 2 — (Futuro) Google Sign-In +> Per upgrade futuro da Anonymous a Google Sign-In: +> 1. Abilitare provider "Google" in Authentication +> 2. Configurare OAuth consent screen in Google Cloud Console +> 3. Implementare linking account (da anonimo a Google) + +--- + +## 4. Configurazione App Android + +### Passo 1 — Aggiungere App Android +1. Dashboard Firebase → "Aggiungi app" → icona Android +2. Package name: `com.precisionvitality.app` (deve corrispondere a `capacitor.config.ts`) +3. App nickname: `CalorieTracker Android` +4. SHA-1: (opzionale per ora, necessario per Google Sign-In) + +### Passo 2 — Scaricare google-services.json +1. Scaricare `google-services.json` +2. Copiare in `android/app/google-services.json` (dopo `npx cap add android`) + +--- + +## 5. Configurazione App iOS + +### Passo 1 — Aggiungere App iOS +1. Dashboard Firebase → "Aggiungi app" → icona iOS +2. Bundle ID: `com.precisionvitality.app` +3. App nickname: `CalorieTracker iOS` + +### Passo 2 — Scaricare GoogleService-Info.plist +1. Scaricare `GoogleService-Info.plist` +2. Copiare in `ios/App/App/GoogleService-Info.plist` (dopo `npx cap add ios`) + +--- + +## 6. Setup Firebase Cloud Functions (Proxy AI) + +### Passo 1 — Upgrade a Blaze Plan (Pay-as-you-go) +> **ATTENZIONE**: Le Cloud Functions richiedono il piano Blaze (pay-as-you-go). +> Il piano Blaze include comunque il tier gratuito di Firestore e Auth. +> Non ci saranno costi se il traffico resta nel free tier. + +1. Console Firebase → icona ingranaggio → Utilizzo e fatturazione +2. Upgrade a Blaze +3. Impostare budget alert: $5/mese + +### Alternativa gratuita: Se non si vuole il piano Blaze +- Usare un backend NestJS su VPS esistente come proxy +- Oppure usare Cloudflare Workers (free tier: 100k req/giorno) + +### Passo 2 — Inizializzare Cloud Functions +```bash +# Nella root del progetto +npm install -g firebase-tools +firebase login +firebase init functions + +# Selezionare: +# - Linguaggio: TypeScript +# - ESLint: Sì +# - Installare dipendenze: Sì +``` + +### Passo 3 — Configurare Secret per API Key +```bash +# Salvare API key come secret (non in codice!) +firebase functions:secrets:set ANTHROPIC_API_KEY +# Inserire: sk-ant-... +``` + +### Passo 4 — Implementare la Function +File `functions/src/index.ts`: + +```typescript +import { onRequest } from 'firebase-functions/v2/https'; +import { defineSecret } from 'firebase-functions/params'; +import Anthropic from '@anthropic-ai/sdk'; + +const anthropicKey = defineSecret('ANTHROPIC_API_KEY'); + +export const estimateCalories = onRequest( + { + cors: true, + region: 'europe-west1', + secrets: [anthropicKey], + timeoutSeconds: 15, + memory: '256MiB', + }, + async (req, res) => { + // Verificare metodo POST + if (req.method !== 'POST') { + res.status(405).send('Method Not Allowed'); + return; + } + + // Verificare auth token + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith('Bearer ')) { + res.status(401).send('Unauthorized'); + return; + } + + // Estrarre e validare input + const { description } = req.body; + if (!description || typeof description !== 'string' || description.length > 500) { + res.status(400).send('Invalid description'); + return; + } + + try { + const client = new Anthropic({ apiKey: anthropicKey.value() }); + + const message = await client.messages.create({ + model: 'claude-haiku-4-5-20251001', + max_tokens: 256, + system: `Sei un nutrizionista. Stima le calorie del pasto descritto. + Rispondi SOLO con JSON: {"calories": number, "confidence": "high"|"medium"|"low", "explanation": "string"}`, + messages: [{ role: 'user', content: description }], + }); + + // Parsare risposta + const text = message.content[0].type === 'text' ? message.content[0].text : ''; + const jsonStr = text.replace(/```json?\n?/g, '').replace(/```/g, '').trim(); + const result = JSON.parse(jsonStr); + + res.json(result); + } catch (error) { + console.error('Estimation error:', error); + res.status(500).json({ error: 'Estimation failed' }); + } + } +); +``` + +### Passo 5 — Deploy +```bash +cd functions +npm install @anthropic-ai/sdk +firebase deploy --only functions +``` + +--- + +## 7. Integrazione Angular + +### Installare dipendenze +```bash +npm install @angular/fire firebase +``` + +### Configurare in app.config.ts (Angular 17+ standalone) +```typescript +import { provideFirebaseApp, initializeApp } from '@angular/fire/app'; +import { provideFirestore, getFirestore, enableIndexedDbPersistence } from '@angular/fire/firestore'; +import { provideAuth, getAuth } from '@angular/fire/auth'; +import { environment } from '../environments/environment'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideFirebaseApp(() => initializeApp(environment.firebase)), + provideFirestore(() => { + const firestore = getFirestore(); + enableIndexedDbPersistence(firestore); + return firestore; + }), + provideAuth(() => getAuth()), + // ... altri providers + ] +}; +``` + +--- + +## 8. File di Configurazione Locale + +### `firestore.rules` (nella root del progetto) +Copiare le regole dal Passo 2 della Sezione 2. + +### `firestore.indexes.json` +```json +{ + "indexes": [ + { + "collectionGroup": "meals", + "queryScope": "COLLECTION", + "fields": [ + { "fieldPath": "timestamp", "order": "DESCENDING" } + ] + } + ], + "fieldOverrides": [] +} +``` + +### `.firebaserc` +```json +{ + "projects": { + "default": "calorie-tracker-XXXXX" + } +} +``` + +### `firebase.json` +```json +{ + "firestore": { + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "functions": { + "source": "functions", + "runtime": "nodejs18" + } +} +``` + +--- + +## 9. Limiti Free Tier Firebase (Spark / Blaze) + +| Risorsa | Limite Gratuito | Note | +|---------|----------------|------| +| Firestore letture | 50.000/giorno | ~1.600 utenti × 30 letture/giorno | +| Firestore scritture | 20.000/giorno | ~2.000 utenti × 10 pasti/giorno | +| Firestore storage | 1 GiB | Sufficiente per ~5M documenti pasto | +| Auth utenti | Illimitati | Anonymous + email + social | +| Cloud Functions invocazioni | 2M/mese | ~66.000/giorno | +| Cloud Functions compute | 400.000 GB-secondi/mese | Sufficiente per proxy AI | + +--- + +## 10. Checklist Configurazione + +- [ ] Progetto Firebase creato +- [ ] Firestore abilitato (europe-west1) +- [ ] Regole sicurezza configurate +- [ ] Anonymous Auth abilitato +- [ ] App Web registrata + config copiata +- [ ] App Android registrata + google-services.json scaricato +- [ ] (Opzionale) App iOS registrata + GoogleService-Info.plist scaricato +- [ ] Cloud Functions inizializzate +- [ ] ANTHROPIC_API_KEY salvata come secret +- [ ] Cloud Function deployata +- [ ] @angular/fire installato e configurato +- [ ] Test lettura/scrittura funzionante +- [ ] Offline persistence verificata diff --git a/docs/REQUIREMENTS.md b/docs/REQUIREMENTS.md new file mode 100644 index 0000000..16a65a6 --- /dev/null +++ b/docs/REQUIREMENTS.md @@ -0,0 +1,312 @@ +# Requisiti — CalorieTracker (Precision Vitality) + +> Documento di riferimento per tutti i requisiti funzionali e sistemistici del progetto. +> Ultimo aggiornamento: 16/04/2026 + +--- + +## Indice Requisiti + +| ID | Tipo | Priorità | Titolo | Dipendenze | +|----|------|----------|--------|------------| +| REQ-001 | SISTEMISTICO | CRITICA | Scaffolding Ionic + Capacitor | — | +| REQ-002 | SISTEMISTICO | CRITICA | Setup Firebase Firestore | REQ-001 | +| REQ-003 | EVOLUTIVO | ALTA | Home screen + FAB | REQ-002 | +| REQ-004 | EVOLUTIVO | ALTA | Modal input numerico | REQ-003 | +| REQ-005 | EVOLUTIVO | ALTA | Modal input LLM | REQ-003, REQ-006 | +| REQ-006 | EVOLUTIVO | ALTA | Servizio Claude API | REQ-001 | +| REQ-007 | EVOLUTIVO | ALTA | CRUD pasti Firestore | REQ-002 | +| REQ-008 | EVOLUTIVO | MEDIA | Statistiche giornaliere | REQ-007 | +| REQ-009 | EVOLUTIVO | MEDIA | Statistiche settimanali | REQ-007 | +| REQ-010 | EVOLUTIVO | MEDIA | Ricerca pasti | REQ-007 | +| REQ-011 | REWORK | MEDIA | Design brief Google Stitch | — | + +--- + +## REQ-001 — Scaffolding Ionic + Capacitor + +**Tipo**: SISTEMISTICO | **Priorità**: CRITICA + +**Obiettivo**: Scaffolding del progetto Ionic 7+ con Capacitor 5+. Configurazione build nativa Android/iOS. Setup ambiente con struttura cartelle, linting e variabili d'ambiente. + +**Stato Target**: +- Progetto Ionic Angular inizializzato con routing e tab layout (Journal, Stats, Plans, Profile) +- Capacitor configurato per Android e iOS +- Variabili ambiente: `ANTHROPIC_API_KEY`, `FIREBASE_*` +- Struttura moduli: `core/`, `shared/`, `features/home`, `features/stats`, `features/search` +- Proxy sviluppo configurato per CORS Anthropic API + +**Passi Implementativi**: +1. `ionic start calorie-tracker tabs --type=angular --capacitor` +2. Aggiungere `@capacitor/android` e `@capacitor/ios` +3. Configurare `capacitor.config.ts` con appId e server URL +4. Creare struttura moduli `features/` con lazy loading +5. Aggiungere `@ionic/storage-angular` per cache locale +6. Configurare `environment.ts` e `environment.prod.ts` +7. Verificare build `ionic build` + `npx cap sync` + +**Criteri Accettazione**: +- [ ] `ionic serve` avvia l'app senza errori +- [ ] `ionic build` produce `www/` senza errori +- [ ] `npx cap sync` completa senza errori +- [ ] Struttura a 4 tab visibile nel browser (Journal, Stats, Plans, Profile) +- [ ] Variabili d'ambiente lette correttamente in `environment.ts` + +--- + +## REQ-002 — Setup Firebase Firestore + +**Tipo**: SISTEMISTICO | **Priorità**: CRITICA | **Dipende da**: REQ-001 + +**Obiettivo**: Setup Firebase con Firestore come database. Schema dati, regole sicurezza e SDK Angular. + +**Schema Firestore**: +``` +users/{userId}/ + meals/{mealId}/ + - id: string + - timestamp: Timestamp + - calories: number + - inputType: 'manual' | 'llm' + - description?: string + - llmConfidence?: 'high' | 'medium' | 'low' + - llmExplanation?: string + - createdAt: Timestamp +``` + +**Regole Sicurezza**: +- Lettura/scrittura solo all'utente autenticato proprietario +- Nessun accesso cross-user + +**Auth**: Anonymous auth abilitato (upgrade Google Sign-In futuro) + +**Passi Implementativi**: +1. Creare Firebase project su console +2. Abilitare Firestore in modalità produzione +3. Abilitare Anonymous Authentication +4. Scaricare `google-services.json` e `GoogleService-Info.plist` +5. Installare `@angular/fire` e configurare in `app.module.ts` +6. Definire regole Firestore in `firestore.rules` +7. Creare `FirestoreService` con CRUD per collection `meals` +8. Testare lettura/scrittura da `ionic serve` + +**Criteri Accettazione**: +- [ ] Scrittura documento su Firestore funzionante +- [ ] Lettura documenti da Firestore funzionante +- [ ] Regole sicurezza bloccano accesso non autenticato +- [ ] `FirestoreService` espone: `addMeal()`, `getMealsByDate()`, `getMealsRange()` +- [ ] Offline persistence abilitata e testata + +--- + +## REQ-003 — Home Screen + FAB + +**Tipo**: EVOLUTIVO | **Priorità**: ALTA | **Dipende da**: REQ-002 + +**Obiettivo**: Home screen (tab Journal) con totale kcal giornaliero, lista pasti e FAB per aggiunta. + +**Specifiche UI** (da design Precision Vitality): +- Header: data odierna centrata (es. "MERCOLEDÌ 16 APRILE") +- Titolo: "Il tuo progresso giornaliero" +- Numero kcal grande (56px, Lexend Bold, verde `#006b1b`) +- Label "calorie oggi" +- Barra progresso: kcal / obiettivo (default 2000) +- Sezione "Diario Alimentare": lista card pasti (ora, descrizione, kcal) +- Badge "AI" (arancione) o "M" (grigio) su ogni pasto +- FAB arancione `#FF7043` in basso a destra con icona "+" +- Empty state: "Nessun pasto registrato oggi. Inizia!" + +**Criteri Accettazione**: +- [ ] Totale kcal odierno visibile e aggiornato real-time +- [ ] Lista pasti del giorno visibile e scrollabile +- [ ] FAB visibile e cliccabile su tutti i device +- [ ] Empty state quando nessun pasto registrato +- [ ] Aggiornamento immediato dopo salvataggio nuovo pasto + +--- + +## REQ-004 — Modal Input Numerico + +**Tipo**: EVOLUTIVO | **Priorità**: ALTA | **Dipende da**: REQ-003 + +**Obiettivo**: Modal per inserimento diretto kcal con tastiera numerica. + +**Specifiche UI** (da design): +- Titolo: "Inserisci calorie" +- Icona fulmine verde +- Motivazionale: "Ogni dato conta per il tuo obiettivo." +- Campo numerico grande (font 40px), placeholder "0", label "KCAL" +- Campo "Descrizione (opzionale)" +- Bottone "Salva" verde a tutta larghezza +- Suggerimenti veloci: 150 (Spuntino), 450 (Pranzo), 600 (Cena) + +**Validazione**: valore > 0 e ≤ 9999 + +**Criteri Accettazione**: +- [ ] Modal si apre dal FAB +- [ ] Tastiera numerica nativa su mobile +- [ ] Validazione blocca valori ≤ 0 e > 9999 +- [ ] Salvataggio corretto su Firestore +- [ ] Toast conferma dopo salvataggio +- [ ] Modal si chiude e Home aggiorna totale + +--- + +## REQ-005 — Modal Input LLM (Descrizione Pasto) + +**Tipo**: EVOLUTIVO | **Priorità**: ALTA | **Dipende da**: REQ-003, REQ-006 + +**Obiettivo**: Modal per descrizione testuale del pasto con stima AI. + +**Specifiche UI** (da design): +- Titolo: "Descrivi il pasto" +- Sottotitolo: "Usa il linguaggio naturale per stimare i tuoi nutrienti in pochi secondi." +- Textarea "IL TUO PASTO" con placeholder +- Bottone "Stima calorie" arancione +- Risultato: badge "ALTA PRECISIONE" / "MEDIA" / "BASSA" +- Kcal stimate in grande + breakdown (carbi, proteine, grassi) +- Campo kcal modificabile +- Bottone "Salva" verde + +**Criteri Accettazione**: +- [ ] Textarea accetta input testuale +- [ ] Bottone "Stima calorie" chiama API (REQ-006) +- [ ] Loading spinner durante chiamata +- [ ] Risultato kcal e badge confidence corretti +- [ ] Campo kcal modificabile dopo stima +- [ ] Salvataggio su Firestore con `inputType: 'llm'` +- [ ] Gestione errore timeout (messaggio + retry) + +--- + +## REQ-006 — Servizio Claude API + +**Tipo**: EVOLUTIVO | **Priorità**: ALTA | **Dipende da**: REQ-001 + +**Obiettivo**: Servizio Angular per stima calorica via Claude API con proxy sicuro. + +**Architettura**: Firebase Cloud Function come proxy (API key mai nel bundle) + +**Specifiche**: +- Model: `claude-haiku-4-5-20251001` +- Max tokens: 256 +- Food dictionary incluso nel system prompt +- Parser JSON robusto (strip markdown code blocks) +- Timeout: 10 secondi +- Risposta: `{ calories, confidence, explanation }` + +**Criteri Accettazione**: +- [ ] Stima kcal corretta per descrizioni test +- [ ] Food dictionary nel system prompt +- [ ] API key non nel bundle app +- [ ] Parser gestisce JSON wrapped in markdown +- [ ] Timeout 10s con errore esplicito + +--- + +## REQ-007 — CRUD Pasti Firestore + +**Tipo**: EVOLUTIVO | **Priorità**: ALTA | **Dipende da**: REQ-002 + +**Obiettivo**: Servizio Angular per CRUD pasti su Firestore. + +**Metodi**: +- `addMeal(data)` — scrittura con timestamp e userId +- `getMealsForDate(date)` — query filtrata per giorno (Observable) +- `getMealsInRange(from, to)` — query per range date +- `deleteMeal(mealId)` — hard delete +- `getTotalCaloriesForDate(date)` — aggregazione client-side + +**Criteri Accettazione**: +- [ ] `addMeal()` scrive documento corretto +- [ ] `getMealsForDate()` restituisce solo pasti del giorno richiesto +- [ ] `getMealsInRange()` restituisce pasti nel range +- [ ] `deleteMeal()` rimuove documento +- [ ] Observable aggiorna componenti senza reload +- [ ] Funziona offline + +--- + +## REQ-008 — Statistiche Giornaliere + +**Tipo**: EVOLUTIVO | **Priorità**: MEDIA | **Dipende da**: REQ-007 + +**Obiettivo**: Vista giornaliera nel tab Stats con totale kcal, breakdown pasti e progresso obiettivo. + +**Specifiche UI**: +- Tab selector: "Oggi" | "Settimana" +- Navigazione tra giorni (frecce) +- Totale kcal, barra progresso (verde/rosso) +- Lista pasti con swipe-to-delete +- Obiettivo calorico: salvato in @ionic/storage (default 2000) + +**Criteri Accettazione**: +- [ ] Totale kcal giornaliero corretto +- [ ] Navigazione tra giorni funzionante +- [ ] Barra progresso aggiornata real-time +- [ ] Colore cambia se obiettivo superato +- [ ] Swipe-to-delete funzionante + +--- + +## REQ-009 — Statistiche Settimanali + +**Tipo**: EVOLUTIVO | **Priorità**: MEDIA | **Dipende da**: REQ-007 + +**Obiettivo**: Grafico a barre 7 giorni con media settimanale. + +**Specifiche UI** (da design): +- Periodo corrente (es. "14-20 apr") con frecce navigazione +- Grafico barre: barre verdi ≤ obiettivo, rosse > obiettivo +- Asse X: LUN, MAR, MER... (giorno corrente evidenziato) +- Legenda: "In target" (verde), "Eccesso" (rosso) +- Card "RIEPILOGO PRESTAZIONI": media kcal/giorno +- Indicatori: variazione % vs settimana precedente, giorni in target + +**Criteri Accettazione**: +- [ ] Grafico a barre con dati reali +- [ ] Colori corretti sopra/sotto obiettivo +- [ ] Media settimanale calcolata +- [ ] Navigazione settimane funzionante +- [ ] Tap su barra apre dettaglio giornaliero +- [ ] Grafico responsive + +--- + +## REQ-010 — Ricerca Pasti + +**Tipo**: EVOLUTIVO | **Priorità**: MEDIA | **Dipende da**: REQ-007 + +**Obiettivo**: Tab ricerca per trovare pasti passati per testo o intervallo date. + +**Specifiche**: +- Searchbar con debounce 300ms +- Filtro date opzionale (da/a) +- Risultati: data, ora, kcal, descrizione, badge tipo +- Funzione "Ri-registra": crea nuovo pasto con stesse kcal e timestamp corrente +- Ordinamento: data DESC +- Range: ultimi 30 giorni (client-side) + +**Criteri Accettazione**: +- [ ] Ricerca per testo funzionante con debounce +- [ ] Filtro date funzionante +- [ ] Badge tipo visibili +- [ ] "Ri-registra" crea nuovo documento +- [ ] Empty state "Nessun risultato" + +--- + +## REQ-011 — Design Brief Google Stitch + +**Tipo**: REWORK | **Priorità**: MEDIA + +**Stato**: COMPLETATO — I mockup sono disponibili in `stitch_calorietracker_design_brief_brief/` + +**Schermate generate**: +1. Home (Journal) — `home_calorietracker/` +2. Bottom sheet scelta input — `scelta_input_calorietracker/` +3. Modal input numerico — `inserisci_calorie_calorietracker/` +4. Modal descrizione LLM — `descrizione_ai_calorietracker/` +5. Statistiche settimanali — `statistiche_calorietracker/` + +**Design System**: `vitality_pulse/DESIGN.md` diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md new file mode 100644 index 0000000..eb50f23 --- /dev/null +++ b/docs/TECHNICAL.md @@ -0,0 +1,292 @@ +# Requisiti Tecnici — CalorieTracker (Precision Vitality) + +> Documento di architettura e decisioni tecniche del progetto. +> Ultimo aggiornamento: 16/04/2026 + +--- + +## 1. Panoramica Architettura + +``` +┌─────────────────────────────────────────────┐ +│ App Ionic/Angular │ +│ ┌───────────┐ ┌───────────┐ ┌─────────┐ │ +│ │ Journal │ │ Stats │ │ Search │ │ +│ │ (Home) │ │ Day/Week │ │ │ │ +│ └─────┬─────┘ └─────┬─────┘ └────┬────┘ │ +│ │ │ │ │ +│ ┌─────┴───────────────┴─────────────┴────┐ │ +│ │ Core Services Layer │ │ +│ │ MealService | CalorieEstimator | Auth │ │ +│ └─────┬───────────────┬─────────────────┘ │ +│ │ │ │ +└────────┼───────────────┼────────────────────┘ + │ │ + ┌────┴────┐ ┌──────┴──────┐ + │Firestore│ │ Firebase CF │ + │ (DB) │ │ (Proxy AI) │ + └─────────┘ └──────┬──────┘ + │ + ┌──────┴──────┐ + │ Anthropic │ + │ Claude API │ + └─────────────┘ +``` + +--- + +## 2. Stack Tecnologico + +| Layer | Tecnologia | Versione | Motivazione | +|-------|------------|----------|-------------| +| Framework UI | Ionic | 7+ | Cross-platform, componenti nativi | +| Framework App | Angular | 17+ | Standalone components, signals | +| Mobile Runtime | Capacitor | 5+ | Build nativo Android/iOS | +| Database | Firebase Firestore | 10+ | Free tier, offline, real-time | +| Auth | Firebase Auth | 10+ | Anonymous + Google Sign-In | +| AI Proxy | Firebase Cloud Functions | Gen2 | Free tier, sicurezza API key | +| AI Model | Claude Haiku 4.5 | claude-haiku-4-5-20251001 | Economico, veloce, sufficiente per stime | +| Chart | Chart.js + ng2-charts | 4+ | Leggero (~200KB), well-maintained | +| Storage locale | @ionic/storage-angular | 4+ | Preferenze utente (obiettivo kcal) | +| Linguaggio | TypeScript | 5.3+ | Type safety | + +--- + +## 3. Requisiti Ambiente di Sviluppo + +| Requisito | Versione Minima | Note | +|-----------|-----------------|------| +| Node.js | 18+ | LTS raccomandato | +| npm | 9+ | Incluso con Node.js | +| Ionic CLI | 7+ | `npm i -g @ionic/cli` | +| Angular CLI | 17+ | Incluso come dipendenza | +| Java JDK | 17+ | Per build Android (Gradle) | +| Android SDK | API 33+ | Per build APK | + +--- + +## 4. Configurazione Variabili d'Ambiente + +### `environment.ts` (development) +```typescript +export const environment = { + production: false, + firebase: { + apiKey: '...', + authDomain: '...', + projectId: '...', + storageBucket: '...', + messagingSenderId: '...', + appId: '...' + }, + cloudFunctionUrl: 'http://localhost:5001/PROJECT_ID/us-central1/estimateCalories', + defaultCalorieTarget: 2000 +}; +``` + +### `environment.prod.ts` (production) +```typescript +export const environment = { + production: true, + firebase: { /* config produzione */ }, + cloudFunctionUrl: 'https://us-central1-PROJECT_ID.cloudfunctions.net/estimateCalories', + defaultCalorieTarget: 2000 +}; +``` + +**IMPORTANTE**: I file `environment.*.ts` sono nel `.gitignore`. Usare `environment.example.ts` come template. + +--- + +## 5. Firebase Cloud Function — Proxy AI + +### Architettura +``` +Client → Cloud Function → Anthropic API → Cloud Function → Client +``` + +### Endpoint +- **URL**: `POST /estimateCalories` +- **Body**: `{ description: string }` +- **Response**: `{ calories: number, confidence: 'high'|'medium'|'low', explanation: string }` +- **Auth**: Firebase Auth token required (header `Authorization: Bearer `) + +### Implementazione Cloud Function +```typescript +// functions/src/index.ts +import { onRequest } from 'firebase-functions/v2/https'; +import Anthropic from '@anthropic-ai/sdk'; + +export const estimateCalories = onRequest( + { cors: true, region: 'europe-west1' }, + async (req, res) => { + // 1. Verificare Firebase Auth token + // 2. Estrarre description dal body + // 3. Chiamare Claude con food dictionary nel system prompt + // 4. Parsare risposta JSON + // 5. Restituire risultato + } +); +``` + +### Sicurezza +- API key Anthropic in Secret Manager di Firebase +- Rate limiting: max 10 richieste/minuto per utente +- Validazione input: description max 500 caratteri +- Timeout: 10 secondi + +--- + +## 6. Schema Dati Firestore + +### Collection `users/{userId}/meals/{mealId}` + +| Campo | Tipo | Obbligatorio | Descrizione | +|-------|------|-------------|-------------| +| id | string | Sì | Auto-generated document ID | +| timestamp | Timestamp | Sì | Data/ora del pasto | +| calories | number | Sì | Kcal totali | +| inputType | string | Sì | `'manual'` o `'llm'` | +| description | string | No | Descrizione testuale | +| llmConfidence | string | No | `'high'`, `'medium'`, `'low'` (solo se llm) | +| llmExplanation | string | No | Spiegazione AI (solo se llm) | +| createdAt | Timestamp | Sì | Data creazione documento | + +### Indici Richiesti +``` +Collection: users/{userId}/meals + - (timestamp DESC) — per query giornaliere e range +``` + +--- + +## 7. Servizi Angular Core + +### `AuthService` +```typescript +- signInAnonymously(): Promise +- getCurrentUser(): Observable +- getUserId(): string +``` + +### `MealService` +```typescript +- addMeal(data: MealInput): Promise +- getMealsForDate(date: Date): Observable +- getMealsInRange(from: Date, to: Date): Observable +- deleteMeal(mealId: string): Promise +- getTotalCaloriesForDate(date: Date): Observable +``` + +### `CalorieEstimatorService` +```typescript +- estimateCalories(description: string): Observable +``` + +### `StorageService` +```typescript +- getCalorieTarget(): Promise +- setCalorieTarget(target: number): Promise +``` + +--- + +## 8. Interfacce TypeScript + +```typescript +interface Meal { + id: string; + timestamp: Date; + calories: number; + inputType: 'manual' | 'llm'; + description?: string; + llmConfidence?: 'high' | 'medium' | 'low'; + llmExplanation?: string; + createdAt: Date; +} + +interface MealInput { + calories: number; + inputType: 'manual' | 'llm'; + description?: string; + llmConfidence?: 'high' | 'medium' | 'low'; + llmExplanation?: string; +} + +interface CalorieEstimate { + calories: number; + confidence: 'high' | 'medium' | 'low'; + explanation: string; +} +``` + +--- + +## 9. Design System — Mapping Tecnico + +### Colori (CSS Custom Properties) +```css +:root { + --pv-primary: #006b1b; + --pv-primary-dim: #005d16; + --pv-accent: #FF7043; + --pv-error: #F44336; + --pv-surface: #f6f6f6; + --pv-surface-container-low: #f0f1f1; + --pv-surface-container-lowest: #ffffff; + --pv-on-surface: #2d2f2f; + --pv-on-surface-variant: #757575; +} +``` + +### Font (da importare) +```css +@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@400;700&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap'); + +:root { + --pv-font-data: 'Lexend', sans-serif; + --pv-font-ui: 'Plus Jakarta Sans', sans-serif; +} +``` + +### Regole Chiave +1. **No borders** — separazione solo via background shifts +2. **No drop shadows su card** — usare tonal layering +3. **Border radius**: `1.5rem` (card principali), `1rem` (elementi nested) +4. **Spacing**: `24px` vertical padding nelle card +5. **Progress bar**: `12px` altezza, full roundedness, gradient `primary-fixed → primary` +6. **FAB**: gradient `primary → primary-dim` a 135°, shadow `0px 12px 32px rgba(0,107,27,0.15)` + +--- + +## 10. Performance e Limiti + +### Firestore (Spark Plan — Gratuito) +- 50.000 letture/giorno +- 20.000 scritture/giorno +- 1 GiB storage +- 10 GiB trasferimento/mese + +### Claude API (Haiku) +- ~$0.25 per 1M input tokens +- ~$1.25 per 1M output tokens +- Stima: ~$0.001 per richiesta singola +- Budget suggerito: $5/mese per uso moderato + +### Ottimizzazioni +- Cache locale food dictionary (caricato una volta) +- Observable Firestore per real-time (no polling) +- Lazy loading moduli Angular +- Offline persistence Firestore per uso senza rete + +--- + +## 11. Sicurezza + +| Rischio | Mitigazione | +|---------|-------------| +| API key Anthropic esposta | Firebase Cloud Function come proxy | +| Accesso dati cross-user | Firestore Security Rules basate su userId | +| Input injection nell'LLM | Sanitizzazione input + max 500 caratteri | +| Firebase config nel bundle | Le Firebase config sono safe da esporre (solo project identifier) | +| Rate abuse | Rate limiting nella Cloud Function (10 req/min/user) | diff --git a/docs/TRACKING.md b/docs/TRACKING.md new file mode 100644 index 0000000..2ea0dd0 --- /dev/null +++ b/docs/TRACKING.md @@ -0,0 +1,99 @@ +# Tracciamento Requisiti — CalorieTracker + +> Questo documento tiene traccia dello stato di avanzamento di ogni requisito sessione per sessione. +> Aggiornare questo file alla fine di ogni sessione di lavoro. + +--- + +## Stato Requisiti + +| ID | Titolo | Stato | Sessione | Note | +|----|--------|-------|----------|------| +| REQ-001 | Scaffolding Ionic + Capacitor | ⬜ DA FARE | — | Prerequisito bloccante | +| REQ-002 | Setup Firebase Firestore | ⬜ DA FARE | — | Richiede REQ-001 | +| REQ-003 | Home screen + FAB | ⬜ DA FARE | — | Richiede REQ-002 | +| REQ-004 | Modal input numerico | ⬜ DA FARE | — | Richiede REQ-003 | +| REQ-005 | Modal input LLM | ⬜ DA FARE | — | Richiede REQ-003 + REQ-006 | +| REQ-006 | Servizio Claude API | ⬜ DA FARE | — | Richiede REQ-001 | +| REQ-007 | CRUD pasti Firestore | ⬜ DA FARE | — | Richiede REQ-002 | +| REQ-008 | Statistiche giornaliere | ⬜ DA FARE | — | Richiede REQ-007 | +| REQ-009 | Statistiche settimanali | ⬜ DA FARE | — | Richiede REQ-007 | +| REQ-010 | Ricerca pasti | ⬜ DA FARE | — | Richiede REQ-007 | +| REQ-011 | Design brief Stitch | ✅ COMPLETATO | S0 | Mockup disponibili | + +### Legenda Stati +- ⬜ DA FARE — Non ancora iniziato +- 🔄 IN CORSO — Lavoro iniziato +- ⏸️ IN PAUSA — Iniziato ma bloccato/sospeso +- ✅ COMPLETATO — Tutti i criteri accettazione soddisfatti +- ❌ BLOCCATO — Bloccato da dipendenza o problema + +--- + +## Ordine di Implementazione Raccomandato + +``` +Fase 1 (Foundation): + REQ-001 → REQ-002 → REQ-007 + +Fase 2 (Core Features): + REQ-003 → REQ-004 (parallelo con REQ-006) + REQ-006 → REQ-005 + +Fase 3 (Analytics & Search): + REQ-008 → REQ-009 + REQ-010 + +Fase 4 (Polish): + REQ-011 (già completato — usare come riferimento) +``` + +--- + +## Log Sessioni + +### Sessione 0 — 16/04/2026 +**Obiettivo**: Setup iniziale progetto +**Completato**: +- Inizializzazione repository Git +- Configurazione remote Gitea (git.i-a.run) +- Creazione CLAUDE.md +- Creazione documentazione progetto (REQUIREMENTS.md, TRACKING.md, TECHNICAL.md, FIRESTORE_SETUP.md, DEVICE_TESTING.md) +- Analisi mockup design Stitch (REQ-011 ✅) + +**Prossima sessione**: Iniziare REQ-001 (scaffolding Ionic) + +--- + +## Decisioni Prese + +| Data | Decisione | Motivazione | +|------|-----------|-------------| +| 16/04/2026 | Proxy API: Firebase Cloud Function | API key non esposta nel bundle mobile | +| 16/04/2026 | Cancellazione pasti: hard delete | Semplicità, niente complessità soft delete | +| 16/04/2026 | Obiettivo calorico: @ionic/storage locale | Non serve persistenza cloud per preferenza utente | +| 16/04/2026 | Ricerca: client-side 30 giorni | Firestore non supporta full-text, sufficiente per uso tipico | +| 16/04/2026 | Nome UI: "Precision Vitality" | Da design brief Stitch approvato | +| 16/04/2026 | 4 tab: Journal, Stats, Plans, Profile | Da design (diverge dai 3 tab originali dei requisiti) | + +--- + +## Problemi Aperti + +| # | Problema | Impatto | Stato | +|---|----------|---------|-------| +| 1 | Deadline business non definita | Pianificazione | Aperto | +| 2 | Target platform prioritario (Android vs iOS) | Build priority | Aperto | +| 3 | Strategia Auth lungo termine | REQ-002 | Aperto — per ora Anonymous | +| 4 | Logo app | REQ-011 | Aperto | +| 5 | Dark mode in scope? | Design | Aperto | + +--- + +## Come Usare Questo Documento + +1. **Inizio sessione**: leggere lo stato attuale dei requisiti e il log dell'ultima sessione +2. **Durante la sessione**: aggiornare lo stato dei requisiti man mano +3. **Fine sessione**: aggiungere entry nel log sessioni con obiettivo, completato e prossimi passi +4. **Decisioni**: registrare ogni decisione architetturale con motivazione +5. **Problemi**: aggiungere problemi aperti che bloccano o impattano il lavoro diff --git a/stitch_calorietracker_design_brief_brief/calorietracker_design_brief.md b/stitch_calorietracker_design_brief_brief/calorietracker_design_brief.md new file mode 100644 index 0000000..e16bd1c --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/calorietracker_design_brief.md @@ -0,0 +1,74 @@ +# GOOGLE STITCH DESIGN BRIEF — CalorieTracker + +**App name:** CalorieTracker +**Platform:** Mobile (iOS + Android), Ionic Capacitor +**Language:** Italiano + +--- + +#### IDENTITÀ VISIVA + +**Mood:** Energico ma pulito. Non medicale, non pesante. Motivante. +**Palette colori:** +- Primario: Verde salute `#4CAF50` (azioni positive, conferme, obiettivo raggiunto) +- Accent: Arancione energia `#FF7043` (FAB, CTA principale, avvisi morbidi) +- Errore/Eccesso: Rosso `#F44336` (obiettivo superato, validazioni) +- Background: Bianco sporco `#FAFAFA` +- Superfici card: Bianco `#FFFFFF` con ombra leggera +- Testo primario: `#212121` +- Testo secondario: `#757575` + +**Tipografia:** +- Font: Roboto (già incluso in Ionic) +- Titolo totale kcal: 48px, bold, colore primario +- Label: 14px, medium +- Corpo lista: 16px, regular + +**Iconografia:** Material Icons (set standard Ionic/Angular Material) + +--- + +#### SCHERMATE DA GENERARE + +**SCHERMATA 1 — Home** +- Header: data odierna (es. "Mercoledì 16 aprile") centrata, testo secondario +- Centro schermata: numero kcal totali giornalieri in grande (48px, verde), label "calorie oggi" sotto +- Barra progresso orizzontale: kcal consumate / 2000 kcal obiettivo, colore verde → rosso se >100% +- Lista pasti sotto la barra: card minimal con [ora | descrizione | kcal] su ogni riga + - Badge piccolo a destra: "AI" (arancione) se stima LLM, "M" (grigio) se manuale +- FAB in basso a destra: cerchio arancione `#FF7043`, icona "+" bianca, dimensione XL +- Empty state se nessun pasto: illustrazione semplice + "Nessun pasto registrato oggi. Inizia!" + +**SCHERMATA 2 — Bottom Sheet scelta input** +- Modal bottom sheet (mezzo schermo) +- Titolo: "Come vuoi registrare?" +- Due opzioni a tutta larghezza, staccate, con icona: + - "Inserisci kcal" — icona tastiera numerica — sfondo bianco con bordo verde + - "Descrivi il pasto" — icona microfono/testo — sfondo bianco con bordo arancione +- Bottone X in alto a destra per chiudere + +**SCHERMATA 3 — Modal input numerico** +- Titolo: "Inserisci calorie" +- Campo numerico grande al centro: font 40px, placeholder "0" +- Label sotto campo: "kcal" +- Campo testo sotto: "Descrizione (opzionale)", font normale, bordo leggero +- Bottone "Salva" verde a tutta larghezza in basso + +**SCHERMATA 4 — Modal descrizione LLM** +- Titolo: "Descrivi il pasto" +- Textarea grande: placeholder "Es. piatto di pasta al pomodoro con 2 cucchiai di parmigiano" +- Bottone "Stima calorie" arancione a tutta larghezza +- Stato risultato (visibile dopo risposta LLM): + - Box risultato: kcal stimati in grande, badge confidence (verde/giallo/rosso) + - Testo spiegazione in corsivo sotto + - Campo kcal modificabile (pre-compilato con stima) + - Bottone "Salva" verde + +**SCHERMATA 5 — Statistiche settimanali** +- Tab selector: "Oggi" | "Settimana" +- Vista settimana: grafico a barre 7 giorni + - Barre verdi se ≤ obiettivo, rosse se sopra + - Asse X: giorni abbreviati (Lun, Mar...) + - Asse Y: kcal (0, 500, 1000, 1500, 2000+) +- Sotto grafico: card "Media settimanale: X kcal/giorno" +- Navigazione settimana: frecce sinistra/destra con label "14–20 apr" \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/code.html b/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/code.html new file mode 100644 index 0000000..724f127 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/code.html @@ -0,0 +1,193 @@ + + + + + +Descrivi il pasto - Precision Vitality + + + + + + + + + + + +
+ +
+

Descrivi il pasto

+

Usa il linguaggio naturale per stimare i tuoi nutrienti in pochi secondi.

+
+ +
+
+ + +
+ + +
+ +
+ +
+
+
+verified + Alta precisione +
+
+650 +KCAL STIMATE +
+ +
+
+85g +Carbi +
+
+22g +Proteine +
+
+18g +Grassi +
+
+

+ Basato su ingredienti standard e porzioni medie per una pizza tradizionale e 33cl di birra. +

+
+
+ +
+ +
+
+ + + \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/screen.png b/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/screen.png new file mode 100644 index 0000000..50bfe79 Binary files /dev/null and b/stitch_calorietracker_design_brief_brief/descrizione_ai_calorietracker/screen.png differ diff --git a/stitch_calorietracker_design_brief_brief/home_calorietracker/code.html b/stitch_calorietracker_design_brief_brief/home_calorietracker/code.html new file mode 100644 index 0000000..f0f8b39 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/home_calorietracker/code.html @@ -0,0 +1,209 @@ + + + + + +Precision Vitality - Journal + + + + + + + + + + +
+
+
+User Profile +
+Precision Vitality +
+ +
+
+ +
+

Mercoledì 16 aprile

+

Il tuo progresso giornaliero

+
+ +
+ +
+
+
+1450 +calorie oggi +
+
+Attuale: 1450 +Obiettivo: 2000 +
+
+
+
+
+
+ +
+

Diario Alimentare

+ +
+
+
08:30
+
+

Colazione Proteica

+
+M +
+
+
+
+350 kcal +
+
+ +
+
+
13:00
+
+

Pasta al pesto

+
+AI +
+
+
+
+650 kcal +
+
+ +
+
+
16:45
+
+

Frutta fresca e noci

+
+AI +
+
+
+
+450 kcal +
+
+
+
+ + + + + \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/home_calorietracker/screen.png b/stitch_calorietracker_design_brief_brief/home_calorietracker/screen.png new file mode 100644 index 0000000..fa3066b Binary files /dev/null and b/stitch_calorietracker_design_brief_brief/home_calorietracker/screen.png differ diff --git a/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/code.html b/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/code.html new file mode 100644 index 0000000..c5ba4f7 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/code.html @@ -0,0 +1,175 @@ + + + + + +Precision Vitality — Inserisci calorie + + + + + + + + + + +
+ +
+ +
+ +

Inserisci calorie

+
+
+ +
+ +
+
+bolt +
+

Ogni dato conta per il tuo obiettivo.

+
+ +
+
+ +kcal +
+
+ +
+ +
+ +edit_note +
+
+ + + +
+
+Suggerimenti veloci +
+
+ + + +
+
+
+ +
+
+
+ +
+Healthy lifestyle background +
+ \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/screen.png b/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/screen.png new file mode 100644 index 0000000..6cdb316 Binary files /dev/null and b/stitch_calorietracker_design_brief_brief/inserisci_calorie_calorietracker/screen.png differ diff --git a/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/code.html b/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/code.html new file mode 100644 index 0000000..5b34482 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/code.html @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + +
+ +
+

Precision Vitality

+
+person +
+
+ +
+
+

Oggi, 24 Maggio

+

1,842

+

kcal rimanenti

+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+

Come vuoi registrare?

+ +
+ +
+ + + + +
+ +
+lightbulb +

+ Puoi dire cose come "Un'insalata russa e due bicchieri d'acqua" per una stima precisa. +

+
+
+
+ + + \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/screen.png b/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/screen.png new file mode 100644 index 0000000..dfe3ba9 Binary files /dev/null and b/stitch_calorietracker_design_brief_brief/scelta_input_calorietracker/screen.png differ diff --git a/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/code.html b/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/code.html new file mode 100644 index 0000000..464ed90 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/code.html @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + +
+
+
+Profile +
+Precision Vitality +
+ +
+
+ +
+ + +
+ +
+ +
+

Periodo Corrente

+

14–20 apr

+
+ +
+ +
+
+ +
+
+
+
+LUN +
+ +
+
+
+
+MAR +
+ +
+
+
+
+warning +
+
+MER +
+ +
+
+
+
+GIO +
+ +
+
+
+
+VEN +
+ +
+
+
+
+SAB +
+ +
+
+
+
+DOM +
+
+
+
+
+In target +
+
+
+Eccesso +
+
+
+ +
+
+
+

Riepilogo Prestazioni

+
+1850 +kcal/giorno +
+

+ Media settimanale: 1850 kcal/giorno. Ottimo lavoro, sei costante! +

+
+
+trending_down +

-12%

+

vs scorsa sett.

+
+
+bolt +

5/7

+

Giorni in target

+
+
+
+
+
+ + + \ No newline at end of file diff --git a/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/screen.png b/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/screen.png new file mode 100644 index 0000000..e2bfad1 Binary files /dev/null and b/stitch_calorietracker_design_brief_brief/statistiche_calorietracker/screen.png differ diff --git a/stitch_calorietracker_design_brief_brief/vitality_pulse/DESIGN.md b/stitch_calorietracker_design_brief_brief/vitality_pulse/DESIGN.md new file mode 100644 index 0000000..9e26838 --- /dev/null +++ b/stitch_calorietracker_design_brief_brief/vitality_pulse/DESIGN.md @@ -0,0 +1,76 @@ +# Design System Document: Precision Vitality + +## 1. Overview & Creative North Star +The "Creative North Star" for this design system is **The Kinetic Editor**. + +While many fitness apps feel like static spreadsheets, this system treats health data as a living, breathing editorial story. We move beyond the "standard tracker" by utilizing high-contrast typography scales, intentional asymmetry, and a sophisticated layering system. The goal is to make the user feel like they are interacting with a premium digital magazine that reacts to their progress. By breaking the grid with overlapping elements and prioritizing tonal depth over rigid borders, we create an environment that feels energetic yet remarkably clean. + +## 2. Color Strategy: Tonal Vitality +Our palette utilizes the Material Design convention to ensure a "Health Green" core that feels professional rather than radioactive. + +### The "No-Line" Rule +**Borders are prohibited.** You must never use a 1px solid border to define sections. Boundaries are created through: +* **Background Shifts:** Placing a `surface-container-lowest` card against a `surface` background. +* **Tonal Transitions:** Using `surface-container-low` for secondary groupings to create natural separation. + +### Surface Hierarchy & Nesting +Treat the UI as a series of physical layers—like stacked sheets of fine paper. +* **Level 0 (Base):** `surface` (#f6f6f6) – The canvas. +* **Level 1 (Sections):** `surface-container-low` (#f0f1f1) – For grouping related modules. +* **Level 2 (Active Cards):** `surface-container-lowest` (#ffffff) – The highest priority interactive elements. + +### The "Glass & Gradient" Rule +To inject "visual soul," use subtle gradients. +* **Primary Action:** Transition from `primary` (#006b1b) to `primary-dim` (#005d16) at a 135° angle. +* **Floating Elements:** Apply Glassmorphism to top navigation bars or floating action buttons using `surface` at 80% opacity with a `24px` backdrop-blur. + +## 3. Typography: Editorial Impact +We pair the geometric precision of **Lexend** for data with the humanist readability of **Plus Jakarta Sans** for interface elements. + +* **Main Kcal Display (`display-lg`):** Lexend, 3.5rem (56px), Bold. Use `primary` color. This is the "Hero" of the screen. +* **Section Headlines (`headline-sm`):** Lexend, 1.5rem. Use `on-surface`. +* **Primary Labels (`label-md`):** Plus Jakarta Sans, 0.75rem, Medium. Use `on-surface-variant` for metadata. +* **Body Copy (`body-lg`):** Plus Jakarta Sans, 1rem, Regular. Use `on-surface`. + +The high contrast between the massive `display-lg` calorie counts and the tight `label-sm` metadata creates an "Editorial" hierarchy that guides the eye instantly to what matters. + +## 4. Elevation & Depth: Tonal Layering +Depth is achieved through "stacking" rather than traditional drop shadows. + +* **The Layering Principle:** Avoid shadows on static cards. Instead, place a `surface-container-lowest` card on a `surface-container` background. The subtle 2-3% shift in hex value provides a sophisticated, modern lift. +* **Ambient Shadows:** For floating elements (e.g., a "Log Food" button), use a 15% opacity `primary` color shadow: `0px 12px 32px rgba(0, 107, 27, 0.15)`. This mimics natural light reflecting off a colored surface. +* **The Ghost Border Fallback:** If accessibility requires a stroke, use `outline-variant` at 15% opacity. Never use 100% opaque lines. + +## 5. Components: The Kinetic Suite + +### Progress Bars (The Core Vital) +* **Style:** Horizontal, `12px` height, `full` roundedness. +* **Container:** `surface-container-highest`. +* **Fill:** A linear gradient from `primary-fixed` to `primary`. +* **Animation:** Always use a "Spring" ease-out (0.6s) to make progress feel energetic. + +### Cards & Modules +* **Rule:** Forbid divider lines. Use `1.5rem (xl)` corner radius for main dashboard cards and `1rem (lg)` for nested items. +* **Spacing:** Use `24px` vertical padding to provide significant breathing room. + +### Buttons +* **Primary:** `primary` background, `on-primary` text. No shadow. `full` roundedness. +* **Secondary (Energy):** `secondary-container` background, `on-secondary-container` text. Use for "Add Calories" or "High Energy" actions. + +### Input Fields +* **Style:** Minimalist. No bottom line. Use a `surface-container-low` background with a `0.5rem (sm)` radius. On focus, transition background to `surface-container-highest`. + +### Contextual Components: "The Macro-Donut" +* Use a large-scale, thin-stroke (4px) ring for Macronutrient breakdowns. Overlap the donut slightly with the `display-lg` calorie title to create a sense of depth and custom-fit design. + +## 6. Do's and Don'ts + +### Do: +* **Do** use asymmetrical margins. For example, give the "Main Kcal" title a larger left-hand gutter (32px) than the body text (16px) to create a rhythmic, magazine-style layout. +* **Do** use `secondary` (Energy Orange) sparingly for high-motivation moments only (e.g., reaching a streak). +* **Do** prioritize "Breathing Room." If a screen feels crowded, increase the `surface-container` spacing rather than adding lines. + +### Don't: +* **Don't** use Material Icons in their "Filled" state if they feel too heavy. Use "Outlined" or "Two-Tone" with `outline` color to maintain the "Clean" mood. +* **Don't** ever use #000000 for text. Always use `on-background` or `on-surface` (#2d2f2f) to keep the look premium and soft. +* **Don't** use standard 4px "Card Shadows." If it doesn't look like it's floating in a real-world space, don't shadow it. \ No newline at end of file