# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

# Krafttrainer

Einzelnutzer-Webapplikation zur Verwaltung und Protokollierung von Kraftübungen.

## Tech Stack

- **Backend:** Go 1.22+ mit `net/http` stdlib Router, SQLite (`go-sqlite3`), `golang-migrate`
- **Frontend:** React 19, Vite 8, TypeScript (strict), Tailwind CSS 4 (`@tailwindcss/vite`), Zustand, Recharts
- **Paketmanager:** pnpm (Version in `packageManager` field fixiert)
- **Produktion:** Single Binary via `embed` (Frontend in `backend/static/` eingebettet)

## Build & Run

```bash
make dev-backend    # Go-Server auf :8090
make dev-frontend   # Vite Dev-Server auf :5173 (Proxy → :8090)
make build          # Single Binary ./krafttrainer (Frontend-Build + Go-Build mit CGO_ENABLED=1)
make clean          # Binary + Dist-Ordner entfernen
```

Frontend TypeScript-Typecheck (ohne Build):
```bash
cd frontend && pnpm exec tsc --noEmit
```

Es gibt keine automatisierten Tests (weder Go-Tests noch Frontend-Tests).

## Deployment

- Läuft auf `192.168.1.118:8090` als systemd-Service (`krafttrainer.service`)
- User: `christoph`, Binary: `/home/christoph/krafttrainer/krafttrainer`
- DB: `/home/christoph/krafttrainer/krafttrainer.db`
- Deploy: `scp krafttrainer christoph@192.168.1.118:~/krafttrainer/`, dann `sudo systemctl restart krafttrainer`

## Architektur

### Backend-Flow

`Handler → Store → DB`. Jeder Handler folgt exakt diesem Muster:
1. `decodeJSON()` mit `DisallowUnknownFields()`
2. `model.Validate()` aufrufen
3. Store-Methode aufrufen
4. Fehler differenzieren und `writeJSON()` / `writeError()` aufrufen

Store-Methoden geben nach Mutationen immer **frisch aus der DB gelesene Objekte** zurück (kein Rekonstruieren aus Input).

### Fehlerbehandlung (Backend)

- Store-Fehler → 500 (generisch, kein DB-Leak)
- Validierungsfehler → 400
- Nicht gefunden (`sql.ErrNoRows`) → 404
- UNIQUE-Verletzung → 409

Sentinel-Strings im Error-Message für Handler-Differenzierung: `"UNIQUE_VIOLATION:"`, `"SESSION_CLOSED"`. Diese werden mit `strings.Contains()` geprüft — kein custom error type.

### Routing

Go 1.22+ Pattern-Matching im stdlib ServeMux mit `{id}`-Platzhaltern. Middleware-Chain: `Recoverer → RequestLogger → CORS`. SPA-Fallback in `main.go`: prüft zuerst ob statische Datei existiert, serviert sonst `index.html`.

### Frontend-Stores (Zustand)

Stores sind **flach und unabhängig** — keine direkte Store-zu-Store-Kommunikation. Feedback läuft ausschließlich über `useToastStore.getState().addToast()`. `activeSessionStore` verwaltet einen Timer-Interval manuell (kein React-Effect-Cleanup — beim `stopTimer()` explizit clearen).

Alle HTTP-Aufrufe gehen über `src/api/client.ts`. `ApiError` (extends Error) hat `status`-Property für HTTP-Statuscodes.

### Datenbank

- SQLite mit WAL-Mode und Foreign Keys (via Connection-String-Parameter in `store.go`)
- Migrations auto-run beim Start via embedded FS (`backend/migrations/embed.go`)
- `exercise_name` in `session_logs` **denormalisiert** gespeichert (damit gelöschte Übungen historische Daten nicht verwaisen lassen)
- UNIQUE-Constraint auf `(session_id, exercise_id, set_number)`
- Soft-Delete bei Übungen via `deleted_at` Timestamp

## Konventionen

- **API Prefix:** Alle Endpoints unter `/api/v1`. Fehler als `{ "error": "..." }`.
- **Gewichte:** Immer in kg. Feldnamen mit `_kg` Suffix (`weight_kg`, `weight_step_kg`).
- **UI-Sprache:** Deutsch.
- **Dark Mode:** Default. `bg-gray-950` Body, `bg-gray-900` Cards, `blue-500` Primary.
- **Touch-Targets:** min 44×44px.

## Vollständige Spezifikation

Siehe `PRD.md` im übergeordneten Verzeichnis (`03_Projekte/fitness-pad/PRD.md`) für:
- Vollständiges SQLite-Schema (Abschnitt 4.1)
- Alle Go-Structs und TypeScript-Typen (Abschnitt 4.2–4.3)
- Komplette API-Spezifikation mit Request/Response-Beispielen (Abschnitt 5)
- Validierungsregeln (Abschnitt 6)
- Frontend-Spezifikation und Komponentenverhalten (Abschnitt 7)
