import React, { useCallback, useEffect, useMemo, useState } from 'react'
import api from '../utils/api'
import NavStateLink from '../components/NavStateLink'
import { useAuth } from '../context/AuthContext'
import { getTenantClubDependencyKey } from '../utils/activeClub'
import { buildFrameworkProgramsListReturnContext } from '../utils/navReturnContext'
import { frameworkSessionDurationLabel } from '../utils/frameworkProgramListHelpers'
function dashIfEmpty(val) {
const s = (val ?? '').toString().trim()
return s.length ? s : '—'
}
function FrameworkSummaryMeta({ r }) {
const trainingTypes =
typeof r.training_type_names_agg === 'string' ? r.training_type_names_agg.trim() : ''
const targetGroups =
typeof r.target_group_names_agg === 'string' ? r.target_group_names_agg.trim() : ''
const styleDir =
typeof r.style_direction_names_agg === 'string'
? r.style_direction_names_agg.trim()
: typeof r.style_direction_name === 'string'
? r.style_direction_name.trim()
: ''
const focus =
typeof r.focus_area_names_agg === 'string'
? r.focus_area_names_agg.trim()
: typeof r.focus_area_name === 'string'
? r.focus_area_name.trim()
: ''
const rowStyle = {
display: 'grid',
gridTemplateColumns: 'minmax(6.5rem, 32%) 1fr',
gap: '0.25rem 0.75rem',
alignItems: 'start',
marginTop: '0.35rem',
lineHeight: 1.45,
}
const durationLabel = frameworkSessionDurationLabel(r)
const goals =
typeof r.goal_titles_agg === 'string' ? r.goal_titles_agg.trim() : ''
return (
Session-Dauer
{durationLabel}
{goals ? (
Entwicklungsziele
{goals}
) : null}
Fokusbereich
{dashIfEmpty(focus)}
Stilrichtungen
{dashIfEmpty(styleDir)}
Trainingsarten
{trainingTypes.length ? trainingTypes : '—'}
Zielgruppen
{targetGroups.length ? targetGroups : '—'}
Kurzbeschreibung
{(r.description && String(r.description).trim()) || '—'}
)
}
export default function TrainingFrameworkProgramsListPage() {
const { user } = useAuth()
const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user])
const frameworkListReturn = useMemo(() => buildFrameworkProgramsListReturnContext(), [])
const [rows, setRows] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const load = useCallback(async () => {
setLoading(true)
setError('')
try {
const list = await api.listTrainingFrameworkPrograms()
setRows(Array.isArray(list) ? list : [])
} catch (e) {
setError(e.message || 'Laden fehlgeschlagen')
setRows([])
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
load()
}, [load, tenantClubDepKey])
async function handleDelete(id, title) {
if (!confirm(`Rahmenprogramm „${title || id}“ wirklich löschen?`)) return
try {
await api.deleteTrainingFrameworkProgram(id)
await load()
} catch (e) {
alert(e.message || 'Löschen fehlgeschlagen')
}
}
return (
<>
Trainingsrahmenprogramme
Vorlagen für Ziele und Sessions — die Verknüpfung mit Gruppenterminen erfolgt in der
Trainingsplanung (Registerkarte oben).
Mehr zur Übernahme in die Planung
Unter Planung wählst du eine Gruppe und übernimmst Slots aus einem Rahmenprogramm in
echte Termine. So bleibt die Bibliothek wiederverwendbar, ohne dass Einzelgruppen fest verdrahtet sind.
Rahmenprogramm anlegen
{error && (
{error}
)}
{loading ? (
) : rows.length === 0 ? (
Noch kein Rahmenprogramm gespeichert. Lege ein neues an — mit Titel, mindestens einem Ziel und optional
Slots samt Übungen.
Rahmenprogramm anlegen
) : (
{rows.map((r) => (
{r.title || `Rahmen #${r.id}`}
{frameworkSessionDurationLabel(r)}
{(r.goals_count ?? '—') + ' Ziele · '}
{(r.slots_count ?? '—') + ' Slots'}
Bearbeiten
handleDelete(r.id, r.title)}>
Löschen
))}
)}
>
)
}