feat(exercises): update to version 0.8.100 and enhance combination exercise handling
All checks were successful
Deploy Development / deploy (push) Successful in 38s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 56s
All checks were successful
Deploy Development / deploy (push) Successful in 38s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 56s
- Bumped app version to 0.8.100, reflecting recent updates. - Improved validation logic for combination exercises in the backend, ensuring proper handling of exercise variants. - Enhanced frontend components, including the ExercisePickerModal, to support filtering and displaying combination exercises. - Updated API payloads and utility functions to accommodate new exercise types and their properties. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8a9f9f960f
commit
3dc4c9c79e
|
|
@ -40,12 +40,28 @@ def _optional_positive_int(val, field_name: str) -> Optional[int]:
|
||||||
|
|
||||||
|
|
||||||
def _validate_variant_for_exercise(cur, exercise_id: Optional[int], variant_id: Optional[int]):
|
def _validate_variant_for_exercise(cur, exercise_id: Optional[int], variant_id: Optional[int]):
|
||||||
|
if not exercise_id:
|
||||||
|
if variant_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400, detail="exercise_variant_id nur zusammen mit exercise_id erlaubt"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
cur.execute(
|
||||||
|
"SELECT COALESCE(exercise_kind, 'simple') AS exercise_kind FROM exercises WHERE id = %s",
|
||||||
|
(int(exercise_id),),
|
||||||
|
)
|
||||||
|
ek_row = cur.fetchone()
|
||||||
|
if not ek_row:
|
||||||
|
raise HTTPException(status_code=400, detail="Übung nicht gefunden")
|
||||||
|
if str(r2d(ek_row).get("exercise_kind") or "simple").strip().lower() == "combination":
|
||||||
|
if variant_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Kombinationsübungen haben keine Varianten — bitte exercise_variant_id weglassen",
|
||||||
|
)
|
||||||
|
return
|
||||||
if not variant_id:
|
if not variant_id:
|
||||||
return
|
return
|
||||||
if not exercise_id:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400, detail="exercise_variant_id nur zusammen mit exercise_id erlaubt"
|
|
||||||
)
|
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"SELECT 1 FROM exercise_variants WHERE id = %s AND exercise_id = %s",
|
"SELECT 1 FROM exercise_variants WHERE id = %s AND exercise_id = %s",
|
||||||
(variant_id, exercise_id),
|
(variant_id, exercise_id),
|
||||||
|
|
@ -434,6 +450,7 @@ def _fetch_sections(cur, unit_id: int) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
SELECT tusi.*,
|
SELECT tusi.*,
|
||||||
e.title AS exercise_title,
|
e.title AS exercise_title,
|
||||||
|
e.exercise_kind AS exercise_kind,
|
||||||
e.summary AS exercise_summary,
|
e.summary AS exercise_summary,
|
||||||
(
|
(
|
||||||
SELECT fa.name FROM exercise_focus_areas efa
|
SELECT fa.name FROM exercise_focus_areas efa
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.8.99"
|
APP_VERSION = "0.8.100"
|
||||||
BUILD_DATE = "2026-05-12"
|
BUILD_DATE = "2026-05-12"
|
||||||
DB_SCHEMA_VERSION = "20260512056"
|
DB_SCHEMA_VERSION = "20260512056"
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ MODULE_VERSIONS = {
|
||||||
"exercises": "2.24.0", # Phase 2: Kombinationsübungen exercise_kind/combination_slots + Archetyp/Profil (Migration 056)
|
"exercises": "2.24.0", # Phase 2: Kombinationsübungen exercise_kind/combination_slots + Archetyp/Profil (Migration 056)
|
||||||
"training_units": "0.2.0",
|
"training_units": "0.2.0",
|
||||||
"training_programs": "0.1.0",
|
"training_programs": "0.1.0",
|
||||||
"planning": "0.9.0", # apply-training-module; Trainingsmodule-Bibliothek (Phase 1)
|
"planning": "0.9.1", # Kombinationsübungen: Sektionen PATCH/validator + exercise_kind GET; Frontend KEINE Varianten bei combination
|
||||||
"training_modules": "1.0.0",
|
"training_modules": "1.0.0",
|
||||||
"import_wiki": "1.0.0",
|
"import_wiki": "1.0.0",
|
||||||
"admin": "1.0.0",
|
"admin": "1.0.0",
|
||||||
|
|
@ -35,6 +35,13 @@ MODULE_VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
{
|
||||||
|
"version": "0.8.100",
|
||||||
|
"date": "2026-05-12",
|
||||||
|
"changes": [
|
||||||
|
"Planungs-API/UI: Kombinationsübungen in Trainingsseinheiten (exercise_kind in Sektions-Responses; PATCH verbietet exercise_variant_id für combination); ExercisePicker ohne simple-only Filter, Badge Kombination.",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.8.99",
|
"version": "0.8.99",
|
||||||
"date": "2026-05-12",
|
"date": "2026-05-12",
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ export default function ExercisePickerModal({
|
||||||
multiSelect = false,
|
multiSelect = false,
|
||||||
onSelectExercises = null,
|
onSelectExercises = null,
|
||||||
enableQuickCreateDraft = false,
|
enableQuickCreateDraft = false,
|
||||||
|
/** Wenn gesetzt: z. B. ['simple'] oder ['combination'] — sonst alle Übungsarten */
|
||||||
|
exerciseKindAny = undefined,
|
||||||
}) {
|
}) {
|
||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
const [catalogs, setCatalogs] = useState({
|
const [catalogs, setCatalogs] = useState({
|
||||||
|
|
@ -213,8 +215,14 @@ export default function ExercisePickerModal({
|
||||||
if (filters.include_archived) q.include_archived = true
|
if (filters.include_archived) q.include_archived = true
|
||||||
if (debouncedSearch) q.search = debouncedSearch
|
if (debouncedSearch) q.search = debouncedSearch
|
||||||
if (debouncedAi) q.ai_search = debouncedAi
|
if (debouncedAi) q.ai_search = debouncedAi
|
||||||
|
if (
|
||||||
|
Array.isArray(exerciseKindAny) &&
|
||||||
|
exerciseKindAny.length > 0
|
||||||
|
) {
|
||||||
|
q.exercise_kind_any = exerciseKindAny
|
||||||
|
}
|
||||||
return q
|
return q
|
||||||
}, [filters, debouncedSearch, debouncedAi])
|
}, [filters, debouncedSearch, debouncedAi, exerciseKindAny])
|
||||||
|
|
||||||
const reload = useCallback(async () => {
|
const reload = useCallback(async () => {
|
||||||
if (!open || !catalogsReady) return
|
if (!open || !catalogsReady) return
|
||||||
|
|
@ -225,7 +233,6 @@ export default function ExercisePickerModal({
|
||||||
...queryBase,
|
...queryBase,
|
||||||
include_archived: true,
|
include_archived: true,
|
||||||
include_variants: true,
|
include_variants: true,
|
||||||
exercise_kind_any: ['simple'],
|
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
})
|
})
|
||||||
|
|
@ -254,7 +261,6 @@ export default function ExercisePickerModal({
|
||||||
...queryBase,
|
...queryBase,
|
||||||
include_archived: true,
|
include_archived: true,
|
||||||
include_variants: true,
|
include_variants: true,
|
||||||
exercise_kind_any: ['simple'],
|
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
offset,
|
offset,
|
||||||
})
|
})
|
||||||
|
|
@ -608,6 +614,19 @@ export default function ExercisePickerModal({
|
||||||
{ex.focus_area}
|
{ex.focus_area}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{(ex.exercise_kind || '').toLowerCase().trim() === 'combination' ? (
|
||||||
|
<span
|
||||||
|
className="exercise-tag"
|
||||||
|
style={{
|
||||||
|
marginTop: 6,
|
||||||
|
marginLeft: 6,
|
||||||
|
background: 'var(--accent-soft)',
|
||||||
|
color: 'var(--accent-dark)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Kombination
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
if (multiSelect) {
|
if (multiSelect) {
|
||||||
|
|
|
||||||
|
|
@ -1055,7 +1055,12 @@ export default function ExerciseProgressionGraphPanel({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ExercisePickerModal open={pickerOpen} onClose={() => setPickContext(null)} onSelectExercise={applyPickedExercise} />
|
<ExercisePickerModal
|
||||||
|
open={pickerOpen}
|
||||||
|
onClose={() => setPickContext(null)}
|
||||||
|
onSelectExercise={applyPickedExercise}
|
||||||
|
exerciseKindAny={['simple']}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -837,9 +837,11 @@ export default function TrainingUnitSectionsEditor({
|
||||||
const variantOpts = Array.isArray(it.variants) ? it.variants : []
|
const variantOpts = Array.isArray(it.variants) ? it.variants : []
|
||||||
const exTitle =
|
const exTitle =
|
||||||
it.exercise_title || (it.exercise_id ? `Übung #${it.exercise_id}` : '')
|
it.exercise_title || (it.exercise_id ? `Übung #${it.exercise_id}` : '')
|
||||||
|
const isCombination =
|
||||||
|
String(it.exercise_kind || 'simple').toLowerCase().trim() === 'combination'
|
||||||
const annotPrev = truncatePreview(it.notes || '', 220)
|
const annotPrev = truncatePreview(it.notes || '', 220)
|
||||||
const annotHasText = Boolean((it.notes || '').trim())
|
const annotHasText = Boolean((it.notes || '').trim())
|
||||||
const hasVariants = variantOpts.length > 0 && it.exercise_id
|
const hasVariants = !isCombination && variantOpts.length > 0 && it.exercise_id
|
||||||
const variantIdPeek =
|
const variantIdPeek =
|
||||||
it.exercise_variant_id === '' || it.exercise_variant_id == null
|
it.exercise_variant_id === '' || it.exercise_variant_id == null
|
||||||
? undefined
|
? undefined
|
||||||
|
|
@ -893,6 +895,20 @@ export default function TrainingUnitSectionsEditor({
|
||||||
) : (
|
) : (
|
||||||
<span className="tu-ex-title-placeholder">Keine Übung gewählt</span>
|
<span className="tu-ex-title-placeholder">Keine Übung gewählt</span>
|
||||||
)}
|
)}
|
||||||
|
{isCombination ? (
|
||||||
|
<span
|
||||||
|
className="exercise-tag"
|
||||||
|
style={{
|
||||||
|
marginLeft: 8,
|
||||||
|
fontSize: '11px',
|
||||||
|
alignSelf: 'center',
|
||||||
|
background: 'var(--accent-soft)',
|
||||||
|
color: 'var(--accent-dark)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Kombination
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
{planningCompactLegend && curMn ? (
|
{planningCompactLegend && curMn ? (
|
||||||
<PlanningModuleRowTag moduleId={curMn} title={modBandTitle} />
|
<PlanningModuleRowTag moduleId={curMn} title={modBandTitle} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,9 @@ export function sectionsToPutPayload(unit, durationOverridesByItemId = {}) {
|
||||||
if (eid === '' || eid == null || Number.isNaN(Number(eid))) {
|
if (eid === '' || eid == null || Number.isNaN(Number(eid))) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const vid = it.exercise_variant_id
|
const isCombo =
|
||||||
|
String(it.exercise_kind || 'simple').toLowerCase().trim() === 'combination'
|
||||||
|
const vid = isCombo ? null : it.exercise_variant_id
|
||||||
let actual =
|
let actual =
|
||||||
durationOverridesByItemId[String(it.id)]?.actual_duration_min ??
|
durationOverridesByItemId[String(it.id)]?.actual_duration_min ??
|
||||||
it.actual_duration_min
|
it.actual_duration_min
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export function exerciseRow() {
|
||||||
item_type: 'exercise',
|
item_type: 'exercise',
|
||||||
exercise_id: '',
|
exercise_id: '',
|
||||||
exercise_variant_id: '',
|
exercise_variant_id: '',
|
||||||
|
exercise_kind: 'simple',
|
||||||
exercise_title: '',
|
exercise_title: '',
|
||||||
variants: [],
|
variants: [],
|
||||||
planned_duration_min: '',
|
planned_duration_min: '',
|
||||||
|
|
@ -23,22 +24,31 @@ export function exerciseRow() {
|
||||||
export async function hydrateExercisePlanningRow(exercise) {
|
export async function hydrateExercisePlanningRow(exercise) {
|
||||||
let variants = Array.isArray(exercise?.variants) ? exercise.variants : []
|
let variants = Array.isArray(exercise?.variants) ? exercise.variants : []
|
||||||
let title = exercise?.title || ''
|
let title = exercise?.title || ''
|
||||||
|
let exerciseKind = exercise?.exercise_kind
|
||||||
const id = exercise?.id
|
const id = exercise?.id
|
||||||
if (!id) return null
|
if (!id) return null
|
||||||
let meta = {}
|
let meta = {}
|
||||||
if (!variants.length) {
|
|
||||||
|
async function fetchFull() {
|
||||||
try {
|
try {
|
||||||
const full = await api.getExercise(id)
|
return await api.getExercise(id)
|
||||||
variants = Array.isArray(full?.variants) ? full.variants : []
|
|
||||||
title = full?.title || title
|
|
||||||
meta = {
|
|
||||||
exercise_visibility: full?.visibility || 'private',
|
|
||||||
exercise_club_id: full?.club_id ?? null,
|
|
||||||
exercise_created_by: full?.created_by ?? null,
|
|
||||||
exercise_status: full?.status || 'draft',
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
variants = []
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!variants.length) {
|
||||||
|
const full = await fetchFull()
|
||||||
|
if (full) {
|
||||||
|
variants = Array.isArray(full.variants) ? full.variants : []
|
||||||
|
title = full.title || title
|
||||||
|
if (exerciseKind == null) exerciseKind = full.exercise_kind
|
||||||
|
meta = {
|
||||||
|
exercise_visibility: full.visibility || 'private',
|
||||||
|
exercise_club_id: full.club_id ?? null,
|
||||||
|
exercise_created_by: full.created_by ?? null,
|
||||||
|
exercise_status: full.status || 'draft',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
meta = {
|
meta = {
|
||||||
|
|
@ -47,25 +57,37 @@ export async function hydrateExercisePlanningRow(exercise) {
|
||||||
exercise_created_by: exercise?.created_by ?? null,
|
exercise_created_by: exercise?.created_by ?? null,
|
||||||
exercise_status: exercise?.status ?? null,
|
exercise_status: exercise?.status ?? null,
|
||||||
}
|
}
|
||||||
if (meta.exercise_visibility == null || meta.exercise_created_by == null) {
|
if (
|
||||||
try {
|
meta.exercise_visibility == null ||
|
||||||
const full = await api.getExercise(id)
|
meta.exercise_created_by == null ||
|
||||||
if (meta.exercise_visibility == null) meta.exercise_visibility = full?.visibility || 'private'
|
exerciseKind == null
|
||||||
if (meta.exercise_club_id == null) meta.exercise_club_id = full?.club_id ?? null
|
) {
|
||||||
if (meta.exercise_created_by == null) meta.exercise_created_by = full?.created_by ?? null
|
const full = await fetchFull()
|
||||||
if (meta.exercise_status == null) meta.exercise_status = full?.status || 'draft'
|
if (full) {
|
||||||
} catch {
|
if (meta.exercise_visibility == null) meta.exercise_visibility = full.visibility || 'private'
|
||||||
/* keep partial meta */
|
if (meta.exercise_club_id == null) meta.exercise_club_id = full.club_id ?? null
|
||||||
|
if (meta.exercise_created_by == null) meta.exercise_created_by = full.created_by ?? null
|
||||||
|
if (meta.exercise_status == null) meta.exercise_status = full.status || 'draft'
|
||||||
|
if (exerciseKind == null) exerciseKind = full.exercise_kind
|
||||||
|
if (!variants.length) variants = Array.isArray(full.variants) ? full.variants : []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
meta.exercise_visibility = meta.exercise_visibility || 'private'
|
meta.exercise_visibility = meta.exercise_visibility || 'private'
|
||||||
meta.exercise_status = meta.exercise_status || 'draft'
|
meta.exercise_status = meta.exercise_status || 'draft'
|
||||||
}
|
}
|
||||||
|
|
||||||
const row = exerciseRow()
|
const row = exerciseRow()
|
||||||
row.exercise_id = id
|
row.exercise_id = id
|
||||||
row.exercise_variant_id = ''
|
row.exercise_variant_id = ''
|
||||||
row.exercise_title = title
|
row.exercise_title = title
|
||||||
row.variants = variants
|
row.exercise_kind =
|
||||||
|
String(exerciseKind || 'simple').toLowerCase().trim() === 'combination' ? 'combination' : 'simple'
|
||||||
|
if (row.exercise_kind === 'combination') {
|
||||||
|
row.variants = []
|
||||||
|
row.exercise_variant_id = ''
|
||||||
|
} else {
|
||||||
|
row.variants = variants
|
||||||
|
}
|
||||||
Object.assign(row, meta)
|
Object.assign(row, meta)
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
@ -106,10 +128,13 @@ export function normalizeUnitToForm(fullUnit) {
|
||||||
return rowNote
|
return rowNote
|
||||||
}
|
}
|
||||||
const smEx = parseOptionalSourceTrainingModuleIdForPayload(it.source_training_module_id)
|
const smEx = parseOptionalSourceTrainingModuleIdForPayload(it.source_training_module_id)
|
||||||
|
const ek = String(it.exercise_kind || 'simple').toLowerCase().trim()
|
||||||
|
const isCombo = ek === 'combination'
|
||||||
return {
|
return {
|
||||||
item_type: 'exercise',
|
item_type: 'exercise',
|
||||||
exercise_id: it.exercise_id,
|
exercise_id: it.exercise_id,
|
||||||
exercise_variant_id: it.exercise_variant_id ?? '',
|
exercise_kind: isCombo ? 'combination' : 'simple',
|
||||||
|
exercise_variant_id: isCombo ? '' : it.exercise_variant_id ?? '',
|
||||||
exercise_title: it.exercise_title || '',
|
exercise_title: it.exercise_title || '',
|
||||||
variants: [],
|
variants: [],
|
||||||
planned_duration_min:
|
planned_duration_min:
|
||||||
|
|
@ -141,23 +166,28 @@ export function normalizeUnitToForm(fullUnit) {
|
||||||
{
|
{
|
||||||
title: 'Übungen',
|
title: 'Übungen',
|
||||||
guidance_notes: '',
|
guidance_notes: '',
|
||||||
items: fullUnit.exercises.map((ex) => ({
|
items: fullUnit.exercises.map((ex) => {
|
||||||
item_type: 'exercise',
|
const ek = String(ex.exercise_kind || 'simple').toLowerCase().trim()
|
||||||
exercise_id: ex.exercise_id,
|
const isCombo = ek === 'combination'
|
||||||
exercise_variant_id: ex.exercise_variant_id ?? '',
|
return {
|
||||||
exercise_title: ex.exercise_title || '',
|
item_type: 'exercise',
|
||||||
variants: [],
|
exercise_kind: ek,
|
||||||
planned_duration_min:
|
exercise_id: ex.exercise_id,
|
||||||
ex.planned_duration_min !== null && ex.planned_duration_min !== undefined
|
exercise_variant_id: isCombo ? '' : (ex.exercise_variant_id ?? ''),
|
||||||
? String(ex.planned_duration_min)
|
exercise_title: ex.exercise_title || '',
|
||||||
: '',
|
variants: [],
|
||||||
actual_duration_min:
|
planned_duration_min:
|
||||||
ex.actual_duration_min !== null && ex.actual_duration_min !== undefined
|
ex.planned_duration_min !== null && ex.planned_duration_min !== undefined
|
||||||
? String(ex.actual_duration_min)
|
? String(ex.planned_duration_min)
|
||||||
: '',
|
: '',
|
||||||
notes: ex.notes ?? '',
|
actual_duration_min:
|
||||||
modifications: ex.modifications ?? '',
|
ex.actual_duration_min !== null && ex.actual_duration_min !== undefined
|
||||||
})),
|
? String(ex.actual_duration_min)
|
||||||
|
: '',
|
||||||
|
notes: ex.notes ?? '',
|
||||||
|
modifications: ex.modifications ?? '',
|
||||||
|
}
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -181,6 +211,7 @@ export async function enrichSectionsWithVariants(sections) {
|
||||||
const ex = await api.getExercise(id)
|
const ex = await api.getExercise(id)
|
||||||
cache.set(id, {
|
cache.set(id, {
|
||||||
title: ex.title || '',
|
title: ex.title || '',
|
||||||
|
exercise_kind: String(ex.exercise_kind || 'simple').toLowerCase().trim(),
|
||||||
variants: Array.isArray(ex.variants) ? ex.variants : [],
|
variants: Array.isArray(ex.variants) ? ex.variants : [],
|
||||||
visibility: ex.visibility || 'private',
|
visibility: ex.visibility || 'private',
|
||||||
club_id: ex.club_id ?? null,
|
club_id: ex.club_id ?? null,
|
||||||
|
|
@ -190,6 +221,7 @@ export async function enrichSectionsWithVariants(sections) {
|
||||||
} catch {
|
} catch {
|
||||||
cache.set(id, {
|
cache.set(id, {
|
||||||
title: '',
|
title: '',
|
||||||
|
exercise_kind: 'simple',
|
||||||
variants: [],
|
variants: [],
|
||||||
visibility: 'private',
|
visibility: 'private',
|
||||||
club_id: null,
|
club_id: null,
|
||||||
|
|
@ -206,11 +238,15 @@ export async function enrichSectionsWithVariants(sections) {
|
||||||
if (!it.exercise_id) return it
|
if (!it.exercise_id) return it
|
||||||
const c = cache.get(it.exercise_id)
|
const c = cache.get(it.exercise_id)
|
||||||
if (!c) return it
|
if (!c) return it
|
||||||
|
const ek = String(c.exercise_kind || 'simple').toLowerCase().trim()
|
||||||
|
const isCombo = ek === 'combination'
|
||||||
return {
|
return {
|
||||||
...it,
|
...it,
|
||||||
|
exercise_kind: isCombo ? 'combination' : 'simple',
|
||||||
exercise_title: it.exercise_title || c.title,
|
exercise_title: it.exercise_title || c.title,
|
||||||
|
exercise_variant_id: isCombo ? '' : it.exercise_variant_id,
|
||||||
variants:
|
variants:
|
||||||
Array.isArray(it.variants) && it.variants.length > 0 ? it.variants : c.variants,
|
isCombo ? [] : Array.isArray(it.variants) && it.variants.length > 0 ? it.variants : c.variants,
|
||||||
exercise_visibility: c.visibility,
|
exercise_visibility: c.visibility,
|
||||||
exercise_club_id: c.club_id,
|
exercise_club_id: c.club_id,
|
||||||
exercise_created_by: c.created_by,
|
exercise_created_by: c.created_by,
|
||||||
|
|
@ -246,7 +282,8 @@ export function buildSectionsPayload(sections) {
|
||||||
if (it.exercise_id === '' || it.exercise_id == null || Number.isNaN(Number(it.exercise_id))) {
|
if (it.exercise_id === '' || it.exercise_id == null || Number.isNaN(Number(it.exercise_id))) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const vid = it.exercise_variant_id
|
const isCombo = String(it.exercise_kind || 'simple').toLowerCase().trim() === 'combination'
|
||||||
|
const vid = isCombo ? null : it.exercise_variant_id
|
||||||
const smEx = parseOptionalSourceTrainingModuleIdForPayload(it.source_training_module_id)
|
const smEx = parseOptionalSourceTrainingModuleIdForPayload(it.source_training_module_id)
|
||||||
const rowEx = {
|
const rowEx = {
|
||||||
item_type: 'exercise',
|
item_type: 'exercise',
|
||||||
|
|
@ -320,7 +357,12 @@ export async function insertTrainingModuleIntoPlanningSections({
|
||||||
if (!hydrated) continue
|
if (!hydrated) continue
|
||||||
hydrated.source_training_module_id = midNum
|
hydrated.source_training_module_id = midNum
|
||||||
hydrated.source_module_title = modTitle
|
hydrated.source_module_title = modTitle
|
||||||
if (mi.exercise_variant_id) hydrated.exercise_variant_id = String(mi.exercise_variant_id)
|
if (
|
||||||
|
hydrated.exercise_kind !== 'combination' &&
|
||||||
|
mi.exercise_variant_id
|
||||||
|
) {
|
||||||
|
hydrated.exercise_variant_id = String(mi.exercise_variant_id)
|
||||||
|
}
|
||||||
hydrated.planned_duration_min =
|
hydrated.planned_duration_min =
|
||||||
mi.planned_duration_min !== null && mi.planned_duration_min !== undefined
|
mi.planned_duration_min !== null && mi.planned_duration_min !== undefined
|
||||||
? String(mi.planned_duration_min)
|
? String(mi.planned_duration_min)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user