feat: enhance TrainingPlanningPage with new training unit creation UI
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 6s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 22s
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 6s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 22s
- Introduced a new layout for creating training units within a card format, improving visual organization and user experience. - Added CSS styles for various elements related to training unit creation, including titles, hints, and action buttons. - Removed the quick template ID state and related functionality to streamline the creation process. - Updated user prompts and hints to guide users more effectively in selecting training groups and creating new training units.
This commit is contained in:
parent
d4b9db9520
commit
2007f3f659
|
|
@ -3824,6 +3824,98 @@ a.analysis-split__nav-item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Trainingsplanung: Abschnitt „Neue Trainingseinheit“ + Vorlage im Modal ───────── */
|
||||||
|
.training-planning-create--in-card {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
padding-top: 1.25rem;
|
||||||
|
border-top: 1px solid var(--border, rgba(0, 0, 0, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__intro {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__title {
|
||||||
|
margin: 0 0 0.45rem;
|
||||||
|
font-size: 1.06rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text1);
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__lede {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: var(--text2);
|
||||||
|
max-width: 52rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__cta {
|
||||||
|
min-height: 44px;
|
||||||
|
padding-left: 1.35rem;
|
||||||
|
padding-right: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__secondary {
|
||||||
|
min-height: 44px;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__hint {
|
||||||
|
margin: 0.85rem 0 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
color: var(--text3);
|
||||||
|
max-width: 48rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-create__hint--warn {
|
||||||
|
color: var(--text2);
|
||||||
|
margin-top: 0.65rem;
|
||||||
|
padding: 0.55rem 0.7rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--surface2);
|
||||||
|
border: 1px solid var(--border2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-template-panel {
|
||||||
|
padding: 1rem 1.1rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border2);
|
||||||
|
background: linear-gradient(165deg, var(--surface2) 0%, var(--surface) 100%);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-template-panel__label {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-template-panel__select {
|
||||||
|
font-size: 0.94rem;
|
||||||
|
padding: 0.55rem 0.65rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-planning-template-panel__help {
|
||||||
|
margin: 0.65rem 0 0;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
color: var(--text2);
|
||||||
|
line-height: 1.48;
|
||||||
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.desktop-sidebar,
|
.desktop-sidebar,
|
||||||
.bottom-nav,
|
.bottom-nav,
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ function TrainingPlanningPage() {
|
||||||
/** Abschnitts-Editor bei Bearbeitung: Planung vs. Nachbereitung (Ist & Abweichungen) */
|
/** Abschnitts-Editor bei Bearbeitung: Planung vs. Nachbereitung (Ist & Abweichungen) */
|
||||||
const [sectionsEditMode, setSectionsEditMode] = useState('planning')
|
const [sectionsEditMode, setSectionsEditMode] = useState('planning')
|
||||||
const [draftPlanTemplateId, setDraftPlanTemplateId] = useState('')
|
const [draftPlanTemplateId, setDraftPlanTemplateId] = useState('')
|
||||||
const [quickTemplateId, setQuickTemplateId] = useState('')
|
|
||||||
const [exercisePickerOpen, setExercisePickerOpen] = useState(false)
|
const [exercisePickerOpen, setExercisePickerOpen] = useState(false)
|
||||||
const [exercisePickerTarget, setExercisePickerTarget] = useState(null)
|
const [exercisePickerTarget, setExercisePickerTarget] = useState(null)
|
||||||
const [planningPeekCtx, setPlanningPeekCtx] = useState(null)
|
const [planningPeekCtx, setPlanningPeekCtx] = useState(null)
|
||||||
|
|
@ -461,28 +460,6 @@ function TrainingPlanningPage() {
|
||||||
return { fpTitle, slotBit, fpId: unit.origin_framework_program_id }
|
return { fpTitle, slotBit, fpId: unit.origin_framework_program_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = () => {
|
const handleCreate = () => {
|
||||||
if (!selectedGroupId) {
|
if (!selectedGroupId) {
|
||||||
alert('Bitte wähle zuerst eine Trainingsgruppe')
|
alert('Bitte wähle zuerst eine Trainingsgruppe')
|
||||||
|
|
@ -1191,80 +1168,39 @@ function TrainingPlanningPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div className="training-planning-create training-planning-create--in-card">
|
||||||
style={{
|
<div className="training-planning-create__intro">
|
||||||
marginTop: '1.25rem',
|
<h3 className="training-planning-create__title">Neue Trainingseinheit</h3>
|
||||||
paddingTop: '1rem',
|
<p className="training-planning-create__lede">
|
||||||
borderTop: '1px solid var(--border, rgba(0,0,0,0.08))'
|
Termin mit Datum, Zeiten und Ablauf (Abschnitte & Übungen) festlegen — optional eine{' '}
|
||||||
}}
|
<strong>Trainingsvorlage</strong> für die Gliederung wählen oder Inhalte aus einem{' '}
|
||||||
>
|
<strong>Rahmenprogramm</strong> übernehmen.
|
||||||
<p style={{ fontSize: '0.88rem', color: 'var(--text2)', marginBottom: '0.75rem' }}>
|
</p>
|
||||||
<strong>Plan anlegen:</strong> neue Trainingseinheit mit Datum, Zeit und Ablauf — oder schnell nur mit Datum (Zeiten aus der Gruppe).
|
|
||||||
{!selectedGroupId && (
|
{!selectedGroupId && (
|
||||||
<span style={{ display: 'block', marginTop: '0.35rem' }}>
|
<p className="training-planning-create__hint training-planning-create__hint--warn">
|
||||||
Wähle oben eine Trainingsgruppe, um die Schaltflächen zu aktivieren.
|
Wähle oben eine Trainingsgruppe, um fortzufahren.
|
||||||
</span>
|
</p>
|
||||||
)}
|
)}
|
||||||
{groups.length === 0 && (
|
{groups.length === 0 && (
|
||||||
<span style={{ display: 'block', marginTop: '0.35rem' }}>
|
<p className="training-planning-create__hint training-planning-create__hint--warn">
|
||||||
Es gibt noch keine aktive Trainingsgruppe — unter{' '}
|
Es gibt noch keine aktive Trainingsgruppe — unter{' '}
|
||||||
<Link to="/clubs">
|
<Link to="/clubs">Vereine</Link> anlegen oder aktivieren.
|
||||||
Vereine
|
|
||||||
</Link>{' '}
|
|
||||||
anlegen oder aktivieren.
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</p>
|
</p>
|
||||||
<div
|
)}
|
||||||
style={{
|
</div>
|
||||||
display: 'flex',
|
<div className="training-planning-create__actions">
|
||||||
flexWrap: 'wrap',
|
|
||||||
gap: '0.5rem',
|
|
||||||
alignItems: 'center'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary training-planning-create__cta"
|
||||||
disabled={!selectedGroupId}
|
disabled={!selectedGroupId}
|
||||||
title={!selectedGroupId ? 'Zuerst eine Trainingsgruppe wählen' : undefined}
|
title={!selectedGroupId ? 'Zuerst eine Trainingsgruppe wählen' : undefined}
|
||||||
onClick={handleCreate}
|
onClick={handleCreate}
|
||||||
>
|
>
|
||||||
+ Neue Trainingseinheit planen
|
Trainingseinheit planen…
|
||||||
</button>
|
</button>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', flexWrap: 'wrap' }}>
|
|
||||||
<label className="form-label" style={{ marginBottom: 0 }}>
|
|
||||||
Schnell (+ optional Vorlage):
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
className="form-input"
|
|
||||||
style={{ minWidth: '180px', marginBottom: 0 }}
|
|
||||||
value={quickTemplateId}
|
|
||||||
onChange={(e) => setQuickTemplateId(e.target.value)}
|
|
||||||
disabled={!selectedGroupId}
|
|
||||||
title={!selectedGroupId ? 'Zuerst Trainingsgruppe wählen' : undefined}
|
|
||||||
>
|
|
||||||
<option value="">Standard (leer)</option>
|
|
||||||
{planTemplates.map((t) => (
|
|
||||||
<option key={t.id} value={String(t.id)}>
|
|
||||||
{t.name}
|
|
||||||
{typeof t.sections_count === 'number' ? ` (${t.sections_count} Abschn.)` : ''}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary training-planning-create__secondary"
|
||||||
disabled={!selectedGroupId}
|
|
||||||
title={!selectedGroupId ? 'Zuerst eine Trainingsgruppe wählen' : undefined}
|
|
||||||
onClick={handleQuickCreate}
|
|
||||||
>
|
|
||||||
Schnell erstellen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary"
|
|
||||||
disabled={!selectedGroupId}
|
disabled={!selectedGroupId}
|
||||||
title={!selectedGroupId ? 'Zuerst eine Trainingsgruppe wählen' : undefined}
|
title={!selectedGroupId ? 'Zuerst eine Trainingsgruppe wählen' : undefined}
|
||||||
onClick={openFrameworkImportModal}
|
onClick={openFrameworkImportModal}
|
||||||
|
|
@ -1272,13 +1208,18 @@ function TrainingPlanningPage() {
|
||||||
Aus Rahmen übernehmen…
|
Aus Rahmen übernehmen…
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="training-planning-create__hint">
|
||||||
|
Vorlage („Ohne Vorlage“ oder gespeicherte Gliederung) stellst du im sich öffnenden Dialog ein; dort auch
|
||||||
|
Kalenderdatum und Zeiten.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!selectedGroupId ? (
|
{!selectedGroupId ? (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<p style={{ color: 'var(--text2)', textAlign: 'center' }}>
|
<p style={{ color: 'var(--text2)', textAlign: 'center' }}>
|
||||||
Wähle oben eine Trainingsgruppe — danach kannst du mit <strong>„Neue Trainingseinheit planen“</strong> starten.
|
Wähle oben eine Trainingsgruppe — danach kannst du unter{' '}
|
||||||
|
<strong>„Trainingseinheit planen…“</strong> einen Termin anlegen.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : planView === 'calendar' ? (
|
) : planView === 'calendar' ? (
|
||||||
|
|
@ -1460,8 +1401,8 @@ function TrainingPlanningPage() {
|
||||||
) : units.length === 0 ? (
|
) : units.length === 0 ? (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<p style={{ color: 'var(--text2)', textAlign: 'center' }}>
|
<p style={{ color: 'var(--text2)', textAlign: 'center' }}>
|
||||||
Keine Trainingseinheiten in diesem Zeitraum. Nutze oben <strong>„Neue Trainingseinheit planen“</strong> oder{' '}
|
Keine Trainingseinheiten in diesem Zeitraum. Unten unter <strong>„Neue Trainingseinheit“</strong> einen
|
||||||
<strong>„Schnell erstellen“</strong>, um den ersten Termin anzulegen.
|
Termin anlegen — optional mit Vorlage im Dialog.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -2099,22 +2040,27 @@ function TrainingPlanningPage() {
|
||||||
})() : null}
|
})() : null}
|
||||||
|
|
||||||
{!editingUnit && (
|
{!editingUnit && (
|
||||||
<div className="form-row" style={{ marginBottom: '1.25rem' }}>
|
<div className="training-planning-template-panel" style={{ marginBottom: '1.35rem' }}>
|
||||||
<label className="form-label">Gliederungsvorlage (optional)</label>
|
<label className="form-label training-planning-template-panel__label" htmlFor="planning-draft-template">
|
||||||
|
Vorlage für den Ablauf
|
||||||
|
</label>
|
||||||
<select
|
<select
|
||||||
className="form-input"
|
id="planning-draft-template"
|
||||||
|
className="form-input training-planning-template-panel__select"
|
||||||
value={draftPlanTemplateId}
|
value={draftPlanTemplateId}
|
||||||
onChange={(e) => applyTemplateFromSelect(e.target.value)}
|
onChange={(e) => applyTemplateFromSelect(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="">Keine Vorlage</option>
|
<option value="">Ohne Vorlage — leere Gliederung (ein Abschnitt)</option>
|
||||||
{planTemplates.map((t) => (
|
{planTemplates.map((t) => (
|
||||||
<option key={t.id} value={String(t.id)}>
|
<option key={t.id} value={String(t.id)}>
|
||||||
{t.name}
|
{t.name}
|
||||||
|
{typeof t.sections_count === 'number' ? ` · ${t.sections_count} Abschn.` : ''}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<p style={{ fontSize: '0.8rem', color: 'var(--text2)', marginTop: '0.35rem' }}>
|
<p className="training-planning-template-panel__help">
|
||||||
Lädt die Abschnitte und Hinweise aus der Vorlage; Übungen fügst du hier ein.
|
Übernimmt nur die <strong>Sektionsstruktur</strong> aus der Bibliothek; Übungen trägst du unten bei
|
||||||
|
den Abschnitten ein. Gespeicherte Vorlagen kannst du unter Planung später erweitern.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user