From 8175e239b472d5fa3b24899483d313394eb79934 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 14 May 2026 21:39:45 +0200 Subject: [PATCH] chore(version): update version and changelog for release 0.8.134 - Bumped APP_VERSION to 0.8.134 and updated the changelog to reflect recent changes. - Continued Frontend Phase 4 with the introduction of the `frontend/src/api/planning.js` module for training planning. - Updated architecture documentation to include details on the new `planning.js` API and its integration with the existing client structure. - Enhanced `utils/api.js` to re-export the new planning module, streamlining API access. --- backend/version.py | 9 +- docs/architecture/README.md | 3 +- docs/architecture/UMSETZUNGSPLAN_ROADMAP.md | 5 +- frontend/src/api/planning.js | 171 +++++++++++++++++ frontend/src/utils/api.js | 200 +------------------- 5 files changed, 188 insertions(+), 200 deletions(-) create mode 100644 frontend/src/api/planning.js diff --git a/backend/version.py b/backend/version.py index 1f9f247..bba4d7e 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,6 +1,6 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.133" +APP_VERSION = "0.8.134" BUILD_DATE = "2026-05-12" DB_SCHEMA_VERSION = "20260514062" @@ -36,6 +36,13 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.8.134", + "date": "2026-05-14", + "changes": [ + "Frontend Phase 4 Welle 2: frontend/src/api/planning.js (Trainingsplanung); utils/api.js re-exportiert Modul, api-Objekt spread planning.", + ], + }, { "version": "0.8.133", "date": "2026-05-14", diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 2cf3501..65dae3b 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -9,7 +9,8 @@ Dieses Bündel ist die **Leitlinie für die große Refaktorierung** nach dem MVP | [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md) | Zielarchitektur (Frontend, API, Daten), Qualitätsziele, Einbindung neuer Features | | [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md) | Erfasste Architekturschuld, Reihenfolge und Massnahmen zur Behebung | | [UMSETZUNGSPLAN_ROADMAP.md](./UMSETZUNGSPLAN_ROADMAP.md) | Phasen, Meilensteine, Abnahmekriterien, Aufwandsschwerpunkte | -| [`frontend/src/api/client.js`](../../frontend/src/api/client.js) | Phase 4: zentraler HTTP-Client (`request`); `utils/api.js` bleibt Facade | +| [`frontend/src/api/client.js`](../../frontend/src/api/client.js) | Phase 4: zentraler HTTP-Client (`request`, `ACTIVE_CLUB_STORAGE_KEY`) | +| [`frontend/src/api/planning.js`](../../frontend/src/api/planning.js) | Phase 4: Trainingsplanung (Einheiten, Vorlagen, Module, Rahmen, KPIs) | | [BASELINE_SNAPSHOT.md](./BASELINE_SNAPSHOT.md) | Phase 0: Bundle-, API- und Last-Baseline (Messvorlagen, Vergleich nach Phase 2) | | [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md) | **Verbindliche** Shinkan-spezifische Regeln (Ergänzung zu den globalen Rules) | diff --git a/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md index d53b3cd..7d2f2b1 100644 --- a/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md +++ b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md @@ -8,7 +8,7 @@ - **Phase 2:** **abgeschlossen** (2026-05-14) — Indizes 058–062, Keyset `/api/exercises` + `/api/training-units`, **`/api/dashboard/kpis`** inkl. `training_home`, EXPLAIN-Vorlagen **`scripts/load/explain-readpaths.sql`**. - **Offen Phase 1:** Inbox optional **TTL** / nur bei sichtbarem Widget. - **Phase 3 (abgeschlossen 2026-05-14):** Übungsliste modularisiert; Trainingsplanung/Übungsformular: **Page-Dateien unter Soft-Limit** — Implementierung in `TrainingPlanningPageRoot.jsx`, `ExerciseFormPageRoot.jsx`, `ExercisesListPageRoot.jsx`; `pages/*.jsx` nur Re-Export. Playwright **Tests 9–10**. -- **Phase 4 (gestartet 2026-05-14):** API **Welle 1** — `frontend/src/api/client.js` (`request`); `utils/api.js` bleibt Facade. +- **Phase 4 (fortlaufend 2026-05-14):** API **Welle 1** `client.js`; **Welle 2** `planning.js`; `utils/api.js` bleibt Facade (`export *`, `api`-Objekt `...planning`). **Ziel:** Nach MVP eine **nachhaltige** Architektur für Wachstum, **Performance** (Server + schwache Clients) und **sichere Feature-Erweiterung**. **Leitdokumente:** [ZIELBILD_ARCHITEKTUR.md](./ZIELBILD_ARCHITEKTUR.md), [SCHULDEN_UND_REMEDIATION.md](./SCHULDEN_UND_REMEDIATION.md), [VERBINDLICHE_REGELN_SHINKAN.md](./VERBINDLICHE_REGELN_SHINKAN.md). @@ -93,13 +93,14 @@ ## Phase 4 – API-Client Modularisierung -**Status:** **gestartet** (2026-05-14) — Welle 1: **`frontend/src/api/client.js`** (`request`, `ACTIVE_CLUB_STORAGE_KEY`); **`utils/api.js`** bleibt vollständige Facade (bestehende Importe unverändert). +**Status:** **fortlaufend** (2026-05-14) — Welle 1: **`client.js`**; Welle 2: **`planning.js`**; **`utils/api.js`** bleibt vollständige Facade. **Fokus:** Wartbarkeit für viele neue Features. | Task | Bezug | Status | |------|-------|--------| | `frontend/src/api/client.js` — zentraler HTTP-Client | A2 | erledigt (Welle 1) | +| `frontend/src/api/planning.js` — Trainingsplanung (Einheiten, Vorlagen, Module, Rahmen, Dashboard-KPIs) | A2 | erledigt (Welle 2) | | Domänen-Module unter `frontend/src/api/` + schrittweise Entlastung von `api.js` | A2 | offen | | Neue Endpoints primär in Domänen-Dateien | S3 | offen | diff --git a/frontend/src/api/planning.js b/frontend/src/api/planning.js new file mode 100644 index 0000000..eb02a91 --- /dev/null +++ b/frontend/src/api/planning.js @@ -0,0 +1,171 @@ +/** + * Trainingsplanung: Einheiten, Vorlagen, Module, Rahmenprogramme, Dashboard-KPIs. + * Facade: weiterhin `utils/api.js` (default + Named Exports). + */ +import { request } from './client.js' + +/** Query-Parameter wie GET /api/training-units. */ +export async function listTrainingUnits(filters = {}) { + const q = new URLSearchParams() + if (filters.group_id != null && filters.group_id !== '') { + q.set('group_id', String(filters.group_id)) + } + if (filters.club_id != null && filters.club_id !== '') { + q.set('club_id', String(filters.club_id)) + } + if (filters.start_date) q.set('start_date', filters.start_date) + if (filters.end_date) q.set('end_date', filters.end_date) + if (filters.status) q.set('status', filters.status) + if (filters.debrief_pending === true) q.set('debrief_pending', 'true') + if (filters.assigned_to_me === true) q.set('assigned_to_me', 'true') + if (filters.sort) q.set('sort', String(filters.sort)) + if (filters.limit != null && filters.limit !== '') q.set('limit', String(filters.limit)) + if (filters.cursor_planned_date) q.set('cursor_planned_date', String(filters.cursor_planned_date)) + if (filters.cursor_planned_time != null && filters.cursor_planned_time !== '') { + q.set('cursor_planned_time', String(filters.cursor_planned_time)) + } + if (filters.cursor_id != null && filters.cursor_id !== '') q.set('cursor_id', String(filters.cursor_id)) + const qs = q.toString() + return request(`/api/training-units${qs ? `?${qs}` : ''}`) +} + +/** Dashboard Kurzüberblick: Entwürfe / meine Übungen / YTD abgeschlossene Einheiten (ein Roundtrip). */ +export async function getDashboardKpis() { + return request('/api/dashboard/kpis') +} + +/** Dashboard: Übungen in geplanten Einheiten, die für den Verein noch auf Sichtbarkeit „Verein“ gehören. */ +export async function getTrainingExerciseClubVisibilityQueue(filters = {}) { + const q = new URLSearchParams() + if (filters.start_date) q.set('start_date', String(filters.start_date)) + if (filters.end_date) q.set('end_date', String(filters.end_date)) + if (filters.assigned_to_me === false) q.set('assigned_to_me', 'false') + if (filters.limit_units != null && filters.limit_units !== '') { + q.set('limit_units', String(filters.limit_units)) + } + const qs = q.toString() + return request(`/api/training-units/exercises-club-visibility-queue${qs ? `?${qs}` : ''}`) +} + +export async function getTrainingUnit(id) { + return request(`/api/training-units/${id}`) +} + +export async function createTrainingUnit(data) { + return request('/api/training-units', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function updateTrainingUnit(id, data) { + return request(`/api/training-units/${id}`, { + method: 'PUT', + body: JSON.stringify(data), + }) +} + +export async function deleteTrainingUnit(id) { + return request(`/api/training-units/${id}`, { method: 'DELETE' }) +} + +export async function quickCreateTrainingUnit(data) { + return request('/api/training-units/quick-create', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +/** Rahmen-Slot → geplante Einheit (tiefe Kopie, origin_framework_slot_id). */ +export async function createTrainingUnitFromFrameworkSlot(data) { + return request('/api/training-units/from-framework-slot', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function listTrainingPlanTemplates() { + return request('/api/training-plan-templates') +} + +export async function getTrainingPlanTemplate(id) { + return request(`/api/training-plan-templates/${id}`) +} + +export async function createTrainingPlanTemplate(data) { + return request('/api/training-plan-templates', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function updateTrainingPlanTemplate(id, data) { + return request(`/api/training-plan-templates/${id}`, { + method: 'PUT', + body: JSON.stringify(data), + }) +} + +export async function deleteTrainingPlanTemplate(id) { + return request(`/api/training-plan-templates/${id}`, { method: 'DELETE' }) +} + +export async function listTrainingModules() { + return request('/api/training-modules') +} + +export async function getTrainingModule(id) { + return request(`/api/training-modules/${id}`) +} + +export async function createTrainingModule(data) { + return request('/api/training-modules', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function updateTrainingModule(id, data) { + return request(`/api/training-modules/${id}`, { + method: 'PUT', + body: JSON.stringify(data), + }) +} + +export async function deleteTrainingModule(id) { + return request(`/api/training-modules/${id}`, { method: 'DELETE' }) +} + +/** Kopiert Modul-Inhalte ans Ende eines Abschnitts (section_order_index 0-basiert). */ +export async function applyTrainingModuleToTrainingUnit(unitId, data) { + return request(`/api/training-units/${unitId}/apply-training-module`, { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function listTrainingFrameworkPrograms() { + return request('/api/training-framework-programs') +} + +export async function getTrainingFrameworkProgram(id) { + return request(`/api/training-framework-programs/${id}`) +} + +export async function createTrainingFrameworkProgram(data) { + return request('/api/training-framework-programs', { + method: 'POST', + body: JSON.stringify(data), + }) +} + +export async function updateTrainingFrameworkProgram(id, data) { + return request(`/api/training-framework-programs/${id}`, { + method: 'PUT', + body: JSON.stringify(data), + }) +} + +export async function deleteTrainingFrameworkProgram(id) { + return request(`/api/training-framework-programs/${id}`, { method: 'DELETE' }) +} diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 58f391b..5df4124 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -7,8 +7,10 @@ import { stripHtmlToText } from './htmlUtils' import { normalizeAdvanceMode, parseComboRepSeriesCountUi } from './combinationMethodProfileUi' import { request, ACTIVE_CLUB_STORAGE_KEY } from '../api/client.js' +import * as planning from '../api/planning.js' export { ACTIVE_CLUB_STORAGE_KEY } +export * from '../api/planning.js' // ============================================================================ // Auth @@ -1253,176 +1255,6 @@ export async function deleteTrainerContext(id) { return request(`/api/trainer-contexts/${id}`, { method: 'DELETE' }) } -// ============================================================================ -// Training Planning -// ============================================================================ - -/** Query-Parameter wie GET /api/training-units. */ -export async function listTrainingUnits(filters = {}) { - const q = new URLSearchParams() - if (filters.group_id != null && filters.group_id !== '') { - q.set('group_id', String(filters.group_id)) - } - if (filters.club_id != null && filters.club_id !== '') { - q.set('club_id', String(filters.club_id)) - } - if (filters.start_date) q.set('start_date', filters.start_date) - if (filters.end_date) q.set('end_date', filters.end_date) - if (filters.status) q.set('status', filters.status) - if (filters.debrief_pending === true) q.set('debrief_pending', 'true') - if (filters.assigned_to_me === true) q.set('assigned_to_me', 'true') - if (filters.sort) q.set('sort', String(filters.sort)) - if (filters.limit != null && filters.limit !== '') q.set('limit', String(filters.limit)) - if (filters.cursor_planned_date) q.set('cursor_planned_date', String(filters.cursor_planned_date)) - if (filters.cursor_planned_time != null && filters.cursor_planned_time !== '') { - q.set('cursor_planned_time', String(filters.cursor_planned_time)) - } - if (filters.cursor_id != null && filters.cursor_id !== '') q.set('cursor_id', String(filters.cursor_id)) - const qs = q.toString() - return request(`/api/training-units${qs ? `?${qs}` : ''}`) -} - -/** Dashboard Kurzüberblick: Entwürfe / meine Übungen / YTD abgeschlossene Einheiten (ein Roundtrip). */ -export async function getDashboardKpis() { - return request('/api/dashboard/kpis') -} - -/** Dashboard: Übungen in geplanten Einheiten, die für den Verein noch auf Sichtbarkeit „Verein“ gehören. */ -export async function getTrainingExerciseClubVisibilityQueue(filters = {}) { - const q = new URLSearchParams() - if (filters.start_date) q.set('start_date', String(filters.start_date)) - if (filters.end_date) q.set('end_date', String(filters.end_date)) - if (filters.assigned_to_me === false) q.set('assigned_to_me', 'false') - if (filters.limit_units != null && filters.limit_units !== '') { - q.set('limit_units', String(filters.limit_units)) - } - const qs = q.toString() - return request(`/api/training-units/exercises-club-visibility-queue${qs ? `?${qs}` : ''}`) -} - -export async function getTrainingUnit(id) { - return request(`/api/training-units/${id}`) -} - -export async function createTrainingUnit(data) { - return request('/api/training-units', { - method: 'POST', - body: JSON.stringify(data) - }) -} - -export async function updateTrainingUnit(id, data) { - return request(`/api/training-units/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }) -} - -export async function deleteTrainingUnit(id) { - return request(`/api/training-units/${id}`, { method: 'DELETE' }) -} - -export async function quickCreateTrainingUnit(data) { - return request('/api/training-units/quick-create', { - method: 'POST', - body: JSON.stringify(data) - }) -} - -/** Rahmen-Slot → geplante Einheit (tiefe Kopie, origin_framework_slot_id). */ -export async function createTrainingUnitFromFrameworkSlot(data) { - return request('/api/training-units/from-framework-slot', { - method: 'POST', - body: JSON.stringify(data) - }) -} - -export async function listTrainingPlanTemplates() { - return request('/api/training-plan-templates') -} - -export async function getTrainingPlanTemplate(id) { - return request(`/api/training-plan-templates/${id}`) -} - -export async function createTrainingPlanTemplate(data) { - return request('/api/training-plan-templates', { - method: 'POST', - body: JSON.stringify(data) - }) -} - -export async function updateTrainingPlanTemplate(id, data) { - return request(`/api/training-plan-templates/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }) -} - -export async function deleteTrainingPlanTemplate(id) { - return request(`/api/training-plan-templates/${id}`, { method: 'DELETE' }) -} - -export async function listTrainingModules() { - return request('/api/training-modules') -} - -export async function getTrainingModule(id) { - return request(`/api/training-modules/${id}`) -} - -export async function createTrainingModule(data) { - return request('/api/training-modules', { - method: 'POST', - body: JSON.stringify(data), - }) -} - -export async function updateTrainingModule(id, data) { - return request(`/api/training-modules/${id}`, { - method: 'PUT', - body: JSON.stringify(data), - }) -} - -export async function deleteTrainingModule(id) { - return request(`/api/training-modules/${id}`, { method: 'DELETE' }) -} - -/** Kopiert Modul-Inhalte ans Ende eines Abschnitts (section_order_index 0-basiert). */ -export async function applyTrainingModuleToTrainingUnit(unitId, data) { - return request(`/api/training-units/${unitId}/apply-training-module`, { - method: 'POST', - body: JSON.stringify(data), - }) -} - -export async function listTrainingFrameworkPrograms() { - return request('/api/training-framework-programs') -} - -export async function getTrainingFrameworkProgram(id) { - return request(`/api/training-framework-programs/${id}`) -} - -export async function createTrainingFrameworkProgram(data) { - return request('/api/training-framework-programs', { - method: 'POST', - body: JSON.stringify(data), - }) -} - -export async function updateTrainingFrameworkProgram(id, data) { - return request(`/api/training-framework-programs/${id}`, { - method: 'PUT', - body: JSON.stringify(data), - }) -} - -export async function deleteTrainingFrameworkProgram(id) { - return request(`/api/training-framework-programs/${id}`, { method: 'DELETE' }) -} - // ============================================================================ // Version & Health // ============================================================================ @@ -1534,32 +1366,8 @@ export const api = { createExerciseProgressionSequence, deleteExerciseProgressionEdgesBatch, - // Training Planning - listTrainingUnits, - getDashboardKpis, - getTrainingExerciseClubVisibilityQueue, - getTrainingUnit, - createTrainingUnit, - updateTrainingUnit, - deleteTrainingUnit, - quickCreateTrainingUnit, - createTrainingUnitFromFrameworkSlot, - listTrainingPlanTemplates, - getTrainingPlanTemplate, - createTrainingPlanTemplate, - updateTrainingPlanTemplate, - deleteTrainingPlanTemplate, - listTrainingModules, - getTrainingModule, - createTrainingModule, - updateTrainingModule, - deleteTrainingModule, - applyTrainingModuleToTrainingUnit, - listTrainingFrameworkPrograms, - getTrainingFrameworkProgram, - createTrainingFrameworkProgram, - updateTrainingFrameworkProgram, - deleteTrainingFrameworkProgram, + // Training Planning → frontend/src/api/planning.js + ...planning, // Catalogs listFocusAreas,