feat(combination-exercises): enhance method profile integration and update specifications
All checks were successful
Deploy Development / deploy (push) Successful in 38s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 11s
Test Suite / playwright-tests (push) Successful in 1m4s
All checks were successful
Deploy Development / deploy (push) Successful in 38s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 11s
Test Suite / playwright-tests (push) Successful in 1m4s
- Updated app version to 0.8.102, reflecting recent enhancements in combination exercises. - Introduced structured method profiles for combination exercises, allowing for detailed planning and coaching support. - Enhanced frontend components to display method profiles in the Exercise and Combination Coach views. - Updated documentation to include new specifications and implementation details for method archetypes and profiles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
919910d52a
commit
12fd3926b2
|
|
@ -9,6 +9,9 @@ Ausführliche fachliche Inhalte:
|
|||
| [DOMAIN_MODEL.md](./DOMAIN_MODEL.md) | Domänenmodell, Variantenlogik (Abschnitt 11.2), **Medien-Archiv** (Abschnitt 2026-05) |
|
||||
| [MEDIA_ASSETS_AND_ARCHIVE_SPEC.md](../technical/MEDIA_ASSETS_AND_ARCHIVE_SPEC.md) | Medien-Archiv, Lifecycle, **Inline-Medien** (Spec Abschnitt 11, umgesetzt) |
|
||||
| [MULTI_TENANCY_RBAC_ARCHITECTURE.md](../technical/MULTI_TENANCY_RBAC_ARCHITECTURE.md) | Zielarchitektur Mandanten/Rollen/Membership & Umsetzungsplan |
|
||||
| [**Trainingsmodule & Kombinationsübungen (Fachspez V3)**](./Shinkan%20Trainingsmodule%20Kombinationsuebungen%20Spezifikation%20V2.md) | Produktlogik Module/Kombinationen, **Methoden-Archetypen**, **Coaching-Stufen (§ 10.4)**, kanonische Archetyp-IDs **§ 10.2.1**, **Anhang A** Implementierungsabgleich |
|
||||
| [**Umsetzungsplan Trainingsmodule & Kombination**](../working/TRAINING_MODULES_IMPLEMENTATION_PLAN.md) | Phase 1–5, Coaching-Pakete 4a–4d, Verweis auf Code-Stand |
|
||||
| [**Technischer Entwurf Module/Kombination**](../technical/TRAINING_MODULES_AND_COMBINATION_EXERCISES_SPEC.md) | API/Daten-Ideen; aktueller Coach-/Archetyp-Abgleich im Kopfabschnitt |
|
||||
|
||||
**Lieferstand & Umsetzung (Stand Code):** [`../PROJECT_STATUS.md`](../PROJECT_STATUS.md), [`../library/FEATURES_DELIVERED_2026-Q2.md`](../library/FEATURES_DELIVERED_2026-Q2.md) (Abschnitt 12), Repo-Root **`docs/HANDOVER.md`**, **`docs/FACHLICHE_NUTZERFUNKTIONEN.md`**.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Trainingsmodule und Kombinationsübungen — fachliche Spezifikation V3
|
||||
|
||||
**Status:** fachlicher Spezifikationsentwurf
|
||||
**Stand:** 2026-05-12
|
||||
**Stand:** 2026-05-12 · **Coaching/Archetypen:** § 10.2.1, § 10.4–10.5, **§ 5.4/§ 6.3** Methoden/Archetypen/Zeitschicht · **Anhang A** (Abgleich Code vs. Spec)
|
||||
**Zweck:** Produkt- und Fachspezifikation für Trainingsmodule, Kombinationsübungen, Trainingsmethodenbezug, Planungsintegration und Coaching-Modus in Shinkan.
|
||||
|
||||
**Wichtige Leitlinie dieser Version:**
|
||||
|
|
@ -335,6 +335,30 @@ Für jeden Archetyp muss fachlich beschrieben sein:
|
|||
|
||||
Die technische Validierung und konkrete Ablage dieser Angaben soll der Coding Agent planen.
|
||||
|
||||
### 5.4 Einordnung: Trainingsformen wie HIIT, Dauer, plyometrisch ↔ Archetypen
|
||||
|
||||
**Wichtige Trennung (bleibt fachlich zwingend):**
|
||||
|
||||
* **Trainingsmethode im Methodenkatalog** (z. B. HIIT, extensive Intervallmethode, Dauermethode, plyometrisches Training) beschreibt primär den **didaktisch/belastungsmethodischen Kontext („was für eine Trainingsqualität ist das?“)**.
|
||||
* **`method_archetype`** beschreibt **das Ablaufmuster („wie soll der Trainer den Block strukturieren und im Coach geführt werden?“)** — insbesondere **Parallelität, Rotation, Sequenz, Zeitdomänen**.
|
||||
|
||||
Dieselbe Methode kann in der Praxis mit **mehreren** Archetypen sinnvoll kombiniert sein; das ist **kein Widerspruch**.
|
||||
|
||||
| Beispiel (Methoden-/Belastungsbegriff) | Typischer Archetyp (Orientierung), nicht Pflicht |
|
||||
| -------------------------------------- | ---------------------------------------------- |
|
||||
| **HIIT**, Tabata-ähnlich, Kurzintervalle oft mit hoher Intention | sehr oft **`time_domain_interval`** oder innerhalb eines Zirkels **`circuit_rotate_time`** mit kurzen Arbeitsphasen; Partnerformen zusätzlich **`pair_superset`**. |
|
||||
| **Klassisches Intervalltraining** (längere Arbeit, definierte Erholung, N Wiederholungen) | überwiegend **`time_domain_interval`** oder **`circuit_rotate_time`**, wenn die „Intervallschicht“ an Stationen gebunden ist. |
|
||||
| **Dauermethode** (überwiegend durchgehend ohne harte Arbeit-Erholung-Takte) | eher **`free_method_block`** oder **`sequence_linear`** mit optionalen Hinweiten; **weniger** `time_domain_interval`, sofern kein geregeltes Intervallschema gemeint ist. |
|
||||
| **Plyometrisch**, Explosivblöcke, Technik-Schichtung | häufig **`sequence_linear`** (Progression vor Ort) oder **`circuit_rotate_time`** / **`time_domain_interval`**, wenn klar Zeitfenster oder Wiederholungsblöcke vorgegeben sind. |
|
||||
| Rein **organisatorisches** Stationslaufen ohne gemeinsamen Intervalltakt | **`circuit_rotate_time`** oder **`station_parcours`**, **`circuit_all_parallel`**, je nach ob rotiert wird oder parallel aktiv ist. |
|
||||
|
||||
**Shinkan-Zielrichtung bleibt trainerzentriert:** Es geht **nicht** um individuelle Pulsonomie eines Sportlers, sondern darum, dass der **Trainer Belastungs- und Erholungsphasen, Durchläufe und ggf. Umlauf-/Parallellogik** vorgibt und der **Coach** diese Vorgaben **sichtbar und später steuerbar** macht
|
||||
(siehe **§ 6.3** zu Phasen jenseits „nur Gesamtminuten auf dem Planungsitem“).
|
||||
|
||||
### 5.5 Erweiterbarkeit von Archetypen (aktuell zurückgestellt)
|
||||
|
||||
Die Idee einer **von Superadmins zur Laufzeit editierbare Archetyp-Registry**, die den Coaching-Modus **völlig frei parametrierbar** macht, wird **zurückgestellt**. Vorerst reicht die **festgelegte, versionierte Liste** kanonischer Archetyp-IDs (**§ 10.2.1**); **weitere Archetypen** können später **wie bisher durch Produkt-/Release entschieden** ergänzt werden (Code oder kuratierter Import), ohne freies „Beliebig-Neuanlegen“ ohne definierten Coach-Verhaltens-Anker.
|
||||
|
||||
---
|
||||
|
||||
## 6. Kombinationsübungen
|
||||
|
|
@ -370,10 +394,32 @@ Eine Kombinationsübung sollte fachlich enthalten:
|
|||
* Archetyp,
|
||||
* Slots / Stationen / Rollen / Schritte,
|
||||
* mögliche Übungen je Slot,
|
||||
* optionale Standardwerte für Dauer, Runden oder Wechsel,
|
||||
* strukturierte **Zeitphasen und Belastungs-/Erholungsvorgaben** innerhalb der Kombination (**`method_profile`**, Überblick § 6.3; Details § 10.5) — **zusätzlich** zu allenfalls geplanten **Gesamtminuten am Planungseintrag**,
|
||||
* optionale klassische Hinweise zu Dauer, Runden oder Wechsel aus der Übung heraus,
|
||||
* Hinweise für den Coaching-Modus.
|
||||
|
||||
### 6.3 Slot- und Pool-Logik
|
||||
### 6.3 Zeitschicht: Phasen innerhalb der Kombination (Bibliothek) und Anpassungen in der Planung
|
||||
|
||||
Ein **einzelnes** Feld „Geplante Minuten für diesen Eintrag in der Einheit“ kann die **innenliegende zeitliche Logik** einer Kombinationsübung **nicht** ersetzen. Für Trainersteuerung (und später für Coaching **Stufe C**) soll die Kombination in der Bibliotheksbeschreibung vorsehen können:
|
||||
|
||||
**A) Kombinationseinheit („global“ über die Slots)**
|
||||
|
||||
* **Arbeits-** und **Erholungszeiten** (Sekunden/Minuten) und **Anzahl der Durchläufe** oder **Intervalle**,
|
||||
* ggf. **gemeinsamer Takt** für alle Teilnehmenden (z. B. rotierender Zirkel oder eine gemeinsame Intervalluhr),
|
||||
* **Erklär- oder Aufbauzeit** vor dem eigentlichen Start,
|
||||
* dort, wo der Archetyp passt: **Runden-/Umlaufzahl** oder vergleichbare Strukturen.
|
||||
|
||||
Alle diese Angaben sind **Anweisungen an den Trainer** und **Coach‑Assistenz**, **keine** individuelle Pulssonde oder ähnliche Personenmessung.
|
||||
|
||||
**B) Optional pro Slot oder Schritt**
|
||||
|
||||
* wenn fachlich sinnvoll: **von Station zu Station variierende Arbeitsphasen** oder Mini‑Sequenzen innerhalb eines Slots — technisch z. B. als strukturierte Liste in `method_profile` mit Bezug zum `slot_index` (Ausarbeitung Coding Agent).
|
||||
|
||||
**Nach Einplanung in eine konkrete Trainingseinheit** muss diese Zeitschicht (oder ihr Abgleich mit der Einheitsposition) für den Trainer **bearbeitbar** bleiben, **ohne** die Bibliotheksvorlage still zu überschreiben (kopier-/instanzbasierte Anpassungen — siehe bereits § 2.5 und § 8.3).
|
||||
|
||||
**Coach:** soll die wirksamen Werte nach **Übernahme** und **Einheitsübersteuerungen** konsistent nachvollziehen (**§ 10.4**).
|
||||
|
||||
### 6.4 Slot- und Pool-Logik
|
||||
|
||||
Slots können fest oder variabel sein.
|
||||
|
||||
|
|
@ -466,6 +512,7 @@ Produktregel:
|
|||
Nach dem Einfügen muss ein Planungsblock lokal angepasst werden können:
|
||||
|
||||
* Dauer ändern,
|
||||
* **bei Kombinationsübungen:** im Idealfall **`method_profile` (Arbeit, Erholung, Durchläufe)** und Stations-/Slot-Anpassungen des **konkreten Vorkommens**, nicht nur Gesamtzeit,
|
||||
* Übung austauschen,
|
||||
* Station ergänzen,
|
||||
* Hinweise anpassen,
|
||||
|
|
@ -549,6 +596,22 @@ Grundsatz:
|
|||
| Intervallblock | Globale Zeit, Intervallzähler, Aufgaben. |
|
||||
| Freier Methodenblock | Kompakte Übersicht und manuelle Steuerung. |
|
||||
|
||||
#### 10.2.1 Kanonische Archetyp-IDs (Abgleich Fachbegriff, UI und API)
|
||||
|
||||
Damit Produktbeschreibung, Formularfelder (`method_archetype`), Trainingscoach und Backend‑Validierung **dieselben Werte** nutzen und es keinen dokumentationsbedingten Drift gibt, gelten diese **festen Schlüssel** (Maschinen‑IDs):
|
||||
|
||||
| Archetyp (fachlicher Name in § 5.2) | Schlüssel `method_archetype` (`exercises.method_archetype`) |
|
||||
| ----------------------------------- | ----------------------------------------------------------- |
|
||||
| Lineare Sequenz | `sequence_linear` |
|
||||
| Rotierender Zirkel (Zeit) | `circuit_rotate_time` |
|
||||
| Parallele Stationen | `circuit_all_parallel` |
|
||||
| Parcours | `station_parcour` |
|
||||
| Partner- / Paarwechsel | `pair_superset` |
|
||||
| Intervallblock (Zeitdomäne) | `time_domain_interval` |
|
||||
| Freier Methodenblock | `free_method_block` |
|
||||
|
||||
Änderungen an dieser Zuordnung nur **gemeinsam** (Produkt, Backend‑Enum und UI‑Konstanten); siehe Implementierungsanhang weiter unten.
|
||||
|
||||
### 10.3 Durchführungsdokumentation
|
||||
|
||||
Perspektivisch sollte dokumentierbar sein:
|
||||
|
|
@ -563,6 +626,43 @@ Perspektivisch sollte dokumentierbar sein:
|
|||
|
||||
Die konkrete technische Umsetzung wird nicht in dieser Spezifikation festgelegt.
|
||||
|
||||
### 10.4 Coaching-Reifegrade (Normierung ohne technisches Pflichtenheft)
|
||||
|
||||
Archetyp-spezifisches Coaching soll **nicht** als ein einziges UX-„Monolith“ gebaut werden, sondern in **nachvollziehbaren Stufen**, damit frühere Umsetzungen nicht überschrieben wirken und der Fortschritt in Doku/Umsetzungsplan nachverfolgt werden kann:
|
||||
|
||||
| Stufe | Bezeichnung (Arbeitstitel) | Inhalt aus Trainersicht | Abgrenzung |
|
||||
| ----- | ---------------------------- | ------------------------ | ----------- |
|
||||
| **A** | **Informations-/Struktursicht** | Pro Kombinationsübung: Kopf‑Kontext aus Katalog **plus** strukturierte Darstellung der **Slots** und der **einzelnen Kandidatenübungen** (Titel, Kurztext, Detail aufklappbar); **ein zeitlicher Schritt im Coach** entspricht weiter **einem** Planungseintrag (ein Item in der Einheit). | Kein eigener Rundenzähler, kein eigener Stations‑Timer‑State pro Archetyp. |
|
||||
| **B** | **Archetyp-Steuerung in der bestehenden Zeitleiste** | Optionale Aufspaltung: z. B. bei **`sequence_linear`** pro Slot **ein Coach‑Schritt** (Weiter/Zurück pro Station), ohne die Datenbank-Semantik der Einheit zu zerstückeln (Virtuelle Schritte oder materialisierte Hilfs‑Einträge – technische Variante dokumentieren). | Bewusste Produkt-/Architekturentscheidung nötig, damit IST‑Zeiten und Abschluss‑PUT konsistent bleiben. |
|
||||
| **C** | **Interaktive Assistenz je Archetyp** | Gemeinschafts-/Stations‑Timer, Wechselimpulse (**`circuit_rotate_time`**), Vorab‑„Erklärphase“‑Flag (**`circuit_all_parallel`**), Abhaken (**`station_parcour`**), gekoppelte A/B‑Ansicht (**`pair_superset`**), globale Intervalluhr (**`time_domain_interval`**) — jeweils an Parameter aus **`method_profile`** angebunden, wo diese in Stufe A/B bereits sichtbar gepflegt werden. | Keine verpflichtende KI‑Steuerung; Trainer kann überspringen (Grundsatz § 10.1). |
|
||||
|
||||
**Aktuelle Zielrichtung:** Stufe **A** soll für **alle** in § 10.2.1 genannten Archetypen **inhaltsgleich** die Slot‑ und Kandidateninformation liefern; **unterschiedliche Kopf-/Hilfstexte und UI-Mikrolayouts** nach Archetyp sind Teil von A und sollten gemeinsam mit Stufe B/C wachsen (kein „still“ abweichendes Verhalten ohne Doku‑Update).
|
||||
|
||||
### 10.5 Fachliche Mindestinfos im **Ablaufprofil** (`method_profile`) pro Archetyp
|
||||
|
||||
`method_profile` ist das **konkretisierende** JSON (o. ä.) zum gewählten Archetyp: Zeiten, Runden, Schalter. Technische Pflichtfelder und Validierung regelt die technische Umsetzung — **fachlich** gilt folgende Minimal-Erwartung, damit Stufe B/C sinnvoll nutzbar ist:
|
||||
|
||||
| Archetyp-Schlüssel | Mindest-Parameter (fachlich sinnvoll; Benennung in der Umsetzung kanonisch festlegen). Typische Zuordnung methodischer Überbegriffe: **§ 5.4** |
|
||||
| ------------------ | ------------------------------------------------------------------------------------- |
|
||||
| `sequence_linear` | Orientierungs-Arbeits-/Pausenhinweise je Schritt oder global; Reihenfolge = Slotreihenfolge — u. a. für **Skillschichtungen**, Aufwärmserien ohne festen Rotationstakt oder **Ausdauer-/Technikketten ohne Intervalltakt**. |
|
||||
| `circuit_rotate_time` | **Arbeit** je Station oder Umlauf, **optional Wechsel/Transition**, **optional Erholung zwischen Runden**, **optional Rundenanzahl**; Kern für rotierenden Zirkel inkl. vieler HIIT-/Zirkelmischformen über Stationen hinweg (**§ 5.4**). |
|
||||
| `circuit_all_parallel` | „Erst gemeinsame Erklärung, dann gleichzeitiger Betrieb aller Stationen“; Zeitfenster Vorab‑Erklärung optional — z. B. wenn keine Rotation, aber gemeinsamer Startzeitpunkt gewünscht ist. |
|
||||
| `station_parcour` | Fokus Stationsbeschreibung; optional freie Besuchsreihenfolge (Profil/Archetyp); weniger zentral **feste Arbeit/Erholung-Takte**, mehr Navigation/Abhaken (später Stufe C). |
|
||||
| `pair_superset` | Arbeit und Wechsel bei **gekoppelten** Rollen; typisch wenn zwei Linien oder Partnerblöcke im Takt gewechselt werden. |
|
||||
| `time_domain_interval` | Klare Zeitdomäne: **Belastungs-, Erholungsblöcke** und **Anzahl Wiederholungen** der Domäne bzw. **Gesamtblockbegrenzung** — zentrale Schicht für viele Formen aus **„Intervall/HIIT/Zeitschachtelungs“‑**Methodenkatalog ohne individuelle Messung (**§ 5.4**). |
|
||||
| `free_method_block` | Keine zusätzlichen Pflichtparameter; **unterstützt** etwa **reibungsarmere Dauer- oder Spielformen**, wo der Trainer keine starke Taktuhr braucht, aber Stationsideen strukturiert bündeln will. |
|
||||
|
||||
#### 10.5.1 Mehrschichtiges Planen (Überblick)
|
||||
|
||||
| Ebene | Inhalt zeitlicher Art |
|
||||
| ----- | --------------------- |
|
||||
| **Einheit / Planungsitem** | z. B. geplante **Gesamtminuten** dieser Platzierung („der Block soll heute etwa 25 Min einnehmen“). |
|
||||
| **Kombinationsübung (Bibliothek)** | strukturierte **Phasen in `method_profile`** (arbeiten, pausieren, Runden…) — § 6.3. |
|
||||
| **Einheitliche Planungsinstanz** | optionale Abweiche vom Bibliotheksprofil **nur für dieses Training** (§ 8.3). |
|
||||
| **Coach** | liest wirksamen Stand (Bibliothek + Overrides) zur **Orientierung**, später automatisierte Taktassistenz (**§ 10.4**). |
|
||||
|
||||
Solange diese Mindestinfos in der Datenpflege noch **nicht** validiert oder nicht geführt erfasst werden, bleibt Coaching bei **Informations-Schicht und manuellen Timern des bestehenden Coach-Dialogs** die fachlich ehrliche Darstellung (siehe Anhang A).
|
||||
|
||||
---
|
||||
|
||||
## 11. Rahmenprogramm-Integration
|
||||
|
|
@ -625,7 +725,7 @@ Für Methoden ist eine besondere Qualitätskontrolle sinnvoll, weil sie als fach
|
|||
* freier Methodenblock,
|
||||
* Planungsblöcke als fachliches Konzept,
|
||||
* lokale Anpassbarkeit nach Einfügen,
|
||||
* einfache Coaching-Ansicht.
|
||||
* Coaching: mindestens **Stufe A** nach § 10.4 für alle Archetypen aus § 10.2.1 (strukturierte Slot-/Kombi-Darstellung; Archetyp-Hilfstexte); **zeitliche/mechanische Archetyp-Steuerung (Stufen B/C)** ausdrücklich als Ausbauschritte.
|
||||
|
||||
### 13.2 Sollte vorbereitet werden
|
||||
|
||||
|
|
@ -677,8 +777,32 @@ Die Spezifikation ist daher kein technisches Pflichtenheft, sondern ein fachlich
|
|||
5. Trainingsmethoden sind eigenständige fachliche Katalogobjekte.
|
||||
6. Eine Übung hat eine Hauptmethode und optional Nebenmethoden.
|
||||
7. Methoden-Archetypen beschreiben Ablaufmuster, nicht die Methode selbst.
|
||||
8. Ablaufprofile konkretisieren den Archetyp für Planung und Coaching.
|
||||
8. Ablaufprofile konkretisieren den Archetyp für Planung und Coaching (siehe § 10.5).
|
||||
9. Einfügen aus Bibliotheken erzeugt lokal bearbeitbare Planungsinhalte.
|
||||
10. Vorlagenänderungen verändern historische oder konkrete Planungen nicht automatisch.
|
||||
11. Rahmenprogramme sollen dieselbe Planungslogik nutzen wie konkrete Einheiten.
|
||||
12. Der Coding Agent entscheidet die technische Umsetzung anhand der bestehenden Codebasis.
|
||||
13. Archetyp-IDs und Coaching-Stufen (§ 10.2.1, § 10.4) sind die **Referenz gegen Code-Drift**; Änderungen nur mit Anhang A und technischer Doku.
|
||||
14. **Zeitliche Phasen** einer Kombination liegen vorrangig in **`method_profile`** und **Gesamtzeit am Planungseintrag**; **Übersteuerungen nur in der Planungsinstanz**, nicht still in der Bibliothek (§ 6.3, § 8.3, § 10.5.1).
|
||||
|
||||
---
|
||||
|
||||
## Anhang A — Implementierungsabgleich (Stand Code: App **0.8.102**, grob)
|
||||
|
||||
Zweck: dieselbe Tabelle für **Produkt / Architekt / Agent** beim nächsten Schritt; verhindert „wir haben X gebaut, die Spec sagt aber Y“ ohne dass es dokumentiert wird.
|
||||
|
||||
| Thema (fachliche Headline aus dieser Spez) | Kurz beschrieben | Stand Code / UX (Referenz nur) | Lücke / nächste sinnvolle Schritte |
|
||||
|--------------------------------------------|-----------------|---------------------------------|-------------------------------------|
|
||||
| **Trainingsmodule (Bibliothek)** | Wiederverwendbare Blöcke, Kopier-Einfügen in Einheit | Bibliothek, API, Übernahme-Modal, Lineage-Spalte | **Phase 3** des Umsetzungsplans: erweiterter Übernahmemodus |
|
||||
| **Kombinationsübung im Katalog** | `exercise_kind=combination`, Slots, Pools (Kandidaten) | Migration 056, CRUD Übung mit `combination_slots`, GET liefert Slots + Kandidatentitel | Fachbezug Haupt-/Nebenmethoden aus § 4/§ 6 dort umsetzen, wo die Domäne es noch nicht abdeckt |
|
||||
| **Archetyp + Ablaufprofil am Katalogobjekt** | `method_archetype`, JSON `method_profile` | Persistenz; Übungsformular: **geführte Felder** nach Archetyp (`CombinationMethodProfileEditor`, `combinationMethodProfileUi.js`) + eingeklapptes Roh‑JSON | Schema‑Validierung serverseitig noch offen; UI für Pflicht je Archetyp (§ 10.5) weiter schärfen |
|
||||
| **Einplanbarkeit (normale Planung)** | Kombi wie Übung in Sektionen; **Zeitprofil‑Overrides** nach § 8.3 / § 10.5.1 | Picker, `exercise_kind` in Form/PUT, keine Variante bei Kombi; **Overrides von `method_profile` am Platzierungseintrag fehlen** | Planungs-UI/API: kopiertes **`method_profile` pro Einheit/item** bearbeitbar; Planungsblöcke (Phase 3) |
|
||||
| **Zeitphasen (global / pro Slot)** | § 6.3 | Über `method_profile` teilweise (globale Schlüssel im Formular); **keine strukturierten slotgebundenen Zeitlisten** im UI | `slot_timing[]` oder äquivalent definieren und editieren |
|
||||
| **Coaching Stufe A** | Slots + Kandidaten sichtbar, Archetyp‑Hinweis, Profil lesbar | `CombinationCoachSlots` zeigt **Key/Value** aus `method_profile`, sonst wie zuvor | Profilwerte **lesend** benutzerfreundlicher labeln (statt nur Schlüsselnamen) |
|
||||
| **Coaching Stufe B** | Zeitleiste archetypnah (z. B. Schritt pro Station) | **Nein** — ein Coach‑Schritt = ein Planungsitem | Designentscheid: virtuelle Substeps vs. DB‑Materialisierung; Auswirkung auf Ist‑Zeit pro Item |
|
||||
| **Coaching Stufe C** | Timer/Wechsel/Abhaken nach Archetyp | Nur **generischer** Coach‑Timer pro Planungsitem | Pro Archetyp UI‑State + Anbindung an `method_profile` |
|
||||
| **Rahmenprogramm** | Gleiche Inhalte wie Einheit | Slot‑Blueprint, `from-framework-slot` | Modul-/Kombi‑UX in Rahmen wie in Einheit konsolidieren (Phase 5) |
|
||||
| **Coaching-Vorschau im Editor** | § 9.3 Schritt 7 | **Nein** / nicht als eigener Modus | Optional: dieselbe `CombinationCoachSlots`‑Ansicht read‑only im Übungseditor |
|
||||
|
||||
**Pflege:** Bei jeder relevanten Codeänderung diese Tabelle **in demselben PR / derselben Session** anpassen (kein stiller Drift).
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@
|
|||
**Status:** Entwurf zur fachlichen und technischen Abstimmung · **Stand:** 2026-05-12
|
||||
**Zweck:** Rahmen für Umsetzung, Integration in Planung/Rahmenprogramm und Durchführung im assistierten Training (Coaching-Modus). Dieses Dokument ist **nicht** implementierungsbindend, bis die markierten **offenen Entscheidungen** geschlossen und der Status angehoben wurde.
|
||||
|
||||
**Abgleich mit Code (Stand ~0.8.101, Drift vermeiden):**
|
||||
|
||||
- **Kanonische Archetyp-IDs:** fest in `backend/routers/exercises.py` (`COMBINATION_ARCHETYPE_IDS`); fachliche Tabelle und UI-Labels in `frontend/src/constants/combinationArchetypes.js` — die **fachliche Master-Zuordnung** Name↔ID steht in `functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` § 10.2.1.
|
||||
- **Coaching:** Stufe **A** (informations-/strukturierte Slot- und Kandidatenansicht + Archetyp-Hilfstext) umgesetzt im Trainings-Coach (`ExerciseFullContent` / `CombinationCoachSlots`); Stufen **B/C** bewusst offen — siehe Fachspez § 10.4 und **Anhang A** dort.
|
||||
- **Umsetzungsplan:** `working/TRAINING_MODULES_IMPLEMENTATION_PLAN.md` (Phasen 2/4 mit „teilweise“).
|
||||
|
||||
**Verwandte Dokumente:**
|
||||
|
||||
| Dokument | Bezug |
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Umsetzungsplan: Trainingsmodule & Kombinationsübungen
|
||||
|
||||
**Bezug:** `functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` (Kopf „V3“, Stand 2026-05-12)
|
||||
**Bezug:** `functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` (Kopf „V3“, inkl. **§ 10.2.1**, **§ 10.4 Coaching-Stufen**, **Anhang A** Implementierungsabgleich — Drift-Schutz)
|
||||
**Technische Entwurfsspezifikation:** `technical/TRAINING_MODULES_AND_COMBINATION_EXERCISES_SPEC.md`
|
||||
**Stand dieses Dokuments:** 2026-05-12
|
||||
**Stand dieses Dokuments:** 2026-05-12 (Abgleich mit Code ~App **0.8.102**)
|
||||
|
||||
## Ziele
|
||||
|
||||
|
|
@ -13,11 +13,20 @@ Umsetzung der MVP-Punkte aus der Fachspezifikation ohne die bestehende Planung z
|
|||
| Phase | Inhalt | Status |
|
||||
|-------|--------|--------|
|
||||
| **1** | **Trainingsmodule (Bibliothek):** Tabellen `training_modules`, `training_module_items`; REST CRUD mit Governance wie andere Bibliotheken; Übernahme in eine bestehende Einheit per `POST /api/training-units/{id}/apply-training-module` (Anfügen ans Ende eines Abschnitts via `section_order_index`); optionale Lineage-Spalte `source_training_module_id` auf Planungsitems; UI: Liste/Editor unter `/planning/training-modules`, Link von der Planung, Modal „Modul übernehmen“ | **umgesetzt (MVP Schritt 1)** |
|
||||
| **2** | Kombinationsübungen: `exercise_kind`/`combination_*`, Slots, Pools, Haupt-/Nebenmethoden-M:N, Archetyp + Ablaufprofil | geplant |
|
||||
| **2** | Kombinationsübungen: `exercise_kind`/`combination_*`, Slots, Pools, `method_archetype`, `method_profile` (JSON) | **teilweise** — Migration 056, CRUD/API, Picker/Liste; Übungsformular: geführtes **`method_profile` nach Archetyp** (`CombinationMethodProfileEditor`, `combinationMethodProfileUi.js`) plus Roh‑JSON; **Backend:** keine strenge Validierung Profil ↔ Archetyp | Haupt-/Nebenmethoden an Kombi wo Spec es verlangt; serverseitige Validierung für Profil‑Schlüssel optional |
|
||||
| **3** | Planungsblöcke: Gruppierung, Auflösen, „als Modul speichern“, erweiterter Übernahmemodus (Zwischenposition) | geplant |
|
||||
| **4** | Coaching-Ansicht: Archetyp-spezifische Darstellung für MVP-Archetypen | geplant |
|
||||
| **4** | Coaching: Archetyp-Support | **teilweise:** **Stufe A** nach Fachspez § 10.4 (Slotliste, Kandidaten aus Katalog geladen, Archetyp-Hilfstexte in `CombinationCoachSlots`/`combinationArchetypes.js`); **Stufe B/C** (Zeitleisten-Splitting, Stations-/Intervall-Timing) — **offen**, siehe Anhang A der Fachspez |
|
||||
| **5** | Rahmenprogramm: Modulübernahme UX in Slot-Blueprint-Editor konsolidieren | geplant |
|
||||
|
||||
## Coaching — verbindliche Arbeitspakete (gegen Spec-Drift)
|
||||
|
||||
| Paket | Spec-Referenz | Kurzinhalt |
|
||||
|-------|----------------|-----------|
|
||||
| **4a (Ist/Ziel)** | § 10.2.1 | Archetyp-Schlüssel bleiben identisch zu `backend/routers/exercises.py` (`COMBINATION_ARCHETYPE_IDS`) und `frontend/src/constants/combinationArchetypes.js`. |
|
||||
| **4b** | § 10.4 Stufe A | Slots + Kandidaten; Archetyp-Hilfstext; `method_profile` **lesend** unter der Kopf-Zeile (Key/Wert‑Liste wenn gepflegt); Feintuning Labels optional. |
|
||||
| **4c** | § 10.4 Stufe B | Entscheidung: virtuelle Substeps vs. persistierte Items; Konsistenz `sectionsToPutPayload`/Ist-Zeit. |
|
||||
| **4d** | § 10.4 Stufe C | Archetyp-spezifische Timer/Wechsel/Abhaken an `method_profile` — nach 4b/4c. |
|
||||
|
||||
## Phase 1 (technische Notizen)
|
||||
|
||||
- **Governance:** `visibility`/`club_id`/`created_by` analog `training_plan_templates`; Listenfilter `library_content_visibility_sql`.
|
||||
|
|
@ -28,3 +37,4 @@ Umsetzung der MVP-Punkte aus der Fachspezifikation ohne die bestehende Planung z
|
|||
|
||||
- `DATABASE_SCHEMA.md` bei größeren Schema-Erweiterungen ergänzen.
|
||||
- `ACCESS_LAYER_ENDPOINT_AUDIT.md` bei neuen mandantenbezogenen Endpunkten fortpflegen.
|
||||
- **Nach jeder Kombi-/Coach-Änderung:** `functional/… Spezifikation V2.md` **Anhang A** und diese Tabelle Phasen 2/4 abstimmen.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.8.101"
|
||||
APP_VERSION = "0.8.102"
|
||||
BUILD_DATE = "2026-05-12"
|
||||
DB_SCHEMA_VERSION = "20260512056"
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ MODULE_VERSIONS = {
|
|||
"groups": "0.1.0",
|
||||
"skills": "0.1.0",
|
||||
"methods": "0.1.0",
|
||||
"exercises": "2.24.1", # Coach/Kombination: Stationen laden Einzelübungen + Archetyp-Hilfstext (Frontend ExerciseFullContent)
|
||||
"exercises": "2.24.2", # Kombi: geführtes method_profile im Übungsformular nach Archetyp + Coach zeigt Profil als Key/Wert
|
||||
"training_units": "0.2.0",
|
||||
"training_programs": "0.1.0",
|
||||
"planning": "0.9.1", # Kombinationsübungen: Sektionen PATCH/validator + exercise_kind GET; Frontend KEINE Varianten bei combination
|
||||
|
|
@ -35,6 +35,13 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.8.102",
|
||||
"date": "2026-05-12",
|
||||
"changes": [
|
||||
"Kombinationsübung beim Anlegen/Bearbeiten: archetypbezogenes Ablaufprofil (geführt) + eingeklappt Roh-JSON (`CombinationMethodProfileEditor`); Schlüsselmanifest `combinationMethodProfileUi.js`. Coach: angelegtes method_profile unter Stationenliste lesbar.",
|
||||
],
|
||||
},
|
||||
{
|
||||
"version": "0.8.101",
|
||||
"date": "2026-05-12",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
**Zweck:** Überblick über die **wesentlichen, produktiv nutzbaren Funktionen** aus Nutzer- und Fachperspektive – zur Weitergabe an Design, Product Discovery oder externe Fachplanung.
|
||||
|
||||
**Technischer Detailstand:** App-Version und Schema siehe `backend/version.py` (Stand Code: **0.8.96**, **DB_SCHEMA_VERSION** siehe dort).
|
||||
**Technischer Detailstand:** App-Version und Schema siehe `backend/version.py` (Stand Code: **0.8.101**, **DB_SCHEMA_VERSION** siehe dort).
|
||||
|
||||
**Vertiefung:** Domänenmodell `.claude/docs/functional/DOMAIN_MODEL.md`, Lieferdetal `.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md`, Projektstatus `.claude/docs/PROJECT_STATUS.md`, Entwickler-Handover `docs/HANDOVER.md`.
|
||||
|
||||
|
|
@ -59,6 +59,7 @@ Die sichtbaren Funktionen hängen von **Rolle** und **Kontext** ab (eingeloggter
|
|||
- **Medien an der Übung:** Upload, Einbettung, Verknüpfung aus dem **Archiv**; Darstellung in Detail- und Bearbeitungsansicht.
|
||||
- **Rich-Text-Felder** (Ablauf, Ziele, Hinweise): **Inline-Verweise auf verknüpfte Medien** über eine einheitliche Platzhalter-/Renderlogik (konsistent mit Archiv-Governance).
|
||||
- **Exercise Blocks** („Bausteine“) und gespeicherte Suchpräferenzen, wo implementiert.
|
||||
- **Kombinationsübungen** („combination“, Migration 056): Sonderform im Übungskatalog mit **Stationen/Slots**, **Trainingsmethode-/Archetyp** (`method_archetype`), optionalem strukturierten **Ablaufprofil** (`method_profile`). In der Planung wie eine Übung ohne Variante einsetzbar; im **Coach** werden Stations-Kandidaten und Archetyp-Hilfstexte angezeigt (Ausbauschritte B/C nach Fachspez § 10.4 dokumentiert unter `.claude/docs/functional/… Kombinationsübungen Spezifikation V2.md` Anhang A).
|
||||
|
||||
### 4.2 Fähigkeiten, Methoden, Kataloge
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ Die sichtbaren Funktionen hängen von **Rolle** und **Kontext** ab (eingeloggter
|
|||
- **Trainingsvorlagen / Mikrovorlagen** (wo eingerichtet): Struktur wiederverwenden.
|
||||
- **Trainingsrahmenprogramm (Bibliothek):** übergeordnete Programme mit **Zielen** und **Slots**; Slot-Inhalt technisch als **Blueprint-Trainingsunit** abgebildet.
|
||||
- **Materialisierung:** aus einem Rahmen-Slot kann eine **konkrete Kalender-Einheit** für eine Gruppe erzeugt werden (API vorhanden; UI-Anbindung kann erweitert werden).
|
||||
- **Durchführung:** Ansicht zum Abarbeiten einer Einheit; **Coaching-Modus** als separater Erlebnispfad.
|
||||
- **Durchführung:** Ansicht zum Abarbeiten einer Einheit; **Coaching-Modus** als separater Erlebnispfad (generischer Zeit-Block pro Platzierung); bei **Kombinationsübungen** zusätzliche **Stations-/Kandidaten-Schicht und Archetyp-Hinweise** siehe Kombination-Fachspez **Anhang A** (implementierter Umfang vs. nächste Stufen).
|
||||
|
||||
### 4.5 Medienbibliothek und Archiv
|
||||
|
||||
|
|
@ -117,7 +118,7 @@ Nicht als „broken“ gemeint, sondern als **typische nächste Ausbaustellen**
|
|||
- Kalender-UX: **„Aus Rahmen übernehmen“** flächendeckend und ggf. bulkfähig anbinden.
|
||||
- **Policies** für geteilte Rahmen (Wer darf Bibliotheks-Rahmen sehen/kopieren?).
|
||||
- **Skill-Kategorie-Admin-UI**, **Dark Mode/Responsive/PWA-Ausbau**, **KI-Suche** über Volltext hinaus – je nach Backlog.
|
||||
- **Moderations-Fläche**, Uploader-Benachrichtigung bei Sperre, **Beschwerdeverfahren** – laut Handover bewusst noch nicht umgesetzt (Nachfolge von P-13).
|
||||
- **Coach / Kombination:** nächste Stufen **Zeitleisten-Splitting** und **Archetyp-Timer** (Fachspez § 10.4 Stufe B/C; Umsetzungsplan Phase 4b–d); **geführtes Erfassen** von `method_profile` im Übungseditor.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -126,3 +127,4 @@ Nicht als „broken“ gemeint, sondern als **typische nächste Ausbaustellen**
|
|||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
| 2026-05-12 | Erstfassung für Übergabe an fachliches Design; Abgleich mit Code-Navigation, `version.py`, `HANDOVER.md`, `FEATURES_DELIVERED`, `DOMAIN_MODEL`. |
|
||||
| 2026-05-12 | Kombinationsübungen + Coaching Stufe A; Verweise auf Fachspezifikation (`…Kombinationsuebungen…` V3 Anhang A) und `TRAINING_MODULES_IMPLEMENTATION_PLAN.md`. |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Shinkan Jinkendo – Entwicklungsstand & Handover
|
||||
|
||||
**Stand:** 2026-05-12
|
||||
**App-Version / DB-Schema:** App **0.8.96**, DB-Schema siehe `backend/version.py` (`DB_SCHEMA_VERSION`)
|
||||
**App-Version / DB-Schema:** App **0.8.101**, DB-Schema siehe `backend/version.py` (`DB_SCHEMA_VERSION`)
|
||||
|
||||
Diese Datei ist die **Einstiegs-Doku für neue Chat-Sessions**: Anforderungen im Detail stehen in `.claude/docs/` (siehe unten); hier der **implementierte Stand**, **Medien-Meilenstein** und **sinnvolle nächste Schritte**.
|
||||
|
||||
|
|
@ -29,6 +29,8 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
| Zugriffsschicht, Mandant, Governance | `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` |
|
||||
| Tenant-Endpoints (Audit) | `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` |
|
||||
| Rahmenprogramm · Planung | `.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md` |
|
||||
| **Trainingsmodule & Kombinationsübungen (Fachspez, Drift-Schutz)** | `.claude/docs/functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` (§ 10.2.1 Archetyp-IDs, § 10.4 Coaching-Stufen, **Anhang A** Code-Abgleich) |
|
||||
| **Umsetzungsplan** (Module/Kombination/Coach) | `.claude/docs/working/TRAINING_MODULES_IMPLEMENTATION_PLAN.md` |
|
||||
| Überblick DB | `.claude/docs/technical/DATABASE_SCHEMA.md` |
|
||||
| Domäne | `.claude/docs/functional/DOMAIN_MODEL.md` |
|
||||
| **Lieferliste inkl. Medien** | `.claude/docs/library/FEATURES_DELIVERED_2026-Q2.md` §12 |
|
||||
|
|
@ -72,6 +74,12 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
- **036 / 037:** Bibliotheks-Rahmen, Slot-Inhalt als **`training_units`** mit **`framework_slot_id`**; **`POST /api/training-units/from-framework-slot`**.
|
||||
- **Code:** `training_framework_programs.py`, `training_planning.py`; Frontend **`TrainingFrameworkProgramEditPage.jsx`**, **`createTrainingUnitFromFrameworkSlot`** in `api.js`.
|
||||
|
||||
### Trainingsmodule, Kombinationsübungen und Coach (Stand ~0.8.101)
|
||||
|
||||
- **Fachspez & Drift-Schutz:** `.claude/docs/functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` (**§ 10.2.1** IDs, **§ 10.4** Coaching-Stufen, **Anhang A** Abgleich).
|
||||
- **Umsetzungsplan:** `.claude/docs/working/TRAINING_MODULES_IMPLEMENTATION_PLAN.md` (Phase **2** / **4** teilweise; Pakete **4a–d**).
|
||||
- **Ist kurz:** Trainingsmodule-Bibliothek (Phase **1**) umgesetzt; Kombi-Katalog (**056**) + Einplanung + Coach **Stufe A** (`CombinationCoachSlots`, `combinationArchetypes.js`). Coach **Stufe B/C** und geführtes **`method_profile`** offen — siehe Fachspez Anhang A.
|
||||
|
||||
---
|
||||
|
||||
## 4. Stand: Medien-Management (Ist, 2026-05-07)
|
||||
|
|
@ -152,6 +160,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
5. **S3/Adapter:** Speicher-Abstraktion (Spec Abschnitt 7) — wenn Produkt es verlangt.
|
||||
6. **Rahmen/UI:** Kalender „aus Rahmen übernehmen” weiter anbinden (parallel, unabhängig von Medien).
|
||||
7. **Fachlicher Nutzerüberblick:** bei größeren UX-Änderungen **`docs/FACHLICHE_NUTZERFUNKTIONEN.md`** mitpflegen.
|
||||
8. **Kombinations-Coach (Archetyp B/C):** Fachspez § 10.4; nach Implementierung **Anhang A** + `TRAINING_MODULES_IMPLEMENTATION_PLAN.md` aktualisieren (kein Doc-Drift).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -159,7 +168,8 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
|
||||
| Bereich | Einstieg |
|
||||
|---------|----------|
|
||||
| Backend API | `backend/main.py`; u. a. **`media_assets.py`**, **`exercises.py`**, **`profiles.py`**, **`training_framework_programs.py`**, `tenant_context.py` |
|
||||
| Backend API | `backend/main.py`; u. a. **`media_assets.py`**, **`exercises.py`** (`COMBINATION_ARCHETYPE_IDS`, `enrich_exercise_detail`), **`profiles.py`**, **`training_framework_programs.py`**, `tenant_context.py` |
|
||||
| Coach-Kombination (Frontend) | `TrainingCoachPage.jsx`, `ExerciseFullContent.jsx`, `CombinationCoachSlots.jsx`, `constants/combinationArchetypes.js` |
|
||||
| Migrationen | `backend/migrations/` (040+ Mitgliedschaft/Governance; **045+** Medien-Stack) |
|
||||
| Frontend API | `frontend/src/utils/api.js` |
|
||||
| Aktiver Verein (UI) | `frontend/src/utils/activeClub.js`, `AuthContext.jsx` |
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
sortCombinationSlotsForDisplay,
|
||||
} from '../constants/combinationArchetypes'
|
||||
|
||||
export default function CombinationCoachSlots({ combinationSlots, methodArchetype }) {
|
||||
export default function CombinationCoachSlots({ combinationSlots, methodArchetype, methodProfile }) {
|
||||
const slots = useMemo(() => sortCombinationSlotsForDisplay(combinationSlots), [combinationSlots])
|
||||
|
||||
const candidateIds = useMemo(() => {
|
||||
|
|
@ -111,6 +111,42 @@ export default function CombinationCoachSlots({ combinationSlots, methodArchetyp
|
|||
{archetypeCoachHint(archeKey)}
|
||||
</p>
|
||||
|
||||
{methodProfile && typeof methodProfile === 'object' && !Array.isArray(methodProfile) && Object.keys(methodProfile).length ? (
|
||||
<div
|
||||
style={{
|
||||
margin: '0 0 14px',
|
||||
padding: '8px 10px',
|
||||
borderRadius: '8px',
|
||||
background: 'var(--surface)',
|
||||
border: '1px solid var(--border)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: '0.7rem', fontWeight: 700, color: 'var(--text3)', textTransform: 'uppercase', marginBottom: '6px' }}>
|
||||
Geplantes Ablaufprofil (Katalog)
|
||||
</div>
|
||||
<dl style={{ margin: 0, fontSize: '0.82rem', lineHeight: 1.45 }}>
|
||||
{Object.entries(methodProfile)
|
||||
.sort(([a], [b]) => a.localeCompare(b, 'de'))
|
||||
.map(([k, val]) => (
|
||||
<div key={k} style={{ marginBottom: '4px', display: 'grid', gridTemplateColumns: 'minmax(72px,1fr) minmax(0,2fr)', gap: '6px 10px' }}>
|
||||
<dt style={{ margin: 0, fontWeight: 600, color: 'var(--text3)', wordBreak: 'break-all' }}>{k}</dt>
|
||||
<dd style={{ margin: 0, color: 'var(--text1)' }}>
|
||||
{typeof val === 'boolean'
|
||||
? val
|
||||
? 'ja'
|
||||
: 'nein'
|
||||
: typeof val === 'number'
|
||||
? String(val)
|
||||
: typeof val === 'string'
|
||||
? val
|
||||
: JSON.stringify(val)}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!slots.length ? (
|
||||
<p style={{ margin: 0, color: 'var(--text3)', fontSize: '0.88rem' }}>Keine Stationen hinterlegt.</p>
|
||||
) : (
|
||||
|
|
|
|||
175
frontend/src/components/CombinationMethodProfileEditor.jsx
Normal file
175
frontend/src/components/CombinationMethodProfileEditor.jsx
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
import React, { useMemo, useState } from 'react'
|
||||
import { archetypeCoachHint, combinationArchetypeLabel } from '../constants/combinationArchetypes'
|
||||
import {
|
||||
METHOD_PROFILE_GUI_FIELDS,
|
||||
parseProfileJson,
|
||||
setFullProfileRawJson,
|
||||
updateProfileGuided,
|
||||
} from '../utils/combinationMethodProfileUi'
|
||||
|
||||
function clampInt(n, min, max) {
|
||||
if (!Number.isFinite(n)) return null
|
||||
let x = n
|
||||
if (typeof min === 'number' && x < min) x = min
|
||||
if (typeof max === 'number' && x > max) x = max
|
||||
return Math.round(x)
|
||||
}
|
||||
|
||||
/**
|
||||
* Kombination: geführtes Ablaufprofil + optionales Roh-JSON.
|
||||
*/
|
||||
export default function CombinationMethodProfileEditor({
|
||||
methodArchetype,
|
||||
methodProfileJson,
|
||||
onChangeMethodProfileJson,
|
||||
}) {
|
||||
const arch = typeof methodArchetype === 'string' ? methodArchetype.trim() : ''
|
||||
const fieldsGui = METHOD_PROFILE_GUI_FIELDS[arch]
|
||||
const fields = Array.isArray(fieldsGui) ? fieldsGui : null
|
||||
const parseState = useMemo(() => parseProfileJson(methodProfileJson || '{}'), [methodProfileJson])
|
||||
const [rawOpenError, setRawOpenError] = useState(null)
|
||||
const [rawDraft, setRawDraft] = useState(null)
|
||||
|
||||
const profileObj = parseState.ok ? parseState.obj : {}
|
||||
|
||||
const applyGuided = (key, value, kind) => {
|
||||
if (kind === 'bool') {
|
||||
const res = updateProfileGuided(arch, methodProfileJson || '{}', key, value, 'bool')
|
||||
if (!res.ok) return
|
||||
onChangeMethodProfileJson(res.json)
|
||||
return
|
||||
}
|
||||
if (value === '' || value === undefined || value === null) {
|
||||
const res = updateProfileGuided(arch, methodProfileJson || '{}', key, '', 'int')
|
||||
if (!res.ok) return
|
||||
onChangeMethodProfileJson(res.json)
|
||||
return
|
||||
}
|
||||
const num = typeof value === 'number' ? value : parseInt(String(value), 10)
|
||||
if (!Number.isFinite(num)) return
|
||||
const def = METHOD_PROFILE_GUI_FIELDS[arch]?.find((f) => f.key === key && f.kind === 'int')
|
||||
const c = clampInt(num, def?.min, def?.max)
|
||||
if (c == null) return
|
||||
const res = updateProfileGuided(arch, methodProfileJson || '{}', key, c, 'int')
|
||||
if (!res.ok) return
|
||||
onChangeMethodProfileJson(res.json)
|
||||
}
|
||||
|
||||
const archeLabel = arch ? combinationArchetypeLabel(arch) : null
|
||||
|
||||
const openAdvanced = () => {
|
||||
setRawOpenError(null)
|
||||
const p = parseProfileJson(methodProfileJson || '{}')
|
||||
setRawDraft(p.ok ? JSON.stringify(p.obj, null, 2) : String(methodProfileJson || ''))
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
{arch ? (
|
||||
<p style={{ fontSize: '12px', color: 'var(--text2)', lineHeight: 1.48, margin: '0 0 12px' }}>
|
||||
<strong style={{ color: 'var(--text1)' }}>
|
||||
Coach & Planung:{' '}
|
||||
{archeLabel && archeLabel !== arch ? `${archeLabel} · ` : ''}
|
||||
</strong>
|
||||
{archetypeCoachHint(arch)}
|
||||
</p>
|
||||
) : (
|
||||
<p style={{ fontSize: '12px', color: 'var(--text3)', margin: '0 0 12px' }}>
|
||||
Wähle einen Archetyp, um das Ablaufprofil strukturiert zu erfassen — oder nur das JSON weiter unten.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!parseState.ok ? (
|
||||
<p style={{ color: 'var(--danger)', fontSize: '13px', marginBottom: '10px' }}>{parseState.error}</p>
|
||||
) : null}
|
||||
|
||||
{fields && fields.length > 0 ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px', marginBottom: '12px' }}>
|
||||
{fields.map((def) => {
|
||||
if (def.kind === 'bool') {
|
||||
const ck = !!profileObj[def.key]
|
||||
return (
|
||||
<label
|
||||
key={def.key}
|
||||
style={{ display: 'flex', gap: '8px', alignItems: 'center', cursor: 'pointer', fontSize: '0.9rem' }}
|
||||
>
|
||||
<input type="checkbox" checked={ck} onChange={(e) => applyGuided(def.key, e.target.checked, 'bool')} />
|
||||
<span>{def.label}</span>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
const v = profileObj[def.key]
|
||||
const str =
|
||||
v === undefined || v === null ? '' : typeof v === 'number' && Number.isFinite(v) ? String(v) : String(v)
|
||||
|
||||
return (
|
||||
<div className="form-row" key={def.key}>
|
||||
<label className="form-label" style={{ fontSize: '12px' }}>
|
||||
{def.label}
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
className="form-input"
|
||||
min={def.min}
|
||||
max={def.max}
|
||||
value={str}
|
||||
placeholder="optional"
|
||||
onChange={(e) => {
|
||||
const t = e.target.value
|
||||
if (t.trim() === '') applyGuided(def.key, '', 'int')
|
||||
else applyGuided(def.key, parseInt(t, 10), 'int')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : arch && fields && fields.length === 0 ? (
|
||||
<p style={{ fontSize: '12px', color: 'var(--text3)', margin: '0 0 10px' }}>
|
||||
Für diesen Archetyp gibt es keine vorgegebenen Profilfelder — nutze die Freitexte der Kombination oder Roh‑JSON bei Bedarf.
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<details
|
||||
style={{
|
||||
marginTop: '4px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--border)',
|
||||
padding: '8px 10px',
|
||||
background: 'var(--surface)',
|
||||
}}
|
||||
onToggle={(ev) => {
|
||||
if (ev.target.open) openAdvanced()
|
||||
}}
|
||||
>
|
||||
<summary style={{ cursor: 'pointer', fontSize: '0.82rem', fontWeight: 600 }}>Erweitert: JSON direkt bearbeiten</summary>
|
||||
<p style={{ fontSize: '11px', color: 'var(--text3)', margin: '8px 0 6px', lineHeight: 1.4 }}>
|
||||
Zusätzliche Schlüssel (Piloten). Geführte Felder können dieselben Schlüssel beim nächsten Speichern überlagern.
|
||||
</p>
|
||||
<textarea
|
||||
className="form-input"
|
||||
rows={8}
|
||||
style={{ fontFamily: 'Consolas,monospace', fontSize: '12px' }}
|
||||
value={rawDraft != null ? rawDraft : methodProfileJson || '{}'}
|
||||
onChange={(e) => {
|
||||
setRawDraft(e.target.value)
|
||||
setRawOpenError(null)
|
||||
}}
|
||||
spellCheck={false}
|
||||
onBlur={() => {
|
||||
const src = rawDraft != null ? rawDraft : methodProfileJson
|
||||
const res = setFullProfileRawJson(src || '{}')
|
||||
if (!res.ok) {
|
||||
setRawOpenError(res.error)
|
||||
return
|
||||
}
|
||||
setRawOpenError(null)
|
||||
setRawDraft(null)
|
||||
onChangeMethodProfileJson(res.json)
|
||||
}}
|
||||
/>
|
||||
{rawOpenError ? <p style={{ color: 'var(--danger)', fontSize: '12px', marginTop: '6px' }}>{rawOpenError}</p> : null}
|
||||
</details>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -114,6 +114,7 @@ export default function ExerciseFullContent({ exercise, loading, error, exercise
|
|||
<CombinationCoachSlots
|
||||
combinationSlots={exercise.combination_slots}
|
||||
methodArchetype={exercise.method_archetype}
|
||||
methodProfile={exercise.method_profile}
|
||||
/>
|
||||
) : null}
|
||||
<h2 style={{ margin: '0 0 8px', fontSize: '1.2rem', lineHeight: 1.35 }}>{exercise.title}</h2>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import ExerciseProgressionGraphPanel from '../components/ExerciseProgressionGrap
|
|||
import ExerciseMediaThumbTile from '../components/ExerciseMediaThumbTile'
|
||||
import MediaPreviewModal from '../components/MediaPreviewModal'
|
||||
import ReportContentModal from '../components/ReportContentModal'
|
||||
import CombinationMethodProfileEditor from '../components/CombinationMethodProfileEditor'
|
||||
import {
|
||||
SHINKAN_EXERCISE_MEDIA_DRAG_MIME,
|
||||
buildExerciseMediaDragPayload,
|
||||
|
|
@ -1016,13 +1017,13 @@ function ExerciseFormPage() {
|
|||
{formData.exercise_kind === 'combination' ? (
|
||||
<>
|
||||
<div className="form-row">
|
||||
<label className="form-label">Methoden-Archetyp (optional)</label>
|
||||
<label className="form-label">Methoden-Archetyp (für Coach & Planung empfohlen)</label>
|
||||
<select
|
||||
className="form-input"
|
||||
value={formData.method_archetype || ''}
|
||||
onChange={(e) => updateFormField('method_archetype', e.target.value)}
|
||||
>
|
||||
<option value="">— später wählen —</option>
|
||||
<option value="">— noch nicht festgelegt —</option>
|
||||
{COMBINATION_ARCHETYPE_OPTIONS.map((o) => (
|
||||
<option key={o.id} value={o.id}>
|
||||
{o.label}
|
||||
|
|
@ -1031,14 +1032,11 @@ function ExerciseFormPage() {
|
|||
</select>
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label className="form-label">Ablaufprofil (JSON, optional)</label>
|
||||
<textarea
|
||||
className="form-input"
|
||||
rows={4}
|
||||
value={formData.method_profile_json || '{}'}
|
||||
onChange={(e) => updateFormField('method_profile_json', e.target.value)}
|
||||
spellCheck={false}
|
||||
placeholder='{"work_seconds":45,"rest_seconds":15,"rounds":3}'
|
||||
<label className="form-label">Ablaufprofil (über Archetyp)</label>
|
||||
<CombinationMethodProfileEditor
|
||||
methodArchetype={formData.method_archetype || ''}
|
||||
methodProfileJson={formData.method_profile_json || '{}'}
|
||||
onChangeMethodProfileJson={(s) => updateFormField('method_profile_json', s)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
165
frontend/src/utils/combinationMethodProfileUi.js
Normal file
165
frontend/src/utils/combinationMethodProfileUi.js
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Geführtes method_profile für Kombinationsübungen — Felder nach method_archetype.
|
||||
* Unbekannte JSON-Schlüssel bleiben beim Zusammenführen erhalten (Erweiterbarkeit).
|
||||
*/
|
||||
|
||||
const INT_MAX = 86400
|
||||
|
||||
function parseProfileJson(raw) {
|
||||
if (typeof raw !== 'string' || !raw.trim()) return { ok: true, obj: {} }
|
||||
try {
|
||||
const p = JSON.parse(raw)
|
||||
if (!p || typeof p !== 'object' || Array.isArray(p)) {
|
||||
return { ok: false, error: 'Ablaufprofil muss ein JSON-Objekt sein.' }
|
||||
}
|
||||
return { ok: true, obj: { ...p } }
|
||||
} catch {
|
||||
return { ok: false, error: 'Ablaufprofil (JSON): Syntax ungültig.' }
|
||||
}
|
||||
}
|
||||
|
||||
/** Pro Archetyp: UI-Feldbeschreibungen (Werte werden in method_profile geschrieben) */
|
||||
export const METHOD_PROFILE_GUI_FIELDS = Object.freeze({
|
||||
sequence_linear: [
|
||||
{
|
||||
key: 'hint_step_duration_sec',
|
||||
kind: 'int',
|
||||
label: 'Orientierung: Sekunden je Station/Schritt (optional)',
|
||||
min: 5,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'block_intro_sec',
|
||||
kind: 'int',
|
||||
label: 'Einführung / Demon am Block Gesamt (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
],
|
||||
circuit_rotate_time: [
|
||||
{
|
||||
key: 'work_seconds',
|
||||
kind: 'int',
|
||||
label: 'Arbeitszeit pro Station (Sek.)',
|
||||
min: 5,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'transition_seconds',
|
||||
kind: 'int',
|
||||
label: 'Wechsel / Rotation (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'rest_seconds',
|
||||
kind: 'int',
|
||||
label: 'Pause zwischen Runden oder Stationen-Folgen (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'rounds',
|
||||
kind: 'int',
|
||||
label: 'Runden (optional, wenn alle Station je Runde angefahren werden)',
|
||||
min: 1,
|
||||
max: 999,
|
||||
},
|
||||
],
|
||||
circuit_all_parallel: [
|
||||
{
|
||||
key: 'explain_before_seconds',
|
||||
kind: 'int',
|
||||
label: 'Zeitfenster Vorab‑Erklärung aller Stationen (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'simultaneous_start',
|
||||
kind: 'bool',
|
||||
label: 'Alle Stationen starten zusammen nach Erklärung',
|
||||
},
|
||||
],
|
||||
station_parcour: [
|
||||
{
|
||||
key: 'allow_free_visit_order',
|
||||
kind: 'bool',
|
||||
label: 'Reihenfolge der Besuche frei (Parcours / Abhaken-Logik später im Coach)',
|
||||
},
|
||||
],
|
||||
pair_superset: [
|
||||
{
|
||||
key: 'switch_seconds',
|
||||
kind: 'int',
|
||||
label: 'Orientierung: Wechselpause A↔B (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'work_seconds_per_side',
|
||||
kind: 'int',
|
||||
label: 'Arbeit pro Rolle oder Seite (Sek., optional)',
|
||||
min: 5,
|
||||
max: INT_MAX,
|
||||
},
|
||||
],
|
||||
time_domain_interval: [
|
||||
{
|
||||
key: 'work_seconds',
|
||||
kind: 'int',
|
||||
label: 'Intervall: Belastungszeit (Sek.)',
|
||||
min: 5,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'rest_seconds',
|
||||
kind: 'int',
|
||||
label: 'Intervall: Erholungszeit (Sek., optional)',
|
||||
min: 0,
|
||||
max: INT_MAX,
|
||||
},
|
||||
{
|
||||
key: 'interval_rounds',
|
||||
kind: 'int',
|
||||
label: 'Anzahl Wiederholungen / Runden der Domäne (optional)',
|
||||
min: 1,
|
||||
max: 999,
|
||||
},
|
||||
],
|
||||
free_method_block: [],
|
||||
})
|
||||
|
||||
/**
|
||||
* Aktualisiert method_profile unter Beibehaltung nicht-GUI Schlüssel.
|
||||
*/
|
||||
export function updateProfileGuided(archetype, rawJson, key, parsedValue, kind) {
|
||||
const arch = typeof archetype === 'string' ? archetype.trim() : ''
|
||||
const parsed = parseProfileJson(rawJson)
|
||||
if (!parsed.ok) return parsed
|
||||
const next = { ...parsed.obj }
|
||||
|
||||
if (kind === 'bool') {
|
||||
if (parsedValue) next[key] = true
|
||||
else delete next[key]
|
||||
} else if (kind === 'int') {
|
||||
if (parsedValue === null || parsedValue === undefined || parsedValue === '') {
|
||||
delete next[key]
|
||||
} else {
|
||||
const n = typeof parsedValue === 'number' ? parsedValue : parseInt(String(parsedValue), 10)
|
||||
if (!Number.isFinite(n)) delete next[key]
|
||||
else next[key] = n
|
||||
}
|
||||
}
|
||||
|
||||
const outJson = JSON.stringify(next)
|
||||
return { ok: true, obj: next, json: outJson === '{}' ? '{}' : outJson }
|
||||
}
|
||||
|
||||
export function setFullProfileRawJson(rawEditable) {
|
||||
const parsed = parseProfileJson(rawEditable)
|
||||
if (!parsed.ok) return parsed
|
||||
const j = JSON.stringify(parsed.obj)
|
||||
return { ok: true, obj: parsed.obj, json: j === '{}' ? '{}' : j }
|
||||
}
|
||||
|
||||
export { parseProfileJson, INT_MAX }
|
||||
Loading…
Reference in New Issue
Block a user