diff --git a/frontend/src/components/ExerciseProgressionPathBuilder.jsx b/frontend/src/components/ExerciseProgressionPathBuilder.jsx index 9a082d6..89bd936 100644 --- a/frontend/src/components/ExerciseProgressionPathBuilder.jsx +++ b/frontend/src/components/ExerciseProgressionPathBuilder.jsx @@ -61,6 +61,48 @@ const OFFER_SOURCE_LABELS = { roadmap_unfilled: 'Roadmap-Stufe', } +const PATH_STEPS_HARD_MAX = 10 + +/** Einfügen wächst den Pfad; Ersetzen (replace_step_index) nicht. */ +function offerGrowsPath(offer) { + const replaceIdx = offer?.replace_step_index + return !(replaceIdx != null && Number.isFinite(Number(replaceIdx))) +} + +function isGapOfferBlockedByPathCapacity(offer, pathLen, maxSteps) { + return offerGrowsPath(offer) && pathLen >= maxSteps +} + +function neededMaxStepsAfterInsert(pathLen) { + return Math.min(PATH_STEPS_HARD_MAX, pathLen + 1) +} + +/** + * Pfad voll, aber Einfügen gewünscht → Nutzer fragen, ob maxSteps dynamisch wächst. + * @returns {boolean} true = fortfahren (ggf. maxSteps erhöht), false = abgebrochen + */ +function confirmPathExpansionIfNeeded(offer, pathLen, maxSteps, setMaxSteps) { + if (!isGapOfferBlockedByPathCapacity(offer, pathLen, maxSteps)) { + return true + } + if (maxSteps >= PATH_STEPS_HARD_MAX) { + alert( + `Maximale Pfadlänge (${PATH_STEPS_HARD_MAX} Schritte) erreicht. Bitte zuerst einen Schritt entfernen.`, + ) + return false + } + const newMax = neededMaxStepsAfterInsert(pathLen) + const titleHint = (offer?.title_hint || 'diese Übung').trim() + const ok = window.confirm( + `Maximale Pfadlänge (${maxSteps}) ist erreicht.\n\n` + + `Soll die Pfadlänge auf ${newMax} Schritte vergrößert werden, um „${titleHint}“ einzufügen?\n\n` + + 'Es wird kein neuer Pfad-Vorschlag generiert.', + ) + if (!ok) return false + setMaxSteps(newMax) + return true +} + function resolveDefaultFocusAreaId(targetSummary, focusAreas) { const targetName = targetSummary?.focus_areas?.[0] if (targetName && Array.isArray(focusAreas) && focusAreas.length) { @@ -193,6 +235,13 @@ export default function ExerciseProgressionPathBuilder({ setQuickAiError('') } + const handleGapFillClick = async (offer) => { + if (!confirmPathExpansionIfNeeded(offer, pathSteps.length, maxSteps, setMaxSteps)) { + return + } + await runGapFillAiSuggest(offer) + } + const runGapFillAiSuggest = async (offer) => { const title = (offer?.title_hint || '').trim() if (title.length < 3) { @@ -611,8 +660,10 @@ export default function ExerciseProgressionPathBuilder({ > Fehlende Schritte — mit KI anlegen

- Die QS hat fehlende Zwischenschritte erkannt — sie sind noch nicht im Pfad ({pathSteps.length}/{maxSteps} Schritte). - „Mit KI anlegen“ startet einen vollständigen KI-Entwurf (Ziel, Anleitung, Fähigkeiten) und fügt die Übung ein. + 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.' + : ' „Mit KI anlegen“ erzeugt einen vollständigen Entwurf und fügt die Übung ein.'}

{gapFillOffers.map((offer) => ( @@ -646,12 +697,20 @@ export default function ExerciseProgressionPathBuilder({ type="button" className="btn btn-primary" style={{ fontSize: '12px', flexShrink: 0 }} - disabled={quickSaving || pathSteps.length >= maxSteps} - onClick={() => runGapFillAiSuggest(offer)} + disabled={ + quickSaving || + (isGapOfferBlockedByPathCapacity(offer, pathSteps.length, maxSteps) && + maxSteps >= PATH_STEPS_HARD_MAX) + } + onClick={() => handleGapFillClick(offer)} title={ - pathSteps.length >= maxSteps - ? `Pfad hat bereits ${maxSteps} Schritte — zuerst einen Schritt entfernen.` - : 'KI-Entwurf mit Pfad-Kontext generieren' + isGapOfferBlockedByPathCapacity(offer, pathSteps.length, maxSteps) + ? maxSteps >= PATH_STEPS_HARD_MAX + ? `Maximal ${PATH_STEPS_HARD_MAX} Schritte — zuerst einen Schritt entfernen.` + : 'Pfad voll — Klick fragt, ob die Pfadlänge vergrößert werden soll' + : offer.replace_step_index != null + ? 'Ersetzt den themenfremden Schritt im Pfad' + : 'KI-Entwurf mit Pfad-Kontext generieren' } > {generatingOfferId === offer.offer_id