# 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) |