refactor(ui): enhance styling and structure of training unit sections and combination plan bracket
All checks were successful
Deploy Development / deploy (push) Successful in 40s
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 / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m5s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 11s
Test Suite / k6 /health Baseline (pull_request) Successful in 33s
Test Suite / playwright-tests (pull_request) Successful in 1m8s
All checks were successful
Deploy Development / deploy (push) Successful in 40s
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 / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m5s
Test Suite / pytest-backend (pull_request) Successful in 34s
Test Suite / lint-backend (pull_request) Successful in 0s
Test Suite / build-frontend (pull_request) Successful in 11s
Test Suite / k6 /health Baseline (pull_request) Successful in 33s
Test Suite / playwright-tests (pull_request) Successful in 1m8s
- Updated CSS for training unit sections to improve layout and responsiveness, ensuring combo planning strips are displayed correctly. - Refactored CombinationPlanBracket component to accept additional class names for better customization. - Removed unused functions and streamlined imports in TrainingUnitSectionsEditor for cleaner code. - Reintroduced ExercisePickerModal with improved placement in ExerciseFormPage for better user experience.
This commit is contained in:
parent
9da29a2231
commit
930a786315
|
|
@ -5414,22 +5414,80 @@ a.analysis-split__nav-item {
|
||||||
0 2px 12px rgba(15, 23, 42, 0.05);
|
0 2px 12px rgba(15, 23, 42, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kombinations‑Strip: volle Breite unter der Zeile, begrenzte Textbreite — Hauptzeile (Name/Min.) nicht verdrängen */
|
/* Kombinationszeile: immer unter Hauptzeile (Titel / Minuten / Aktionen), nicht daneben */
|
||||||
|
.training-unit-sections-editor .tu-item-row--exercise.tu-item-row--combo {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .tu-item-row--exercise.tu-item-row--combo .tu-item-row__mainline {
|
||||||
|
flex: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kombinations‑Strip: volle Breite; oben „Ablauf bearbeiten“, darunter Klammer‑Vorschau */
|
||||||
.training-unit-sections-editor .tu-combo-planning-strip {
|
.training-unit-sections-editor .tu-combo-planning-strip {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
padding: 10px 12px 12px;
|
||||||
|
border-top: 1px solid color-mix(in srgb, var(--border2) 85%, var(--accent) 12%);
|
||||||
|
background: color-mix(in srgb, var(--surface2) 65%, var(--surface));
|
||||||
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.training-unit-sections-editor .tu-combo-planning-strip__meta {
|
.training-unit-sections-editor--item-drag .tu-item-row--combo .tu-combo-planning-strip {
|
||||||
width: 100%;
|
padding-left: 44px;
|
||||||
max-width: min(100%, 42rem);
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .tu-combo-planning-strip__toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .tu-combo-planning-strip__meta--fallback {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--text2);
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .tu-combo-planning-strip__bracket-wrap {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.training-unit-sections-editor .tu-combo-planning-strip > .btn {
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed {
|
||||||
align-self: flex-start;
|
font-size: 0.93rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__station {
|
||||||
|
padding: 8px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__chip {
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__globals-title {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__head-main {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__kicker {
|
||||||
|
font-size: 0.62rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.training-unit-sections-editor .combo-plan-bracket--planning-embed .combo-plan-bracket__archetype {
|
||||||
|
font-size: 0.88rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tu-planning-mod-tag {
|
.tu-planning-mod-tag {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ export default function CombinationPlanBracket({
|
||||||
/** 'none' | 'link' (Router) | 'button' (z. B. ExercisePeekModal / PWA-sicher) */
|
/** 'none' | 'link' (Router) | 'button' (z. B. ExercisePeekModal / PWA-sicher) */
|
||||||
candidateInteraction = 'none',
|
candidateInteraction = 'none',
|
||||||
onCandidatePeek,
|
onCandidatePeek,
|
||||||
|
className,
|
||||||
}) {
|
}) {
|
||||||
const arch = typeof methodArchetype === 'string' ? methodArchetype.trim() : ''
|
const arch = typeof methodArchetype === 'string' ? methodArchetype.trim() : ''
|
||||||
const archLabel = arch ? combinationArchetypeLabel(arch) : null
|
const archLabel = arch ? combinationArchetypeLabel(arch) : null
|
||||||
|
|
@ -59,7 +60,7 @@ export default function CombinationPlanBracket({
|
||||||
const coachHint = arch ? archetypeCoachHint(arch) : ''
|
const coachHint = arch ? archetypeCoachHint(arch) : ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="combo-plan-bracket">
|
<div className={['combo-plan-bracket', className].filter(Boolean).join(' ')}>
|
||||||
<div className="combo-plan-bracket__accent" aria-hidden />
|
<div className="combo-plan-bracket__accent" aria-hidden />
|
||||||
<div className="combo-plan-bracket__body">
|
<div className="combo-plan-bracket__body">
|
||||||
<header className="combo-plan-bracket__head">
|
<header className="combo-plan-bracket__head">
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { GripVertical, Pencil } from 'lucide-react'
|
||||||
import CombinationMethodProfileEditor from './CombinationMethodProfileEditor'
|
import CombinationMethodProfileEditor from './CombinationMethodProfileEditor'
|
||||||
import CombinationPlanBracket from './CombinationPlanBracket'
|
import CombinationPlanBracket from './CombinationPlanBracket'
|
||||||
import { comboPlanningProfileJsonForEditor, effectiveComboMethodProfile } from '../utils/comboPlanningMethodProfile'
|
import { comboPlanningProfileJsonForEditor, effectiveComboMethodProfile } from '../utils/comboPlanningMethodProfile'
|
||||||
import { combinationArchetypeLabel, sortCombinationSlotsForDisplay } from '../constants/combinationArchetypes'
|
import { sortCombinationSlotsForDisplay } from '../constants/combinationArchetypes'
|
||||||
import {
|
import {
|
||||||
cloneJsonSerializablePlanningProfile,
|
cloneJsonSerializablePlanningProfile,
|
||||||
comboSlotsOutlineForProfileEditor,
|
comboSlotsOutlineForProfileEditor,
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
sectionPlannedMinutes,
|
sectionPlannedMinutes,
|
||||||
} from '../utils/trainingUnitSectionsForm'
|
} from '../utils/trainingUnitSectionsForm'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
import { effectiveStationTimingSummary, readSlotProfilesV1 } from '../utils/combinationMethodProfileUi'
|
|
||||||
import { isCompactTagLegendMode } from '../config/planningModuleUx'
|
import { isCompactTagLegendMode } from '../config/planningModuleUx'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
|
|
||||||
|
|
@ -74,60 +73,6 @@ function compactComboPlanningCaption(it) {
|
||||||
return overridden ? 'Planung angepasst' : 'wie Katalog'
|
return overridden ? 'Planung angepasst' : 'wie Katalog'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Globale Eckdaten aus effective profile (optional unter Stationenliste). */
|
|
||||||
function comboRoughGlobalTimingHint(profileObj, archetypeKey) {
|
|
||||||
if (!profileObj || typeof profileObj !== 'object' || Array.isArray(profileObj)) return null
|
|
||||||
const bits = []
|
|
||||||
const rounds = profileObj.rounds
|
|
||||||
const ws = profileObj.work_seconds
|
|
||||||
const rb = profileObj.rest_between_rounds_sec
|
|
||||||
const hint = profileObj.hint_step_duration_sec
|
|
||||||
const globRest = profileObj.rest_between_sets_sec
|
|
||||||
if (rounds != null && rounds !== '') bits.push(`${rounds} Runden`)
|
|
||||||
if (ws != null && ws !== '') bits.push(`${ws}s Arbeit`)
|
|
||||||
if (rb != null && rb !== '') bits.push(`Pause ${rb}s`)
|
|
||||||
if (globRest != null && globRest !== '') bits.push(`Sets-Pause ${globRest}s`)
|
|
||||||
if (hint != null && hint !== '') bits.push(`Orientierung ~${hint}s`)
|
|
||||||
const arch = (archetypeKey || '').trim()
|
|
||||||
if (arch === 'time_domain_interval') {
|
|
||||||
const iw = profileObj.interval_work_sec
|
|
||||||
const ir = profileObj.interval_rest_sec
|
|
||||||
const ig = profileObj.interval_groups
|
|
||||||
if (iw != null && iw !== '') bits.push(`${iw}s Intervall`)
|
|
||||||
if (ir != null && ir !== '') bits.push(`${ir}s Erholung`)
|
|
||||||
if (ig != null && ig !== '') bits.push(`${ig} Gruppen`)
|
|
||||||
}
|
|
||||||
return bits.length ? bits.join(' · ') : null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Pro Station eine kompakte Textzeile für die Planungsliste. */
|
|
||||||
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) => {
|
|
||||||
const siRaw = slot.slot_index
|
|
||||||
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 ${idx + 1}`)
|
|
||||||
const candIds = (slot.candidate_exercise_ids || [])
|
|
||||||
.map((raw) => (typeof raw === 'number' ? raw : parseInt(String(raw), 10)))
|
|
||||||
.filter((n) => Number.isFinite(n))
|
|
||||||
const namesJoined =
|
|
||||||
candIds.length === 0
|
|
||||||
? '(keine Übung)'
|
|
||||||
: candIds.map((id) => titles[String(id)] || `Übung ${id}`).join(' ↔ ')
|
|
||||||
const timing = effectiveStationTimingSummary(archRaw, mp, byIx.get(ix))
|
|
||||||
let line = `${stationLbl}: ${namesJoined}`
|
|
||||||
if (timing) line += ` · ${timing}`
|
|
||||||
return line
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stabile Farbzurodnung aus Modul-ID (nur Darstellung). */
|
/** Stabile Farbzurodnung aus Modul-ID (nur Darstellung). */
|
||||||
function planningModulePalette(moduleId) {
|
function planningModulePalette(moduleId) {
|
||||||
const id = normalizedPlanningModuleChainId(moduleId)
|
const id = normalizedPlanningModuleChainId(moduleId)
|
||||||
|
|
@ -703,7 +648,8 @@ export default function TrainingUnitSectionsEditor({
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
'training-unit-sections-editor' +
|
'training-unit-sections-editor' +
|
||||||
(wideExerciseGrid ? ' training-unit-sections-editor--wide' : '')
|
(wideExerciseGrid ? ' training-unit-sections-editor--wide' : '') +
|
||||||
|
(enableItemDragReorder ? ' training-unit-sections-editor--item-drag' : '')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{(!hideHeading || headingAccessory) ? (
|
{(!hideHeading || headingAccessory) ? (
|
||||||
|
|
@ -1017,10 +963,6 @@ export default function TrainingUnitSectionsEditor({
|
||||||
|
|
||||||
const stripArchRaw =
|
const stripArchRaw =
|
||||||
isCombination && it.exercise_id ? String(it.catalog_method_archetype || '').trim() : ''
|
isCombination && it.exercise_id ? String(it.catalog_method_archetype || '').trim() : ''
|
||||||
const stripArchLbl =
|
|
||||||
stripArchRaw && isCombination ? combinationArchetypeLabel(stripArchRaw) : null
|
|
||||||
const stripBullets =
|
|
||||||
isCombination && it.exercise_id ? comboPlanningStripBulletTexts(it) : []
|
|
||||||
const stripMpEff =
|
const stripMpEff =
|
||||||
isCombination && it.exercise_id
|
isCombination && it.exercise_id
|
||||||
? effectiveComboMethodProfile(
|
? effectiveComboMethodProfile(
|
||||||
|
|
@ -1028,17 +970,15 @@ export default function TrainingUnitSectionsEditor({
|
||||||
it.planning_method_profile,
|
it.planning_method_profile,
|
||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
const stripGlobalRough =
|
|
||||||
isCombination && it.exercise_id && stripMpEff
|
|
||||||
? comboRoughGlobalTimingHint(stripMpEff, stripArchRaw)
|
|
||||||
: null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment key={`${insertSlotKeyPrefix}sec-${sIdx}-blk-${iIdx}`}>
|
<Fragment key={`${insertSlotKeyPrefix}sec-${sIdx}-blk-${iIdx}`}>
|
||||||
{!planningCompactLegend &&
|
{!planningCompactLegend &&
|
||||||
renderModulePlanningHead(modBandTitle, modOutline, showModuleBand)}
|
renderModulePlanningHead(modBandTitle, modOutline, showModuleBand)}
|
||||||
<div
|
<div
|
||||||
className={`${rowCommon} tu-item-row--exercise${fromModClass}`}
|
className={`${rowCommon} tu-item-row--exercise${fromModClass}${
|
||||||
|
isCombination && it.exercise_id ? ' tu-item-row--combo' : ''
|
||||||
|
}`}
|
||||||
{...dndRowProps}
|
{...dndRowProps}
|
||||||
style={modBorderVarStyle}
|
style={modBorderVarStyle}
|
||||||
>
|
>
|
||||||
|
|
@ -1215,76 +1155,48 @@ export default function TrainingUnitSectionsEditor({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isCombination && it.exercise_id ? (
|
{isCombination && it.exercise_id ? (
|
||||||
<div
|
<div className="tu-combo-planning-strip">
|
||||||
className="tu-combo-planning-strip"
|
<div className="tu-combo-planning-strip__toolbar">
|
||||||
style={{
|
<button
|
||||||
padding: '8px 12px 10px',
|
type="button"
|
||||||
paddingLeft: enableItemDragReorder ? 44 : 12,
|
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
||||||
borderTop: '1px solid var(--border)',
|
aria-haspopup="dialog"
|
||||||
background: 'var(--surface2)',
|
aria-label="Ablaufprofil Kombination für diese Planung bearbeiten"
|
||||||
}}
|
onClick={() => setComboPlanningModal({ sIdx, iIdx })}
|
||||||
>
|
>
|
||||||
<div
|
Ablauf bearbeiten…
|
||||||
className="tu-combo-planning-strip__meta"
|
</button>
|
||||||
style={{
|
|
||||||
fontSize: '0.78rem',
|
|
||||||
color: 'var(--text2)',
|
|
||||||
lineHeight: 1.45,
|
|
||||||
}}
|
|
||||||
title="Stationen und grobe Zeiten aus Katalog bzw. Planungs-Anpassung — Details unter „Ablauf bearbeiten“ oder „Vorschau“"
|
|
||||||
>
|
|
||||||
<div style={{ marginBottom: stripBullets.length || stripGlobalRough ? 6 : 0 }}>
|
|
||||||
<strong style={{ color: 'var(--text1)', fontWeight: 600 }}>Archetyp: </strong>
|
|
||||||
<span style={{ color: 'var(--text1)' }}>
|
|
||||||
{stripArchLbl || stripArchRaw || '—'}
|
|
||||||
</span>
|
|
||||||
<span style={{ marginLeft: 10, fontWeight: 500, whiteSpace: 'nowrap' }}>
|
|
||||||
{compactComboPlanningCaption(it)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{stripGlobalRough ? (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginBottom: stripBullets.length ? 6 : 0,
|
|
||||||
fontSize: '0.74rem',
|
|
||||||
color: 'var(--text3)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<strong style={{ color: 'var(--text2)', fontWeight: 600 }}>Block: </strong>
|
|
||||||
{stripGlobalRough}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{stripBullets.length > 0 ? (
|
|
||||||
<ul
|
|
||||||
style={{
|
|
||||||
margin: 0,
|
|
||||||
paddingLeft: '1.05rem',
|
|
||||||
fontSize: '0.74rem',
|
|
||||||
color: 'var(--text2)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{stripBullets.map((line, bi) => (
|
|
||||||
<li key={`combo-strip-${sIdx}-${iIdx}-${bi}`} style={{ marginBottom: 2 }}>
|
|
||||||
{line}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<div style={{ fontSize: '0.74rem', color: 'var(--text3)', fontStyle: 'italic' }}>
|
|
||||||
Stationen laden oder noch keine Kombi-Stationen im Katalog …
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
{(it.combination_slots || []).length > 0 ? (
|
||||||
type="button"
|
<div className="tu-combo-planning-strip__bracket-wrap">
|
||||||
className="btn btn-secondary framework-ctrl framework-ctrl--xs"
|
<CombinationPlanBracket
|
||||||
style={{ flexShrink: 0 }}
|
className="combo-plan-bracket--planning-embed"
|
||||||
aria-haspopup="dialog"
|
methodArchetype={stripArchRaw}
|
||||||
aria-label="Ablaufprofil Kombination für diese Planung bearbeiten"
|
methodProfile={stripMpEff || {}}
|
||||||
onClick={() => setComboPlanningModal({ sIdx, iIdx })}
|
combinationSlots={sortCombinationSlotsForDisplay(it.combination_slots)}
|
||||||
>
|
planningAdjusted={
|
||||||
Ablauf bearbeiten…
|
it.planning_method_profile != null &&
|
||||||
</button>
|
typeof it.planning_method_profile === 'object' &&
|
||||||
|
!Array.isArray(it.planning_method_profile)
|
||||||
|
}
|
||||||
|
candidateInteraction={onPeekExercise ? 'button' : 'none'}
|
||||||
|
onCandidatePeek={
|
||||||
|
onPeekExercise
|
||||||
|
? (exId) => onPeekExercise(Number(exId), null, undefined)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="tu-combo-planning-strip__meta tu-combo-planning-strip__meta--fallback"
|
||||||
|
title="Stationen aus dem Katalog — nach ersten Laden oder wenn die Kombination noch keine Slots hat."
|
||||||
|
>
|
||||||
|
<div style={{ fontSize: '0.74rem', color: 'var(--text3)', fontStyle: 'italic', margin: 0 }}>
|
||||||
|
Stationen werden geladen oder die Kombination hat im Katalog noch keine Stationsliste …
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2403,18 +2403,6 @@ function ExerciseFormPage() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ExercisePickerModal
|
|
||||||
open={comboStationPickerIx !== null}
|
|
||||||
onClose={() => setComboStationPickerIx(null)}
|
|
||||||
exerciseKindAny={['simple']}
|
|
||||||
multiSelect
|
|
||||||
enableQuickCreateDraft
|
|
||||||
onSelectExercises={(picked) => {
|
|
||||||
if (comboStationPickerIx === null) return
|
|
||||||
mergePickedExercisesIntoSlot(comboStationPickerIx, picked)
|
|
||||||
setComboStationPickerIx(null)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{reportTarget && (
|
{reportTarget && (
|
||||||
<ReportContentModal
|
<ReportContentModal
|
||||||
targetType="media_asset"
|
targetType="media_asset"
|
||||||
|
|
@ -2426,6 +2414,19 @@ function ExerciseFormPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<ExercisePickerModal
|
||||||
|
open={comboStationPickerIx !== null}
|
||||||
|
onClose={() => setComboStationPickerIx(null)}
|
||||||
|
exerciseKindAny={['simple']}
|
||||||
|
multiSelect
|
||||||
|
enableQuickCreateDraft
|
||||||
|
onSelectExercises={(picked) => {
|
||||||
|
if (comboStationPickerIx === null) return
|
||||||
|
mergePickedExercisesIntoSlot(comboStationPickerIx, picked)
|
||||||
|
setComboStationPickerIx(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<p style={{ fontSize: '12px', color: 'var(--text3)', marginTop: '16px' }}>
|
<p style={{ fontSize: '12px', color: 'var(--text3)', marginTop: '16px' }}>
|
||||||
<strong>KI-Ausbaustufe:</strong> Backend laut Spec{' '}
|
<strong>KI-Ausbaustufe:</strong> Backend laut Spec{' '}
|
||||||
<code style={{ fontSize: '11px' }}>POST /api/exercises/ai/suggest</code> und{' '}
|
<code style={{ fontSize: '11px' }}>POST /api/exercises/ai/suggest</code> und{' '}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user