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