feat(combo-planning): replace summarizeSlotProfileBrief with effectiveStationTimingSummary
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 57s
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 57s
- Updated CombinationCoachSlots, CombinationPlanBracket, and TrainingUnitSectionsEditor components to utilize effectiveStationTimingSummary for improved timing display. - Adjusted station title handling to enhance clarity and consistency across components. - Refactored utility functions to streamline slot timing summaries and improve overall user experience in combination planning.
This commit is contained in:
parent
ed15f73727
commit
79dabbca5a
|
|
@ -10,7 +10,7 @@ import {
|
|||
combinationArchetypeLabel,
|
||||
sortCombinationSlotsForDisplay,
|
||||
} from '../constants/combinationArchetypes'
|
||||
import { readSlotProfilesV1, summarizeSlotProfileBrief } from '../utils/combinationMethodProfileUi'
|
||||
import { effectiveStationTimingSummary, readSlotProfilesV1 } from '../utils/combinationMethodProfileUi'
|
||||
|
||||
export default function CombinationCoachSlots({
|
||||
combinationSlots,
|
||||
|
|
@ -190,10 +190,10 @@ export default function CombinationCoachSlots({
|
|||
const slotTitle =
|
||||
(slot.title && String(slot.title).trim()) ||
|
||||
(candIds.length <= 1 && slot.candidates?.[0]?.title) ||
|
||||
`Station ${slot.slot_index != null ? Number(slot.slot_index) + 1 : si + 1}`
|
||||
`Station ${si + 1}`
|
||||
|
||||
const ix = slot.slot_index != null ? Number(slot.slot_index) : si
|
||||
const timingSummary = summarizeSlotProfileBrief(slotTimingByIx.get(ix))
|
||||
const timingSummary = effectiveStationTimingSummary(archeKey, methodProfile || {}, slotTimingByIx.get(ix))
|
||||
|
||||
return (
|
||||
<li key={`${slot.slot_index ?? si}-${slotTitle}`} style={{ lineHeight: 1.45 }}>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
combinationArchetypeLabel,
|
||||
sortCombinationSlotsForDisplay,
|
||||
} from '../constants/combinationArchetypes'
|
||||
import { describeGlobalComboProfile, readSlotProfilesV1, summarizeSlotProfileBrief } from '../utils/combinationMethodProfileUi'
|
||||
import { describeGlobalComboProfile, effectiveStationTimingSummary, readSlotProfilesV1 } from '../utils/combinationMethodProfileUi'
|
||||
|
||||
function candidateLine(slot) {
|
||||
const cands = slot.candidates
|
||||
|
|
@ -95,14 +95,18 @@ export default function CombinationPlanBracket({
|
|||
const ixParsed =
|
||||
siRaw === '' || siRaw == null ? si : typeof siRaw === 'number' ? siRaw : parseInt(String(siRaw), 10)
|
||||
const stationIx = Number.isFinite(ixParsed) ? ixParsed : si
|
||||
const stationTitle = ((slot.title || '').trim() || `Station ${stationIx}`).trim()
|
||||
const displayStep = si + 1
|
||||
const stationTitle = ((slot.title || '').trim() || `Station ${displayStep}`).trim()
|
||||
const names = candidateLine(slot)
|
||||
const timing = summarizeSlotProfileBrief(timingByIx.get(stationIx))
|
||||
const timing = effectiveStationTimingSummary(arch, methodProfile || {}, timingByIx.get(stationIx))
|
||||
|
||||
return (
|
||||
<li key={`slot-${stationIx}-${si}`} className="combo-plan-bracket__station">
|
||||
<div className="combo-plan-bracket__station-index" title={`slot_index ${stationIx}`}>
|
||||
S{stationIx}
|
||||
<div
|
||||
className="combo-plan-bracket__station-index"
|
||||
title={`Technischer Slot-Index (slot_index): ${stationIx}`}
|
||||
>
|
||||
S{displayStep}
|
||||
</div>
|
||||
<div className="combo-plan-bracket__station-main">
|
||||
<div className="combo-plan-bracket__station-title">{stationTitle}</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {
|
|||
sectionPlannedMinutes,
|
||||
} from '../utils/trainingUnitSectionsForm'
|
||||
import api from '../utils/api'
|
||||
import { readSlotProfilesV1, summarizeSlotProfileBrief } from '../utils/combinationMethodProfileUi'
|
||||
import { effectiveStationTimingSummary, readSlotProfilesV1 } from '../utils/combinationMethodProfileUi'
|
||||
import { isCompactTagLegendMode } from '../config/planningModuleUx'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
||||
|
|
@ -109,6 +109,7 @@ function comboPlanningStripBulletTexts(it) {
|
|||
const slots = sortCombinationSlotsForDisplay(it.combination_slots || [])
|
||||
if (!slots.length) return []
|
||||
const mp = effectiveComboMethodProfile(it.catalog_method_profile || {}, it.planning_method_profile)
|
||||
const archRaw = String(it.catalog_method_archetype || '').trim()
|
||||
const byIx = new Map(readSlotProfilesV1(mp).map((r) => [Number(r.slot_index), r]))
|
||||
const titles = it.combo_member_title_by_id || {}
|
||||
return slots.map((slot, idx) => {
|
||||
|
|
@ -116,7 +117,7 @@ function comboPlanningStripBulletTexts(it) {
|
|||
const siParsed =
|
||||
siRaw === '' || siRaw == null ? idx : typeof siRaw === 'number' ? siRaw : parseInt(String(siRaw), 10)
|
||||
const ix = Number.isFinite(siParsed) ? siParsed : idx
|
||||
const stationLbl = ((slot.title || '').trim() || `Station ${ix}`)
|
||||
const stationLbl = ((slot.title || '').trim() || `Station ${idx + 1}`)
|
||||
const candIds = (slot.candidate_exercise_ids || [])
|
||||
.map((raw) => (typeof raw === 'number' ? raw : parseInt(String(raw), 10)))
|
||||
.filter((n) => Number.isFinite(n))
|
||||
|
|
@ -124,7 +125,7 @@ function comboPlanningStripBulletTexts(it) {
|
|||
candIds.length === 0
|
||||
? '(keine Übung)'
|
||||
: candIds.map((id) => titles[String(id)] || `Übung ${id}`).join(' ↔ ')
|
||||
const timing = summarizeSlotProfileBrief(byIx.get(ix))
|
||||
const timing = effectiveStationTimingSummary(archRaw, mp, byIx.get(ix))
|
||||
let line = `${stationLbl}: ${namesJoined}`
|
||||
if (timing) line += ` · ${timing}`
|
||||
return line
|
||||
|
|
|
|||
|
|
@ -322,6 +322,82 @@ export function summarizeSlotProfileBrief(r) {
|
|||
return bits.join(' · ')
|
||||
}
|
||||
|
||||
function globalTimingHintsForArchetype(arch, mp) {
|
||||
if (!mp || typeof mp !== 'object' || Array.isArray(mp)) return []
|
||||
const bits = []
|
||||
switch (arch) {
|
||||
case 'circuit_rotate_time':
|
||||
if (mp.work_seconds != null && mp.work_seconds !== '') bits.push(`${mp.work_seconds}s Arbeit je Station`)
|
||||
if (mp.transition_seconds != null && mp.transition_seconds !== '')
|
||||
bits.push(`Rotation ${mp.transition_seconds}s`)
|
||||
if (mp.rest_seconds != null && mp.rest_seconds !== '') bits.push(`Pause ${mp.rest_seconds}s`)
|
||||
if (mp.rounds != null && mp.rounds !== '') bits.push(`${mp.rounds} Umlauf‑Runden`)
|
||||
break
|
||||
case 'sequence_linear':
|
||||
if (mp.hint_step_duration_sec != null && mp.hint_step_duration_sec !== '')
|
||||
bits.push(`~${mp.hint_step_duration_sec}s je Station`)
|
||||
if (mp.rounds != null && mp.rounds !== '') bits.push(`${mp.rounds} Sequenz‑Durchläufe`)
|
||||
if (mp.block_intro_sec != null && mp.block_intro_sec !== '') bits.push(`Block‑Intro ${mp.block_intro_sec}s`)
|
||||
break
|
||||
case 'time_domain_interval':
|
||||
if (mp.work_seconds != null && mp.work_seconds !== '') bits.push(`${mp.work_seconds}s Intervall‑Arbeit`)
|
||||
if (mp.rest_seconds != null && mp.rest_seconds !== '') bits.push(`${mp.rest_seconds}s Erholung`)
|
||||
if (mp.interval_rounds != null && mp.interval_rounds !== '') bits.push(`${mp.interval_rounds} Intervall‑Zyklen`)
|
||||
break
|
||||
case 'pair_superset':
|
||||
if (mp.work_seconds_per_side != null && mp.work_seconds_per_side !== '')
|
||||
bits.push(`${mp.work_seconds_per_side}s Arbeit`)
|
||||
if (mp.switch_seconds != null && mp.switch_seconds !== '') bits.push(`Wechsel ${mp.switch_seconds}s`)
|
||||
break
|
||||
case 'station_parcour':
|
||||
if (mp.rounds != null && mp.rounds !== '') bits.push(`${mp.rounds} Parcours‑Runden`)
|
||||
break
|
||||
case 'circuit_all_parallel':
|
||||
if (mp.rounds != null && mp.rounds !== '') bits.push(`${mp.rounds} Runden`)
|
||||
if (mp.explain_before_seconds != null && mp.explain_before_seconds !== '')
|
||||
bits.push(`Erklärung ${mp.explain_before_seconds}s`)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return bits
|
||||
}
|
||||
|
||||
function isWeakSlotTimingSummary(txt) {
|
||||
if (!txt || typeof txt !== 'string') return true
|
||||
const t = txt.trim()
|
||||
return t === 'Zeit' || t === 'Coach' || t === 'Ziel‑Wdh.'
|
||||
}
|
||||
|
||||
/**
|
||||
* Stationszeile für Lesetext: Slot‑Zeiten + bei Bedarf globale Eckdaten (Zirkel‑Sekunden, Runden …).
|
||||
*/
|
||||
export function effectiveStationTimingSummary(archetypeKey, profileObj, slotRow) {
|
||||
const arch = typeof archetypeKey === 'string' ? archetypeKey.trim() : ''
|
||||
const mp = profileObj && typeof profileObj === 'object' && !Array.isArray(profileObj) ? profileObj : {}
|
||||
const slotTxt = summarizeSlotProfileBrief(slotRow)
|
||||
const hints = globalTimingHintsForArchetype(arch, mp)
|
||||
const hintStr = hints.join(' · ')
|
||||
|
||||
if (!isWeakSlotTimingSummary(slotTxt)) {
|
||||
const extras = []
|
||||
for (const h of hints) {
|
||||
if (
|
||||
/Runden|Durchläufe|Zyklen|Umlauf/i.test(h) &&
|
||||
slotTxt &&
|
||||
!/Runden|Serien|×|\d+s Arbeit|\d+s Erholung|\d+s Intervall/i.test(slotTxt)
|
||||
) {
|
||||
extras.push(h)
|
||||
}
|
||||
}
|
||||
return extras.length ? `${slotTxt} · ${extras.join(' · ')}` : slotTxt
|
||||
}
|
||||
|
||||
if (hintStr) return hintStr
|
||||
if (slotTxt) return slotTxt
|
||||
return null
|
||||
}
|
||||
|
||||
function normalizeOptionalNonNegInt(v) {
|
||||
if (v === '' || v === undefined || v === null) return undefined
|
||||
const n = typeof v === 'number' ? v : parseInt(String(v), 10)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user