refactor: streamline exercise selection process in training pages
Some checks failed
Deploy Development / deploy (push) Successful in 33s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Failing after 43s

- Simplified the exercise selection logic in TrainingUnitSectionsEditor, TrainingFrameworkProgramEditPage, and TrainingPlanningPage by removing the multi-select option.
- Updated the ExercisePickerModal to always enable multi-select, enhancing user experience when selecting exercises.
- Refactored the handling of selected exercises to improve clarity and maintainability of the code.
This commit is contained in:
Lars 2026-05-05 14:52:19 +02:00
parent 4080088d42
commit 86192df508
3 changed files with 65 additions and 126 deletions

View File

@ -809,19 +809,10 @@ export default function TrainingUnitSectionsEditor({
<button
type="button"
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
onClick={() => addItem(sIdx, 'exercise')}
onClick={() => onRequestExercisePick?.({ sectionIndex: sIdx })}
>
+ Übung
</button>
<button
type="button"
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
onClick={() =>
onRequestExercisePick?.({ sectionIndex: sIdx, multi: true })
}
>
+ mehrere Übungen
</button>
<button
type="button"
className="btn btn-secondary framework-ctrl framework-ctrl--xs"

View File

@ -627,12 +627,11 @@ export default function TrainingFrameworkProgramEditPage() {
),
}))
}}
onRequestExercisePick={({ sectionIndex, itemIndex, multi }) =>
onRequestExercisePick={({ sectionIndex, itemIndex }) =>
setSectionPickerCtx({
slotIdx: si,
sectionIndex,
itemIndex: typeof itemIndex === 'number' ? itemIndex : undefined,
multi: !!multi,
})
}
onPeekExercise={(id, variantId) =>
@ -1108,75 +1107,45 @@ export default function TrainingFrameworkProgramEditPage() {
<ExercisePickerModal
open={sectionPickerCtx != null}
multiSelect={!!sectionPickerCtx?.multi}
multiSelect
onClose={() => setSectionPickerCtx(null)}
onSelectExercises={
sectionPickerCtx?.multi
? async (picked) => {
if (!sectionPickerCtx || !picked?.length) return
const { slotIdx, sectionIndex: sIdx } = sectionPickerCtx
const rows = []
for (const ex of picked) {
const row = await hydrateExercisePlanningRow(ex)
if (row) rows.push(row)
}
if (!rows.length) return
setForm((prev) => ({
...prev,
slots: prev.slots.map((sl, ii) =>
ii !== slotIdx
? sl
: {
...sl,
sections: (
sl.sections && sl.sections.length ? sl.sections : [defaultSection('Ablauf')]
).map((sec, si) =>
si !== sIdx ? sec : { ...sec, items: [...(sec.items || []), ...rows] }
),
}
),
}))
setSectionPickerCtx(null)
}
: undefined
}
onSelectExercise={async (exercise) => {
if (!sectionPickerCtx) return
if (sectionPickerCtx.multi) return
if (typeof sectionPickerCtx.itemIndex !== 'number') return
onSelectExercises={async (picked) => {
if (!sectionPickerCtx || !picked?.length) return
const rows = []
for (const ex of picked) {
const row = await hydrateExercisePlanningRow(ex)
if (row) rows.push(row)
}
if (!rows.length) return
const { slotIdx, sectionIndex: sIdx, itemIndex: iIdx } = sectionPickerCtx
const row = await hydrateExercisePlanningRow(exercise)
if (!row) return
setForm((prev) => ({
...prev,
slots: prev.slots.map((sl, ii) =>
ii !== slotIdx
? sl
: {
...sl,
sections: (sl.sections && sl.sections.length ? sl.sections : [defaultSection('Ablauf')]).map(
(sec, si) =>
si !== sIdx
? sec
: {
...sec,
items: (sec.items || []).map((r2, ji) =>
ji !== iIdx
? r2
: r2.item_type !== 'exercise'
? r2
: {
...r2,
exercise_id: row.exercise_id,
exercise_variant_id: row.exercise_variant_id,
exercise_title: row.exercise_title,
variants: row.variants,
}
),
}
),
slots: prev.slots.map((sl, ii) => {
if (ii !== slotIdx) return sl
const baseSecs = sl.sections && sl.sections.length ? sl.sections : [defaultSection('Ablauf')]
return {
...sl,
sections: baseSecs.map((sec, si) => {
if (si !== sIdx) return sec
const items = [...(sec.items || [])]
if (typeof iIdx === 'number') {
const cur = items[iIdx]
if (!cur || cur.item_type !== 'exercise') return sec
const [first, ...tail] = rows
items[iIdx] = {
...cur,
exercise_id: first.exercise_id,
exercise_variant_id: first.exercise_variant_id,
exercise_title: first.exercise_title,
variants: first.variants,
}
if (tail.length) items.splice(iIdx + 1, 0, ...tail)
return { ...sec, items }
}
),
return { ...sec, items: [...items, ...rows] }
}),
}
}),
}))
setSectionPickerCtx(null)
}}

View File

@ -709,11 +709,10 @@ function TrainingPlanningPage() {
sections: updater(prev.sections),
}))
}
onRequestExercisePick={({ sectionIndex, itemIndex, multi }) => {
onRequestExercisePick={({ sectionIndex, itemIndex }) => {
setExercisePickerTarget({
sIdx: sectionIndex,
iIdx: typeof itemIndex === 'number' ? itemIndex : undefined,
multi: !!multi,
})
setExercisePickerOpen(true)
}}
@ -824,61 +823,41 @@ function TrainingPlanningPage() {
)}
<ExercisePickerModal
open={exercisePickerOpen}
multiSelect={!!exercisePickerTarget?.multi}
multiSelect
onClose={() => {
setExercisePickerOpen(false)
setExercisePickerTarget(null)
}}
onSelectExercises={
exercisePickerTarget?.multi
? async (picked) => {
if (!exercisePickerTarget || !picked?.length) return
const { sIdx } = exercisePickerTarget
const rows = []
for (const ex of picked) {
const row = await hydrateExercisePlanningRow(ex)
if (row) rows.push(row)
}
if (!rows.length) return
setFormData((prev) => ({
...prev,
sections: prev.sections.map((s, si) =>
si !== sIdx ? s : { ...s, items: [...(s.items || []), ...rows] }
),
}))
setExercisePickerOpen(false)
setExercisePickerTarget(null)
}
: undefined
}
onSelectExercise={async (ex) => {
if (!exercisePickerTarget || exercisePickerTarget.multi) return
const row = await hydrateExercisePlanningRow(ex)
if (!row) return
onSelectExercises={async (picked) => {
if (!exercisePickerTarget || !picked?.length) return
const rows = []
for (const ex of picked) {
const row = await hydrateExercisePlanningRow(ex)
if (row) rows.push(row)
}
if (!rows.length) return
const { sIdx, iIdx } = exercisePickerTarget
if (typeof iIdx !== 'number') return
setFormData((prev) => ({
...prev,
sections: prev.sections.map((s, si) =>
si !== sIdx
? s
: {
...s,
items: s.items.map((r2, ii) =>
ii !== iIdx
? r2
: r2.item_type !== 'exercise'
? r2
: {
...r2,
exercise_id: row.exercise_id,
exercise_variant_id: row.exercise_variant_id,
exercise_title: row.exercise_title,
variants: row.variants,
}
),
}
),
sections: prev.sections.map((s, si) => {
if (si !== sIdx) return s
const items = [...(s.items || [])]
if (typeof iIdx === 'number') {
const cur = items[iIdx]
if (!cur || cur.item_type !== 'exercise') return s
const [first, ...tail] = rows
items[iIdx] = {
...cur,
exercise_id: first.exercise_id,
exercise_variant_id: first.exercise_variant_id,
exercise_title: first.exercise_title,
variants: first.variants,
}
if (tail.length) items.splice(iIdx + 1, 0, ...tail)
return { ...s, items }
}
return { ...s, items: [...items, ...rows] }
}),
}))
setExercisePickerOpen(false)
setExercisePickerTarget(null)