Enhance Exercise Progression Path Management with Dynamic Step Capacity
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 46s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m13s
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 46s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m13s
- Introduced logic to manage path capacity dynamically, allowing users to expand the maximum number of steps when inserting new offers. - Implemented confirmation prompts for users when the path is full, enhancing user experience and decision-making. - Updated the `ExerciseProgressionPathBuilder` component to reflect these changes, improving the handling of gap-fill offers and user interactions. - Adjusted UI messages to clarify the implications of adding new steps and the conditions under which users can expand the path.
This commit is contained in:
parent
d4e9bded23
commit
0677663268
|
|
@ -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({
|
|||
>
|
||||
<strong style={{ fontSize: '13px' }}>Fehlende Schritte — mit KI anlegen</strong>
|
||||
<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).
|
||||
„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.'}
|
||||
</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||
{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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user