shinkan-jinkendo/frontend/src/pages/TrainingFrameworkProgramsListPage.jsx

146 lines
5.0 KiB
React
Raw Normal View History

import React, { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import api from '../utils/api'
const MODE_LABELS = {
concrete: 'Konkret (Gruppe / Einheiten)',
library: 'Bibliothek',
}
export default function TrainingFrameworkProgramsListPage() {
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])
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 (
<div style={{ padding: '2rem' }}>
<div style={{ maxWidth: '900px', margin: '0 auto' }}>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
justifyContent: 'space-between',
gap: '1rem',
marginBottom: '1.25rem',
}}
>
<div>
<h1 style={{ marginBottom: '0.35rem' }}>Trainingsrahmenprogramme</h1>
<p style={{ color: 'var(--text2)', fontSize: '0.95rem', maxWidth: '36rem' }}>
Mehrere Entwicklungsziele und Übungen über Session‑Slots verteilen — als Vorlage in der Bibliothek oder
im Kontext einer Gruppe.
</p>
</div>
<Link to="/planning/framework-programs/new" className="btn btn-primary" style={{ textDecoration: 'none' }}>
+ Neues Rahmenprogramm
</Link>
</div>
<p style={{ marginBottom: '1rem' }}>
<Link to="/planning" style={{ color: 'var(--accent-dark)' }}>
← Zurück zur Trainingsplanung
</Link>
</p>
{error && (
<div className="card" style={{ borderLeft: '4px solid var(--danger)', marginBottom: '1rem' }}>
{error}
</div>
)}
{loading ? (
<div style={{ textAlign: 'center', padding: '2rem' }}>
<div className="spinner" />
<p>Laden…</p>
</div>
) : rows.length === 0 ? (
<div className="card">
<p style={{ color: 'var(--text2)' }}>
Noch kein Rahmenprogramm angelegt. Über <strong>Neues Rahmenprogramm</strong> startest du mit Titel,
Zielen und Slots.
</p>
</div>
) : (
<ul style={{ listStyle: 'none' }}>
{rows.map((r) => (
<li key={r.id} className="card" style={{ marginBottom: '12px' }}>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
justifyContent: 'space-between',
gap: '0.75rem',
}}
>
<div>
<Link
to={`/planning/framework-programs/${r.id}`}
style={{ fontWeight: 600, fontSize: '1.05rem', color: 'var(--text1)' }}
>
{r.title || `Rahmen #${r.id}`}
</Link>
<div style={{ fontSize: '0.85rem', color: 'var(--text2)', marginTop: '0.35rem' }}>
<span>{MODE_LABELS[r.plan_mode] || r.plan_mode}</span>
{typeof r.goals_count === 'number' || typeof r.slots_count === 'number' ? (
<span>
{' '}
· {r.goals_count ?? '—'} Ziele · {r.slots_count ?? '—'} Slots
</span>
) : null}
</div>
{r.description ? (
<p style={{ marginTop: '0.5rem', fontSize: '0.9rem', color: 'var(--text2)' }}>{r.description}</p>
) : null}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
<Link
to={`/planning/framework-programs/${r.id}`}
className="btn btn-secondary"
style={{ textDecoration: 'none' }}
>
Bearbeiten
</Link>
<button type="button" className="btn btn-secondary" onClick={() => handleDelete(r.id, r.title)}>
Löschen
</button>
</div>
</div>
</li>
))}
</ul>
)}
</div>
</div>
)
}