# Shinkan Jinkendo – Technisches Setup

> **Trainer- und Vereinsplattform für Kampfsport-Trainingsplanung**  
> Basis: Mitai Jinkendo Ökosystem  
> Domain: shinkan.jinkendo.de (真観 - "Wahre Beobachtung")

---

## 1. Fachliche Einordnung

**Shinkan ist KEINE persönliche Tracking-App.**

Shinkan ist eine **trainer- und vereinszentrierte Planungs- und Inhaltsplattform** mit Fokus auf:
- Übungsverwaltung und -suche
- Trainingsplanung für Gruppen
- Standardisierung und Wiederverwendung
- Freigabe und Governance von Inhalten
- Import aus bestehendem MediaWiki

**Primäre Nutzer in MVP:** Trainer, Vereinsadmins, Redakteure  
**Nicht in MVP:** Individuelle Sportler-Apps, persönliches Tracking, Gürtel-Tracking

**Abgrenzung zu Mitai:**
- Mitai = persönliches Körper- und Trainings-Tracking
- Shinkan = Trainer-Arbeit, Übungen, Trainingsplanung, Vereinsstandards

---

## 2. Übernommene Infrastruktur von Mitai

| Komponente | Status | Verwendung |
|-----------|---------|------------|
| Auth-System | ✅ 1:1 übernehmen | Token + bcrypt |
| Membership-Basis | ✅ Übernehmen | Tiers, Features (vereinfacht) |
| User-Profiles | ✅ Übernehmen | Multi-Profile pro Account |
| Tech-Stack | ✅ 1:1 | React 18 + FastAPI + PostgreSQL 16 |
| Docker/Compose | ✅ 1:1 | Dev + Prod Umgebungen |
| PWA | ✅ 1:1 | Mobile + Desktop |
| Design-System | ✅ Basis | CSS-Variablen, mobile-first |
| Versioning | ✅ 1:1 | version.py + Migrations |
| Gitea CI/CD | ✅ 1:1 | Auto-Deploy dev/prod |

**Ports:**
- Prod: 3003/8003 (Frontend/Backend)
- Dev:  3098/8098 (Frontend/Backend)

---

## 3. Technische Basis (identisch zu Mitai)

```
Frontend:  React 18 + Vite + PWA (Node 20)
Backend:   FastAPI Python 3.12
Datenbank: PostgreSQL 16 Alpine
Container: Docker + Docker Compose
Auth:      Token-basiert + bcrypt
KI:        OpenRouter API (optional, nicht MVP-kritisch)
```

---

## 4. Verzeichnisstruktur (angepasst)

```
shinkan-jinkendo/
├── backend/
│   ├── main.py                 # App-Setup + Router-Registration
│   ├── db.py                   # PostgreSQL Connection Pool
│   ├── db_init.py              # DB-Init + Migrations
│   ├── auth.py                 # Hash, Verify, Sessions, Feature-Access
│   ├── models.py               # Pydantic Models
│   ├── version.py              # Versionskontrolle
│   ├── migrations/             # SQL-Migrationen (001_*.sql)
│   └── routers/
│       ├── auth.py             # Login, Register, Sessions
│       ├── profiles.py         # User-Profiles
│       ├── clubs.py            # Vereine / Sparten
│       ├── groups.py           # Trainingsgruppen
│       ├── skills.py           # Fähigkeiten (Admin + CRUD)
│       ├── methods.py          # Trainingsmethoden (Admin + CRUD)
│       ├── exercises.py        # Übungen (Kern-Modul)
│       ├── training_units.py   # Trainingseinheiten
│       ├── training_programs.py # Trainingsprogramme
│       ├── planning.py         # Planungsansichten
│       ├── import_wiki.py      # MediaWiki-Import
│       ├── admin.py            # Admin-Panel
│       └── membership.py       # Subscription (vereinfacht)
│
├── frontend/
│   ├── src/
│   │   ├── App.jsx             # Root, Auth-Gates, Navigation
│   │   ├── app.css             # CSS-Variablen + Styles
│   │   ├── version.js          # Frontend-Versionierung
│   │   ├── config/
│   │   │   ├── appNav.js       # Hauptnavigation
│   │   │   └── adminNav.js     # Admin-Navigation
│   │   ├── context/
│   │   │   ├── AuthContext.jsx
│   │   │   └── ProfileContext.jsx
│   │   ├── pages/
│   │   │   ├── Dashboard.jsx
│   │   │   ├── ExercisesPage.jsx      # Übungssuche + CRUD
│   │   │   ├── PlanningPage.jsx       # Trainingsplanung
│   │   │   ├── GroupsPage.jsx         # Gruppenverwaltung
│   │   │   ├── SkillsPage.jsx         # Fähigkeiten (Admin)
│   │   │   ├── MethodsPage.jsx        # Methoden (Admin)
│   │   │   ├── ImportPage.jsx         # MediaWiki-Import
│   │   │   └── SettingsPage.jsx
│   │   └── utils/
│   │       ├── api.js          # ALLE API-Calls
│   │       └── planning.js     # Planungshelfer
│   └── public/
│       ├── manifest.json       # PWA-Manifest
│       └── icons/
│
├── .claude/
│   ├── commands/               # Slash-Commands
│   ├── docs/
│   │   ├── functional/
│   │   │   └── SHINKAN_REQUIREMENTS.md  # Anforderungsdokument
│   │   ├── technical/
│   │   │   └── SHINKAN_DOMAIN_MODEL.md  # Domänenmodell
│   │   └── rules/              # Architektur-Regeln (wie Mitai)
│   └── library/
│
├── docker-compose.yml          # Production
├── docker-compose.dev-env.yml  # Development
├── .env
├── .gitignore
└── CLAUDE.md                   # Agent-Einstieg
```

---

## 5. Domänenmodell (MVP Core)

### 5.1 Organisationsstruktur

```sql
-- Vereine
clubs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(50),
    description TEXT,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

-- Sparten (optional, kann auch später)
divisions (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    name VARCHAR(200) NOT NULL,
    focus_area VARCHAR(100),  -- karate, selbstverteidigung, gewaltschutz
    created_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsgruppen
training_groups (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    name VARCHAR(200) NOT NULL,
    focus VARCHAR(100),
    level VARCHAR(50),
    age_group VARCHAR(50),
    weekday VARCHAR(20),
    time_start TIME,
    time_end TIME,
    location VARCHAR(200),
    trainer_id INT REFERENCES profiles(id),
    co_trainer_ids JSONB,  -- [1, 2, 3]
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)
```

### 5.2 Fähigkeiten und Methoden (Kataloge)

```sql
-- Fähigkeiten (global)
skills (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(100),  -- kihon, kumite, kata, selbstverteidigung, fitness
    description TEXT,
    importance INT,  -- 1-5
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsmethoden (global)
training_methods (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(20),
    category VARCHAR(100),  -- intervall, rollenspiel, zirkel, koordination, etc.
    description TEXT,
    typical_duration INT,  -- in Minuten
    typical_group_size VARCHAR(50),
    related_skills JSONB,  -- [skill_id, skill_id, ...]
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
)
```

### 5.3 Übungen (Kernobjekt)

```sql
-- Übungen
exercises (
    id SERIAL PRIMARY KEY,
    title VARCHAR(300) NOT NULL,
    summary TEXT,  -- Kurzbeschreibung
    goal TEXT NOT NULL,  -- Ziel der Übung
    execution TEXT NOT NULL,  -- Durchführung
    preparation TEXT,  -- Vorbereitung / Aufbau
    trainer_notes TEXT,  -- Hinweise für Trainer
    equipment JSONB,  -- ["Matten", "Pratzen", "Bälle"]
    
    -- Metadaten
    duration_min INT,
    duration_max INT,
    group_size_min INT,
    group_size_max INT,
    age_groups JSONB,  -- ["minis", "kinder", "erwachsene"]
    
    -- Fachliche Zuordnung
    focus_area VARCHAR(100),  -- karate, selbstverteidigung, gewaltschutz
    secondary_areas JSONB,
    training_character VARCHAR(50),  -- grundlage, aufbau, vertiefung, festigung, diagnose
    
    -- Methode
    primary_method_id INT REFERENCES training_methods(id),
    secondary_method_ids JSONB,
    
    -- Freigabe
    visibility VARCHAR(50) DEFAULT 'private',  -- private, club, official
    status VARCHAR(50) DEFAULT 'draft',  -- draft, in_review, approved, archived
    created_by INT REFERENCES profiles(id),
    club_id INT REFERENCES clubs(id),
    
    -- Import
    import_source VARCHAR(50),  -- mediawiki, manual
    import_id VARCHAR(200),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Übungs-Fähigkeiten (M:N)
exercise_skills (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    skill_id INT REFERENCES skills(id),
    is_primary BOOLEAN DEFAULT false,
    intensity INT,  -- 1-5
    development_contribution VARCHAR(50),  -- low, medium, high
    required_level INT,  -- Eingangs-Niveau
    target_level INT,  -- Ziel-Niveau
    created_at TIMESTAMP DEFAULT NOW()
)

-- Übungsvarianten
exercise_variants (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    variant_name VARCHAR(200) NOT NULL,
    description TEXT,
    execution_changes TEXT,
    duration_min INT,
    duration_max INT,
    equipment_changes JSONB,
    difficulty_adjustment VARCHAR(50),  -- easier, harder, adapted
    age_group_override JSONB,
    skill_focus_override JSONB,
    created_at TIMESTAMP DEFAULT NOW()
)

-- Medien (1:N)
exercise_media (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    media_type VARCHAR(50),  -- image, video, document, sketch
    file_path TEXT,
    title VARCHAR(200),
    description TEXT,
    sort_order INT,
    is_primary BOOLEAN DEFAULT false,
    context VARCHAR(100),  -- ablauf, detail, trainer_hint
    created_at TIMESTAMP DEFAULT NOW()
)
```

### 5.4 Trainingsplanung

```sql
-- Training Templates / Standards (vereinbar)
training_templates (
    id SERIAL PRIMARY KEY,
    name VARCHAR(300) NOT NULL,
    type VARCHAR(50),  -- template, standard
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    
    goal TEXT,
    focus_areas JSONB,
    duration_total INT,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    version INT DEFAULT 1,
    created_by INT REFERENCES profiles(id),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsabschnitte (für Templates + Units)
training_sections (
    id SERIAL PRIMARY KEY,
    parent_type VARCHAR(50),  -- template, unit
    parent_id INT,
    
    title VARCHAR(200) NOT NULL,
    section_type VARCHAR(100),  -- aufwaermen, kihon, kumite, kata, abschluss, etc.
    sort_order INT,
    duration_planned INT,
    goal TEXT,
    notes TEXT,
    
    is_combination BOOLEAN DEFAULT false,  -- Übungen als Komplex
    combination_method_id INT REFERENCES training_methods(id),
    
    created_at TIMESTAMP DEFAULT NOW()
)

-- Übungen in Abschnitten
section_exercises (
    id SERIAL PRIMARY KEY,
    section_id INT REFERENCES training_sections(id) ON DELETE CASCADE,
    exercise_id INT REFERENCES exercises(id),
    variant_id INT REFERENCES exercise_variants(id),
    sort_order INT,
    duration_planned INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
)

-- Konkrete Trainingseinheiten
training_units (
    id SERIAL PRIMARY KEY,
    group_id INT REFERENCES training_groups(id),
    date DATE NOT NULL,
    time_start TIME,
    time_end TIME,
    
    derived_from_template_id INT REFERENCES training_templates(id),
    derived_from_unit_id INT REFERENCES training_units(id),
    
    title VARCHAR(300),
    goal TEXT,
    focus_areas JSONB,
    
    -- Durchführung
    actual_time_start TIME,
    actual_time_end TIME,
    completion_status VARCHAR(50),  -- planned, in_progress, completed
    
    -- Reflexion
    reflection_text TEXT,
    what_worked_well TEXT,
    what_to_improve TEXT,
    
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
)

-- Trainingsprogramme
training_programs (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    
    name VARCHAR(300) NOT NULL,
    description TEXT,
    goal TEXT,
    duration_weeks INT,
    focus_areas JSONB,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    created_by INT REFERENCES profiles(id),
    
    created_at TIMESTAMP DEFAULT NOW()
)

-- Programm-Einheiten (Reihenfolge)
program_units (
    id SERIAL PRIMARY KEY,
    program_id INT REFERENCES training_programs(id) ON DELETE CASCADE,
    unit_order INT,
    template_id INT REFERENCES training_templates(id),
    week_number INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
)
```

### 5.5 Governance

```sql
-- Änderungsanfragen (für geschützte Inhalte)
content_change_requests (
    id SERIAL PRIMARY KEY,
    content_type VARCHAR(50),  -- exercise, template, skill, method
    content_id INT,
    requested_by INT REFERENCES profiles(id),
    change_description TEXT,
    change_details JSONB,
    status VARCHAR(50) DEFAULT 'pending',  -- pending, approved, rejected
    reviewed_by INT REFERENCES profiles(id),
    review_notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    reviewed_at TIMESTAMP
)
```

### 5.6 MediaWiki-Import

```sql
-- Import-Tracking
wiki_import_log (
    id SERIAL PRIMARY KEY,
    import_type VARCHAR(50),  -- skill, method, exercise
    import_status VARCHAR(50),  -- success, partial, failed
    items_total INT,
    items_imported INT,
    items_failed INT,
    error_log JSONB,
    imported_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
)

-- Import-Referenzen (für Re-Import-Schutz)
wiki_import_references (
    id SERIAL PRIMARY KEY,
    wiki_page_title VARCHAR(300),
    wiki_page_id VARCHAR(100),
    content_type VARCHAR(50),  -- skill, method, exercise
    local_id INT,
    last_imported TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(wiki_page_title, content_type)
)
```

---

## 6. MVP Core-Features (Release 1)

### 6.1 Pflicht in MVP

**Organisationsstruktur:**
- ✅ Verein anlegen/verwalten
- ✅ Trainingsgruppen anlegen/verwalten
- ✅ Trainer zuordnen

**Kataloge:**
- ✅ Fähigkeiten-Katalog (Admin CRUD)
- ✅ Methoden-Katalog (Admin CRUD)

**Übungen:**
- ✅ Übung anlegen (minimal: Titel, Ziel, Durchführung)
- ✅ Übung vollständig beschreiben (alle Felder)
- ✅ Übungsvarianten anlegen
- ✅ Fähigkeiten zuordnen (Haupt/Neben, Intensität)
- ✅ Methoden zuordnen (Haupt/Neben)
- ✅ Medien hochladen (Bilder, Videos)
- ✅ Übung suchen und filtern
  - Schnellsuche (Titel, Schlagworte)
  - Filter (Bereich, Zielgruppe, Fähigkeit, Dauer, Gruppengröße)
- ✅ Übung drucken
- ✅ Freigabelogik (privat / Verein / offiziell)

**Trainingsplanung:**
- ✅ Trainingseinheit für Gruppe + Termin planen
- ✅ Training aus Vorlage ableiten
- ✅ Training aus altem Training kopieren
- ✅ Trainingsabschnitte verwalten
- ✅ Übungen zu Abschnitten zuordnen
- ✅ Kombinations-Flag für Abschnitte
- ✅ Kalenderansicht (Gruppe, Datum)
- ✅ Druckansicht
- ✅ Mobile Durchführungssicht
- ✅ Notizen zu Training + Übungen
- ✅ Reflexion nach Training

**Import:**
- ✅ Einseitiger MediaWiki-Import
  - Fähigkeiten
  - Methoden
  - Übungen
- ✅ Import-Tracking (Erfolg/Fehler)
- ✅ Duplikat-Erkennung (Wiki-Referenz)

### 6.2 Bewusst NICHT in MVP

**Kern-Features, die warten:**
- ❌ KI-Trainingsplanung
- ❌ KI-Suche mit externen Calls
- ❌ Sportler-Tracking (individuelle Entwicklung)
- ❌ Gürtel/Graduierungs-System
- ❌ Technik-Mastery-Tracking
- ❌ Reifegradmodell (vollständig)
- ❌ Turnier-/Wettkampfbegleitung
- ❌ Community/Marktplatz
- ❌ Komplexe Membership-Matrix
- ❌ Vollhistorisierung jeder Änderung
- ❌ Bidirektionale Wiki-Sync
- ❌ Offline-Sync (vollständig)

**Rollen/Governance (vereinfacht):**
- ❌ Feingranulare Rechte-Matrix
- ❌ Workflow-basierte Freigabe
- ❌ Multi-Tenant-Administration

---

## 7. Initiale Migrationen (MVP)

### 7.1 001_auth_membership.sql

```sql
-- Von Mitai übernehmen:
-- profiles, sessions, features, tier_limits, subscriptions
-- (siehe Mitai 001_*.sql + 002_*.sql)
```

### 7.2 002_organization.sql

```sql
-- Vereine
CREATE TABLE clubs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(50),
    description TEXT,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Sparten (optional, kann später)
CREATE TABLE divisions (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id) ON DELETE CASCADE,
    name VARCHAR(200) NOT NULL,
    focus_area VARCHAR(100),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Trainingsgruppen
CREATE TABLE training_groups (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id) ON DELETE CASCADE,
    division_id INT REFERENCES divisions(id),
    name VARCHAR(200) NOT NULL,
    focus VARCHAR(100),
    level VARCHAR(50),
    age_group VARCHAR(50),
    weekday VARCHAR(20),
    time_start TIME,
    time_end TIME,
    location VARCHAR(200),
    trainer_id INT REFERENCES profiles(id),
    co_trainer_ids JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_groups_club ON training_groups(club_id);
CREATE INDEX idx_groups_trainer ON training_groups(trainer_id);
```

### 7.3 003_catalogs.sql

```sql
-- Fähigkeiten
CREATE TABLE skills (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(100),
    description TEXT,
    importance INT CHECK (importance BETWEEN 1 AND 5),
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Trainingsmethoden
CREATE TABLE training_methods (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    abbreviation VARCHAR(20),
    category VARCHAR(100),
    description TEXT,
    typical_duration INT,
    typical_group_size VARCHAR(50),
    related_skills JSONB,
    keywords JSONB,
    status VARCHAR(50) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_skills_category ON skills(category);
CREATE INDEX idx_methods_category ON training_methods(category);
```

### 7.4 004_exercises.sql

```sql
-- Übungen
CREATE TABLE exercises (
    id SERIAL PRIMARY KEY,
    title VARCHAR(300) NOT NULL,
    summary TEXT,
    goal TEXT NOT NULL,
    execution TEXT NOT NULL,
    preparation TEXT,
    trainer_notes TEXT,
    equipment JSONB,
    
    duration_min INT,
    duration_max INT,
    group_size_min INT,
    group_size_max INT,
    age_groups JSONB,
    
    focus_area VARCHAR(100),
    secondary_areas JSONB,
    training_character VARCHAR(50),
    
    primary_method_id INT REFERENCES training_methods(id),
    secondary_method_ids JSONB,
    
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'draft',
    created_by INT REFERENCES profiles(id),
    club_id INT REFERENCES clubs(id),
    
    import_source VARCHAR(50),
    import_id VARCHAR(200),
    
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Übungs-Fähigkeiten
CREATE TABLE exercise_skills (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    skill_id INT REFERENCES skills(id),
    is_primary BOOLEAN DEFAULT false,
    intensity INT CHECK (intensity BETWEEN 1 AND 5),
    development_contribution VARCHAR(50),
    required_level INT,
    target_level INT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Varianten
CREATE TABLE exercise_variants (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    variant_name VARCHAR(200) NOT NULL,
    description TEXT,
    execution_changes TEXT,
    duration_min INT,
    duration_max INT,
    equipment_changes JSONB,
    difficulty_adjustment VARCHAR(50),
    age_group_override JSONB,
    skill_focus_override JSONB,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Medien
CREATE TABLE exercise_media (
    id SERIAL PRIMARY KEY,
    exercise_id INT REFERENCES exercises(id) ON DELETE CASCADE,
    media_type VARCHAR(50),
    file_path TEXT,
    title VARCHAR(200),
    description TEXT,
    sort_order INT,
    is_primary BOOLEAN DEFAULT false,
    context VARCHAR(100),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_exercises_created_by ON exercises(created_by);
CREATE INDEX idx_exercises_club ON exercises(club_id);
CREATE INDEX idx_exercises_visibility ON exercises(visibility);
CREATE INDEX idx_exercise_skills_skill ON exercise_skills(skill_id);
```

### 7.5 005_training_planning.sql

```sql
-- Templates / Standards
CREATE TABLE training_templates (
    id SERIAL PRIMARY KEY,
    name VARCHAR(300) NOT NULL,
    type VARCHAR(50),
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    goal TEXT,
    focus_areas JSONB,
    duration_total INT,
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    version INT DEFAULT 1,
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Abschnitte
CREATE TABLE training_sections (
    id SERIAL PRIMARY KEY,
    parent_type VARCHAR(50),
    parent_id INT,
    title VARCHAR(200) NOT NULL,
    section_type VARCHAR(100),
    sort_order INT,
    duration_planned INT,
    goal TEXT,
    notes TEXT,
    is_combination BOOLEAN DEFAULT false,
    combination_method_id INT REFERENCES training_methods(id),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Übungen in Abschnitten
CREATE TABLE section_exercises (
    id SERIAL PRIMARY KEY,
    section_id INT REFERENCES training_sections(id) ON DELETE CASCADE,
    exercise_id INT REFERENCES exercises(id),
    variant_id INT REFERENCES exercise_variants(id),
    sort_order INT,
    duration_planned INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Trainingseinheiten
CREATE TABLE training_units (
    id SERIAL PRIMARY KEY,
    group_id INT REFERENCES training_groups(id) ON DELETE CASCADE,
    date DATE NOT NULL,
    time_start TIME,
    time_end TIME,
    derived_from_template_id INT REFERENCES training_templates(id),
    derived_from_unit_id INT REFERENCES training_units(id),
    title VARCHAR(300),
    goal TEXT,
    focus_areas JSONB,
    actual_time_start TIME,
    actual_time_end TIME,
    completion_status VARCHAR(50),
    reflection_text TEXT,
    what_worked_well TEXT,
    what_to_improve TEXT,
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Programme
CREATE TABLE training_programs (
    id SERIAL PRIMARY KEY,
    club_id INT REFERENCES clubs(id),
    division_id INT REFERENCES divisions(id),
    name VARCHAR(300) NOT NULL,
    description TEXT,
    goal TEXT,
    duration_weeks INT,
    focus_areas JSONB,
    visibility VARCHAR(50) DEFAULT 'private',
    status VARCHAR(50) DEFAULT 'active',
    created_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE program_units (
    id SERIAL PRIMARY KEY,
    program_id INT REFERENCES training_programs(id) ON DELETE CASCADE,
    unit_order INT,
    template_id INT REFERENCES training_templates(id),
    week_number INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_units_group_date ON training_units(group_id, date);
CREATE INDEX idx_sections_parent ON training_sections(parent_type, parent_id);
```

### 7.6 006_governance.sql

```sql
-- Änderungsanfragen
CREATE TABLE content_change_requests (
    id SERIAL PRIMARY KEY,
    content_type VARCHAR(50),
    content_id INT,
    requested_by INT REFERENCES profiles(id),
    change_description TEXT,
    change_details JSONB,
    status VARCHAR(50) DEFAULT 'pending',
    reviewed_by INT REFERENCES profiles(id),
    review_notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    reviewed_at TIMESTAMP
);

CREATE INDEX idx_change_requests_status ON content_change_requests(status);
```

### 7.7 007_wiki_import.sql

```sql
-- Import-Log
CREATE TABLE wiki_import_log (
    id SERIAL PRIMARY KEY,
    import_type VARCHAR(50),
    import_status VARCHAR(50),
    items_total INT,
    items_imported INT,
    items_failed INT,
    error_log JSONB,
    imported_by INT REFERENCES profiles(id),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Import-Referenzen
CREATE TABLE wiki_import_references (
    id SERIAL PRIMARY KEY,
    wiki_page_title VARCHAR(300),
    wiki_page_id VARCHAR(100),
    content_type VARCHAR(50),
    local_id INT,
    last_imported TIMESTAMP,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(wiki_page_title, content_type)
);

CREATE INDEX idx_wiki_refs_page ON wiki_import_references(wiki_page_title);
```

---

## 8. MediaWiki-Import-Strategie

### 8.1 Grundprinzip

**Einseitig, einmalig, dann Shinkan-nativ weiter**

- ✅ Import aus MediaWiki nach Shinkan
- ❌ KEINE bidirektionale Synchronisation
- ❌ KEINE Live-Kopplung
- ❌ KEINE kontinuierliche Aktualisierung

### 8.2 Import-Reihenfolge

1. **Fähigkeiten** (skills) - Basis-Katalog
2. **Methoden** (training_methods) - Methodenkatalog
3. **Übungen** (exercises) - Hauptinhalt

**Programme/Trainings:** Nur wenn Datenqualität ausreicht

### 8.3 Import-Logik

```python
# Pseudo-Code Import-Workflow
for wiki_page in wiki_pages:
    # Duplikat-Check
    existing = check_wiki_reference(wiki_page.title, content_type)
    if existing:
        log_skip(wiki_page)
        continue
    
    # Parse + Transform
    data = parse_wiki_page(wiki_page)
    
    # Validate
    if not validate_minimal_fields(data):
        log_error(wiki_page, "missing required fields")
        continue
    
    # Import
    try:
        local_id = create_content(data)
        create_wiki_reference(wiki_page, local_id)
        log_success(wiki_page, local_id)
    except Exception as e:
        log_error(wiki_page, str(e))
```

### 8.4 Import-Metadaten

Jeder importierte Inhalt erhält:
- `import_source = 'mediawiki'`
- `import_id = wiki_page_id`
- Referenz in `wiki_import_references`
- Status = `draft` (muss nachbearbeitet werden)

### 8.5 Nach-Import-Workflow

Nach Import müssen Trainer/Admins:
- Importierte Inhalte prüfen
- Unvollständige Felder ergänzen
- Fähigkeiten/Methoden zuordnen
- Status auf `approved` setzen

---

## 9. Docker-Setup (wie Mitai)

### 9.1 docker-compose.yml (Production)

```yaml
version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    container_name: shinkan-db-prod
    environment:
      POSTGRES_DB: shinkan
      POSTGRES_USER: shinkan_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - shinkan-db-data:/var/lib/postgresql/data
    ports:
      - "5434:5432"
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: shinkan-api
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_NAME: shinkan
      DB_USER: shinkan_user
      DB_PASSWORD: ${DB_PASSWORD}
      APP_URL: https://shinkan.jinkendo.de
      ALLOWED_ORIGINS: https://shinkan.jinkendo.de
    volumes:
      - shinkan-media:/app/media
    ports:
      - "8003:8000"
    depends_on:
      - postgres
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      args:
        VITE_API_URL: https://shinkan.jinkendo.de
    container_name: shinkan-ui
    ports:
      - "3003:80"
    restart: unless-stopped

volumes:
  shinkan-db-data:
  shinkan-media:
```

### 9.2 docker-compose.dev-env.yml (Development)

```yaml
version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    container_name: dev-shinkan-postgres
    environment:
      POSTGRES_DB: shinkan_dev
      POSTGRES_USER: shinkan_dev
      POSTGRES_PASSWORD: dev_password
    volumes:
      - dev-shinkan-db-data:/var/lib/postgresql/data
    ports:
      - "5435:5432"
    restart: unless-stopped

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: dev-shinkan-api
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_NAME: shinkan_dev
      DB_USER: shinkan_dev
      DB_PASSWORD: dev_password
      APP_URL: https://dev.shinkan.jinkendo.de
      ALLOWED_ORIGINS: https://dev.shinkan.jinkendo.de
    volumes:
      - dev-shinkan-media:/app/media
    ports:
      - "8098:8000"
    depends_on:
      - postgres
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      args:
        VITE_API_URL: https://dev.shinkan.jinkendo.de
    container_name: dev-shinkan-ui
    ports:
      - "3098:80"
    restart: unless-stopped

volumes:
  dev-shinkan-db-data:
  dev-shinkan-media:
```

---

## 10. Initiale Version

```python
# backend/version.py

APP_VERSION = "0.1.0"
BUILD_DATE = "2026-04-21"
DB_SCHEMA_VERSION = "20260421"

MODULE_VERSIONS = {
    "auth": "1.0.0",
    "profiles": "1.0.0",
    "clubs": "0.1.0",
    "groups": "0.1.0",
    "skills": "0.1.0",
    "methods": "0.1.0",
    "exercises": "0.1.0",
    "training_units": "0.1.0",
    "training_programs": "0.1.0",
    "planning": "0.1.0",
    "import_wiki": "0.1.0",
    "admin": "1.0.0",
    "membership": "1.0.0",
}

CHANGELOG = [
    {
        "version": "0.1.0",
        "date": "2026-04-21",
        "changes": [
            "Initial MVP Setup",
            "Feature: Übungsverwaltung (Kern-Modul)",
            "Feature: Fähigkeiten- und Methodenkataloge",
            "Feature: Trainingsplanung für Gruppen",
            "Feature: Trainingsabschnitte mit Kombinations-Flag",
            "Feature: MediaWiki-Import (einseitig)",
            "Feature: Freigabelogik (privat/Verein/offiziell)",
        ]
    }
]
```

---

## 11. Umsetzungs-Reihenfolge (Empfehlung)

### Phase 1: Basis (2-3 Tage)
- Repository + Docker-Setup
- Auth + Membership von Mitai kopieren
- DB-Migrationen 001-003
- Version.py anpassen

### Phase 2: Kataloge (2 Tage)
- Skills CRUD (Admin)
- Methods CRUD (Admin)
- Frontend Admin-Pages

### Phase 3: Übungen (5-7 Tage)
- Exercises CRUD
- Exercise-Skills M:N
- Exercise-Variants
- Exercise-Media
- Suche + Filter
- Druckansicht

### Phase 4: Trainingsplanung (5-7 Tage)
- Training-Groups CRUD
- Training-Templates/Standards
- Training-Sections
- Training-Units
- Kalenderansicht
- Mobile Durchführungssicht

### Phase 5: Import (3-4 Tage)
- MediaWiki-Parser
- Import-Workflow (Skills, Methods, Exercises)
- Import-Log + Duplikat-Erkennung

### Phase 6: Governance (2-3 Tage)
- Freigabelogik
- Change-Requests (Basis)
- Eigene vs. Vereins-Inhalte

**Geschätzte Gesamtdauer MVP:** 4-5 Wochen

---

## 12. Bewusst nur vorbereitet, nicht umgesetzt (in MVP)

Diese Elemente sollen durch das Datenmodell **nicht verbaut** werden, gehören aber **nicht in MVP**:

**Reifegradmodelle:**
- Skill-Levels pro Modell
- Maturity-Stages
- Target-Descriptions pro Level

**Individuelle Sportler-Entwicklung:**
- Athlete-Profiles
- Skill-Progress-Tracking
- Assessment-History

**Erweiterte Governance:**
- Workflow-basierte Freigabe
- Review-Zyklen
- Versionierung mit Branches

**Community:**
- Shared Content-Pool
- Bewertungen
- Kommentare

**KI:**
- Trainingsplanung
- Übungssuche mit Semantik
- Automatische Programm-Generierung

---

## 13. Offene Punkte / Entscheidungsbedarf

```
🤔 MediaWiki-Parser: Welches Format nutzt das aktuelle Wiki? (Semantic MediaWiki)
🤔 Medien-Upload: Lokal oder S3/Cloud?
🤔 Suche: PostgreSQL Full-Text oder ElasticSearch später?
🤔 Offline: Service Worker + IndexedDB oder nur gute Mobile-UX?
🤔 Druckformat: PDF-Export oder nur HTML-Print?
🤔 Sparten: Schon in MVP oder erst später?
```

---

## 14. Nächste konkrete Schritte

1. **Repository erstellen** auf Gitea: `shinkan-jinkendo`
2. **Basis-Dateien kopieren** aus Mitai:
   - `backend/auth.py`, `db.py`, `db_init.py`
   - `frontend/src/context/`, `utils/api.js`
3. **Migrationen schreiben** (001-007)
4. **Version.py anpassen**
5. **CLAUDE.md erstellen** für Shinkan
6. **Erste Router implementieren:**
   - `skills.py`
   - `methods.py`
   - `exercises.py`

---

**Version:** 2.0 (komplett überarbeitet)  
**Stand:** 21.04.2026  
**Autor:** Claude Code (basierend auf Anforderungsdokument)
