/** * Kombination: konsolidierte Darstellung globales Profil + Stationen mit Zeiten (Vorschau, Plan-Ansicht, Druck). */ import React, { useMemo } from 'react' import { Link } from 'react-router-dom' import { archetypeCoachHint, combinationArchetypeLabel, sortCombinationSlotsForDisplay, } from '../constants/combinationArchetypes' import { describeGlobalComboProfile, effectiveStationTimingSummary, readSlotProfilesV1, stationPrimaryLoadLabel, } from '../utils/combinationMethodProfileUi' /** @returns {{ exerciseId: number, label: string }[]} */ export function normalizeCombinationSlotCandidates(slot) { const out = [] const cands = slot.candidates && slot.candidates.length ? slot.candidates : (slot.candidate_exercise_ids || []).map((id) => ({ exercise_id: id, title: null })) for (const c of cands) { const rawId = c.exercise_id if (rawId == null) continue const n = typeof rawId === 'number' ? rawId : parseInt(String(rawId), 10) if (!Number.isFinite(n)) continue const label = ((c.title || '').trim() || `Übung #${n}`).trim() out.push({ exerciseId: n, label }) } return out } export default function CombinationPlanBracket({ methodArchetype, methodProfile, combinationSlots, planningAdjusted = false, /** 'none' | 'link' (Router) | 'button' (z. B. ExercisePeekModal / PWA-sicher) */ candidateInteraction = 'none', onCandidatePeek, className, }) { const arch = typeof methodArchetype === 'string' ? methodArchetype.trim() : '' const archLabel = arch ? combinationArchetypeLabel(arch) : null const globals = describeGlobalComboProfile(arch, methodProfile || {}) const slotsSorted = useMemo(() => sortCombinationSlotsForDisplay(combinationSlots || []), [combinationSlots]) const timingByIx = useMemo(() => { const mp = methodProfile || {} const rows = readSlotProfilesV1(mp) const m = new Map() for (const r of rows) { m.set(Number(r.slot_index), r) } return m }, [methodProfile]) const coachHint = arch ? archetypeCoachHint(arch) : '' return (
Kombinations‑Plan {archLabel || arch || 'Archetyp'} {planningAdjusted ? ( Planung angepasst ) : null}
{coachHint ?

{coachHint}

: null}
{globals.length > 0 ? (
Runden · Zeiten · Pausen (global)
    {globals.map((g) => (
  • {g.caption} {g.value}
  • ))}
) : (

Keine globalen Zahlenfelder gesetzt — Steuerung erfolgt nur je Station oder über den Freitext der Kombination.

)}
    {slotsSorted.map((slot, si) => { const siRaw = slot.slot_index const ixParsed = siRaw === '' || siRaw == null ? si : typeof siRaw === 'number' ? siRaw : parseInt(String(siRaw), 10) const stationIx = Number.isFinite(ixParsed) ? ixParsed : si const displayStep = si + 1 const stationTitle = ((slot.title || '').trim() || `Station ${displayStep}`).trim() const candRows = normalizeCombinationSlotCandidates(slot) const names = candRows.length ? candRows.map((r) => r.label).join(' ↔ ') : '' const slotProfRow = timingByIx.get(stationIx) const loadBadge = stationPrimaryLoadLabel(slotProfRow) const timing = effectiveStationTimingSummary(arch, methodProfile || {}, slotProfRow) return (
  1. {loadBadge || '—'}
    {stationTitle}
    {candidateInteraction === 'button' && typeof onCandidatePeek === 'function' && candRows.length > 0 ? (
    {candRows.map((c, ci) => ( {ci > 0 ? ↔ : null} ))}
    ) : candidateInteraction === 'link' && candRows.length > 0 ? (
    {candRows.map((c, ci) => ( {ci > 0 ? ↔ : null} {c.label} ))}
    ) : (
    {names || '(keine Einzelübung)'}
    )} {timing ? (
    Zeit / Steuerung {timing}
    ) : (
    Keine eigene Stations‑Zeit im Profil — ggf. nur globale Vorgaben oder Freitext.
    )}
  2. ) })}
) }