From e09a2284e9fb1a8598a6408d5326ac945897fd29 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 14 May 2026 16:02:54 +0200 Subject: [PATCH] chore(version): update version and changelog for release 0.8.131 - Bumped APP_VERSION to 0.8.131 and updated the changelog to reflect recent changes. - Added the TrainingPlanningUnitFormModal component to the TrainingPlanningPage for enhanced training unit management. - Refactored frameworkLineageText utility function for better code organization and reusability in the training planning context. - Updated BASELINE_SNAPSHOT documentation to include new metrics and logging details for k6 health checks. --- backend/version.py | 9 +- docs/architecture/BASELINE_SNAPSHOT.md | 16 + .../TrainingPlanningUnitFormModal.jsx | 485 ++++++++++++++++ frontend/src/pages/TrainingPlanningPage.jsx | 521 ++---------------- .../src/utils/trainingPlanningPageHelpers.js | 9 + 5 files changed, 561 insertions(+), 479 deletions(-) create mode 100644 frontend/src/components/planning/TrainingPlanningUnitFormModal.jsx diff --git a/backend/version.py b/backend/version.py index 013f24b..7dc65a0 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,6 +1,6 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.130" +APP_VERSION = "0.8.131" BUILD_DATE = "2026-05-12" DB_SCHEMA_VERSION = "20260514062" @@ -36,6 +36,13 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.8.131", + "date": "2026-05-13", + "changes": [ + "Frontend Phase 3: TrainingPlanningUnitFormModal (Neu/Bearbeiten-Einheit); frameworkLineageText in trainingPlanningPageHelpers; BASELINE_SNAPSHOT §3.4 k6-Log-Mapping.", + ], + }, { "version": "0.8.130", "date": "2026-05-13", diff --git a/docs/architecture/BASELINE_SNAPSHOT.md b/docs/architecture/BASELINE_SNAPSHOT.md index 0018eca..0b77b98 100644 --- a/docs/architecture/BASELINE_SNAPSHOT.md +++ b/docs/architecture/BASELINE_SNAPSHOT.md @@ -90,6 +90,22 @@ Messung: Repo-Root → `cd frontend && npm run build` (Vite Production). |----------|-------------------|------------------| | 10 VUs, 30 s `/health` | *—* | *nach Messung* | +### 3.4 Aus dem Deployment-/CI-Log übernehmen (k6 `k6-health-baseline`) + +Das Skript `scripts/load/k6-health-baseline.js` nutzt **10 VUs**, **30 s**, Ziel **`GET {BASE_URL}/health`** (siehe Workflow-Env für `BASE_URL`). + +**In die Tabelle oben (Abschnitt 3.3) eintragen — aus der k6-Zusammenfassung am Ende des Jobs:** + +| Feld in BASELINE_SNAPSHOT | Wo im k6-Log (typisch) | +|---------------------------|-------------------------| +| **p95** (Latenz ms) | Zeile **`http_req_duration`** → Wert **`p(95)=…`** (ganze Zahl oder ms mit Einheit wie `12.34ms`) | +| **Fehlerquote** | Zeile **`http_req_failed`** → z. B. `0.00%` bzw. `✓ 0%` — oder kurz „0 %“ notieren | +| **Checks** (optional) | Zeile **`checks`** → Anteil **`✓`** (soll **100 %** sein, sonst Hinweis) | +| **Datum / BASE_URL** | Deploy-Datum + die **öffentliche** Basis-URL des Laufs (wie im Workflow gesetzt, z. B. `https://dev.shinkan.jinkendo.de`) | +| **App-Version** (optional) | dieselbe wie im Deploy (`backend/version.py` / Release), damit M2-Vergleich ressortfähig bleibt | + +**Zusätzlich (Abschnitt 2.2):** nur die Zeile **`/health` GET`** mit dem **gleichen** p95 befüllen, wenn ihr dort noch Platzhalter habt — echte API-Routen (`/api/...`) kommen weiter aus Monitoring/k6 mit Auth, nicht aus diesem Job. + --- ## 4. Nächster Schritt (Roadmap) diff --git a/frontend/src/components/planning/TrainingPlanningUnitFormModal.jsx b/frontend/src/components/planning/TrainingPlanningUnitFormModal.jsx new file mode 100644 index 0000000..fb61f7e --- /dev/null +++ b/frontend/src/components/planning/TrainingPlanningUnitFormModal.jsx @@ -0,0 +1,485 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import TrainingPlanExerciseVisibilityPanel from '../TrainingPlanExerciseVisibilityPanel' +import TrainingUnitSectionsEditor from '../TrainingUnitSectionsEditor' +import { frameworkLineageText } from '../../utils/trainingPlanningPageHelpers' + +/** + * Großes Modal: Neue Trainingseinheit / Einheit bearbeiten (Planung, Trainer, Abschnitte, Durchführung, Notizen). + */ +export default function TrainingPlanningUnitFormModal({ + open, + editingUnit, + formData, + updateFormField, + setFormData, + onSubmit, + onCancel, + draftPlanTemplateId, + onDraftTemplateSelect, + planTemplates, + clubDirectory, + clubDirectoryForCo, + planningModalClubId, + user, + onMetaRefresh, + sectionsEditMode, + setSectionsEditMode, + onSaveAsTemplate, + onRequestTrainingModulePick, + onRequestExercisePick, + onPeekExercise, +}) { + if (!open) return null + + return ( +
+
+

+ {editingUnit ? 'Trainingseinheit bearbeiten' : 'Neue Trainingseinheit'} +

+ + {editingUnit?.origin_framework_slot_id + ? (() => { + const L = frameworkLineageText(editingUnit) + return ( +
+ Herkunft:{' '} + {editingUnit.origin_framework_program_id ? ( + + {L.fpTitle} + + ) : ( + L.fpTitle + )} + · {L.slotBit} +

+ Inhalt stammt aus dem Session-Blueprint des Rahmenprogramms. Änderungen gelten nur für diese + geplante Einheit; die Zuordnung zum Rahmen bleibt zur Nachverfolgung erhalten. +

+
+ ) + })() + : null} + + {!editingUnit && ( +
+ + +

+ Übernimmt nur die Sektionsstruktur aus der Bibliothek; Übungen trägst du unten bei den + Abschnitten ein. Gespeicherte Vorlagen kannst du unter Planung später erweitern. +

+
+ )} + +
+

Planung

+ +
+
+ + updateFormField('planned_date', e.target.value)} + required + /> +
+ +
+ + updateFormField('planned_time_start', e.target.value)} + /> +
+ +
+ + updateFormField('planned_time_end', e.target.value)} + /> +
+
+ +
+ + updateFormField('planned_focus', e.target.value)} + placeholder="z.B. Grundlagen, Kinder altersgerecht" + /> +
+ +
+

Trainerzuordnung (diese Einheit)

+
+ + +

+ Für Vertretungen genügt in der Regel die Vereinsmitgliedschaft; Zuweisen dürfen u. a. Haupt-/Co‑Trainer + dieser Gruppe, der/die Ersteller:in der Einheit oder Vereinsadmins. +

+
+
+ +
+ {!formData.session_assistants_inherit ? ( +
+ {clubDirectoryForCo.map((m) => { + const mid = typeof m.id === 'number' ? m.id : parseInt(String(m.id), 10) + const labelText = `${(m.name || '').trim() || m.email || `Profil ${mid}`}` + const isOn = Number.isFinite(mid) && formData.session_assistant_profile_ids.includes(mid) + return ( + + ) + })} +
+ ) : null} + {!clubDirectory.length ? ( +

+ Keine Einträge im Vereins-Mitgliederverzeichnis oder noch nicht geladen (nur für Vereinsinterne). +

+ ) : null} +
+ + + +
+ {editingUnit ? ( +
+
+ + Ablauf bearbeiten als + +
+ {[ + { id: 'planning', label: 'Planung' }, + { id: 'debrief', label: 'Nachbereitung' }, + ].map((opt, i) => ( + + ))} +
+
+

+ {sectionsEditMode === 'debrief' + ? 'Ist‑Minuten rechts in derselben Spaltenbreite wie „Min“ (Plan); Abweichungen als Text über die volle Breite.' + : 'Ablauf, Übungen und geplante Minuten. Ist‑Werte und Abweichungen unter „Nachbereitung“.'} +

+
+ ) : null} + + + + } + sections={formData.sections} + wideExerciseGrid + onSectionsChange={(updater) => + setFormData((prev) => ({ + ...prev, + sections: updater(prev.sections), + })) + } + onRequestTrainingModulePick={onRequestTrainingModulePick} + onRequestExercisePick={onRequestExercisePick} + onPeekExercise={onPeekExercise} + showExecutionExtras={Boolean(editingUnit) && sectionsEditMode === 'debrief'} + /> +
+ +
+ + {editingUnit && ( + <> +

Durchführung

+ +
+
+ + updateFormField('actual_date', e.target.value)} + /> +
+ +
+ + updateFormField('actual_time_start', e.target.value)} + /> +
+ +
+ + updateFormField('actual_time_end', e.target.value)} + /> +
+ +
+ + updateFormField('attendance_count', e.target.value)} + /> +
+
+ +
+ + +
+ + {formData.status === 'completed' ? ( +
+ +
+ ) : null} + + )} + +

Notizen

+ +
+ +