# Frontend-Dokumentation

## Übersicht

Das Frontend ist eine **Progressive Web App (PWA)** gebaut mit React 18, Vite und React Router. Die Architektur folgt einem **Component-based Pattern** mit Context-basiertem State Management (kein Redux).

**Technologien:**
- React 18 (ohne TypeScript)
- Vite (Build Tool + Dev Server)
- React Router v6 (Client-side Routing)
- Recharts (Chart-Bibliothek)
- Lucide React (Icon Library)
- Day.js (Datum-Handling)

**Bundle-Größe:** ~450 KB (gzip), PWA-Cache für Offline-Nutzung

---

## Seiten-Übersicht

| Seite | Route | Beschreibung | Auth | Admin |
|-------|-------|--------------|------|-------|
| **LoginScreen** | `/` (ohne Auth) | E-Mail + Passwort Login, SHA256→bcrypt Auto-Migration | ❌ | ❌ |
| **Register** | `/register` | Selbst-Registrierung + E-Mail-Verifizierung | ❌ | ❌ |
| **Verify** | `/verify?token=...` | E-Mail-Verifizierung nach Registrierung | ❌ | ❌ |
| **SetupScreen** | `/` (First Run) | Initiales Setup (erster Admin-Account) | ❌ | ❌ |
| **Dashboard** | `/` | Übersicht: Quick Weight, Stats, Charts, Widgets | ✅ | ❌ |
| **CaptureHub** | `/capture` | Quick-Entry-Auswahl (Gewicht/Umfänge/Caliper/Fotos/Aktivität/Schlaf) | ✅ | ❌ |
| **WeightScreen** | `/weight` | Gewichts-Tracking mit Inline-Edit | ✅ | ❌ |
| **CircumScreen** | `/circum` | Umfänge (8 Punkte) | ✅ | ❌ |
| **CaliperScreen** | `/caliper` | Hautfaltenmessungen (4 Methoden) | ✅ | ❌ |
| **MeasureWizard** | `/wizard` | Geführte Messung (Schritt-für-Schritt) | ✅ | ❌ |
| **ActivityPage** | `/activity` | Training + Trainingstypen (v9d) | ✅ | ❌ |
| **NutritionPage** | `/nutrition` | 3-Tab Layout: Entry / Import / Charts | ✅ | ❌ |
| **SleepPage** | `/sleep` | Schlaf-Tracking + Phasen + Apple Health Import | ✅ | ❌ |
| **RestDaysPage** | `/rest-days` | Ruhetage (Kraft/Cardio/Entspannung) | ✅ | ❌ |
| **VitalsPage** | `/vitals` | 3-Tab: Baseline / Blutdruck / Import | ✅ | ❌ |
| **History** | `/history` | Verlauf mit Charts (Gewicht, KF%, Umfänge, etc.) | ✅ | ❌ |
| **Analysis** | `/analysis` | KI-Auswertung + Pipeline | ✅ | ❌ |
| **SettingsPage** | `/settings` | Profil, PIN-Change, Export, Feature-Usage-Übersicht | ✅ | ❌ |
| **SubscriptionPage** | `/subscription` | Membership-Status (v9c) | ✅ | ❌ |
| **GuidePage** | `/guide` | Anleitungen (Caliper, Umfänge) | ✅ | ❌ |
| **AdminPanel** | `/admin/*` (in Settings) | Admin-Übersicht | ✅ | ✅ |
| **AdminTierLimitsPage** | `/admin/tier-limits` | Tier-Limits Matrix (v9c) | ✅ | ✅ |
| **AdminFeaturesPage** | `/admin/features` | Feature-Verwaltung (v9c) | ✅ | ✅ |
| **AdminTiersPage** | `/admin/tiers` | Tier-Verwaltung (v9c) | ✅ | ✅ |
| **AdminCouponsPage** | `/admin/coupons` | Coupon-System (v9c) | ✅ | ✅ |
| **AdminUserRestrictionsPage** | `/admin/user-restrictions` | User-spezifische Limits (v9c) | ✅ | ✅ |
| **AdminTrainingTypesPage** | `/admin/training-types` | Trainingstypen-CRUD (v9d) | ✅ | ✅ |
| **AdminActivityMappingsPage** | `/admin/activity-mappings` | Activity Mapping-Verwaltung (v9d) | ✅ | ✅ |
| **AdminTrainingProfiles** | `/admin/training-profiles` | Training Type Profiling (v9d #15) | ✅ | ✅ |

**Gesamt:** 31 Seiten (22 User-facing, 9 Admin)

---

## Komponenten

### Wiederverwendbare Komponenten

| Komponente | Props | Beschreibung |
|-----------|-------|--------------|
| **Avatar** | `profile, size` | Runder Avatar mit Initialen + Farbe |
| **Markdown** | `text` | Lightweight Markdown-Renderer (## Headings, **bold**, Listen) |
| **TrialBanner** | – | Trial-Countdown-Banner (3 Urgency-Level) |
| **EmailVerificationBanner** | – | E-Mail-Verifizierungs-Hinweis |
| **FeatureUsageOverview** | – | Tabelle mit allen Feature-Limits + Usage (v9c Phase 3) |
| **UsageBadge** | `feature` | Inline-Badge mit Limit-Status (z.B. "3/10") |
| **TrainingTypeDistribution** | `days` | Pie-Chart für Trainingstypen-Verteilung |
| **SleepWidget** | `days` | Dashboard-Widget mit Schlaf-Stats |
| **RestDaysWidget** | `weeks` | Dashboard-Widget mit aktuellen Ruhetagen |

**Location:** `frontend/src/components/`

### Inline-Komponenten (in Seiten definiert)

**Dashboard.jsx:**
- `QuickWeight` – Schnelle Gewichts-Eingabe mit Feature-Limit-Check
- `StatCard` – Statistik-Karte mit Delta-Anzeige
- `Pill` – Status-Pill mit Tooltip (WHR, WHtR, KF, Protein Ø7T)

**SettingsPage.jsx:**
- `ProfileForm` – Formular für Profil-Bearbeitung

**NutritionPage.jsx:**
- `EntryTab` – Manuelle Eingabe + CSV-Import
- `ImportHistoryTab` – Import-Historie mit Gruppierung
- `ChartsTab` – Korrelationen + Wochendaten

**VitalsPage.jsx:**
- `BaselineTab` – Morgenmessungen (RHR, HRV, VO2 Max, SpO2)
- `BloodPressureTab` – Blutdruck mehrfach täglich + Context-Tagging
- `ImportTab` – CSV-Import (Omron Deutsch, Apple Health)

---

## Context / State Management

### 1. AuthContext (`frontend/src/context/AuthContext.jsx`)

**Verantwortlichkeit:** Session-Management + Login/Logout

**State:**
```javascript
{
  session: {
    token: string,
    profile_id: string,
    role: 'user' | 'admin',
    profile: { id, name, email, tier, ... }
  },
  loading: boolean,
  needsSetup: boolean,  // First-run detection
}
```

**Methods:**
- `login(credentials)` – Login mit E-Mail + Passwort (oder Legacy profile_id + PIN)
- `setup(formData)` – Initial Setup (First Run)
- `logout()` – Logout + Token-Löschung
- `setAuthFromToken(token, profile)` – Direkt-Login (für E-Mail-Verifizierung)
- `checkStatus()` – Auth-Status prüfen (beim App-Start)

**Computed:**
- `isAdmin` – `session.role === 'admin'`
- `canUseAI` – `session.profile.ai_enabled !== 0`
- `canExport` – `session.profile.export_enabled !== 0`

**Storage:**
- `localStorage.bodytrack_token` – Auth-Token
- `localStorage.bodytrack_active_profile` – Aktive Profile-ID

**Flow:**
```
App-Start → checkStatus()
  ↓
GET /api/auth/status → {needs_setup: true/false}
  ↓ (wenn needs_setup = false)
GET /api/auth/me (mit Token aus localStorage)
  ↓
Session gesetzt → App.jsx zeigt Dashboard
```

### 2. ProfileContext (`frontend/src/context/ProfileContext.jsx`)

**Verantwortlichkeit:** Aktives Profil + Profil-Liste

**State:**
```javascript
{
  profiles: Array<Profile>,     // Alle Profile
  activeProfile: Profile,        // Aktuelles Profil
  loading: boolean,
}
```

**Methods:**
- `setActiveProfile(profile)` – Profil wechseln (speichert in localStorage)
- `refreshProfiles()` – Profile neu laden (nach Update)

**Flow:**
```
session.profile_id ändert sich
  ↓
GET /api/profiles (mit X-Auth-Token)
  ↓
profiles gesetzt, activeProfile = match(session.profile_id)
```

**Hinweis:** Profile-Wechsel ist derzeit Single-User-optimiert (Multi-User-Support in Planung).

---

## API-Integration (`frontend/src/utils/api.js`)

**Zweck:** Zentrale API-Schnittstelle – **ALLE** API-Calls gehen über `api.js`

**Features:**
- Automatisches Token-Injection (`X-Auth-Token` Header)
- Automatisches Profile-ID-Injection (`X-Profile-Id` Header, derzeit deprecated)
- Einheitliche Fehlerbehandlung (parst `{detail: "..."}` aus Backend)
- Typed-like API (alle Methoden dokumentiert)

**Beispiel:**
```javascript
import { api } from '../utils/api'

// GET-Request
const weights = await api.listWeight(365)  // limit=365

// POST-Request
await api.upsertWeight('2026-03-23', 75.5, 'Morgens nüchtern')

// DELETE-Request
await api.deleteWeight(entryId)

// File-Upload
const result = await api.importCsv(file)
```

**Headers-Injection:**
```javascript
function hdrs(extra={}) {
  const h = {...extra}
  if (_profileId) h['X-Profile-Id'] = _profileId  // Deprecated, bleibt für Legacy
  const token = getToken()
  if (token) h['X-Auth-Token'] = token
  return h
}
```

**Error-Handling:**
```javascript
if (!res.ok) {
  const err = await res.text()
  try {
    const parsed = JSON.parse(err)
    throw new Error(parsed.detail || err)
  } catch {
    throw new Error(err)
  }
}
```

**API-Methoden (285 Zeilen):**
- **Profiles:** `getActiveProfile, listProfiles, createProfile, updateProfile, deleteProfile`
- **Weight:** `listWeight, upsertWeight, updateWeight, deleteWeight, weightStats`
- **Circumferences:** `listCirc, upsertCirc, updateCirc, deleteCirc`
- **Caliper:** `listCaliper, upsertCaliper, updateCaliper, deleteCaliper`
- **Activity:** `listActivity, createActivity, updateActivity, deleteActivity, activityStats, bulkCategorizeActivities, importActivityCsv`
- **Nutrition:** `importCsv, listNutrition, nutritionCorrelations, nutritionWeekly, nutritionImportHistory, createNutrition, updateNutrition, deleteNutrition`
- **Photos:** `uploadPhoto, listPhotos, photoUrl`
- **AI:** `insightTrend, listPrompts, runInsight, insightPipeline, listInsights, latestInsights`
- **Export:** `exportZip, exportJson, exportCsv` (Download-Handling inkludiert)
- **Admin:** `adminListProfiles, adminCreateProfile, adminDeleteProfile, adminSetPermissions, changePin`
- **Auth:** `register, verifyEmail, resendVerification`
- **Subscription (v9c):** `getMySubscription, getMyUsage, getMyLimits, redeemCoupon, getFeatureUsage`
- **Admin Features (v9c):** `listFeatures, createFeature, updateFeature, deleteFeature`
- **Admin Tiers (v9c):** `listTiers, createTier, updateTier, deleteTier, getTierLimitsMatrix, updateTierLimit, updateTierLimitsBatch`
- **Admin User Restrictions (v9c):** `listUserRestrictions, createUserRestriction, updateUserRestriction, deleteUserRestriction`
- **Admin Coupons (v9c):** `listCoupons, createCoupon, updateCoupon, deleteCoupon, getCouponRedemptions`
- **Admin Access Grants (v9c):** `listAccessGrants, createAccessGrant, updateAccessGrant, revokeAccessGrant`
- **Training Types (v9d):** `listTrainingTypes, listTrainingTypesFlat, getTrainingCategories, adminListTrainingTypes, adminCreateTrainingType, adminUpdateTrainingType, adminDeleteTrainingType, getAbilitiesTaxonomy`
- **Training Profiles (v9d #15):** `getProfileStats, getProfileTemplates, getProfileTemplate, applyProfileTemplate, getTrainingParameters, batchEvaluateActivities`
- **Activity Mappings (v9d):** `adminListActivityMappings, adminCreateActivityMapping, adminUpdateActivityMapping, adminDeleteActivityMapping, adminGetMappingCoverage`
- **Sleep (v9d):** `listSleep, getSleepByDate, createSleep, updateSleep, deleteSleep, getSleepStats, getSleepDebt, getSleepTrend, getSleepPhases, importAppleHealthSleep`
- **Rest Days (v9d):** `listRestDays, createRestDay, getRestDay, updateRestDay, deleteRestDay, getRestDaysStats, validateActivity`
- **Vitals Baseline (v9d):** `listBaseline, getBaselineByDate, createBaseline, updateBaseline, deleteBaseline, getBaselineStats, importBaselineAppleHealth`
- **Blood Pressure (v9d):** `listBloodPressure, getBPByDate, createBloodPressure, updateBloodPressure, deleteBloodPressure, getBPStats, importBPOmron`

---

## Berechnungs-Utils

### 1. calc.js (`frontend/src/utils/calc.js`)

**Zweck:** Körperfett-Berechnungen + Derived Metrics

**Funktionen:**

**`calcBodyFat(method, skinfolds, sex, age)`**
- Berechnet Körperfett-% nach 4 Methoden:
  - `jackson3` – Jackson-Pollock 3-Punkt (Standard)
  - `jackson7` – Jackson-Pollock 7-Punkt
  - `durnin` – Durnin-Womersley 4-Punkt
  - `parrillo` – Parrillo 9-Punkt (linear)
- Nutzt Siri-Formel: `BF% = (495 / D) - 450`
- Parameter:
  - `method`: String ('jackson3', 'jackson7', 'durnin', 'parrillo')
  - `skinfolds`: Object mit Hautfalten in mm (z.B. `{chest: 12, abdomen: 24, thigh: 18}`)
  - `sex`: 'm' | 'f'
  - `age`: Number

**`getBfCategory(pct, sex)`**
- Kategorisiert Körperfett-% in Bereiche:
  - Männer: Essenziell (<6%), Athletisch (6-14%), Fit (14-18%), Durchschnitt (18-25%), Übergewicht (>25%)
  - Frauen: Essenziell (<14%), Athletisch (14-21%), Fit (21-25%), Durchschnitt (25-32%), Übergewicht (>32%)
- Returns: `{max, label, color, desc}`

**`calcDerived(measurement, height)`**
- Berechnet abgeleitete Metriken:
  - **WHR** (Waist-Hip-Ratio): `waist / hip` (Ziel: <0.90 M / <0.85 F)
  - **WHtR** (Waist-to-Height-Ratio): `waist / height` (Ziel: <0.50)
  - **FFMI** (Fat-Free Mass Index): `lean_mass / (height_m²)` (Natural Limit: ~25 M / ~22 F)
- Returns: `{whr, whtr, ffmi}`

**`getRuleBasedAssessment(current, previous, profile)`**
- Generiert automatische Interpretationen basierend auf:
  - Körperfett-Kategorie
  - Änderungen seit letzter Messung
  - FFMI (Muskel-Index)
  - WHR / WHtR (Fettverteilung)
  - Taillenumfang (WHO-Grenzwerte)
- Returns: `{findings: Array, summary: string, summaryType: 'good'|'warn'|'bad'}`

**Guide-Daten:**
- `CIRCUMFERENCE_GUIDE` – Messanleitung für 8 Umfangspunkte (wo, wie, Posture, Tipps)
- `CALIPER_GUIDE` – Messanleitung für Hautfalten-Punkte

### 2. interpret.js (`frontend/src/utils/interpret.js`)

**Zweck:** Interpretation von Messwerten

**`getInterpretation(measurement, profile, prevMeasurement)`**
- Analysiert Messung und generiert strukturierte Interpretation:
  - Körperfett-Status (mit Kategorie + Farbe)
  - WHR-Status
  - WHtR-Status
  - FFMI-Status
  - BMI-Status
  - Vergleich zur letzten Messung (Deltas)
- Returns: Array von Interpretations-Objects:
  ```javascript
  {
    category: 'Körperfett',
    icon: '🫧',
    status: 'good' | 'warn' | 'bad',
    title: 'Athletischer Körperfettanteil',
    detail: 'Ausgezeichnet. Typisch für aktive Sportler...',
    value: '12.5%',
    badge: 'Athletisch',
    color: '#1D9E75',
  }
  ```

**`getStatusColor(status)`** – Farbe für Status ('good'→Grün, 'warn'→Orange, 'bad'→Rot)

**`getStatusBg(status)`** – Background-Farbe für Status

### 3. Markdown.jsx (`frontend/src/utils/Markdown.jsx`)

**Zweck:** Leichtgewichtiger Markdown-Renderer für KI-Texte

**Unterstützte Syntax:**
- `# Heading 1`, `## Heading 2`, `### Heading 3`
- `**bold**`, `*italic*`
- `- Bullet List`, `1. Numbered List`
- `---` (Horizontal Rule)
- Line Breaks

**Verwendung:**
```jsx
<Markdown text={aiInsight.content} />
```

**Vorteil:** Kein remark/rehype-Dependency – nur 134 Zeilen pures React

---

## CSS-System (`frontend/src/app.css`)

### CSS-Variablen (Light + Dark Mode)

**Farben:**
```css
:root {
  --bg: #f4f3ef;              /* Hintergrund */
  --surface: #ffffff;         /* Cards */
  --surface2: #f9f8f5;        /* Inputs, Secondary */
  --border: rgba(0,0,0,0.09); /* Standard-Border */
  --border2: rgba(0,0,0,0.16);/* Input-Border */
  --text1: #1c1b18;           /* Primär-Text */
  --text2: #5a5955;           /* Sekundär-Text */
  --text3: #9a9892;           /* Muted */
  --accent: #1D9E75;          /* Primär-Farbe */
  --accent-light: #E1F5EE;    /* Accent-Background */
  --accent-dark: #0a5c43;     /* Hover */
  --danger: #D85A30;          /* Fehler/Löschen */
  --warn: #EF9F27;            /* Warnung */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #181816;
    --surface: #222220;
    --surface2: #1e1e1c;
    --border: rgba(255,255,255,0.08);
    --text1: #eeecea;
    --text2: #aaa9a4;
    --text3: #686762;
    --accent-light: #04342C;
    --accent-dark: #5DCAA5;
  }
}
```

**Layout:**
```css
--nav-h: 64px;       /* Bottom Navigation Höhe */
--header-h: 52px;    /* App-Header Höhe */
```

### Utility Classes

**Cards:**
```css
.card           /* Standard-Card (white background, border, rounded) */
.card-title     /* Card-Überschrift (uppercase, small, muted) */
```

**Stats:**
```css
.stats-grid     /* 2-Column Grid für Stats */
.stat-card      /* Einzelne Stat-Card */
.stat-val       /* Wert (groß, bold) */
.stat-label     /* Label (klein, muted) */
.stat-delta     /* Delta (z.B. "+2.5 kg") */
.delta-pos      /* Positive Änderung (grün) */
.delta-neg      /* Negative Änderung (rot) */
```

**Forms:**
```css
.form-section       /* Formular-Sektion mit Abstand */
.form-section-title /* Sektions-Titel (uppercase, border-bottom) */
.form-row           /* Zeile mit Label + Input + Unit */
.form-label         /* Label (links, flex:1) */
.form-input         /* Input (90px breit, text-align:right) */
.form-unit          /* Einheit (z.B. "kg", 24px breit) */
.form-select        /* Select-Dropdown */
.form-sub           /* Sub-Label (klein, muted) */
```

**Buttons:**
```css
.btn              /* Base Button */
.btn-primary      /* Primär-Button (accent) */
.btn-secondary    /* Sekundär-Button (grau) */
.btn-danger       /* Löschen-Button (rot) */
.btn-full         /* Full-Width Button */
```

**Tabs:**
```css
.tabs             /* Tab-Container (segmented control) */
.tab              /* Einzelner Tab */
.tab.active       /* Aktiver Tab (white background, shadow) */
```

**Misc:**
```css
.badge            /* Inline-Badge (klein, rounded) */
.spinner          /* Loading-Spinner (CSS-Animation) */
.empty-state      /* Leerer Zustand (zentriert, muted) */
.muted            /* Muted-Text (text3) */
```

### Responsive Design

**Mobile-First Approach:**
- Standard-Layout für 375px–600px (Mobile)
- Max-Width: 600px (zentriert auf Desktop)
- Bottom-Navigation für Mobile (64px hoch)
- Touch-optimierte Button-Größen (min 44px)

**Bottom Navigation:**
```css
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 600px;
  height: var(--nav-h);
  z-index: 20;
}
```

**Safe Area (iPhone):**
```css
padding-bottom: env(safe-area-inset-bottom, 0);  /* Notch-Handling */
```

**Desktop-Optimierung:**
- App zentriert mit max-width: 600px
- Kein responsives Layout für >600px (bewusst Mobile-optimiert)

---

## PWA-Konfiguration

### Service Worker

**Location:** `frontend/public/service-worker.js`

**Cache-Strategie:**
- **Static Assets:** Cache-First (HTML, CSS, JS, Icons)
- **API-Calls:** Network-First mit Fallback
- **Photos:** Cache-First mit Expiry

**Registrierung:**
```javascript
// frontend/src/main.jsx
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
}
```

### Manifest

**Location:** `frontend/public/manifest.json`

**Wichtige Felder:**
```json
{
  "name": "Mitai Jinkendo",
  "short_name": "Mitai",
  "theme_color": "#1D9E75",
  "background_color": "#f4f3ef",
  "display": "standalone",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
```

**Installation:**
- iOS: "Zum Home-Bildschirm"
- Android: "Installieren"-Prompt
- Desktop: Chrome/Edge Install-Button

---

## Chart-Bibliothek (Recharts)

**Verwendete Charts:**

| Chart-Typ | Verwendung | Seite |
|-----------|-----------|-------|
| **LineChart** | Gewicht, Körperfett, Umfänge, Vitalwerte | Dashboard, History |
| **BarChart** | Wöchentliche Ernährung, Aktivität | NutritionPage, History |
| **PieChart** | Trainingstypen-Verteilung | Dashboard, ActivityPage |
| **ScatterChart** | Korrelationen (Gewicht vs. Kalorien) | NutritionPage |
| **ComposedChart** | Multi-Axis (Gewicht + KF% kombiniert) | History |

**Standard-Konfiguration:**
```jsx
<ResponsiveContainer width="100%" height={240}>
  <LineChart data={data}>
    <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
    <XAxis dataKey="date" tick={{ fontSize: 11 }} stroke="var(--text3)" />
    <YAxis tick={{ fontSize: 11 }} stroke="var(--text3)" />
    <Tooltip contentStyle={{ background: 'var(--surface)', border: '1px solid var(--border)' }} />
    <Line type="monotone" dataKey="weight" stroke="var(--accent)" strokeWidth={2} dot={false} />
  </LineChart>
</ResponsiveContainer>
```

**Farb-Schema:**
- Gewicht: `var(--accent)` (#1D9E75)
- Körperfett: `#D85A30` (Danger)
- Umfänge: Individual Colors (siehe `CIRCUMFERENCE_GUIDE`)
- 7-Tage-Durchschnitt: `var(--accent-dark)` (#0a5c43, gestrichelt)

---

## Feature Usage Badges (v9c Phase 3)

**Zweck:** Sichtbarkeit der Feature-Limits direkt in der UI

**Komponenten:**

### 1. UsageBadge (`components/UsageBadge.jsx`)

**Inline-Badge mit Limit-Status:**
```jsx
<UsageBadge feature="weight_entries" />
// Rendert: "5/10" (grün) oder "10/10 🔒" (rot)
```

**Logik:**
```javascript
const { allowed, used, limit, remaining } = await api.getFeatureUsage()
const color = allowed ? 'var(--accent)' : 'var(--danger)'
const text = limit === null ? '∞' : `${used}/${limit}`
```

**Verwendung:** In Buttons (z.B. "Speichern 5/10")

### 2. FeatureUsageOverview (`components/FeatureUsageOverview.jsx`)

**Tabelle mit allen Features:**
```jsx
<FeatureUsageOverview />
```

**Darstellung:**
| Feature | Genutzt | Limit | Verbleibend | Status |
|---------|---------|-------|-------------|--------|
| Gewichtseinträge | 45 | 100 | 55 | ✓ OK |
| KI-Aufrufe | 10 | 10 | 0 | 🔒 Limit erreicht |
| Daten-Export | 1 | 5 | 4 | ✓ OK |

**Farbcodierung:**
- Grün: `allowed === true`
- Rot: `allowed === false`
- Gelb: `remaining < 10% && allowed`

**Location:** Settings-Seite (Tab "Quota")

---

## Routing-Architektur

### App-Struktur (`App.jsx`)

```
AuthProvider
  ↓
ProfileProvider
  ↓
BrowserRouter
  ↓
AppShell
  ├── Public Routes (ohne Auth)
  │   ├── /register → Register
  │   ├── /verify?token=... → Verify
  │   └── /reset-password?token=... → ResetPassword
  │
  ├── Auth Gates
  │   ├── authLoading → Spinner
  │   ├── needsSetup → SetupScreen
  │   └── !session → LoginScreen
  │
  └── Authenticated Routes
      ├── Header (Logo + Logout + Avatar)
      ├── Main (Scrollable Content)
      │   └── Routes (31 Seiten)
      └── Nav (Bottom Navigation, 5 Items)
```

### Navigation-Items

```javascript
const links = [
  { to: '/',         icon: <LayoutDashboard/>, label: 'Übersicht' },
  { to: '/capture',  icon: <PlusSquare/>,      label: 'Erfassen' },
  { to: '/history',  icon: <TrendingUp/>,      label: 'Verlauf' },
  { to: '/analysis', icon: <BarChart2/>,       label: 'Analyse' },
  { to: '/settings', icon: <Settings/>,        label: 'Einst.' },
]
```

**Besonderheit:** Active-State via React Router `NavLink` (`isActive` prop)

### Route-Guards

**Auth-Schutz:**
```jsx
if (!session) return <LoginScreen/>
```

**Admin-Schutz:**
```jsx
// In AdminPanel-Seiten:
const { isAdmin } = useAuth()
if (!isAdmin) return <div>Nur für Admins</div>
```

**Setup-Check:**
```jsx
if (needsSetup) return <SetupScreen/>
```

---

## Performance-Optimierungen

### 1. Code Splitting

**React.lazy() für Admin-Seiten:**
```javascript
const AdminPanel = React.lazy(() => import('./pages/AdminPanel'))
```

**Vorteil:** Admin-Code nicht im Initial Bundle (~80 KB gespart)

### 2. Memoization

**useMemo für teure Berechnungen:**
```javascript
const stats = useMemo(() => {
  return calculateStats(data)
}, [data])
```

**Verwendung:** Chart-Daten-Transformation, Aggregationen

### 3. Lazy Loading

**Images:**
```jsx
<img src={photoUrl} loading="lazy" />
```

**Charts:**
- Nur sichtbare Charts rendern (Intersection Observer in Planung)

### 4. API-Call-Batching

**Parallel-Loading:**
```javascript
const [stats, insights, weights] = await Promise.all([
  api.getStats(),
  api.latestInsights(),
  api.listWeight(30),
])
```

**Verwendung:** Dashboard initial load

---

## Error-Handling

### 1. API-Fehler

**Pattern in allen Seiten:**
```javascript
const [error, setError] = useState(null)

try {
  const data = await api.someEndpoint()
  setData(data)
} catch(e) {
  setError(e.message)  // api.js parsed bereits {detail: "..."}
} finally {
  setLoading(false)
}
```

**Anzeige:**
```jsx
{error && (
  <div style={{
    background: 'rgba(216,90,48,0.1)',
    color: 'var(--danger)',
    padding: '10px 14px',
    borderRadius: 8,
    border: '1px solid rgba(216,90,48,0.2)'
  }}>
    {error}
  </div>
)}
```

### 2. Network-Fehler

**Offline-Detection:**
```javascript
useEffect(() => {
  const handleOnline = () => setOnline(true)
  const handleOffline = () => setOnline(false)
  window.addEventListener('online', handleOnline)
  window.addEventListener('offline', handleOffline)
  return () => {
    window.removeEventListener('online', handleOnline)
    window.removeEventListener('offline', handleOffline)
  }
}, [])
```

**Anzeige:** Banner "Keine Internetverbindung – Änderungen werden gespeichert sobald Online"

### 3. Form-Validierung

**Client-Side:**
```javascript
if (!weight || weight < 20 || weight > 300) {
  setError('Gewicht zwischen 20 und 300 kg')
  return
}
```

**Server-Side:**
- Backend wirft `HTTPException(400, detail="...")` → Frontend zeigt `detail`

---

## Besonderheiten & Design-Entscheidungen

### 1. Warum kein TypeScript?

**Entscheidung:** Bewusst auf TypeScript verzichtet

**Gründe:**
- Schnellere Prototyping-Geschwindigkeit
- Weniger Build-Komplexität
- JSDoc-Kommentare für Dokumentation ausreichend
- Type Safety durch Backend (Pydantic validiert alle Inputs)

### 2. Warum Context statt Redux?

**Entscheidung:** Context API ausreichend für diesen Use-Case

**Gründe:**
- Nur 2 globale States (Auth + Profile)
- Kein komplexes State-Update-Pattern nötig
- Weniger Boilerplate
- Performance ausreichend (keine häufigen Re-Renders)

**Hinweis:** Bei >5 Contexts würde Redux Sinn machen

### 3. Warum Custom Markdown statt remark/rehype?

**Entscheidung:** Eigener Markdown-Renderer (134 Zeilen)

**Gründe:**
- Nur Subset von Markdown benötigt (Headings, Bold, Listen)
- remark + rehype + plugins = ~200 KB Bundle-Size
- Custom-Renderer = 0 Dependencies
- Full Control über Styling

### 4. Warum Recharts statt Chart.js?

**Entscheidung:** Recharts für alle Charts

**Gründe:**
- React-native (kein Canvas, sondern SVG)
- Declarative API passt zu React
- Responsive by default
- Kleineres Bundle als Chart.js

### 5. Inline-Editing statt Modal-Forms

**Entscheidung:** Inline-Edit für alle Listen (Gewicht, Ernährung, Vitalwerte)

**Pattern:**
```javascript
const [editingId, setEditingId] = useState(null)

{entries.map(e => (
  editingId === e.id
    ? <EditForm entry={e} onSave={...} onCancel={...} />
    : <ViewRow entry={e} onEdit={() => setEditingId(e.id)} />
))}
```

**Vorteil:**
- Keine Modal-Komponente nötig
- Besserer Mobile-UX (kein Overlay)
- Schnelleres Editing (kein Dialog öffnen)

---

## Bekannte Limitationen

### 1. Desktop-Optimierung

**Problem:** App ist Mobile-First, Desktop-Layout nicht optimiert

**Aktuell:** Max-Width 600px, zentriert auf Desktop

**Geplant (v10+):** Responsive Grid-Layout für Desktop (Sidebar + Multi-Column)

### 2. Offline-Modus

**Problem:** Service Worker cached nur Static Assets, nicht API-Responses

**Aktuell:** Offline = Keine Daten-Eingabe möglich

**Geplant (v10+):** IndexedDB für Offline-Queue

### 3. Multi-Profil-Support

**Problem:** Profile-Wechsel funktioniert, aber Session ist Single-User

**Aktuell:** Logout + Login für Profil-Wechsel

**Geplant (v9f+):** Multi-Session-Support (Switch ohne Logout)

### 4. Accessibility

**Problem:** ARIA-Labels fehlen, Keyboard-Navigation unvollständig

**Aktuell:** Maus/Touch-optimiert

**Geplant (v10+):** WCAG 2.1 AA Compliance

---

## Testing

**Aktueller Stand:** Kein automatisiertes Testing implementiert

**Geplant (v10+):**
- Unit-Tests: Vitest für utils (calc.js, interpret.js)
- Component-Tests: React Testing Library
- E2E-Tests: Playwright für kritische Flows (Login, Gewicht-Eingabe, KI-Analyse)

**Manual Testing:**
- Alle Features manuell auf iOS Safari, Android Chrome, Desktop Firefox getestet
- Regression-Tests bei jedem Deploy

---

## Zusammenfassung

**Architektur-Highlights:**
- ✅ 31 Seiten (22 User, 9 Admin)
- ✅ Context-basiertes State Management (Auth + Profile)
- ✅ Zentrale API-Schnittstelle (api.js)
- ✅ Berechnungs-Utils für Body-Metrics (calc.js, interpret.js)
- ✅ CSS-Variablen für Light/Dark Mode
- ✅ PWA mit Service Worker
- ✅ Recharts für alle Charts
- ✅ Feature Usage Badges (v9c Phase 3)
- ✅ Mobile-First Design (max-width 600px)
- ✅ Inline-Editing statt Modals
- ✅ Custom Markdown-Renderer (0 Dependencies)

**Performance:**
- Initial Bundle: ~450 KB (gzip)
- Code Splitting: Admin-Seiten lazy-loaded
- API-Call-Batching: Parallel-Loading für Dashboard

**Nächste Schritte:**
- Testing (Vitest + React Testing Library)
- Desktop-Responsive-Layout
- Offline-Modus (IndexedDB)
- Accessibility (WCAG 2.1 AA)
