diff --git a/frontend/src/components/ExerciseProgressionPathBuilder.jsx b/frontend/src/components/ExerciseProgressionPathBuilder.jsx index 5784f75..b6a2deb 100644 --- a/frontend/src/components/ExerciseProgressionPathBuilder.jsx +++ b/frontend/src/components/ExerciseProgressionPathBuilder.jsx @@ -1,7 +1,7 @@ /** * Planungs-KI Phase C3/E3: Ziel → Übungspfad vorschlagen → Lücken mit KI anlegen → in Graph speichern. */ -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import api from '../utils/api' import ExerciseAiQuickCreateModal from './exercises/ExerciseAiQuickCreateModal' import ExerciseGapFillPrepModal from './exercises/ExerciseGapFillPrepModal' @@ -136,6 +136,93 @@ const OFFER_SOURCE_LABELS = { const PATH_STEPS_HARD_MAX = 10 +const WIZARD_STEPS = [ + { id: 1, label: 'Ziel & Start/Ziel', short: 'Ziel' }, + { id: 2, label: 'Roadmap', short: 'Roadmap' }, + { id: 3, label: 'Match', short: 'Match' }, + { id: 4, label: 'Lücken & Speichern', short: 'Speichern' }, +] + +function computeMaxReachableStep(editableMajorSteps, pathSteps) { + if (pathSteps.length > 0) return 4 + if (editableMajorSteps.length >= 2) return 2 + return 1 +} + +function PlanningWizardStepper({ currentStep, maxReachable, onStepChange, disabled }) { + return ( + + ) +} + const ROADMAP_PHASES = ['einstieg', 'grundlage', 'vertiefung', 'anwendung', 'perfektion'] const LOAD_PROFILE_OPTIONS = [ @@ -355,6 +442,12 @@ export default function ExerciseProgressionPathBuilder({ const [gapPrepFocusAreaId, setGapPrepFocusAreaId] = useState('') const [gapPrepError, setGapPrepError] = useState('') const [loadedPlanningHint, setLoadedPlanningHint] = useState(false) + const [wizardStep, setWizardStep] = useState(1) + + const maxReachableStep = useMemo( + () => computeMaxReachableStep(editableMajorSteps, pathSteps), + [editableMajorSteps, pathSteps], + ) const buildPlanningArtifact = useCallback( () => @@ -404,6 +497,7 @@ export default function ExerciseProgressionPathBuilder({ setRoadmapDirty(false) setStartTargetAnalyzed(false) setError('') + setWizardStep(1) api .getExerciseProgressionGraph(Number(graphId)) @@ -422,6 +516,7 @@ export default function ExerciseProgressionPathBuilder({ const majors = mapMajorStepsFromApi(art.progression_roadmap) if (majors.length >= 2) { setEditableMajorSteps(majors) + setWizardStep(2) } } if ( @@ -442,6 +537,12 @@ export default function ExerciseProgressionPathBuilder({ } }, [graphId]) + useEffect(() => { + if (wizardStep > maxReachableStep) { + setWizardStep(maxReachableStep) + } + }, [wizardStep, maxReachableStep]) + useEffect(() => { let cancelled = false Promise.all([ @@ -931,6 +1032,7 @@ export default function ExerciseProgressionPathBuilder({ setPathSkillExpectations(null) setRoadmapDirty(false) setLoadedPlanningHint(false) + setWizardStep(2) await persistPlanningRoadmapToGraph() } catch (e) { console.error(e) @@ -980,6 +1082,7 @@ export default function ExerciseProgressionPathBuilder({ applyPathMatchResponse(res, q) setMaxSteps(validSteps.length) setLoadedPlanningHint(false) + setWizardStep(3) await persistPlanningRoadmapToGraph() } catch (e) { console.error(e) @@ -1033,6 +1136,7 @@ export default function ExerciseProgressionPathBuilder({ setPathSkillExpectations(null) setEditableMajorSteps([]) setRoadmapDirty(false) + setWizardStep(1) if (typeof onSaved === 'function') await onSaved() const msg = skippedAi > 0 @@ -1057,26 +1161,43 @@ export default function ExerciseProgressionPathBuilder({ >
- Zuerst didaktische Roadmap vorschlagen und anpassen, dann Übungen je Major Step aus der Bibliothek matchen. - Lücken können mit KI als Übung angelegt werden. + In vier Schritten: Ziel festlegen → Roadmap bearbeiten → Übungen matchen → Lücken schließen und speichern.
- {loadedPlanningHint && editableMajorSteps.length > 0 && pathSteps.length === 0 ? ( -
- Gespeicherte Planung für diesen Graph geladen — Roadmap anpassen und erneut matchen, oder neuen Vorschlag
- starten.
+
+
+ {error}
) : null} -+ Gespeicherte Planung geladen — Sie können bei Schritt 2 weitermachen oder hier neu starten. +
+ ) : null} +- Optional zuerst „Start/Ziel analysieren“, anpassen, dann Roadmap-Stufen. Sind Start und Ziel leer, - geschieht die Analyse beim Roadmap-Vorschlag automatisch mit. Manuelle Eingaben haben immer Vorrang. -
-+ Optional zuerst „Start/Ziel analysieren“, anpassen, dann Roadmap-Stufen. Sind Start und Ziel leer, + geschieht die Analyse beim Roadmap-Vorschlag automatisch mit. Manuelle Eingaben haben immer Vorrang. +
+- {error} -
- ) : null} - - {(progressionRoadmap?.goal_analysis || - progressionRoadmap?.pipeline_phase === 'start_target_only') ? ( -+ Noch keine Roadmap — zuerst in Schritt 1 „Roadmap vorschlagen“. + +
+ ) : (+ Noch kein Match — zuerst in Schritt 2 „Übungen matchen“. + +
+ ) : ( + <> + {(semanticBrief || targetSummary || pathSkillExpectations) ? ( +- {pathQa.off_topic_count} Schritt(e) ohne Bezug zum Pfad-Thema — siehe Lücken-Angebote unten. + {pathQa.off_topic_count} Schritt(e) ohne Bezug zum Pfad-Thema — Lücken in Schritt 4 schließen.
) : null} {pathQa.reorder_applied ? ( @@ -1547,88 +1726,7 @@ export default function ExerciseProgressionPathBuilder({- Fehlende oder zu ersetzende Schritte ({pathSteps.length}/{maxSteps} im Pfad). - {pathSteps.length >= maxSteps - ? ' Der Pfad ist voll — beim Einfügen können Sie die Pfadlänge dynamisch vergrößern (ohne neuen Vorschlag); Ersatz-Angebote ersetzen einen Schritt.' - : ' Zuerst Kontext prüfen und ergänzen, dann KI-Entwurf erstellen und einfügen.'} -
{offer.rationale}
- ) : null} - {offer.from_title && offer.to_title ? ( -- Zwischen „{offer.from_title}“ und „{offer.to_title}“ - {offer.replace_step_index != null ? ' (ersetzt themenfremden Schritt)' : ''} -
- ) : null} -+ Noch kein Pfad — zuerst Schritt 3 abschließen. + +
+ ) : ( + <> + {gapFillOffers.length > 0 ? ( ++ Fehlende oder zu ersetzende Schritte ({pathSteps.length}/{maxSteps} im Pfad). + {pathSteps.length >= maxSteps + ? ' Der Pfad ist voll — beim Einfügen können Sie die Pfadlänge dynamisch vergrößern (ohne neuen Vorschlag); Ersatz-Angebote ersetzen einen Schritt.' + : ' Zuerst Kontext prüfen und ergänzen, dann KI-Entwurf erstellen und einfügen.'} +
+{offer.rationale}
+ ) : null} + {offer.from_title && offer.to_title ? ( ++ Zwischen „{offer.from_title}“ und „{offer.to_title}“ + {offer.replace_step_index != null ? ' (ersetzt themenfremden Schritt)' : ''} +
+ ) : null} ++ Keine offenen Lücken — Pfad kann direkt gespeichert werden. +
+ )} + +