Added detailed documentation for: - Color system (CSS variables) - CSS class conventions - Spacing & sizing standards - Component patterns (loading, error, empty states) - Jinkendo logo system - Available custom commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
Mitai Jinkendo – Entwickler-Kontext für Claude Code
Projekt-Übersicht
Mitai Jinkendo (身体 Jinkendo) ist eine selbst-gehostete PWA für Körper-Tracking (Gewicht, Körperfett, Umfänge, Ernährung, Aktivität) mit KI-Auswertung. Teil der Jinkendo-App-Familie (人拳道 – Der menschliche Weg der Kampfkunst).
Produktfamilie: mitai · miken · ikigai · shinkan · kenkou (alle unter jinkendo.de)
Tech-Stack
| Komponente | Technologie | Version |
|---|---|---|
| Frontend | React 18 + Vite + PWA | Node 20 |
| Backend | FastAPI (Python) | Python 3.12 |
| Datenbank | SQLite (v9a) → PostgreSQL (v9b geplant) | - |
| Container | Docker + Docker Compose | - |
| Webserver | nginx (Reverse Proxy) | Alpine |
| Auth | Token-basiert + bcrypt | - |
| KI | OpenRouter API (claude-sonnet-4) | - |
Ports
| Service | Prod | Dev |
|---|---|---|
| Frontend | 3002 | 3099 |
| Backend | 8002 | 8099 |
Verzeichnisstruktur
mitai-jinkendo/
├── backend/
│ ├── main.py # FastAPI App, alle Endpoints (~2000 Zeilen)
│ ├── requirements.txt
│ └── Dockerfile
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Root, Auth-Gates, Navigation
│ │ ├── app.css # Globale Styles, CSS-Variablen
│ │ ├── context/
│ │ │ ├── AuthContext.jsx # Session, Login, Logout
│ │ │ └── ProfileContext.jsx # Aktives Profil
│ │ ├── pages/ # Alle Screens
│ │ └── utils/
│ │ ├── api.js # Alle API-Calls (injiziert Token automatisch)
│ │ ├── calc.js # Körperfett-Formeln
│ │ ├── interpret.js # Regelbasierte Auswertung
│ │ ├── Markdown.jsx # Eigener MD-Renderer
│ │ └── guideData.js # Messanleitungen
│ └── public/ # Icons (Jinkendo Ensō-Logo)
├── .gitea/workflows/
│ ├── deploy-prod.yml # Auto-Deploy bei Push auf main
│ ├── deploy-dev.yml # Auto-Deploy bei Push auf develop
│ └── test.yml # Build-Test bei jedem Push
├── docker-compose.yml # Produktion (Ports 3002/8002)
├── docker-compose.dev-env.yml # Development (Ports 3099/8099)
└── CLAUDE.md # Diese Datei
Aktuelle Version: v9a
Was implementiert ist:
- ✅ Multi-User mit E-Mail + Passwort Login (bcrypt)
- ✅ Auth-Middleware auf ALLE Endpoints (44 Endpoints geschützt)
- ✅ Rate Limiting (Login: 5/min, Reset: 3/min)
- ✅ CORS konfigurierbar via ALLOWED_ORIGINS in .env
- ✅ Admin/User Rollen, KI-Limits, Export-Berechtigungen
- ✅ Gewicht, Umfänge, Caliper (4 Formeln), Ernährung, Aktivität
- ✅ FDDB CSV-Import (Ernährung), Apple Health CSV-Import (Aktivität)
- ✅ KI-Analyse: 6 Einzel-Prompts + 3-stufige Pipeline (parallel)
- ✅ Konfigurierbare Prompts mit Template-Variablen
- ✅ Verlauf mit 5 Tabs + Zeitraumfilter + KI pro Sektion
- ✅ Dashboard mit Kennzahlen, Zielfortschritt, Combo-Chart
- ✅ Assistent-Modus (Schritt-für-Schritt Messung)
- ✅ PWA (iPhone Home Screen), Jinkendo Ensō-Logo
- ✅ E-Mail (SMTP) für Password-Recovery
- ✅ Admin-Panel: User verwalten, KI-Limits, E-Mail-Test
- ✅ Multi-Environment: Prod (mitai.jinkendo.de) + Dev (dev.mitai.jinkendo.de)
- ✅ Gitea CI/CD mit Auto-Deploy auf Raspberry Pi 5
Was in v9b kommt:
- 🔲 PostgreSQL Migration (aktuell noch SQLite)
- 🔲 Selbst-Registrierung mit E-Mail-Bestätigung
- 🔲 Freemium Tier-System (free/basic/premium/selfhosted)
- 🔲 14-Tage Trial automatisch
- 🔲 Einladungslinks für Beta-Nutzer
- 🔲 Admin kann Tiers manuell setzen
Was in v9c kommt:
- 🔲 OAuth2-Grundgerüst für Fitness-Connectoren
- 🔲 Strava Connector
- 🔲 Withings Connector (Waage)
- 🔲 Garmin Connector
Deployment
Infrastruktur
Internet → privat.stommer.com (Fritz!Box DynDNS)
→ Synology NAS (Reverse Proxy + Let's Encrypt)
→ Raspberry Pi 5 (192.168.2.49, Docker)
Git Workflow
develop branch → Auto-Deploy → dev.mitai.jinkendo.de (Port 3099/8099)
main branch → Auto-Deploy → mitai.jinkendo.de (Port 3002/8002)
Deployment-Befehle (manuell falls nötig)
# Prod
cd /home/lars/docker/bodytrack
docker compose -f docker-compose.yml build --no-cache
docker compose -f docker-compose.yml up -d
# Dev
cd /home/lars/docker/bodytrack-dev
docker compose -f docker-compose.dev-env.yml build --no-cache
docker compose -f docker-compose.dev-env.yml up -d
Datenbank-Schema (SQLite, v9a)
Wichtige Tabellen:
profiles– Nutzer (role, pin_hash/bcrypt, email, auth_type, ai_enabled)sessions– Auth-Tokens mit Ablaufdatumweight_log– Gewichtseinträge (profile_id, date, weight)circumference_log– 8 Umfangspunktecaliper_log– Hautfaltenmessung, 4 Methodennutrition_log– Kalorien + Makros (aus FDDB-CSV)activity_log– Training (aus Apple Health oder manuell)ai_insights– KI-Auswertungen (scope = prompt-slug)ai_prompts– Konfigurierbare Prompts mit Templates (11 Prompts)ai_usage– KI-Calls pro Tag pro Profil
Auth-Flow (v9a)
Login-Screen → E-Mail + Passwort → Token im localStorage
Token → X-Auth-Token Header → Backend require_auth()
Profile-Id → aus Session (nicht aus Header!)
SHA256 Passwörter → automatisch zu bcrypt migriert beim Login
API-Konventionen
- Alle Endpoints:
/api/... - Auth-Header:
X-Auth-Token: <token> - Responses: immer JSON
- Fehler:
{"detail": "Fehlermeldung"} - Rate Limit überschritten: HTTP 429
Umgebungsvariablen (.env)
OPENROUTER_API_KEY= # KI-Calls
OPENROUTER_MODEL=anthropic/claude-sonnet-4
SMTP_HOST= # E-Mail
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
SMTP_FROM=
APP_URL=https://mitai.jinkendo.de
ALLOWED_ORIGINS=https://mitai.jinkendo.de
DATA_DIR=/app/data
PHOTOS_DIR=/app/photos
Wichtige Hinweise für Claude Code
- Ports immer 3002/8002 (Prod) oder 3099/8099 (Dev) – nie ändern
- npm install (nicht npm ci) – kein package-lock.json vorhanden
- SQLite safe_alters – neue Spalten immer via safe_alters Liste
- Pipeline-Prompts haben slug-Prefix
pipeline_– nie als Einzelanalyse zeigen - dayjs.week() braucht Plugin – stattdessen native JS ISO-Wochenberechnung
- useNavigate() nur in React-Komponenten, nicht in Helper-Functions
- api.js nutzen für alle API-Calls – injiziert Token automatisch
- bcrypt für alle neuen Passwort-Operationen verwenden
- session=Depends(require_auth) als separater Parameter – nie in Header() einbetten
Code-Style
- React: Functional Components, Hooks
- CSS: Inline-Styles + globale CSS-Variablen (var(--accent), var(--text1), etc.)
- API-Calls: immer über
api.js(injiziert Token automatisch) - Kein TypeScript (bewusst, für Einfachheit)
- Python: keine Type-Hints Pflicht, aber bei neuen Funktionen erwünscht
Design-System
Farben (CSS-Variablen)
--accent: #1D9E75 /* Jinkendo Grün – Buttons, Links, Akzente */
--accent-dark: #085041 /* Dunkelgrün – Icon-Hintergrund, Header */
--accent-light: #E1F5EE /* Hellgrün – Hintergründe, Badges */
--bg: /* Seitenhintergrund (hell/dunkel auto) */
--surface: /* Card-Hintergrund */
--surface2: /* Sekundäre Fläche */
--border: /* Rahmen */
--text1: /* Primärer Text */
--text2: /* Sekundärer Text */
--text3: /* Muted Text, Labels */
--danger: #D85A30 /* Fehler, Warnungen */
CSS-Klassen
.card /* Weißer Container, border-radius 12px, box-shadow */
.btn /* Basis-Button */
.btn-primary /* Grüner Button (#1D9E75) */
.btn-secondary /* Grauer Button */
.btn-full /* 100% Breite */
.form-input /* Eingabefeld, volle Breite */
.form-label /* Feldbezeichnung, klein, uppercase */
.form-row /* Label + Input + Unit nebeneinander */
.form-unit /* Einheit rechts (kg, cm, etc.) */
.section-gap /* margin-bottom zwischen Sektionen */
.spinner /* Ladekreis, animiert */
Abstände & Größen
Seiten-Padding: 16px seitlich
Card-Padding: 16-20px
Border-Radius: 12px (Cards), 8px (Buttons/Inputs), 50% (Avatare)
Icon-Größe: 16-20px inline, 24px standalone
Font-Größe: 12px (Labels), 14px (Body), 16-18px (Subtitel), 20-24px (Titel)
Font-Weight: 400 (normal), 600 (semi-bold), 700 (bold)
Bottom-Padding: 80px (für Mobile-Navigation)
Komponenten-Muster
Titelzeile einer Seite:
<div style={{display:'flex',alignItems:'center',
justifyContent:'space-between',marginBottom:20}}>
<div style={{fontSize:20,fontWeight:700,color:'var(--text1)'}}>
Seitentitel
</div>
<button className="btn btn-primary">Aktion</button>
</div>
Ladezustand:
if (loading) return (
<div style={{display:'flex',justifyContent:'center',padding:40}}>
<div className="spinner"/>
</div>
)
Fehlerzustand:
if (error) return (
<div style={{color:'var(--danger)',padding:16,textAlign:'center'}}>
{error}
</div>
)
Leerer Zustand:
{items.length === 0 && (
<div style={{textAlign:'center',padding:40,color:'var(--text3)'}}>
<div style={{fontSize:32,marginBottom:8}}>📭</div>
<div>Noch keine Einträge</div>
</div>
)}
Metric Card:
<div className="card" style={{padding:16,textAlign:'center'}}>
<div style={{fontSize:12,color:'var(--text3)',marginBottom:4}}>LABEL</div>
<div style={{fontSize:24,fontWeight:700,color:'var(--accent)'}}>
{value}
</div>
<div style={{fontSize:12,color:'var(--text3)'}}>Einheit</div>
</div>
Jinkendo Logo-System
Grundelement: Ensō-Kreis (offen, Lücke 4-5 Uhr)
Farbe Ensō: #1D9E75
Hintergrund: #085041 (dunkelgrün)
Kern-Symbol: #5DCAA5 (mintgrün)
Wortmarke: Jin(light) + ken(bold #1D9E75) + do(light)
Verfügbare Custom Commands
/deploy → Commit + Push vorbereiten
/merge-to-prod → develop → main mergen
/test → Manuelle Tests durchführen
/new-feature → Neues Feature-Template
/ui-component → Neue Komponente erstellen
/ui-page → Neue Seite erstellen
/fix-bug → Bug analysieren und beheben
/add-endpoint → Neuen API-Endpoint hinzufügen
/db-add-column → Neue DB-Spalte hinzufügen