import React, { useCallback, useEffect, useMemo, useState } from 'react' import api from '../utils/api' import NavStateLink from '../components/NavStateLink' import FrameworkProgramsFilterBlock from '../components/planning/FrameworkProgramsFilterBlock' import FrameworkProgramListCard from '../components/planning/FrameworkProgramListCard' import SkillProfileFullModal from '../components/skills/SkillProfileFullModal' import { useAuth } from '../context/AuthContext' import { getTenantClubDependencyKey } from '../utils/activeClub' import { buildFrameworkProgramsListReturnContext } from '../utils/navReturnContext' import { EMPTY_FRAMEWORK_IMPORT_FILTERS, filterFrameworkPrograms, hasActiveFrameworkImportFilters, } from '../utils/frameworkProgramListHelpers' 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 [catalogFocusAreas, setCatalogFocusAreas] = useState([]) const [catalogTrainingTypes, setCatalogTrainingTypes] = useState([]) const [catalogTargetGroups, setCatalogTargetGroups] = useState([]) const [catalogSkills, setCatalogSkills] = useState([]) const [filters, setFilters] = useState(() => ({ ...EMPTY_FRAMEWORK_IMPORT_FILTERS })) const [skillSummaries, setSkillSummaries] = useState({}) const [summariesLoading, setSummariesLoading] = useState(false) const [profileModal, setProfileModal] = useState(null) const filteredRows = useMemo( () => filterFrameworkPrograms(rows, filters, skillSummaries), [rows, filters, skillSummaries] ) const filterActive = hasActiveFrameworkImportFilters(filters) const load = useCallback(async () => { setLoading(true) setError('') try { const [list, fa, tt, tg, skills] = await Promise.all([ api.listTrainingFrameworkPrograms(), api.listFocusAreas({ status: 'active' }), api.listTrainingTypes({ status: 'active' }), api.listTargetGroups({ status: 'active' }), api.listSkillsCatalog({ status: 'active' }), ]) setRows(Array.isArray(list) ? list : []) setCatalogFocusAreas(Array.isArray(fa) ? fa : []) setCatalogTrainingTypes(Array.isArray(tt) ? tt : []) setCatalogTargetGroups(Array.isArray(tg) ? tg : []) setCatalogSkills(Array.isArray(skills) ? skills : []) } catch (e) { setError(e.message || 'Laden fehlgeschlagen') setRows([]) setCatalogFocusAreas([]) setCatalogTrainingTypes([]) setCatalogTargetGroups([]) setCatalogSkills([]) } finally { setLoading(false) } }, []) useEffect(() => { load() }, [load, tenantClubDepKey]) useEffect(() => { if (!rows.length) { setSkillSummaries({}) return undefined } let cancelled = false setSummariesLoading(true) api .batchSkillProfileSummaries({ frameworkProgramIds: rows.map((r) => r.id) }) .then((data) => { if (!cancelled) setSkillSummaries(data?.summaries || {}) }) .catch(() => { if (!cancelled) setSkillSummaries({}) }) .finally(() => { if (!cancelled) setSummariesLoading(false) }) return () => { cancelled = true } }, [rows, 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 Entwicklungsziele und Sessions — die Übernahme in Gruppentermine erfolgt in der Trainingsplanung.

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}
) : null} {loading ? (
) : rows.length === 0 ? (

Noch keine Rahmenprogramme

Lege ein neues Programm an — mit Titel, mindestens einem Entwicklungsziel und optional Sessions samt Übungsablauf.

Erstes Rahmenprogramm anlegen
) : ( <> {filteredRows.length === 0 ? (

Kein Treffer

{filterActive ? 'Kein Rahmenprogramm passt zu den gewählten Filtern. Passe die Kriterien an oder setze den Filter zurück.' : 'Keine Einträge.'}

) : (
    {filteredRows.map((r) => (
  • setProfileModal({ artifactType: 'framework_program', artifactId: row.id, title: (row.title || '').trim() || `Rahmen #${row.id}`, }) } />
  • ))}
)} )} setProfileModal(null)} artifactType={profileModal?.artifactType} artifactId={profileModal?.artifactId} title={profileModal?.title} />
) }