From c4fbabd8f614bfb308a199a2f65df9effa3f1afa Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 5 May 2026 13:39:30 +0200 Subject: [PATCH] chore: update versioning and enhance training framework features - Incremented APP_VERSION to 0.8.10 and DB_SCHEMA_VERSION to 20260505037. - Updated project status and domain model documentation to reflect recent changes. - Enhanced training framework program handling with new slot-blueprint structure. - Introduced API endpoint for creating training units from framework slots. - Improved documentation for training planning and governance concepts. --- .claude/docs/PROJECT_STATUS.md | 45 +- .claude/docs/functional/DOMAIN_MODEL.md | 10 +- ...INING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md | 17 +- .../library/FEATURES_DELIVERED_2026-Q2.md | 34 +- .claude/docs/technical/DATABASE_SCHEMA.md | 55 +- .../docs/technical/TRAINING_FRAMEWORK_SPEC.md | 104 ++-- CLAUDE.md | 15 +- docs/HANDOVER.md | 22 +- .../components/TrainingUnitSectionsEditor.jsx | 442 +++++++++++++ .../TrainingFrameworkProgramEditPage.jsx | 508 ++++----------- frontend/src/pages/TrainingPlanningPage.jsx | 589 +----------------- .../src/utils/trainingUnitSectionsForm.js | 169 +++++ 12 files changed, 940 insertions(+), 1070 deletions(-) create mode 100644 frontend/src/components/TrainingUnitSectionsEditor.jsx create mode 100644 frontend/src/utils/trainingUnitSectionsForm.js diff --git a/.claude/docs/PROJECT_STATUS.md b/.claude/docs/PROJECT_STATUS.md index cd08aec..1fcaccc 100644 --- a/.claude/docs/PROJECT_STATUS.md +++ b/.claude/docs/PROJECT_STATUS.md @@ -1,30 +1,30 @@ # Shinkan Jinkendo - Projekt-Status -**Stand:** 2026-04-30 -**Version (Code):** 0.8.7 (`backend/version.py`, APP_VERSION) -**DB-Schema-Version:** `20260430034` +**Stand:** 2026-05-05 +**Version (Code):** 0.8.10 (`backend/version.py`, APP_VERSION) +**DB-Schema-Version:** `20260505037` **Branch:** develop --- ## Executive Summary -**Aktueller Meilenstein:** **Progressionsgraph zwischen Übungen** (DB 032–034, API `exercise-progression-graphs`, UI Tabs + Formularblock) — **Zwischenstand**: linear/Reihen/Schwestern gut nutzbar; **parallele gleichwertige Alternativ‑„Pakete“** noch ohne dedizierte UX (**TRAINING_FRAMEWORK_SPEC.md** §4). Ausreichend, um mit **Trainingsplanung / Rahmen** (**CURR‑002 (2)**) weiterzuarbeiten. +**Aktueller Meilenstein:** **Trainingsrahmenprogramm Bibliothek + Slot‑Blueprint** (DB **036–037**): Rahmenkopf nur als Vorlage mit Kontext‑Stammdaten; pro Slot genau eine **Blueprint‑`training_unit`** mit **`framework_unit_sections`/`_items`** wie die Planung; Kalenderliste blendet Blueprints aus; **`POST /api/training-units/from-framework-slot`** materialisiert Kopien mit **`origin_framework_slot_id`**. Parallel: **Progressionsgraph** (032–034) bleibt unterstützend (**`TRAINING_FRAMEWORK_SPEC.md`** §3–§4). -**Letzte dokumentierte Änderungen (April 2026):** +**Letzte dokumentierte Änderungen (Mai 2026):** -- ✅ Migration **032–034**: `exercise_progression_graphs`, `exercise_progression_edges` inkl. **`notes`**, optionale Varianten-Endpunkte. -- ✅ **`POST /api/exercise-progression-graphs/{id}/edges/sequence`** und **`…/edges/delete-batch`**. -- ✅ **Übungsliste:** Tabs Liste · Progressionsgraphen; **Übung bearbeiten:** Block Progressionsgraph. -- ✅ Zuvor geliefert: Varianten Ende-zu-Ende (030), Listen-Suche UX, Medien-Limits, RichText — siehe unten und Feature-Doc. +- ✅ Migration **036:** Rahmen nur Bibliothek; Fokus/Stil + M:N Trainingsarten/Zielgruppen; Entfall `plan_mode`/`group_id` am Kopf. +- ✅ Migration **037:** `training_units.framework_slot_id` / `origin_framework_slot_id`; Migration Entfall **`training_framework_slot_exercises`**. +- ✅ APIs: erweiterte Rahmen‑Hydration (`sections`, `exercises`, `blueprint_training_unit_id`); Planung siehe **`TRAINING_FRAMEWORK_SPEC.md`** §2.4. +- ✅ Frontend: `createTrainingUnitFromFrameworkSlot` in `api.js`. -**Referenz:** Ausführliche technische Liste → [`library/FEATURES_DELIVERED_2026-Q2.md`](library/FEATURES_DELIVERED_2026-Q2.md) · Zwischenstand Graph → [`technical/TRAINING_FRAMEWORK_SPEC.md`](technical/TRAINING_FRAMEWORK_SPEC.md) +**Referenz:** [`library/FEATURES_DELIVERED_2026-Q2.md`](library/FEATURES_DELIVERED_2026-Q2.md) · Rahmen/Graph: [`technical/TRAINING_FRAMEWORK_SPEC.md`](technical/TRAINING_FRAMEWORK_SPEC.md) **Nächste Schritte (Auszug):** -1. **Trainingsplanungs-/Rahmenmodul** nach CURR‑002 (2), CURR‑009–013 (Graph bleibt unterstützend). -2. Prod-Deployment Migrationen bis **034** und Smoke-Tests. -3. Optional Backlog Graph: Alternativgruppen / bessere Visualisierung verzweigter Graphen. +1. Kalender‑UI: „Aus Rahmen übernehmen“ an **`from-framework-slot`** anbinden; ggf. Bulk. +2. Governance: Sichtbarkeit **club/official** für Rahmen so ausprägen, dass andere Trainer kopieren dürfen (Policy + API). +3. Optional Backlog Graph: Alternativgruppen / bessere Visualisierung (**§4**). --- @@ -42,6 +42,7 @@ | 028–029 | exercise_media / skills Stufen | ✅ | 🔲 | | **030** | **training_unit_exercises.exercise_variant_id** | ✅ | 🔲 | | **032–034** | **Progressionsgraph Übung→Übung** | ✅ | 🔲 | +| **035–037** | **Rahmenprogramm, Bibliothek‑Kopf, Slot‑Blueprint‑Units** | ✅ | 🔲 | --- @@ -74,8 +75,10 @@ Die exakten Zahlen hängen von der Umgebung ab (siehe Admin/DB). Die Skills/Übu **Trainingsplanung:** -- [x] Training Units / Einbinden von Übungen +- [x] Training Units / strukturierter Ablauf (Sektionen + Items) - [x] **Optionale Zuordnung einer Übungsvariante** pro Eintrag (`exercise_variant_id`) +- [x] **Trainingsrahmenprogramm Bibliothek** (Ziele, Slots, Kontext) + **Slot‑Blueprints** in `training_units` (036–037) +- [x] **Materialisierung** aus Rahmen‑Slot (`POST …/training-units/from-framework-slot`; UI‑Anbindung optional) - [ ] Kalender-View / erweiterte Roadmap (Backlog) **MediaWiki Import:** @@ -122,7 +125,7 @@ Die exakten Zahlen hängen von der Umgebung ab (siehe Admin/DB). Die Skills/Übu ### Dev -Branch `develop`; Migrations bis mindestens **034** auf dem aktuellen Entwicklungsstand; Details in `backend/version.py`. +Branch `develop`; Migrations bis mindestens **037** auf dem aktuellen Entwicklungsstand; Details in `backend/version.py`. ### Prod @@ -134,16 +137,16 @@ Deployment der oben genannten Migrationen und Datenabgleich nach internem Prozes | Dokument | Pfad | Stand | Status | |----------|------|-------|--------| -| Lieferliste Q2 2026 | `library/FEATURES_DELIVERED_2026-Q2.md` | 2026-04-30 | ✅ Aktualisiert (032–034) | -| Trainingsrahmen (Zwischenstand Graph) | `technical/TRAINING_FRAMEWORK_SPEC.md` | 2026-04-30 | ✅ | +| Lieferliste Q2 2026 | `library/FEATURES_DELIVERED_2026-Q2.md` | 2026-05-05 | ✅ Aktualisiert (u. a. 036–037) | +| Trainingsrahmen + Graph | `technical/TRAINING_FRAMEWORK_SPEC.md` | 2026-05-05 | ✅ §2 Blueprint | | Anforderungen (Index) | `functional/SHINKAN_REQUIREMENTS.md` | 2026-04-27 | ✅ Neu | -| Database Schema | `technical/DATABASE_SCHEMA.md` | 2026-04-30 | ✅ Aktualisiert (034) | -| Domain Model | `functional/DOMAIN_MODEL.md` | 2026-04-30 | ✅ Aktualisiert | +| Database Schema | `technical/DATABASE_SCHEMA.md` | 2026-05-05 | ✅ Aktualisiert (037) | +| Domain Model | `functional/DOMAIN_MODEL.md` | 2026-05-05 | ✅ Aktualisiert | | API Übungen | `technical/EXERCISES_API_SPEC.md` | 2026-04-30 | ✅ Ergänzt Progressions-API | | Frontend Routing | `technical/EXERCISES_FRONTEND_ROUTING.md` | 2026-04-30 | ✅ Ergänzt UI-Hinweise | | Search & Filter | `technical/SEARCH_FILTER_SPEC.md` | 2026-04-27 | ✅ Aktualisiert (Liste UX) | | Media Upload | `technical/MEDIA_UPLOAD_SPEC.md` | 2026-04-27 | ✅ Aktualisiert (Limits) | -| Projektstatus | `PROJECT_STATUS.md` | 2026-04-30 | ✅ Diese Datei | +| Projektstatus | `PROJECT_STATUS.md` | 2026-05-05 | ✅ Diese Datei | --- @@ -154,4 +157,4 @@ Deployment der oben genannten Migrationen und Datenabgleich nach internem Prozes --- -**Letzte Aktualisierung:** 2026-04-30 +**Letzte Aktualisierung:** 2026-05-05 diff --git a/.claude/docs/functional/DOMAIN_MODEL.md b/.claude/docs/functional/DOMAIN_MODEL.md index d09e115..fa96c38 100644 --- a/.claude/docs/functional/DOMAIN_MODEL.md +++ b/.claude/docs/functional/DOMAIN_MODEL.md @@ -1,7 +1,7 @@ # Shinkan Jinkendo - Fachliches Domänenmodell -**Version:** 0.4.2 -**Stand:** 2026-05-05 (Migration 035: Rahmen‑Vorlage `training_framework_programs`; Progressionsgraph unverändert 032–034) +**Version:** 0.4.3 +**Stand:** 2026-05-05 (Migration **036–037:** Rahmen nur Bibliothek; Slot‑Inhalt über Blueprint‑`training_units` + Sektionen/Items wie Planung — siehe `TRAINING_FRAMEWORK_SPEC.md` §2) **Basis:** `shinkan_anforderungsdokument_entwurf.md` + Fähigkeitsmatrix --- @@ -468,9 +468,11 @@ skill_level_definitions ( ### Trainingsrahmen‑Vorlage (Rahmenprogramm, CURR‑002 Stufe 2 / CURR‑009) -**Abgrenzung:** Eine **einzeilige** Trainingsplan‑Mikrovorlage (`training_plan_template`) strukturiert **eine** Einheit; das **Rahmenprogramm** ist eine **eigene Bibliotheksentität** mit **sortierten Session‑Slots**, **mindestens einem** formulierten **Entwicklungsziel** (Zielliste, **CURR‑011**) und **direkten Übungszuordnungen** pro Slot („Stückliste“, **CURR‑010**). Der persistierte **Progressionsgraph** zwischen Übungen bleibt **optional** und ersetzt keine Slot-Zuordnung (**CURR‑013**). +**Abgrenzung:** Eine **einzeilige** Trainingsplan‑Mikrovorlage (`training_plan_template`) strukturiert **eine** Einheit; das **Rahmenprogramm** ist eine **eigene Bibliotheksentität** mit **sortierten Session‑Slots**, **mindestens einem** formulierten **Entwicklungsziel** (Zielliste, **CURR‑011**) und einem **vollständigen Ablauf** pro Slot (**`training_unit_sections` + `training_unit_section_items`** wie bei geplanten Einheiten — **CURR‑010** inhaltlich, technisch seit **037** identisch zur Planungsstruktur). Der persistierte **Progressionsgraph** zwischen Übungen bleibt **optional** (**CURR‑013**). -**Zwei Nutzungsmodi (CURR‑012):** **`concrete`** (Kurzfrist‑/Gruppenkontext; optional `group_id`, Slots dürfen **`training_unit_id`** tragen) vs. **`library`** (zeit‑/gruppenlose Vorlage; **`group_id`** und Slot‑Einheitsverknüpfungen sind fachlich gesperrt — technisch werden Einheits-FKs beim Wechsel geleert). **Materialisierung / Bulk‑Anlegen** von `training_units` aus dem Rahmen ist ein **separater** Schritt (Stub/PR). **optional `training_plan_template_id` pro Slot** ist bewusst **deferred** (**C5**/CURR‑010). +**Bibliothek only (036):** Kein Kopf‑`plan_mode`/keine Kopf‑`group_id`; Zuordnung zu Gruppe und Datum erfolgt nur über **kopierte** Kalender‑`training_units` (Instanz). + +**Konkretisierung (037/API):** `POST /api/training-units/from-framework-slot` legt eine geplante Einheit aus dem Slot‑Blueprint an; **`origin_framework_slot_id`** dient als Herkunftsreferenz (**Lineage light**; weiteres Feedback/Lineage‑Konzept: Konzeptpapier Schritt **E**). --- diff --git a/.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md b/.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md index 3e6b8d2..d5366b8 100644 --- a/.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md +++ b/.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md @@ -1,7 +1,7 @@ # Konzept: Trainingsplanung über Einheiten hinweg, Kurspläne, Governance, Assessments **Status:** Arbeitspapier (lebend) -**Stand:** 2026-04-30 (CURR‑002 Stufe 1 Zwischenstand im Produkt; Rahmen CURR‑002 (2) als nächster Schritt) +**Stand:** 2026-05-05 (Rahmen‑Bibliothek **036**, Slot‑Blueprint **037** / API `from-framework-slot`; CURR‑002 Stufe 1 Graph unverändert 032–034) **Zweck:** Erkenntnisse und **getroffene Entscheidungen** festhalten, um Spec- und Implementierungsdrift zu vermeiden. **Kanons:** Bei Widersprüchen mit produktiven Specs zuerst diese Datei mit dem Team abstimmen; technische Details ergänzen später in `technical/`. @@ -29,7 +29,7 @@ | Stufe | Inhalt | Status | |--------|--------|--------| | **1** | **Progressionsbezüge** zwischen Übungen **persistent speicherbar** (Progressionsbaum / -graph zwischen Übungseinheiten, nicht nur UI) | ✅ **Zwischenstand im Produkt** (Migrationen 032–034, UI/API); UX für **parallele gleichwertige Alternativ‑Pakete** noch kein Erstklass‑Fall — siehe `technical/TRAINING_FRAMEWORK_SPEC.md` §4 | -| **2** | **Planungs-/Rahmenmodus:** Übungen (beliebig oder aus Progression) auf **mehrere** Session-Slots / Trainingseinheiten **verteilen**, **mehrere Ziele**; speicherbare Rahmen-Vorlage (CURR‑002 (2) i. V. m. **CURR‑010–013**) | **nächster Implementierungsschwerpunkt** — Stufe 1 ist nicht Blocker (**CURR‑013**) | +| **2** | **Planungs-/Rahmenmodus:** Übungen auf **mehrere** Session-Slots verteilen; **mehrere Ziele**; speicherbare Rahmen-Vorlage (CURR‑002 (2) i. V. m. **CURR‑010–013**) | **in Arbeit**: Bibliotheks‑Backend + Slot‑Blueprint + Kopie‑API (**037**); **UI Kalender**/Bulk folgt (**CURR‑012**) | | **3** | **Konkrete Einheit:** aus Rahmen-/Verteilungsplan **Vorschläge** beim Ausarbeiten laden; Bezug zur Idee **„Warenkorb“** bei der Übungsplanung | folgt nach 2 | ### 2.b Übrige Konzept-Schritte (noch durchzuarbeiten) @@ -40,11 +40,11 @@ | **B** | **Governance-Muster** (einheitliche Sichtbarkeit; Bibliothek vs. Instanz) | ✅ Leitplan §2.c; Entscheidungen §5 CURR-005–007 | | **C** | **Rahmenprogramm** — §2.d (**C1–C4** ✅ · **C5** Leitplan) | ✅ Kern i. V. m. **CURR‑009–013** | | **D** | **Kurs-/Stufenprogramm:** nach Rahmenprogramm; plantechnisch ähnlich | 📌 zeitlich nachgelagert (CURR-003) | -| **E** | **Lineage & Feedback** (Einheit ↔ Vorlage/Rahmen; Issues zur Nachbesserung) | ⬜ offen | +| **E** | **Lineage & Feedback** (Einheit ↔ Vorlage/Rahmen; Issues zur Nachbesserung) | ➕ **teilweise:** `training_units.origin_framework_slot_id`; vollständiges Konzept/UX offen | | **F** | **Assessments** | 📌 Backlog (CURR-003) | | **G** | **Progressions-Automatik** (KI, komplexe Vorschläge) | 📌 Backlog (CURR-003) | -**Aktueller Fokus:** Umsetzung **Trainingsplanungs-/Rahmenmodul** (**CURR‑002 (2)**): Entitäten, Slots, mehrere Ziele — Progressionsgraph Stufe 1 ist **bereits** als unterstützende Bibliotheksfunktion vorhanden (**TRAINING_FRAMEWORK_SPEC.md**). Schritt **E** (Lineage) als nächstes Konzeptpaket möglich. +**Aktueller Fokus:** **Kalender-/Planungs‑UI** an **`POST /api/training-units/from-framework-slot`** und Visibility für geteilte Rahmen; weiteres Lineage (**Schritt E**) ergänzend zu **`origin_framework_slot_id`**. --- @@ -59,8 +59,9 @@ Ein **wiedererkennbares Muster** für alle **Bibliotheksobjekte** (Übung, Train | Objekt | Relevante Felder / Muster | |--------|---------------------------| | `exercises`, `exercise_blocks` | `visibility` ∈ `private` \| `club` \| `official`, `club_id`, `created_by` — **Referenzmuster** | -| `training_plan_templates` | `club_id`, `created_by`, **kein** `visibility` — Abweichung; nachziehen oder Semantik explizit festlegen (siehe CURR-007) | -| `training_units` | `group_id`, `created_by`, `plan_template_id` — **Instanz**; Zugriff fachlich über Gruppe/Trainer | +| `training_plan_templates` | `club_id`, `created_by`, **`visibility`** seit **035** (Backfill **`club`**); Referenz‑Muster an Übung angleichen (**CURR‑007** erledigt für dieses Feld) | +| `training_framework_programs` | `visibility`, `club_id`, `created_by`; Kontext `focus_area_id`, `style_direction_id`; keine Kopf‑`group_id`; Slot‑Inhalt über **Blueprint‑`training_units`** (**036–037**) | +| `training_units` | `group_id`, `created_by`, `plan_template_id` — **Instanz** oder **Blueprint** (`framework_slot_id`); Lineage‑Light **`origin_framework_slot_id`** | #### B.3 Prinzipien (binding mit §5) @@ -114,12 +115,14 @@ Ein **wiedererkennbares Muster** für alle **Bibliotheksobjekte** (Übung, Train **Klartext zur früheren Frage „C4a vs. C4b“:** Bei **Modus A** passen **automatisches Anlegen n Einheiten** (früheres C4a) **oder** Zuordnung zu **bereits geplanten** Einheiten (C4b) — je nach Produkt/UI. Bei **Modus B** existieren erst bei der Übernahme überhaupt Gruppe/Zeiten; die Bibliotheksvorlage bleibt **neutral**. +**Stand Code 036–037:** Am Rahmenkopf gibt es **keine** **`plan_mode`/`group_id`** mehr — die Bibliothek ist immer „Modus B“; konkrete Gruppe/Zeit entstehen **nur** in **`training_units`** (Kalender‑Zeilen oder Übernahme‑API **`from-framework-slot`**). + --- #### C2 (Klärung fürs Team) „**C2**“ im Entwurf bezog sich auf „**wie** weiß ich pro Slot welche Übungen (nur Progression oder auch Mikro‑Vorlage)?“ — **aktueller Beschluss:** -Pro Slot: **Zuordnung von Übung(en)** **direkt** (wie „Stückliste“) ist **tragend**; **Progressionsgraph** liefert **Vorschläge / Pakete**, wenn admins sie pflegen — **Trainer** können **ohne** Graph planen. +Pro Slot: **Zuordnung von Übung(en)** als **Teil des vollständigen Ablaufs** (wie geplante Einheit: **Sektionen und Items**, **037**) ist **tragend**; **Progressionsgraph** liefert **Vorschläge / Pakete**, wenn Admins sie pflegen — **Trainer** können **ohne** Graph planen. --- diff --git a/.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md b/.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md index a4d04d7..90045d9 100644 --- a/.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md +++ b/.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md @@ -1,9 +1,9 @@ -# Gelieferte Features & technische Basis (April 2026) +# Gelieferte Features & technische Basis (Q2 2026) -**Stand:** 2026-04-30 -**Referenz:** `backend/version.py` — **APP_VERSION 0.8.7**, **DB_SCHEMA_VERSION 20260430034** +**Stand:** 2026-05-05 +**Referenz:** `backend/version.py` — **APP_VERSION 0.8.10**, **DB_SCHEMA_VERSION 20260505037** -Dieses Dokument bündelt die in der Entwicklungsphase erreichten **lieferbaren** Funktionen und die zugehörigen **technischen Artefakte**. **Progressionsgraph zwischen Übungen** (Zwischenstand, Grenzen): **`technical/TRAINING_FRAMEWORK_SPEC.md`** §3–§4. Detail-Spezifikationen bleiben in den verlinkten Pfaden unter `.claude/docs/technical/` und `.claude/docs/functional/`. +Dieses Dokument bündelt die in der Entwicklungsphase erreichten **lieferbaren** Funktionen und die zugehörigen **technischen Artefakte**. Trainingsrahmen‑Bibliothek + Slot‑Blueprint: **`technical/TRAINING_FRAMEWORK_SPEC.md`** §2. **Progressionsgraph zwischen Übungen** (Zwischenstand, Grenzen): **§§3–4**. Detail-Spezifikationen bleiben in den verlinkten Pfaden unter `.claude/docs/technical/` und `.claude/docs/functional/`. --- @@ -15,6 +15,9 @@ Dieses Dokument bündelt die in der Entwicklungsphase erreichten **lieferbaren** | **028** | `exercise_media` erweitert (Embed/Metadaten), `exercise_skills` Level-Felder (VARCHAR); Medien-API | | **029** | Kanonische Fähigkeitsstufen (basis–optimierung), `model_levels`-Namen | | **030** | `training_unit_exercises.exercise_variant_id` → FK `exercise_variants(id)` ON DELETE SET NULL | +| **035** | **`training_framework_programs`** + Ziele, Slots (+ frühere Slot‑Übungstabelle, heute entfallen nach **037**); **`training_plan_templates.visibility`** | +| **036** | Rahmen nur Bibliothek: Kontext + M:N Trainingsarten/Zielgruppen; keine Modus-Spalten / keine Kopf‑`group_id` | +| **037** | **`training_units.framework_slot_id`**, strukturierter Ablauf wie Planung; Entfall **`training_framework_slot_exercises`**; **`origin_framework_slot_id`** | --- @@ -55,9 +58,10 @@ Logik: `_upload_limit_bytes(session)` vor `read()`-Prüfung. ## 4. Backend – Trainingsplanung (`routers/training_planning.py`) -- `training_unit_exercises`: Schreiben/Lesen von **`exercise_variant_id`**. -- Validierung: Variante muss zur gewählten **`exercise_id`** gehören. -- JOIN liefert u. a. **`exercise_variant_name`** beim Lesen einer Einheit. +- Strukturierte Einheiten: **`training_unit_sections`** + **`training_unit_section_items`** (Migration **031**) — Hauptpfad beim Lesen/Schreiben von Einheiten. +- **`training_unit_exercises`:** Legacy-/Nebenpfad; weiterhin **`exercise_variant_id`** (Migration **030**) mit Validierung gegen die gewählte **`exercise_id`**; JOINs liefern u. a. **`exercise_variant_name`**. +- **Blueprint‑Zeilen (`framework_slot_id` gesetzt):** **`GET /api/training-units`** listet diese **nicht**; **`PUT`** mit eingeschränkten Regeln (**kein** `plan_template_id` / kein Reset aus Vorlage über diesen Kopf wie bei Kalender‑Einheit). +- Übernahme aus Rahmen: **`POST /api/training-units/from-framework-slot`** ({ `framework_slot_id`, `group_id`, `planned_date` }) — tiefe Kopie inkl. Sektionen/Items; **`origin_framework_slot_id`** setzt Lineage‑Light. --- @@ -109,16 +113,26 @@ Hinweis: Es gibt **keine** separaten Routen `/exercises/:id/variants/...` — Be --- -## 11. Nächste sinnvolle Schritte (nicht Lieferstand) +## 11. Trainingsrahmen: Bibliothek + Slot‑Blueprint (DB **036–037**) -- Trainingsplanungs-/Rahmenmodul (**CURR‑002 (2)**) — Progressionsgraph ist unterstützend, siehe **`TRAINING_FRAMEWORK_SPEC.md`** §4. +- **036:** `training_framework_programs` nur Bibliothek — `focus_area_id`, `style_direction_id`, M:N `training_framework_program_training_types` / `_target_groups`; Entfall `plan_mode`, `group_id`; Slot‑Verknüpfungen zu Kalender‑Einheiten geleert. +- **037:** Pro Slot genau eine **`training_units`**‑Zeile mit **`framework_slot_id`**; Ablauf über **`training_unit_sections`** / **`training_unit_section_items`** (wie Planung); Legacy **`training_framework_slot_exercises`** Datenmigration + **`DROP` TABLE**; geplante Kopien können **`origin_framework_slot_id`** tragen. +- **Router `training_framework_programs.py`:** CRUD **`/api/training-framework-programs`**, Slots im Speichern mit neuen Blueprint‑`training_units`, Hydration **`sections`/`exercises`/`blueprint_training_unit_id`**; siehe **`TRAINING_FRAMEWORK_SPEC.md`** §2.3. +- **Frontend:** **`TrainingFrameworkProgramEditPage.jsx`**, **`createTrainingUnitFromFrameworkSlot`** (`api.js`). +- **Doku:** **`technical/TRAINING_FRAMEWORK_SPEC.md`** §2; **`technical/DATABASE_SCHEMA.md`**; **`functional/DOMAIN_MODEL.md`** (Trainingsrahmen‑Abschnitt). + +--- + +## 12. Nächste sinnvolle Schritte (nicht Lieferstand) + +- Trainingsplanung: Kalender‑UI‑Anbindung **„aus Rahmen übernehmen“**; Visibility/Policies für geteilte Rahmen (**CURR‑004** später). - Progressions-Serien als **Blöcke** (angekündigt; Voraussetzung: `prerequisite_variant_id` / `progression_level` vorhanden). - Serverseitige **Suchvorschläge** (Autocomplete-Endpoint), falls datalist nicht reicht. - Optional: Streaming/chunked Upload für sehr große Videos (RAM-Thema). --- -## 12. Verweise +## 13. Verweise | Thema | Dokument | |--------|----------| diff --git a/.claude/docs/technical/DATABASE_SCHEMA.md b/.claude/docs/technical/DATABASE_SCHEMA.md index 73420bd..58a3668 100644 --- a/.claude/docs/technical/DATABASE_SCHEMA.md +++ b/.claude/docs/technical/DATABASE_SCHEMA.md @@ -1,8 +1,8 @@ # Shinkan Jinkendo - Datenbank-Schema (Technisch) -**Version:** 0.5.1 +**Version:** 0.5.2 **Stand:** 2026-05-05 -**Hinweis:** Produktiver Deploy sollte mindestens bis Migration **035** (Trainingsrahmenprogramm + Vorlagen-`visibility`) geführt sein — Details siehe `backend/version.py` (`DB_SCHEMA_VERSION`). +**Hinweis:** Produktiver Deploy sollte mindestens bis Migration **037** (Rahmen‑Slot‑Blueprints in `training_units`; Entfall `training_framework_slot_exercises`) geführt sein — Details siehe `backend/version.py` (`DB_SCHEMA_VERSION`). --- @@ -44,7 +44,9 @@ Dieses Dokument beschreibt die **technische Datenbankstruktur** von Shinkan Jink | **032** | **2026-04-30** | **Progressionsgraph Übung→Übung:** `exercise_progression_graphs`, `exercise_progression_edges` | ✅ | | **033** | **2026-04-30** | **`exercise_progression_edges.notes`** | ✅ | | **034** | **2026-04-30** | **Kanten-Endpunkte optional `exercise_variants`; UNIQUE/CHECK** | ✅ | -| **035** | **2026-05-05** | **Rahmenprogramm:** `training_framework_programs` (+ Ziele, Slots, Slot-Übungen); **`training_plan_templates.visibility`** (Backfill `club`) — siehe `TRAINING_FRAMEWORK_SPEC.md` | ✅ | +| **035** | **2026-05-05** | **Rahmenprogramm:** `training_framework_programs` (+ Ziele, Slots, früher `training_framework_slot_exercises`); **`training_plan_templates.visibility`** (Backfill `club`) — siehe `TRAINING_FRAMEWORK_SPEC.md` | ✅ | +| **036** | **2026-05-05** | **Rahmen nur Bibliothek:** Kopf mit `focus_area_id`, `style_direction_id`, M:N Trainingsarten/Zielgruppen; Entfall `plan_mode`, `group_id`; Slot‑`training_unit_id` geleert — siehe `036_framework_program_context_only_library.sql` | ✅ | +| **037** | **2026-05-05** | **Slot‑Blueprint:** `training_units.framework_slot_id` (+ CHECK Blueprint vs. Kalender), `origin_framework_slot_id`; Migration Slot‑Übungen → `training_unit_sections`/`training_unit_section_items`; **`DROP training_framework_slot_exercises`** | ✅ | --- @@ -270,14 +272,51 @@ exercise_variants (id, exercise_id, name, description, ...) exercise_media (id, exercise_id, type, url, title, description, ...) ``` -### Training Planning +### Trainingsrahmenprogramm Bibliothek (Migrationen **035–036**) + +Kopf ohne Gruppenbindung (`training_framework_programs`), Ziele, Slots. Slot‑spezifischer Ablauf liegt nach **037** nicht mehr in eigener Übungstabelle, sondern in **`training_units`** mit **`framework_slot_id`** — siehe nächster Abschnitt. ```sql -training_units (id, group_id, date, title, description, ...) -training_unit_exercises ( - training_unit_id, exercise_id, sort_order, - exercise_variant_id -- FK exercise_variants(id) ON DELETE SET NULL (Migration 030) +training_framework_programs (… focus_area_id, style_direction_id, visibility, club_id, created_by …) +training_framework_goals (framework_program_id, sort_order, title, notes) +training_framework_slots (framework_program_id, sort_order, title, notes, training_unit_id -- ungenutzt) +training_framework_program_training_types (framework_program_id, training_type_id) +training_framework_program_target_groups (framework_program_id, target_group_id) +``` + +### Training Planning & Rahmen‑Blueprint (Migrationen 006, 031, **037**) + +Geplante Einheit und **Rahmen‑Slot‑Blueprint** teilen sich **`training_units`** und den strukturierten Ablauf über **Sektionen** (031). Blueprint‑Zeilen haben **`framework_slot_id`** gesetzt (genau eine Zeile pro Slot); Kalender‑Zeilen haben **`framework_slot_id IS NULL`** und **`group_id` / `planned_date`** gesetzt. Kopien aus dem Rahmen können **`origin_framework_slot_id`** setzen. + +```sql +training_units ( + id, + group_id INT NULL REFERENCES training_groups(id), -- Pflicht für Kalender‑Zeilen (CHECK) + planned_date DATE NULL, -- Pflicht für Kalender‑Zeilen (CHECK) + planned_time_start, planned_time_end, planned_focus, + actual_date, actual_time_start, actual_time_end, attendance_count, + status, notes, trainer_notes, + created_by, plan_template_id REFERENCES training_plan_templates(id), + framework_slot_id INT NULL REFERENCES training_framework_slots(id) ON DELETE CASCADE, + origin_framework_slot_id INT NULL REFERENCES training_framework_slots(id) ON DELETE SET NULL, + … ) +training_unit_sections ( + training_unit_id, order_index, title, guidance_notes, + source_template_section_id REFERENCES training_plan_template_sections(id) +) +training_unit_section_items ( + section_id, order_index, item_type CHECK ('exercise'|'note'), + exercise_id, exercise_variant_id, planned_duration_min, actual_duration_min, + notes, modifications, note_body +) +``` + +**Legacy (Migration 006, für ältere Codepfade noch referenzierbar):** `training_unit_exercises`; produktiver Standardablauf liegt in **Sections/Items**. + +**Trainingsvorlagen (031):** `training_plan_templates`, `training_plan_template_sections`. + +```sql exercise_blocks (id, name, description, created_by, club_id, ...) -- Migration 017 ``` diff --git a/.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md b/.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md index a444212..2062851 100644 --- a/.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md +++ b/.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md @@ -1,6 +1,6 @@ # Trainingsrahmenprogramm — Technische Spezifikation -**Status:** Zwischenstand dokumentiert · **Stand:** 2026-05-05 +**Status:** Rahmen‑Bibliothek + Slot‑Blueprint dokumentiert · **Stand:** 2026-05-05 (Migration **036–037**) **Bindendes Fachkonzept / Entscheide:** `.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` (CURR‑001 bis CURR‑013) **Relevant für nächsten Schritt:** CURR‑002 **(2)** Trainingsplanung / Rahmen über mehrere Einheiten — der hier dokumentierte **Progressionsgraph Stufe 1** ist bewusst **unterstützend**, keine Pflicht für Slot-Zuordnungen (**CURR‑013**). @@ -14,7 +14,7 @@ | `EXERCISES_DATABASE_FINAL.md`, `EXERCISES_ARCHITECTURE.md`, `EXERCISES_API_SPEC.md` | **Übungskatalog** inkl. Varianten-Progression **innerhalb einer Übung** (Migration 014). Kanten **zwischen** Übungen siehe **§3**. | | `DATABASE_SCHEMA.md` | **Nachgeordnete** Übersicht: Migrationshistorie und Tabellenliste; Detail-DDL primär **hier §2–§3** + SQL unter `backend/migrations/`. | | `functional/DOMAIN_MODEL.md` | Fachliche Begriffe; Kurzverweis auf Progressionsgraph ergänzt. | -| `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` | **Was** und **warum** (Modus A/B, Governance, CURR‑Tabelle). | +| `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` | **Was** und **warum** (Bibliothek vs. Instanz, Governance, CURR‑Tabelle). | **Konsequenz:** Diese Datei bleibt der **technische Arbeitspool** für Rahmenprogramm Stufe 1–2. Abschnitt **§4** beschreibt explizit den **aktuellen Produktfreigabe-Umfang** und **bekannte Lücken** (damit Trainingsplanung weiter gebaut werden kann ohne falscher Erwartung an „Alternative‑Pakete“ in der UI). @@ -22,84 +22,91 @@ ## 2. Rahmenprogramm (CURR‑002 Stufe 2) — Checkliste & technische Ausarbeitung -### 2.0 Technische Entscheidung: eine Tabelle + `plan_mode` (Modus A | B) +### 2.0 Technische Entscheidung: nur Bibliothek + Slot‑Blueprint = `training_units` -**Entscheid:** **Eine** Hauptentität `training_framework_programs` mit **`plan_mode` ∈ {`concrete`, `library`}** statt zweier getrennter Tabellentypen. +**Fachlich:** Das Rahmenprogramm ist eine **wiederverwendbare Bibliotheksvorlage** ohne Bindung an eine Trainingsgruppe oder einen Kalendertermin (**Migration 036** entfernte `plan_mode`, `group_id` am Kopf sowie die Nutzung von `training_framework_slots.training_unit_id` für „konkrete“ Kopplung). -**Begründung:** Gleiche Lebenszyklus‑ und CRUD‑Form (Header, Ziele, Slots, Übungen); die fachliche Unterscheidung A/B lässt sich mit **CHECK**- und API‑Regeln ausdrücken (`library` ⇒ `group_id IS NULL`, keine `training_unit_id` an Slots), ohne doppelte Router/Joins. Zwei physische Typen würden ohne Mehrwert Polymorphismus in der API erzwingen oder eine künstliche Supertyp‑Tabelle nach sich ziehen. +**Slot‑Inhalt (Migration 037):** Pro `training_framework_slot` existiert genau eine **Blueprint‑Zeile** in `training_units` mit **`framework_slot_id`** (partieller **UNIQUE**-Index); der Ablauf entspricht **derselben** Struktur wie geplante Einheiten über **`training_unit_sections`** und **`training_unit_section_items`** (Übungen, Notizen, Varianten wie in der Planung). Die frühere Tabelle **`training_framework_slot_exercises`** wird nach Datenübernahme **`DROP`**pt. -**Abgrenzung Konzept §6:** Dokumentiert hier; Funktionskonzept kann auf diesen Abschnitt verweisen. +**Geplante Einheit aus Rahmen:** **`POST /api/training-units/from-framework-slot`** kopiert diese Blueprint‑Unit (**tiefe Kopie**) mit **`group_id` + `planned_date`**; **`origin_framework_slot_id`** hält die Herkunft (Lineage‑Light). **`GET /api/training-units`** blendet Einheiten mit **`framework_slot_id IS NOT NULL`** aus (Kalender/API‑Liste ohne Rahmen‑Blueprints). + +**CHECK‑Constraint auf `training_units`:** Zeile ist entweder **Blueprint** (`framework_slot_id` gesetzt, `group_id`/`planned_date` NULL, kein `origin_framework_slot_id`) oder **Kalender‑Einheit** (`framework_slot_id` NULL, `group_id` und `planned_date` gesetzt; `origin_framework_slot_id` optional). + +**Konsequenz Konzept CURR‑012 („concrete/library“):** Persistiert wird **ein** Kopf ohne Modus-Spalte: Immer Bibliotheks‑Rolle; Konkretisierung nur über Planung/API‑Kopie. Historische DDL mit `plan_mode` siehe **`035`**/`036` in dieser Datei (**§5 Changelog**) und `backend/migrations/`. ### 2.1 Checkliste (Abhak-Stand) -- [x] **Entität(en):** eigene Bibliotheks-Entität `training_framework_programs` (**CURR‑009**); `training_plan_templates` unverändert **eine‑Einheit‑Mikrovorlage** (**C5**). -- [x] **Modus A vs. B:** ein Datensatz + `plan_mode` + Nullables (**§2.0**); `library` erzwingt `group_id` NULL; **`training_plan_template_id` pro Slot** — **deferred** (Technical: MVP ohne Spalte, bis Nutzen geklärt, **CURR‑010**). -- [x] **Zielliste:** `training_framework_goals`, API erzwingt **≥ 1** Ziel beim Anlegen/Ersetzen (**CURR‑011**). -- [x] **Slots:** `training_framework_slots` mit **`sort_order`**, optional **Titel/Notizen**; Übungen über **`training_framework_slot_exercises`** (Sortierung **`order_index`**, optional **`exercise_variant_id`**). -- [x] **Progressionsgraph:** Stufe 1 besteht (**§3–§4**); **kein Pflichtbezug** pro Slot (**CURR‑013**). -- [x] **Konkretkontakt Modus A:** optional **`training_unit_id`** pro Slot; bei gesetzter Rahmen-**`group_id`** muss die Einheit zur gleichen Gruppe gehören. **Live‑Writes** zurück in die Bibliotheksvorlage: **nicht** vorgesehen (**CURR‑006**). -- [x] **Instanziierung (Modus B):** Persistenz der Vorlage (**MVP**); Bulk-Anlage von **`training_units`** aus dem Rahmen — **Ausbauschritt**/zweiter PR (**CURR‑012** C4a/b). -- [x] **Governance neue Objekte:** `visibility`, `club_id`, `created_by` wie Progressionsgraph (**CURR‑005**). **`training_plan_templates.visibility`** nachgezogen in derselben Migration **035** mit Backfill **`club`** (**CURR‑007**, **CURR‑008**; frühe Installationen). -- [x] **REST Rahmenprogramm:** `/api/training-framework-programs` (**§2.3**); Progressions‑API weiter **§3.3**. +- [x] **Entität(en):** `training_framework_programs` (**CURR‑009**); `training_plan_templates` unverändert **eine‑Einheit‑Mikrovorlage** (**C5**). +- [x] **Bibliothek only (036):** Kopf ohne `plan_mode`/`group_id`; Kontextfilter **`focus_area_id`**, **`style_direction_id`**; M:N **`training_framework_program_training_types`**, **`training_framework_program_target_groups`**. +- [x] **Zielliste:** `training_framework_goals`, API **≥ 1** Ziel (**CURR‑011**). +- [x] **Slots:** `training_framework_slots` mit **`sort_order`**, optional **Titel/Notizen**; **Ablauf** über zugehörige **Blueprint‑`training_units`** + Sektionen/Items (**037**), nicht mehr `training_framework_slot_exercises`. +- [x] **Progressionsgraph:** Stufe 1 (**§3–§4**); **kein Pflichtbezug** pro Slot (**CURR‑013**). +- [x] **Kein Live‑Write** von Kalendereinheiten zurück in die Vorlage (**CURR‑006**); Konkretisierung = **Kopie** (siehe **§2.4**). +- [x] **Instanziierung (MVP):** `POST /api/training-units/from-framework-slot` — weiterer Ausbau: Bulk, Kalender‑UI‑Flow, **`training_plan_template_id` pro Slot** weiterhin optional/deferred (**CURR‑010**). +- [x] **Governance:** `visibility`, `club_id`, **`training_plan_templates.visibility`** (**035**) — (**CURR‑005–008**). +- [x] **REST Rahmenprogramm:** `/api/training-framework-programs` (**§2.3**); Planung (**§2.4**). -### 2.2 DDL‑Skizze (Migration **035**, Kurzüberblick) +### 2.2 DDL‑Überblick (Migrationen **035** → **036** → **037**) + +Auszug aktueller Zustand nach **036/037** (Details: `backend/migrations/035_training_framework_programs.sql`, `036_framework_program_context_only_library.sql`, `037_training_framework_blueprint_units.sql`): ```sql --- Header +-- Kopf (ohne Modus-Spalten nach 036) training_framework_programs ( id, title NOT NULL, description, - plan_mode NOT NULL CHECK (IN 'concrete','library'), - group_id FK training_groups NULL, - planned_period_start, planned_period_end NULL, -- reine Meta-/UI‑Hilfe + planned_period_start, planned_period_end NULL, visibility NOT NULL, club_id, created_by, - CHECK ((plan_mode = 'library' AND group_id IS NULL) OR plan_mode = 'concrete'), - timestamps + update_trigger + focus_area_id, style_direction_id NULL REFERENCES …, + … timestamps ) --- Ziele (≥1 über API beim Speichern) training_framework_goals ( id, framework_program_id FK CASCADE, sort_order UNIQUE per framework, title, notes ) --- Slots training_framework_slots ( id, framework_program_id FK CASCADE, sort_order UNIQUE per framework, - title, notes, training_unit_id FK training_units SET NULL + title, notes, + training_unit_id FK … (Spalte technisch noch vorhanden; fachlich ungenutzt, per 036 geleert) ) --- Stückliste pro Slot (Übung → FK CASCADE beim Löschen der Übung) -training_framework_slot_exercises ( - id, slot_id FK CASCADE, - exercise_id FK exercises CASCADE, - exercise_variant_id FK exercise_variants NULL, - order_index UNIQUE per slot -) +-- Blueprint: eigene Einheit wie in der Planung (031) +training_units.framework_slot_id -- UNIQUE (partial index), FK → slots ON DELETE CASCADE +training_units.origin_framework_slot_id -- optional auf Kopien aus dem Rahmen (SET NULL) +-- CHECK chk_training_units_blueprint_vs_scheduled (Blueprint vs. Kalender‑Zeile) +-- training_framework_slot_exercises → entfallen (037) ``` -**Löschkaskaden:** löschen eines **Rahmens** ⇒ Ziele + Slots ⇒ Slot‑Übungen (alles **`ON DELETE CASCADE`** vom Rahmen über Slots). löschen eines **`training_unit`** ⇒ FK am Slot **`SET NULL`**. löschen einer **Übung** ⇒ Zeilen in **`training_framework_slot_exercises`** entfallen (`CASCADE`). - -**Deferred (nicht im MVP):** `training_plan_templates.id` FK je Slot (**CURR‑010** Optional). +**Löschkaskaden:** Rahmen löschen ⇒ Ziele + Slots; **Slot löschen** ⇒ zugehörige Blueprint‑`training_unit` per **`ON DELETE CASCADE`** auf `framework_slot_id`. ### 2.3 REST‑Überblick (`router` `training_framework_programs`) | Methode | Pfad | Zweck | |---------|------|--------| -| GET | `/training-framework-programs` | Liste; Admin/Superadmin alle, sonst eigene (`created_by`); Aggregation `goals_count`, `slots_count` | -| GET | `/training-framework-programs/{id}` | Detail inkl. `goals[]`, `slots[]` mit jeweils `exercises[]` (inkl. Titel‑Joins) | -| POST | `/training-framework-programs` | Neu; **Pflicht:** `title`, **`plan_mode`**, **`goals`** (≥ 1 Eintrag mit `title`); optional `slots` | -| PUT | `/training-framework-programs/{id}` | Header‑Felder; optional volles Ersetzen von **`goals`** und/oder **`slots`** wie bei Vorlagen‑Sektionen | -| DELETE | `/training-framework-programs/{id}` | Rahmen löschen (**CASCADE** Kinder) | +| GET | `/training-framework-programs` | Liste; Admin/Superadmin alle, sonst eigene (`created_by`); u. a. `goals_count`, `slots_count`, Kontext‑Counts | +| GET | `/training-framework-programs/{id}` | Detail inkl. `goals[]`, `slots[]` mit je **`blueprint_training_unit_id`**, **`sections[]`**, **`exercises[]`** (letzteres aus Sektionen geflacht, kompatibel zum Editor) | +| POST | `/training-framework-programs` | Neu; **Pflicht:** `title`, **`goals`** (≥ 1); optional `slots` (weiterhin **`exercises[]`** pro Slot möglich — Backend materialisiert Sektionen); Header: `focus_area_id`, `style_direction_id`, `training_type_ids`, `target_group_ids`, … | +| PUT | `/training-framework-programs/{id}` | Header; volles Ersetzen von **`goals`** und/oder **`slots`** (neue Slots ⇒ neue Blueprint‑Units) | +| DELETE | `/training-framework-programs/{id}` | Rahmen + Kinder | -**AuthZ:** Schreibzugriff nur mit **`_has_planning_role`** (wie `training_plan_templates`); Lesen/Ändern/Löschen: **Admin/Superadmin** oder **Ersteller** (`created_by`). +**AuthZ:** wie zuvor — Planungsrolle zum Schreiben; Lesen/Schreiben/Löschen des Rahmens: Admin/Superadmin oder Ersteller. -**Payload‑Hinweise (JSON):** +**Payload‑Hinweise (JSON), Slots:** -- `goals`: `[{ sort_order?, title, notes? }, …]` — **`sort_order`** default Reihenfolge im Array -- `slots`: `[{ sort_order?, title?, notes?, training_unit_id?, exercises: [{ exercise_id, exercise_variant_id?, order_index? }] }, …]` -- Bei **`library`:** **`training_unit_id`** an Slots → **400**; nach Wechsel auf **`library`** werden bestehende Slot-Verknüpfungen zu **`training_units`** geleert. +- Weiterhin: `exercises: [{ exercise_id, exercise_variant_id?, order_index? }]` (wird intern in eine Sektion übernommen). +- Erweitert möglich: `sections` wie bei **`PUT /api/training-units/{id}`** (voller Ablauf mit Notizen/Items). -**Minimal‑UI:** im Lieferumfang dieser Iteration nicht enthalten (**OpenAPI `/docs`** / Postman); siehe Funktionskonzept §6. +### 2.4 Planung / Kalender (`router` `training_planning.py`, Auszug) + +| Methode | Pfad | Zweck | +|---------|------|--------| +| GET | `/training-units` | Nur **Kalender‑Einheiten** (`framework_slot_id IS NULL`) | +| GET/PUT | `/training-units/{id}` | Blueprint lesen/bearbeiten: möglich mit Rahmen‑Auth; spezielle Regeln im **PUT** (kein Template‑Reset, kein `plan_template_id` am Blueprint) | +| DELETE | `/training-units/{id}` | Blueprint **nicht** über diesen Pfad löschen (Fehlerhinweis); Slot entfernen über Rahmen‑**PUT** | +| POST | `/training-units/from-framework-slot` | Body: `framework_slot_id`, `group_id`, `planned_date` — tiefe Kopie + **`origin_framework_slot_id`** | + +**Frontend:** `createTrainingUnitFromFrameworkSlot` in `frontend/src/utils/api.js`. --- @@ -151,7 +158,7 @@ Listenqueries liefern Join‑Felder **`from_exercise_title`**, **`to_exercise_ti ## 4. Zwischenstand für Produkt / Trainingsplanung (bewusste Grenzen) -**Freigabe:** Der beschriebene Stand gilt als **ausreichend**, um mit dem **Trainingsplanungsmodul** und später Rahmen/Slots (**CURR‑002 (2)**) weiterzuarbeiten — ohne dass Pflicht zur Pflege komplexer Graph‑Strukturen entsteht (**CURR‑013**). +**Freigabe:** Der beschriebene Stand unterstützt **Rahmen‑Bibliothek mit vollem Ablauf pro Slot** (wie Planung) und **Kopie in die Gruppenplanung**; der **Progressionsgraph** bleibt **unterstützend** (**CURR‑013**). Offen: Kalender‑UI‑Flow, Bulk‑Instanziierung, erweiterte Lineage/Feedback (**Konzept Schritt E**). **Was gut nutzbar ist** @@ -179,7 +186,8 @@ Details weiterhin Diskussionsgrundlage in `TRAINING_CURRICULUM_AND_GOVERNANCE_CO | Datum | Änderung | |-------|----------| -| 2026-05-05 | **CURR‑002 (2):** §2 Rahmenprogramm — Entscheid **eine Tabelle + `plan_mode`**, DDL‑Skizze, REST‑Überblick; Migration **035**; `training_plan_templates.visibility`. | +| 2026-05-05 | **037 / API:** Nur Bibliothek + **Blueprint** pro Slot über `training_units` + Sektionen/Items; `training_framework_slot_exercises` entfernt; `POST …/training-units/from-framework-slot`; Planungsliste ohne Blueprints. **036** dokumentiert am Kopf (Kontext, M:N, kein plan_mode/group_id). **§2** vollständig ersetzt. | +| 2026-05-05 | **CURR‑002 (2):** §2 Rahmenprogramm — Entscheid **eine Tabelle + `plan_mode`**, DDL‑Skizze, REST‑Überblick; Migration **035**; `training_plan_templates.visibility`. *(Historisch — Modus-Spalten durch **036** ersetzt.)* | | 2026-04-30 | **Zwischen-Doku:** §3 auf Migrationen 032–034 + API **sequence/delete-batch** + Frontend erweitert; **§4** Produktfreigabe vs. Lücken (parallele Alternativen); Changelog §5. | | 2026-04-30 | §3: erste Fassung Migration 032 + REST‑Basis (CURR‑002 (1)). | | 2026-04-28 | Erstanlage Stub mit Checkliste. | diff --git a/CLAUDE.md b/CLAUDE.md index 9b706c1..fa53f8f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,12 +78,12 @@ frontend/src/ **Siehe:** `backend/version.py` (`APP_VERSION`, `DB_SCHEMA_VERSION`, `MODULE_VERSIONS`) und `.claude/docs/PROJECT_STATUS.md`. -Kurz (Stand 2026-04-27): App **0.7.9**, DB-Schema-Version **20260427030**; Kern-Features: Übungen mit Varianten, Medien, Trainingsplanung mit optionaler Variantenwahl. +Kurz (Stand 2026-05-05): App **0.8.10**, DB‑Schema‑Version **`20260505037`**; Kern: Übungen, Varianten, Medien, Planung mit Sektionen, **Trainingsrahmen Bibliothek + Slot‑Blueprint** (036–037), Progressionsgraph, Reifegrad/Matrix‑Stack — Details `PROJECT_STATUS.md` und `TRAINING_FRAMEWORK_SPEC.md` §2. ### Log (Auszug) +- 2026-05-05: Rahmen nur Bibliothek (**036**), Slot‑Ablauf = `training_units` + Sektionen (**037**), `POST /api/training-units/from-framework-slot`, keine `training_framework_slot_exercises` mehr — siehe `DATABASE_SCHEMA.md` / `FEATURES_DELIVERED_2026-Q2.md`. - 2026-04-27: Übungsvarianten API/UI, Migration 030, Listen-UX-Suche, Admin-Upload-Limits — siehe `PROJECT_STATUS.md` und `docs/library/FEATURES_DELIVERED_2026-Q2.md`. -- 2026-04-21: Repository- und Initial-Setup (Historie; Details in Git). ## Domänenmodell (MVP Core) @@ -104,12 +104,11 @@ Kurz (Stand 2026-04-27): App **0.7.9**, DB-Schema-Version **20260427030**; Kern- - `exercise_skills` - M:N Übung ↔ Fähigkeit - `exercise_media` - Medien (Bilder, Videos) -**Trainingsplanung:** -- `training_templates` - Vorlagen / Standards -- `training_sections` - Trainingsabschnitte -- `section_exercises` - Übungen in Abschnitten -- `training_units` - Konkrete Trainingseinheiten -- `training_programs` - Trainingsprogramme +**Trainingsplanung / Rahmen:** +- `training_plan_templates` + Sektionsvorlagen — Mikrovorlage pro Einheit (Migration **031**) +- `training_units` — Kalender‑Instanzen **und** Rahmen‑Slot‑Blueprints (`framework_slot_id` ab **037**) +- `training_framework_programs` + Ziele + Slots (Migration **035–036**) — Bibliotheks‑Rahmen +- Legacy: `training_templates` / `section_exercises` o. ä. — in älteren Skizzen; produktiver Pfad siehe Migrationen **006**/**031** **Governance:** - `content_change_requests` - Änderungsanfragen diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md index 388f537..8ffbc8c 100644 --- a/docs/HANDOVER.md +++ b/docs/HANDOVER.md @@ -1,6 +1,6 @@ # Shinkan Jinkendo – Entwicklungsstand & Handover -**Stand:** 2026-04-28 +**Stand:** 2026-05-05 **App-Version / DB-Schema:** siehe `backend/version.py` Diese Datei ist die **Einstiegs-Doku für neue Chat-Sessions**: Anforderungen im Detail stehen in `.claude/docs/` (siehe unten); hier der **implementierte Stand** und **nächste Baustellen**. @@ -25,6 +25,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl | Übungen: API, DB, Architektur, Routing | `.claude/docs/technical/EXERCISES_API_SPEC.md`, `EXERCISES_DATABASE_FINAL.md`, `EXERCISES_ARCHITECTURE.md`, `EXERCISES_FRONTEND_ROUTING.md` | | Media / Upload | `.claude/docs/technical/MEDIA_UPLOAD_SPEC.md` | | MediaWiki-Import | `.claude/docs/technical/MEDIAWIKI_IMPORT_SPEC.md` | +| Rahmenprogramm · Planung (`training_units` Blueprints), Progressionsgraph | `.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md`; Überblick DB → `.claude/docs/technical/DATABASE_SCHEMA.md`; Domäne → `.claude/docs/functional/DOMAIN_MODEL.md` | --- @@ -64,7 +65,16 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl --- -## 3. Stand: Übungen (Lücke für nächste Session) +## 3. Trainingsrahmenprogramm & Planungs‑Blueprint (kurz) + +- **Migration 036:** Rahmenkopf nur Bibliothek (Kontext: Fokusbereich, Stilrichtung; M:N Trainingsarten/Zielgruppen); keine `plan_mode`/keine Kopf‑`group_id`. +- **Migration 037:** Pro Rahmen‑Slot eine **`training_units`‑Zeile mit `framework_slot_id`**; strukturierter Ablauf wie echte Einheiten (`training_unit_sections` / `training_unit_section_items`). Tabelle **`training_framework_slot_exercises`** entfällt. +- **API:** Rahmen unter **`/api/training-framework-programs`** (Slots liefern u. a. **`blueprint_training_unit_id`**, **`sections[]`**, **`exercises[]`**); Kalenderliste **`GET /api/training-units`** ohne Blueprints; Übernahme **`POST /api/training-units/from-framework-slot`**. +- **Code:** `backend/routers/training_framework_programs.py`, `training_planning.py`; Frontend **`TrainingFrameworkProgramEditPage.jsx`**; **`createTrainingUnitFromFrameworkSlot`** in `api.js`. + +--- + +## 4. Stand: Übungen (Lücke für nächste Session) **Ist (laut Projektdoku und aktuellem Produktziel):** @@ -79,18 +89,18 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl --- -## 4. Technische Referenz (kurz) +## 5. Technische Referenz (kurz) | Bereich | Einstieg | |---------|----------| -| Backend API | `backend/main.py`, `backend/routers/maturity_models.py`, `matrix_stack_bundle.py`, `exercises.py`, `catalogs.py`, `skills.py` | -| Migrationen | `backend/migrations/` (u. a. 024–027 Reifegrad/Bindings) | +| Backend API | `backend/main.py`; Router u. a. **`training_framework_programs.py`**, **`training_planning.py`**, `maturity_models.py`, `matrix_stack_bundle.py`, `exercises.py`, `catalogs.py`, `skills.py` | +| Migrationen | `backend/migrations/` (u. a. 024–027 Reifegrad/Bindings; **035–037** Rahmenprogramm / Slot‑Blueprint) | | Frontend API | `frontend/src/utils/api.js` | | Version / Changelog | `backend/version.py` | --- -## 5. Veraltete Hinweise +## 6. Veraltete Hinweise Die Datei `.claude/docs/working/HANDOVER_NEXT_SESSION.md` (2026-04-22) ist **historisch**; für den aktuellen Stand gilt **`docs/HANDOVER.md`**. diff --git a/frontend/src/components/TrainingUnitSectionsEditor.jsx b/frontend/src/components/TrainingUnitSectionsEditor.jsx new file mode 100644 index 0000000..727751f --- /dev/null +++ b/frontend/src/components/TrainingUnitSectionsEditor.jsx @@ -0,0 +1,442 @@ +import React, { useCallback } from 'react' +import { + defaultSection, + exerciseRow, + noteRow, + sectionPlannedMinutes, +} from '../utils/trainingUnitSectionsForm' + +/** + * @param {(updater: (prev: Array) => Array) => void} props.onSectionsChange — wie React setState + */ +export default function TrainingUnitSectionsEditor({ + sections, + onSectionsChange, + onRequestExercisePick, + onPeekExercise, + showExecutionExtras = false, + heading = 'Abschnitte & Übungen', + hideHeading = false, +}) { + const ensure = (prev) => + prev && prev.length ? prev : [defaultSection()] + + const patch = useCallback( + (updater) => { + onSectionsChange((prev) => updater(ensure(prev))) + }, + [onSectionsChange] + ) + + const updateSectionField = (sIdx, field, val) => { + patch((prev) => + prev.map((s, i) => (i === sIdx ? { ...s, [field]: val } : s)) + ) + } + + const addSection = () => { + patch((prev) => [...prev, defaultSection(`Abschnitt ${prev.length + 1}`)]) + } + + const removeSection = (sIdx) => { + patch((prev) => { + const next = prev.filter((_, i) => i !== sIdx) + return next.length ? next : [defaultSection()] + }) + } + + const moveSection = (sIdx, dir) => { + patch((prev) => { + const p = [...prev] + const ta = sIdx + dir + if (ta < 0 || ta >= p.length) return p + ;[p[sIdx], p[ta]] = [p[ta], p[sIdx]] + return p + }) + } + + const addItem = (sIdx, kind) => { + patch((prev) => + prev.map((s, i) => + i !== sIdx + ? s + : { + ...s, + items: [...(s.items || []), kind === 'note' ? noteRow() : exerciseRow()], + } + ) + ) + } + + const removeItem = (sIdx, iIdx) => { + patch((prev) => + prev.map((s, si) => + si !== sIdx ? s : { ...s, items: (s.items || []).filter((_, ii) => ii !== iIdx) } + ) + ) + } + + const moveItem = (sIdx, iIdx, dir) => { + patch((prev) => + prev.map((s, si) => { + if (si !== sIdx) return s + const items = [...(s.items || [])] + const ta = iIdx + dir + if (ta < 0 || ta >= items.length) return s + ;[items[iIdx], items[ta]] = [items[ta], items[iIdx]] + return { ...s, items } + }) + ) + } + + const updateItem = (sIdx, iIdx, field, val) => { + patch((prev) => + prev.map((s, si) => + si !== sIdx + ? s + : { + ...s, + items: (s.items || []).map((row, ii) => + ii === iIdx ? { ...row, [field]: val } : row + ), + } + ) + ) + } + + const list = ensure(sections) + + return ( +
+ {!hideHeading ? ( +

{heading}

+ ) : null} + {list.map((sec, sIdx) => { + const planMin = sectionPlannedMinutes(sec) + return ( +
+
+ updateSectionField(sIdx, 'title', e.target.value)} + placeholder="Abschnittstitel (z. B. Aufwärmen)" + /> +
+ + +
+ +
+