Parlellsession- Plan #35
|
|
@ -8,9 +8,9 @@ import api from '../utils/api'
|
||||||
import ExercisePeekModal from '../components/ExercisePeekModal'
|
import ExercisePeekModal from '../components/ExercisePeekModal'
|
||||||
import CombinationPlanBracket from '../components/CombinationPlanBracket'
|
import CombinationPlanBracket from '../components/CombinationPlanBracket'
|
||||||
import {
|
import {
|
||||||
buildPlanRunViewModel,
|
buildPlanRunViewModelFromSections,
|
||||||
itemStableKey,
|
itemStableKey,
|
||||||
sortedSections,
|
sectionsWithPlanLocForDisplay,
|
||||||
sortedItems,
|
sortedItems,
|
||||||
} from '../utils/trainingPlanUtils'
|
} from '../utils/trainingPlanUtils'
|
||||||
import { effectiveComboMethodProfile } from '../utils/comboPlanningMethodProfile'
|
import { effectiveComboMethodProfile } from '../utils/comboPlanningMethodProfile'
|
||||||
|
|
@ -115,8 +115,8 @@ export default function TrainingUnitRunPage() {
|
||||||
}
|
}
|
||||||
}, [idNum, persistChecked])
|
}, [idNum, persistChecked])
|
||||||
|
|
||||||
const sections = useMemo(() => sortedSections(unit), [unit])
|
const sections = useMemo(() => sectionsWithPlanLocForDisplay(unit), [unit])
|
||||||
const planModel = useMemo(() => buildPlanRunViewModel(unit), [unit])
|
const planModel = useMemo(() => buildPlanRunViewModelFromSections(sections), [sections])
|
||||||
|
|
||||||
const printStreamOptions = useMemo(() => {
|
const printStreamOptions = useMemo(() => {
|
||||||
const opts = []
|
const opts = []
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
cloneJsonSerializablePlanningProfile,
|
cloneJsonSerializablePlanningProfile,
|
||||||
|
inheritPlanLocForPhasedSave,
|
||||||
phaseRunsFromSections,
|
phaseRunsFromSections,
|
||||||
sectionIndicesForParallelStream,
|
sectionIndicesForParallelStream,
|
||||||
streamsForParallelPhaseOrders,
|
streamsForParallelPhaseOrders,
|
||||||
|
|
@ -37,11 +38,79 @@ export function sumExerciseMinutesInSection(sec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lesemodell für Plan & Ablauf / Druck / Coach: Phasenläufe mit Ganzgruppe vs. Split.
|
* GET liefert `planLoc` oft nicht auf flachen `sections`, aber `unit.phases` (verschachtelt).
|
||||||
* Legacy ohne planLoc: ein Block.
|
* Baut pro Abschnitt `planLoc` für phaseRuns / Darstellung (camelCase wie im Editor).
|
||||||
*/
|
*/
|
||||||
export function buildPlanRunViewModel(unit) {
|
function planLocBySectionIdFromPhases(phases) {
|
||||||
const sections = sortedSections(unit)
|
const byId = new Map()
|
||||||
|
if (!Array.isArray(phases)) return byId
|
||||||
|
for (const ph of phases) {
|
||||||
|
const po = Number(ph.order_index ?? ph.orderIndex ?? 0) || 0
|
||||||
|
const pk = String(ph.phase_kind ?? ph.phaseKind ?? '')
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
const phaseTitle = ph.title ?? ph.phaseTitle ?? null
|
||||||
|
const phaseGuidanceNotes = ph.guidance_notes ?? ph.guidanceNotes ?? null
|
||||||
|
if (pk === 'whole_group') {
|
||||||
|
for (const sec of ph.sections || []) {
|
||||||
|
const sid = sec.id != null ? Number(sec.id) : NaN
|
||||||
|
if (!Number.isFinite(sid)) continue
|
||||||
|
byId.set(sid, {
|
||||||
|
phaseKind: 'whole_group',
|
||||||
|
phaseOrderIndex: po,
|
||||||
|
parallelStreamOrderIndex: null,
|
||||||
|
phaseTitle,
|
||||||
|
phaseGuidanceNotes,
|
||||||
|
streamTitle: null,
|
||||||
|
streamNotes: null,
|
||||||
|
streamAssignedTrainerProfileIds: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (pk === 'parallel') {
|
||||||
|
for (const st of ph.streams || []) {
|
||||||
|
const so = Number(st.order_index ?? st.orderIndex ?? 0) || 0
|
||||||
|
const streamTitle = st.title ?? st.streamTitle ?? null
|
||||||
|
const streamNotes = st.notes ?? st.streamNotes ?? null
|
||||||
|
const streamAssignedTrainerProfileIds =
|
||||||
|
st.assigned_trainer_profile_ids ?? st.streamAssignedTrainerProfileIds ?? null
|
||||||
|
for (const sec of st.sections || []) {
|
||||||
|
const sid = sec.id != null ? Number(sec.id) : NaN
|
||||||
|
if (!Number.isFinite(sid)) continue
|
||||||
|
byId.set(sid, {
|
||||||
|
phaseKind: 'parallel',
|
||||||
|
phaseOrderIndex: po,
|
||||||
|
parallelStreamOrderIndex: so,
|
||||||
|
phaseTitle,
|
||||||
|
phaseGuidanceNotes,
|
||||||
|
streamTitle,
|
||||||
|
streamNotes,
|
||||||
|
streamAssignedTrainerProfileIds,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return byId
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sectionsWithPlanLocForDisplay(unit) {
|
||||||
|
const sorted = sortedSections(unit)
|
||||||
|
const byId = planLocBySectionIdFromPhases(unit?.phases)
|
||||||
|
const merged = sorted.map((s) => {
|
||||||
|
const sid = s.id != null ? Number(s.id) : NaN
|
||||||
|
if (Number.isFinite(sid) && byId.has(sid)) {
|
||||||
|
return { ...s, planLoc: { ...byId.get(sid) } }
|
||||||
|
}
|
||||||
|
if (s.planLoc && s.planLoc.phaseKind) return s
|
||||||
|
return { ...s }
|
||||||
|
})
|
||||||
|
return inheritPlanLocForPhasedSave(merged)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Läuft auf bereits angereichter Abschnittsliste (gleiche Objektreferenzen wie in Slices).
|
||||||
|
*/
|
||||||
|
export function buildPlanRunViewModelFromSections(sections) {
|
||||||
if (!sections.length) {
|
if (!sections.length) {
|
||||||
return { mode: 'empty', runs: [], totalMin: 0, runCumulativeEnds: [] }
|
return { mode: 'empty', runs: [], totalMin: 0, runCumulativeEnds: [] }
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +157,9 @@ export function buildPlanRunViewModel(unit) {
|
||||||
const streamOrders = streamsForParallelPhaseOrders(slice, po)
|
const streamOrders = streamsForParallelPhaseOrders(slice, po)
|
||||||
const streams = streamOrders.map((so) => {
|
const streams = streamOrders.map((so) => {
|
||||||
const idxs = sectionIndicesForParallelStream(slice, po, so)
|
const idxs = sectionIndicesForParallelStream(slice, po, so)
|
||||||
const streamSecs = idxs.map((i) => slice[i])
|
const streamSecs = idxs
|
||||||
|
.map((i) => slice[i])
|
||||||
|
.sort((a, b) => (a.order_index ?? 0) - (b.order_index ?? 0))
|
||||||
const first = streamSecs[0]
|
const first = streamSecs[0]
|
||||||
const streamTitle = first?.planLoc?.streamTitle ?? null
|
const streamTitle = first?.planLoc?.streamTitle ?? null
|
||||||
const minutes = streamSecs.reduce((s, sec) => s + sumExerciseMinutesInSection(sec), 0)
|
const minutes = streamSecs.reduce((s, sec) => s + sumExerciseMinutesInSection(sec), 0)
|
||||||
|
|
@ -119,6 +190,11 @@ export function buildPlanRunViewModel(unit) {
|
||||||
return { mode: 'phased', runs, totalMin: cum, runCumulativeEnds }
|
return { mode: 'phased', runs, totalMin: cum, runCumulativeEnds }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {object} unit Trainingseinheit inkl. `sections`, optional `phases` (GET) */
|
||||||
|
export function buildPlanRunViewModel(unit) {
|
||||||
|
return buildPlanRunViewModelFromSections(sectionsWithPlanLocForDisplay(unit))
|
||||||
|
}
|
||||||
|
|
||||||
function coachContextLabelForSection(sec, sectionsList) {
|
function coachContextLabelForSection(sec, sectionsList) {
|
||||||
const pl = sec?.planLoc
|
const pl = sec?.planLoc
|
||||||
if (!pl?.phaseKind) return 'Ablauf'
|
if (!pl?.phaseKind) return 'Ablauf'
|
||||||
|
|
@ -134,10 +210,10 @@ function coachContextLabelForSection(sec, sectionsList) {
|
||||||
|
|
||||||
/** Flache Reihenfolge für Coach-Timeline (global wie im Editor, inkl. gemischter Split-Abschnitte). */
|
/** Flache Reihenfolge für Coach-Timeline (global wie im Editor, inkl. gemischter Split-Abschnitte). */
|
||||||
export function flattenPlanTimeline(unit) {
|
export function flattenPlanTimeline(unit) {
|
||||||
const model = buildPlanRunViewModel(unit)
|
const sections = sectionsWithPlanLocForDisplay(unit)
|
||||||
|
const model = buildPlanRunViewModelFromSections(sections)
|
||||||
if (model.mode === 'empty') return []
|
if (model.mode === 'empty') return []
|
||||||
|
|
||||||
const sections = sortedSections(unit)
|
|
||||||
const list = []
|
const list = []
|
||||||
|
|
||||||
const pushSectionItems = (sec, coachCtx) => {
|
const pushSectionItems = (sec, coachCtx) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user