Progressionsgraph verbessert #54
|
|
@ -61,6 +61,48 @@ const OFFER_SOURCE_LABELS = {
|
||||||
roadmap_unfilled: 'Roadmap-Stufe',
|
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) {
|
function resolveDefaultFocusAreaId(targetSummary, focusAreas) {
|
||||||
const targetName = targetSummary?.focus_areas?.[0]
|
const targetName = targetSummary?.focus_areas?.[0]
|
||||||
if (targetName && Array.isArray(focusAreas) && focusAreas.length) {
|
if (targetName && Array.isArray(focusAreas) && focusAreas.length) {
|
||||||
|
|
@ -193,6 +235,13 @@ export default function ExerciseProgressionPathBuilder({
|
||||||
setQuickAiError('')
|
setQuickAiError('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleGapFillClick = async (offer) => {
|
||||||
|
if (!confirmPathExpansionIfNeeded(offer, pathSteps.length, maxSteps, setMaxSteps)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await runGapFillAiSuggest(offer)
|
||||||
|
}
|
||||||
|
|
||||||
const runGapFillAiSuggest = async (offer) => {
|
const runGapFillAiSuggest = async (offer) => {
|
||||||
const title = (offer?.title_hint || '').trim()
|
const title = (offer?.title_hint || '').trim()
|
||||||
if (title.length < 3) {
|
if (title.length < 3) {
|
||||||
|
|
@ -611,8 +660,10 @@ export default function ExerciseProgressionPathBuilder({
|
||||||
>
|
>
|
||||||
<strong style={{ fontSize: '13px' }}>Fehlende Schritte — mit KI anlegen</strong>
|
<strong style={{ fontSize: '13px' }}>Fehlende Schritte — mit KI anlegen</strong>
|
||||||
<p style={{ fontSize: '12px', color: 'var(--text3)', margin: '6px 0 10px', lineHeight: 1.45 }}>
|
<p style={{ fontSize: '12px', color: 'var(--text3)', margin: '6px 0 10px', lineHeight: 1.45 }}>
|
||||||
Die QS hat fehlende Zwischenschritte erkannt — sie sind noch nicht im Pfad ({pathSteps.length}/{maxSteps} Schritte).
|
Fehlende oder zu ersetzende Schritte ({pathSteps.length}/{maxSteps} im Pfad).
|
||||||
„Mit KI anlegen“ startet einen vollständigen KI-Entwurf (Ziel, Anleitung, Fähigkeiten) und fügt die Übung ein.
|
{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.'}
|
||||||
</p>
|
</p>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||||
{gapFillOffers.map((offer) => (
|
{gapFillOffers.map((offer) => (
|
||||||
|
|
@ -646,11 +697,19 @@ export default function ExerciseProgressionPathBuilder({
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
style={{ fontSize: '12px', flexShrink: 0 }}
|
style={{ fontSize: '12px', flexShrink: 0 }}
|
||||||
disabled={quickSaving || pathSteps.length >= maxSteps}
|
disabled={
|
||||||
onClick={() => runGapFillAiSuggest(offer)}
|
quickSaving ||
|
||||||
|
(isGapOfferBlockedByPathCapacity(offer, pathSteps.length, maxSteps) &&
|
||||||
|
maxSteps >= PATH_STEPS_HARD_MAX)
|
||||||
|
}
|
||||||
|
onClick={() => handleGapFillClick(offer)}
|
||||||
title={
|
title={
|
||||||
pathSteps.length >= maxSteps
|
isGapOfferBlockedByPathCapacity(offer, pathSteps.length, maxSteps)
|
||||||
? `Pfad hat bereits ${maxSteps} Schritte — zuerst einen Schritt entfernen.`
|
? 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'
|
: 'KI-Entwurf mit Pfad-Kontext generieren'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user