All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 41s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m18s
- Introduced new CSS styles for exercise cards and selection sections, improving visual feedback for selected exercises. - Updated ExerciseListCard to support a new `selectionPinned` prop, allowing for a badge display on selected exercises. - Refactored selection handling in ExercisesListPageRoot to manage selected entries more effectively, replacing the previous Set-based approach. - Enhanced SaveSelectedExercisesAsModuleModal to support appending exercises to existing modules, improving module management capabilities. - Updated session state handling to include selected entries, ensuring persistence across sessions.
91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
/** Minimaler Snapshot einer Übung für die modulübergreifende Auswahl (filterunabhängig). */
|
|
export function snapshotExerciseForSelection(exercise) {
|
|
if (!exercise || exercise.id == null) return null
|
|
const id = Number(exercise.id)
|
|
if (!Number.isFinite(id) || id < 1) return null
|
|
return {
|
|
id,
|
|
title: exercise.title || '',
|
|
summary: exercise.summary || '',
|
|
visibility: exercise.visibility,
|
|
status: exercise.status,
|
|
exercise_kind: exercise.exercise_kind,
|
|
created_by: exercise.created_by,
|
|
focus_area: exercise.focus_area,
|
|
focus_area_names: exercise.focus_area_names,
|
|
style_direction_names: exercise.style_direction_names,
|
|
training_type_names: exercise.training_type_names,
|
|
media_count: exercise.media_count,
|
|
variant_count: exercise.variant_count,
|
|
media: Array.isArray(exercise.media) ? exercise.media : [],
|
|
}
|
|
}
|
|
|
|
export function normalizeSelectedEntries(raw) {
|
|
if (!Array.isArray(raw)) return []
|
|
const out = []
|
|
const seen = new Set()
|
|
for (const item of raw) {
|
|
const snap = snapshotExerciseForSelection(item)
|
|
if (!snap || seen.has(snap.id)) continue
|
|
seen.add(snap.id)
|
|
out.push(snap)
|
|
}
|
|
return out
|
|
}
|
|
|
|
export function mergeSelectedWithListEntries(selectedEntries, exercises) {
|
|
const byId = new Map()
|
|
for (const e of exercises || []) {
|
|
const id = Number(e?.id)
|
|
if (Number.isFinite(id) && id > 0) byId.set(id, e)
|
|
}
|
|
return (selectedEntries || []).map((entry) => byId.get(Number(entry.id)) || entry)
|
|
}
|
|
|
|
export function moduleItemToPayload(row, orderIndex) {
|
|
if ((row?.item_type || 'exercise') === 'note') {
|
|
return {
|
|
item_type: 'note',
|
|
order_index: orderIndex,
|
|
note_body: row.note_body ?? '',
|
|
}
|
|
}
|
|
const eid = Number(row.exercise_id)
|
|
if (!Number.isFinite(eid) || eid < 1) return null
|
|
const vidRaw = row.exercise_variant_id
|
|
const vid =
|
|
vidRaw === '' || vidRaw == null || row.exercise_kind === 'combination'
|
|
? null
|
|
: Number(vidRaw)
|
|
return {
|
|
item_type: 'exercise',
|
|
order_index: orderIndex,
|
|
exercise_id: eid,
|
|
exercise_variant_id: Number.isFinite(vid) && vid > 0 ? vid : null,
|
|
planned_duration_min:
|
|
row.planned_duration_min !== '' && row.planned_duration_min != null
|
|
? Number(row.planned_duration_min)
|
|
: null,
|
|
notes: row.notes != null && String(row.notes).trim() ? String(row.notes).trim() : null,
|
|
}
|
|
}
|
|
|
|
export function buildRowsPayload(rows) {
|
|
return rows
|
|
.map((row, idx) =>
|
|
moduleItemToPayload(
|
|
{
|
|
...row,
|
|
exercise_id: row.exercise_id,
|
|
exercise_variant_id: row.exercise_variant_id,
|
|
planned_duration_min: row.planned_duration_min,
|
|
notes: row.notes,
|
|
exercise_kind: row.exercise_kind,
|
|
},
|
|
idx
|
|
)
|
|
)
|
|
.filter(Boolean)
|
|
}
|