feat: update version to 0.8.0 and enhance training planning features
Some checks failed
Deploy Development / deploy (push) Successful in 33s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Has been cancelled

- Incremented application version to 0.8.0 and updated database schema version to 20260428031.
- Introduced support for training plan templates, allowing users to create and manage reusable training structures.
- Enhanced the Training Planning UI to include sections and exercises, improving the organization of training units.
- Updated API endpoints for training plan templates, enabling CRUD operations for better integration with the frontend.
- Improved validation and permission checks for creating training units, ensuring proper access control.
This commit is contained in:
Lars 2026-04-28 16:31:45 +02:00
parent 7134fd1a25
commit d7e1a82a37
6 changed files with 1509 additions and 516 deletions

View File

@ -0,0 +1,113 @@
-- Migration 031: Trainingsvorlagen (Sektionen) und strukturierter Ablauf pro Einheit
-- Freie Anmerkungszeilen (note) zwischen Übungen (exercise) mit optionaler Variante/Dauer.
-- ── Vorlagen (wiederverwendbare Gliederung für Gruppen/Trainer) ─────────────
CREATE TABLE IF NOT EXISTS training_plan_templates (
id SERIAL PRIMARY KEY,
club_id INT REFERENCES clubs(id) ON DELETE SET NULL,
created_by INT REFERENCES profiles(id) ON DELETE SET NULL,
name VARCHAR(200) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS training_plan_template_sections (
id SERIAL PRIMARY KEY,
template_id INT NOT NULL REFERENCES training_plan_templates(id) ON DELETE CASCADE,
order_index INT NOT NULL,
title VARCHAR(200) NOT NULL,
guidance_text TEXT,
UNIQUE (template_id, order_index)
);
CREATE INDEX IF NOT EXISTS idx_training_plan_templates_club ON training_plan_templates(club_id);
CREATE INDEX IF NOT EXISTS idx_training_plan_templates_creator ON training_plan_templates(created_by);
CREATE INDEX IF NOT EXISTS idx_training_plan_template_sections_template ON training_plan_template_sections(template_id);
DROP TRIGGER IF EXISTS training_plan_templates_update ON training_plan_templates;
CREATE TRIGGER training_plan_templates_update
BEFORE UPDATE ON training_plan_templates
FOR EACH ROW EXECUTE FUNCTION update_timestamp();
-- ── Verknüpfung Einheit ↔ genutzte Vorlage (nur Metadaten) ──────────────────
ALTER TABLE training_units
ADD COLUMN IF NOT EXISTS plan_template_id INT REFERENCES training_plan_templates(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_training_units_plan_template ON training_units(plan_template_id);
-- ── Konkrete Sektionen je Trainingseinheit ────────────────────────────────
CREATE TABLE IF NOT EXISTS training_unit_sections (
id SERIAL PRIMARY KEY,
training_unit_id INT NOT NULL REFERENCES training_units(id) ON DELETE CASCADE,
order_index INT NOT NULL,
title VARCHAR(200) NOT NULL DEFAULT 'Abschnitt',
guidance_notes TEXT,
source_template_section_id INT REFERENCES training_plan_template_sections(id) ON DELETE SET NULL,
UNIQUE (training_unit_id, order_index)
);
CREATE INDEX IF NOT EXISTS idx_training_unit_sections_unit ON training_unit_sections(training_unit_id);
-- ── Positionen: Übung oder freie Anmerkung ────────────────────────────────
CREATE TABLE IF NOT EXISTS training_unit_section_items (
id SERIAL PRIMARY KEY,
section_id INT NOT NULL REFERENCES training_unit_sections(id) ON DELETE CASCADE,
order_index INT NOT NULL,
item_type VARCHAR(20) NOT NULL CHECK (item_type IN ('exercise', 'note')),
exercise_id INT REFERENCES exercises(id) ON DELETE SET NULL,
exercise_variant_id INT REFERENCES exercise_variants(id) ON DELETE SET NULL,
planned_duration_min INT,
actual_duration_min INT,
notes TEXT,
modifications TEXT,
note_body TEXT,
UNIQUE (section_id, order_index),
CHECK (
(item_type = 'exercise' AND exercise_id IS NOT NULL AND note_body IS NULL)
OR
(item_type = 'note' AND exercise_id IS NULL)
)
);
CREATE INDEX IF NOT EXISTS idx_training_unit_section_items_section ON training_unit_section_items(section_id);
CREATE INDEX IF NOT EXISTS idx_training_unit_section_items_exercise ON training_unit_section_items(exercise_id)
WHERE exercise_id IS NOT NULL;
-- ── Bestehende Zeilen migrieren: eine Sektion „Übungen“ pro Einheit ─────────
INSERT INTO training_unit_sections (training_unit_id, order_index, title)
SELECT id, 0, 'Übungen'
FROM training_units tu
WHERE NOT EXISTS (
SELECT 1 FROM training_unit_sections tus WHERE tus.training_unit_id = tu.id
);
INSERT INTO training_unit_section_items (
section_id,
order_index,
item_type,
exercise_id,
exercise_variant_id,
planned_duration_min,
actual_duration_min,
notes,
modifications,
note_body
)
SELECT
tus.id,
tue.order_index,
'exercise',
tue.exercise_id,
tue.exercise_variant_id,
tue.planned_duration_min,
tue.actual_duration_min,
tue.notes,
tue.modifications,
NULL
FROM training_unit_exercises tue
INNER JOIN training_unit_sections tus
ON tus.training_unit_id = tue.training_unit_id
AND tus.order_index = 0;
DROP TABLE IF EXISTS training_unit_exercises;

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
# Shinkan Jinkendo Version Information
APP_VERSION = "0.7.9"
BUILD_DATE = "2026-04-27"
DB_SCHEMA_VERSION = "20260427030"
APP_VERSION = "0.8.0"
BUILD_DATE = "2026-04-28"
DB_SCHEMA_VERSION = "20260428031"
MODULE_VERSIONS = {
"auth": "1.0.0",
@ -14,7 +14,7 @@ MODULE_VERSIONS = {
"exercises": "2.1.0", # Varianten-CRUD API + UI; Listen mit include_variants
"training_units": "0.1.0",
"training_programs": "0.1.0",
"planning": "0.2.0",
"planning": "0.3.0",
"import_wiki": "1.0.0",
"admin": "1.0.0",
"membership": "1.0.0",
@ -24,7 +24,14 @@ MODULE_VERSIONS = {
CHANGELOG = [
{
"version": "0.7.9",
"version": "0.8.0",
"date": "2026-04-28",
"changes": [
"DB 031: Trainingsvorlagen (Sektionen) + Struktur pro Einheit (Sektionen, Übungen/Notizen, Dauer)",
"API: /api/training-plan-templates CRUD; Trainingseinheiten mit sections[] + plan_template_id",
"Trainingsplanung UI: Abschnitte, Zwischen-Anmerkungen, Vorlagen auswählen / speichern",
],
},
"date": "2026-04-27",
"changes": [
"Übungsvarianten: POST/PUT/DELETE /api/exercises/{id}/variants + reorder",

File diff suppressed because it is too large Load Diff

View File

@ -790,6 +790,32 @@ export async function quickCreateTrainingUnit(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' })
}
// ============================================================================
// Version & Health
// ============================================================================
@ -863,6 +889,11 @@ export const api = {
updateTrainingUnit,
deleteTrainingUnit,
quickCreateTrainingUnit,
listTrainingPlanTemplates,
getTrainingPlanTemplate,
createTrainingPlanTemplate,
updateTrainingPlanTemplate,
deleteTrainingPlanTemplate,
// Catalogs
listFocusAreas,

View File

@ -10,7 +10,7 @@ export const PAGE_VERSIONS = {
ExercisesPage: "1.1.0", // Updated: Katalog-Integration
ClubsPage: "1.0.0",
SkillsPage: "1.0.0",
TrainingPlanningPage: "1.0.0",
TrainingPlanningPage: "1.1.0",
AdminCatalogsPage: "2.2.0", // Updated: Frontend API Calls & Field Names für renamed tables
TrainerContextsPage: "1.0.0", // New: Trainer-Kontext-Verwaltung
}