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 <noreply@anthropic.com>
293 lines
9.0 KiB
Markdown
293 lines
9.0 KiB
Markdown
# 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 <token>`)
|
|
|
|
### 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<User>
|
|
- getCurrentUser(): Observable<User | null>
|
|
- getUserId(): string
|
|
```
|
|
|
|
### `MealService`
|
|
```typescript
|
|
- addMeal(data: MealInput): Promise<string>
|
|
- getMealsForDate(date: Date): Observable<Meal[]>
|
|
- getMealsInRange(from: Date, to: Date): Observable<Meal[]>
|
|
- deleteMeal(mealId: string): Promise<void>
|
|
- getTotalCaloriesForDate(date: Date): Observable<number>
|
|
```
|
|
|
|
### `CalorieEstimatorService`
|
|
```typescript
|
|
- estimateCalories(description: string): Observable<CalorieEstimate>
|
|
```
|
|
|
|
### `StorageService`
|
|
```typescript
|
|
- getCalorieTarget(): Promise<number>
|
|
- setCalorieTarget(target: number): Promise<void>
|
|
```
|
|
|
|
---
|
|
|
|
## 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) |
|