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 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 09:44:35 +02:00
commit 7fd03a99ba
19 changed files with 2765 additions and 0 deletions

416
docs/DEVICE_TESTING.md Normal file
View File

@@ -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

349
docs/FIRESTORE_SETUP.md Normal file
View File

@@ -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

312
docs/REQUIREMENTS.md Normal file
View File

@@ -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`

292
docs/TECHNICAL.md Normal file
View File

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

99
docs/TRACKING.md Normal file
View File

@@ -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