/** * Trainingsablauf anzeigen, drucken und lokal auf der Matte abhaken (Fortschritt im Browser gespeichert). */ import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Link, useNavigate, useParams } from 'react-router-dom' import api from '../utils/api' import ExercisePeekModal from '../components/ExercisePeekModal' import { itemStableKey, sortedSections, sortedItems } from '../utils/trainingPlanUtils' function storageKey(unitId) { return `sj_training_run_checked_${unitId}` } function formatMin(m) { if (m === null || m === undefined || m === '') return null const n = Number(m) if (!Number.isFinite(n)) return null return `${n} Min.` } function statusLabel(s) { if (s === 'completed') return 'Durchgeführt' if (s === 'cancelled') return 'Abgesagt' return 'Geplant' } export default function TrainingUnitRunPage() { const { unitId } = useParams() const navigate = useNavigate() const idNum = unitId ? parseInt(unitId, 10) : NaN const [unit, setUnit] = useState(null) const [loadError, setLoadError] = useState(null) const [loading, setLoading] = useState(true) const [checked, setChecked] = useState(() => new Set()) const [peekExerciseId, setPeekExerciseId] = useState(null) const loadChecked = useCallback((uid) => { try { const raw = sessionStorage.getItem(storageKey(uid)) if (!raw) return new Set() const arr = JSON.parse(raw) if (!Array.isArray(arr)) return new Set() return new Set(arr.map(String)) } catch { return new Set() } }, []) useEffect(() => { if (!unitId || Number.isNaN(idNum)) { setLoadError('Ungültige Trainingseinheit') setLoading(false) return } let cancelled = false ;(async () => { setLoading(true) setLoadError(null) try { const u = await api.getTrainingUnit(idNum) if (!cancelled) { setUnit(u) setChecked(loadChecked(idNum)) } } catch (e) { if (!cancelled) setLoadError(e.message || 'Laden fehlgeschlagen') } finally { if (!cancelled) setLoading(false) } })() return () => { cancelled = true } }, [unitId, idNum, loadChecked]) const persistChecked = useCallback( (next) => { setChecked(next) try { sessionStorage.setItem(storageKey(idNum), JSON.stringify([...next])) } catch { /* ignore quota */ } }, [idNum] ) const toggle = useCallback( (key) => { const next = new Set(checked) if (next.has(key)) next.delete(key) else next.add(key) persistChecked(next) }, [checked, persistChecked] ) const clearProgress = useCallback(() => { persistChecked(new Set()) try { sessionStorage.removeItem(storageKey(idNum)) } catch { /* ignore */ } }, [idNum, persistChecked]) const sections = useMemo(() => sortedSections(unit), [unit]) const totalPlannedMin = useMemo(() => { let t = 0 for (const sec of sections) { for (const it of sortedItems(sec)) { if (it.item_type === 'exercise' && it.planned_duration_min != null) { const n = Number(it.planned_duration_min) if (Number.isFinite(n)) t += n } } } return t }, [sections]) if (loading) { return (

Plan wird geladen…

) } if (loadError || !unit) { return (

{loadError || 'Trainingseinheit nicht gefunden.'}

) } return (
setPeekExerciseId(null)} />

Training {unit.planned_date && ` · ${unit.planned_date}`} {unit.planned_time_start && ` · ${String(unit.planned_time_start).slice(0, 5)}`} {unit.planned_time_end && `–${String(unit.planned_time_end).slice(0, 5)}`}

{unit.group_name && ( Gruppe: {unit.group_name} {unit.club_name && ` (${unit.club_name})`} )} {unit.group_location && ( Ort: {unit.group_location} )} {unit.planned_focus && ( Fokus: {unit.planned_focus} )} Status: {statusLabel(unit.status)} {totalPlannedMin > 0 && ( Geplante Zeit (Übungen): ca. {totalPlannedMin} Min. )}
{unit.notes && (
Hinweis Teilnehmer: {unit.notes}
)}
{sections.length === 0 ? (

Noch keine Abschnitte in diesem Plan. Unter Planung bearbeiten.

) : (
{sections.map((sec, si) => { const secOrder = sec.order_index ?? si const items = sortedItems(sec) return (

{sec.title || `Abschnitt ${si + 1}`}

{sec.guidance_notes && (

{sec.guidance_notes}

)}
    {items.map((it, ii) => { const ck = itemStableKey(it, secOrder, ii) const done = checked.has(ck) if (it.item_type === 'note') { return (
  • ) } const title = it.exercise_title || (it.exercise_id ? `Übung #${it.exercise_id}` : 'Übung') const variant = it.exercise_variant_name ? ` (${it.exercise_variant_name})` : '' const plan = formatMin(it.planned_duration_min) const extras = [] if (it.exercise_focus_area) extras.push(it.exercise_focus_area) const metaParts = [...extras, plan].filter(Boolean) return (
  • ) })}
) })}
)} {unit.trainer_notes && (
Nur Trainer

{unit.trainer_notes}

)}
) }