import React, { useState, useEffect, useCallback } from 'react' import { Link } from 'react-router-dom' import api from '../utils/api' import { useAuth } from '../context/AuthContext' import ExercisePickerModal from '../components/ExercisePickerModal' import ExercisePeekModal from '../components/ExercisePeekModal' import TrainingUnitSectionsEditor from '../components/TrainingUnitSectionsEditor' import { defaultSection, normalizeUnitToForm, enrichSectionsWithVariants, buildSectionsPayload, } from '../utils/trainingUnitSectionsForm' function TrainingPlanningPage() { const { user } = useAuth() const [groups, setGroups] = useState([]) const [selectedGroupId, setSelectedGroupId] = useState('') const [units, setUnits] = useState([]) const [planTemplates, setPlanTemplates] = useState([]) const [loading, setLoading] = useState(true) const [showModal, setShowModal] = useState(false) const [editingUnit, setEditingUnit] = useState(null) const [draftPlanTemplateId, setDraftPlanTemplateId] = useState('') const [quickTemplateId, setQuickTemplateId] = useState('') const [exercisePickerOpen, setExercisePickerOpen] = useState(false) const [exercisePickerTarget, setExercisePickerTarget] = useState(null) const [planningPeekExerciseId, setPlanningPeekExerciseId] = useState(null) const today = new Date().toISOString().split('T')[0] const thirtyDaysLater = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0] const [startDate, setStartDate] = useState(today) const [endDate, setEndDate] = useState(thirtyDaysLater) const [formData, setFormData] = useState({ group_id: '', planned_date: '', planned_time_start: '', planned_time_end: '', planned_focus: '', actual_date: '', actual_time_start: '', actual_time_end: '', attendance_count: '', status: 'planned', notes: '', trainer_notes: '', sections: [defaultSection()] }) useEffect(() => { loadData() }, []) useEffect(() => { if (selectedGroupId) { loadUnits() } }, [selectedGroupId, startDate, endDate]) const loadPlanTemplates = useCallback(async () => { try { const tpl = await api.listTrainingPlanTemplates() setPlanTemplates(tpl) } catch (e) { console.error('Vorlagen laden:', e) } }, []) const loadData = async () => { try { const groupsData = await api.listTrainingGroups({ status: 'active' }) setGroups(groupsData) await loadPlanTemplates() if (groupsData.length > 0) { const ownGroup = groupsData.find((g) => g.trainer_id === user?.id) if (ownGroup) { setSelectedGroupId(ownGroup.id) } else if (groupsData.length === 1) { setSelectedGroupId(groupsData[0].id) } } } catch (err) { console.error('Failed to load data:', err) alert('Fehler beim Laden: ' + err.message) } finally { setLoading(false) } } const loadUnits = async () => { if (!selectedGroupId) return try { const unitsData = await api.listTrainingUnits({ group_id: selectedGroupId, start_date: startDate, end_date: endDate }) setUnits(unitsData) } catch (err) { console.error('Failed to load units:', err) } } const handleQuickCreate = async () => { if (!selectedGroupId) { alert('Bitte wähle zuerst eine Trainingsgruppe') return } const date = prompt('Datum für neue Trainingseinheit (YYYY-MM-DD):', today) if (!date) return try { const body = { group_id: parseInt(selectedGroupId, 10), planned_date: date } if (quickTemplateId) { body.plan_template_id = parseInt(quickTemplateId, 10) } await api.quickCreateTrainingUnit(body) await loadUnits() } catch (err) { alert('Fehler beim Erstellen: ' + err.message) } } const handleCreate = () => { if (!selectedGroupId) { alert('Bitte wähle zuerst eine Trainingsgruppe') return } const group = groups.find((g) => g.id === parseInt(selectedGroupId, 10)) setEditingUnit(null) setDraftPlanTemplateId('') setFormData({ group_id: selectedGroupId, planned_date: today, planned_time_start: group?.time_start?.slice(0, 5) || '', planned_time_end: group?.time_end?.slice(0, 5) || '', planned_focus: '', actual_date: '', actual_time_start: '', actual_time_end: '', attendance_count: '', status: 'planned', notes: '', trainer_notes: '', sections: [defaultSection('Hauptteil')] }) setShowModal(true) } const applyTemplateFromSelect = async (templateId) => { setDraftPlanTemplateId(templateId) if (!templateId) return try { const tpl = await api.getTrainingPlanTemplate(parseInt(templateId, 10)) setFormData((fd) => ({ ...fd, sections: (tpl.sections || []).length ? tpl.sections.map((s) => ({ title: s.title, guidance_notes: s.guidance_text || '', items: [] })) : [defaultSection()] })) } catch (err) { alert('Vorlage laden: ' + err.message) } } const handleEdit = async (unit) => { try { const fullUnit = await api.getTrainingUnit(unit.id) setEditingUnit(fullUnit) setDraftPlanTemplateId(fullUnit.plan_template_id ? String(fullUnit.plan_template_id) : '') let sections = normalizeUnitToForm(fullUnit) sections = await enrichSectionsWithVariants(sections) setFormData({ group_id: fullUnit.group_id, planned_date: fullUnit.planned_date || '', planned_time_start: fullUnit.planned_time_start?.slice(0, 5) || '', planned_time_end: fullUnit.planned_time_end?.slice(0, 5) || '', planned_focus: fullUnit.planned_focus || '', actual_date: fullUnit.actual_date || '', actual_time_start: fullUnit.actual_time_start?.slice(0, 5) || '', actual_time_end: fullUnit.actual_time_end?.slice(0, 5) || '', attendance_count: fullUnit.attendance_count ?? '', status: fullUnit.status || 'planned', notes: fullUnit.notes || '', trainer_notes: fullUnit.trainer_notes || '', sections, }) setShowModal(true) } catch (err) { alert('Fehler beim Laden: ' + err.message) } } const handleSaveAsTemplate = async () => { const name = window.prompt('Name für die neue Trainingsvorlage (nur Abschnitts-Gliederung):') if (!name?.trim()) return try { await api.createTrainingPlanTemplate({ name: name.trim(), sections: formData.sections.map((s) => ({ title: s.title || 'Abschnitt', guidance_text: s.guidance_notes?.trim() ? s.guidance_notes.trim() : null })) }) await loadPlanTemplates() alert('Vorlage gespeichert.') } catch (err) { alert('Speichern: ' + err.message) } } const handleDelete = async (unit) => { if (!confirm(`Trainingseinheit vom ${unit.planned_date} wirklich löschen?`)) return try { await api.deleteTrainingUnit(unit.id) await loadUnits() } catch (err) { alert('Fehler beim Löschen: ' + err.message) } } const handleSubmit = async (e) => { e.preventDefault() if (!formData.group_id || !formData.planned_date) { alert('Gruppe und Datum sind Pflichtfelder') return } try { const sectionsPayload = buildSectionsPayload(formData.sections) const payload = { planned_date: formData.planned_date, planned_time_start: formData.planned_time_start || null, planned_time_end: formData.planned_time_end || null, planned_focus: formData.planned_focus || null, actual_date: formData.actual_date || null, actual_time_start: formData.actual_time_start || null, actual_time_end: formData.actual_time_end || null, attendance_count: formData.attendance_count ? parseInt(formData.attendance_count, 10) : null, status: formData.status || 'planned', notes: formData.notes || null, trainer_notes: formData.trainer_notes || null, sections: sectionsPayload } if (!editingUnit) { payload.group_id = parseInt(formData.group_id, 10) if (draftPlanTemplateId) { payload.plan_template_id = parseInt(draftPlanTemplateId, 10) } } if (editingUnit) { await api.updateTrainingUnit(editingUnit.id, payload) } else { await api.createTrainingUnit(payload) } setShowModal(false) await loadUnits() } catch (err) { alert('Fehler beim Speichern: ' + err.message) } } const updateFormField = (field, value) => { setFormData((prev) => ({ ...prev, [field]: value })) } if (loading) { return (

Laden...

) } const selectedGroup = groups.find((g) => g.id === parseInt(selectedGroupId, 10)) return (

Trainingsplanung

Wähle eine Trainingsgruppe, lege dann Termine mit Inhalt (Abschnitte und Übungen) an — ein Plan entsteht aus einer oder mehreren{' '} Trainingseinheiten im gewählten Zeitraum.

Mehrere Einheiten strukturieren auf einmal:{' '} Trainingsrahmenprogramme {' '} (Ziele, Slots, Übungen als Vorlage).

{!loading && groups.length === 0 && (

Erst Verein & Gruppe anlegen

Ohne Trainingsgruppe kann hier nichts gebucht werden. Unter Vereine legst du einen Verein an (kurzer Name genügt), optional eine Sparte, dann eine Trainingsgruppe. Wochentage, feste Zeiten oder Eigenschaften sind optional und kannst du später ergänzen.

Zu Vereinen & Trainingsgruppen
)}
setStartDate(e.target.value)} />
setEndDate(e.target.value)} />
{selectedGroup && (

{selectedGroup.location || 'Kein Ort angegeben'} {selectedGroup.weekday && ` · ${selectedGroup.weekday}`} {selectedGroup.time_start && ` · ${selectedGroup.time_start.slice(0, 5)} - ${selectedGroup.time_end?.slice(0, 5)}`}

)}

Plan anlegen: neue Trainingseinheit mit Datum, Zeit und Ablauf — oder schnell nur mit Datum (Zeiten aus der Gruppe). {!selectedGroupId && ( Wähle oben eine Trainingsgruppe, um die Schaltflächen zu aktivieren. )} {groups.length === 0 && ( Es gibt noch keine aktive Trainingsgruppe — unter{' '} Vereine {' '} anlegen oder aktivieren. )}

{!selectedGroupId ? (

Wähle oben eine Trainingsgruppe — danach kannst du mit „Neue Trainingseinheit planen“ starten.

) : units.length === 0 ? (

Keine Trainingseinheiten in diesem Zeitraum. Nutze oben „Neue Trainingseinheit planen“ oder{' '} „Schnell erstellen“, um den ersten Termin anzulegen.

) : (
{units.map((unit) => (

{unit.planned_date} {unit.planned_time_start && ` · ${unit.planned_time_start.slice(0, 5)} - ${unit.planned_time_end?.slice(0, 5)}`}

{unit.planned_focus && (

Fokus: {unit.planned_focus}

)}
{unit.status === 'planned' && 'Geplant'} {unit.status === 'completed' && 'Durchgeführt'} {unit.status === 'cancelled' && 'Abgesagt'} {unit.attendance_count !== null && unit.attendance_count !== undefined && ( {unit.attendance_count} Teilnehmer )}
Plan & Ablauf Im Training (Coach)
{unit.notes && (

{unit.notes}

)}
))}
)} {showModal && (

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

{!editingUnit && (

Lädt die Abschnitte und Hinweise aus der Vorlage; Übungen fügst du hier ein.

)}

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" />

Abschnitte & Übungen

setFormData((prev) => ({ ...prev, sections: updater(prev.sections), })) } onRequestExercisePick={({ sectionIndex, itemIndex }) => { setExercisePickerTarget({ sIdx: sectionIndex, iIdx: itemIndex }) setExercisePickerOpen(true) }} onPeekExercise={(id) => setPlanningPeekExerciseId(id)} showExecutionExtras={!!editingUnit} />
{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)} />
)}

Notizen