shinkan-jinkendo/backend/migrations/031_training_plan_templates_and_sections.sql
Lars d7e1a82a37
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
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.
2026-04-28 16:31:45 +02:00

114 lines
4.5 KiB
SQL

-- 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;