feat: update version to 0.8.0 and enhance training planning features
- 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:
parent
7134fd1a25
commit
d7e1a82a37
113
backend/migrations/031_training_plan_templates_and_sections.sql
Normal file
113
backend/migrations/031_training_plan_templates_and_sections.sql
Normal 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
|
|
@ -1,8 +1,8 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.7.9"
|
APP_VERSION = "0.8.0"
|
||||||
BUILD_DATE = "2026-04-27"
|
BUILD_DATE = "2026-04-28"
|
||||||
DB_SCHEMA_VERSION = "20260427030"
|
DB_SCHEMA_VERSION = "20260428031"
|
||||||
|
|
||||||
MODULE_VERSIONS = {
|
MODULE_VERSIONS = {
|
||||||
"auth": "1.0.0",
|
"auth": "1.0.0",
|
||||||
|
|
@ -14,7 +14,7 @@ MODULE_VERSIONS = {
|
||||||
"exercises": "2.1.0", # Varianten-CRUD API + UI; Listen mit include_variants
|
"exercises": "2.1.0", # Varianten-CRUD API + UI; Listen mit include_variants
|
||||||
"training_units": "0.1.0",
|
"training_units": "0.1.0",
|
||||||
"training_programs": "0.1.0",
|
"training_programs": "0.1.0",
|
||||||
"planning": "0.2.0",
|
"planning": "0.3.0",
|
||||||
"import_wiki": "1.0.0",
|
"import_wiki": "1.0.0",
|
||||||
"admin": "1.0.0",
|
"admin": "1.0.0",
|
||||||
"membership": "1.0.0",
|
"membership": "1.0.0",
|
||||||
|
|
@ -24,7 +24,14 @@ MODULE_VERSIONS = {
|
||||||
|
|
||||||
CHANGELOG = [
|
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",
|
"date": "2026-04-27",
|
||||||
"changes": [
|
"changes": [
|
||||||
"Übungsvarianten: POST/PUT/DELETE /api/exercises/{id}/variants + reorder",
|
"Übungsvarianten: POST/PUT/DELETE /api/exercises/{id}/variants + reorder",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
// Version & Health
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -863,6 +889,11 @@ export const api = {
|
||||||
updateTrainingUnit,
|
updateTrainingUnit,
|
||||||
deleteTrainingUnit,
|
deleteTrainingUnit,
|
||||||
quickCreateTrainingUnit,
|
quickCreateTrainingUnit,
|
||||||
|
listTrainingPlanTemplates,
|
||||||
|
getTrainingPlanTemplate,
|
||||||
|
createTrainingPlanTemplate,
|
||||||
|
updateTrainingPlanTemplate,
|
||||||
|
deleteTrainingPlanTemplate,
|
||||||
|
|
||||||
// Catalogs
|
// Catalogs
|
||||||
listFocusAreas,
|
listFocusAreas,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export const PAGE_VERSIONS = {
|
||||||
ExercisesPage: "1.1.0", // Updated: Katalog-Integration
|
ExercisesPage: "1.1.0", // Updated: Katalog-Integration
|
||||||
ClubsPage: "1.0.0",
|
ClubsPage: "1.0.0",
|
||||||
SkillsPage: "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
|
AdminCatalogsPage: "2.2.0", // Updated: Frontend API Calls & Field Names für renamed tables
|
||||||
TrainerContextsPage: "1.0.0", // New: Trainer-Kontext-Verwaltung
|
TrainerContextsPage: "1.0.0", // New: Trainer-Kontext-Verwaltung
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user