Refactor ExercisePickerModal to Utilize Effective Query for AI Suggestions
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 40s
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 `effectivePickerQuery` to streamline search input handling, combining `debouncedSearch` and `debouncedAi` for improved query accuracy.
- Updated the `useExerciseAiQuickCreateFields` hook to use the new effective query, enhancing the quick create functionality.
- Modified conditional checks to utilize `effectivePickerQuery`, ensuring better user feedback based on search input.
- Improved placeholder text and labels for clarity in the search fields, enhancing user experience during exercise selection.
This commit is contained in:
Lars 2026-05-22 22:21:06 +02:00
parent 45e3b5f4f6
commit 905bce198f

View File

@ -75,6 +75,11 @@ export default function ExercisePickerModal({
const usePlanningSearch = Boolean(planningContext?.unitId && Number(planningContext.unitId) > 0) const usePlanningSearch = Boolean(planningContext?.unitId && Number(planningContext.unitId) > 0)
const effectivePickerQuery = useMemo(
() => [debouncedSearch, debouncedAi].filter(Boolean).join(' ').trim(),
[debouncedSearch, debouncedAi]
)
const { const {
title: quickTitle, title: quickTitle,
sketch: quickSketch, sketch: quickSketch,
@ -83,7 +88,7 @@ export default function ExercisePickerModal({
setSketch: setQuickSketch, setSketch: setQuickSketch,
setFocusAreaId: setQuickFocusAreaId, setFocusAreaId: setQuickFocusAreaId,
resetQuickCreateFields, resetQuickCreateFields,
} = useExerciseAiQuickCreateFields(debouncedSearch, { enabled: open && enableQuickCreateDraft }) } = useExerciseAiQuickCreateFields(effectivePickerQuery, { enabled: open && enableQuickCreateDraft })
const toggleMultiPick = (ex) => { const toggleMultiPick = (ex) => {
setMultiPicked((prev) => setMultiPicked((prev) =>
@ -106,7 +111,7 @@ export default function ExercisePickerModal({
catalogsReady && catalogsReady &&
!loading && !loading &&
list.length === 0 && list.length === 0 &&
(usePlanningSearch || debouncedSearch.length >= 3) (usePlanningSearch || effectivePickerQuery.length >= 3)
useEffect(() => { useEffect(() => {
if (!open) return if (!open) return
@ -246,6 +251,7 @@ export default function ExercisePickerModal({
if (filters.include_archived) q.include_archived = true if (filters.include_archived) q.include_archived = true
if (debouncedSearch) q.search = debouncedSearch if (debouncedSearch) q.search = debouncedSearch
if (debouncedAi) q.ai_search = debouncedAi if (debouncedAi) q.ai_search = debouncedAi
if (!debouncedSearch && debouncedAi) q.search = debouncedAi
if ( if (
Array.isArray(exerciseKindAny) && Array.isArray(exerciseKindAny) &&
exerciseKindAny.length > 0 exerciseKindAny.length > 0
@ -260,7 +266,7 @@ export default function ExercisePickerModal({
setLoading(true) setLoading(true)
try { try {
if (usePlanningSearch) { if (usePlanningSearch) {
const query = [debouncedSearch, debouncedAi].filter(Boolean).join(' ').trim() const query = effectivePickerQuery
const res = await api.suggestPlanningExercises({ const res = await api.suggestPlanningExercises({
unit_id: Number(planningContext.unitId), unit_id: Number(planningContext.unitId),
section_order_index: section_order_index:
@ -338,6 +344,7 @@ export default function ExercisePickerModal({
queryBase, queryBase,
usePlanningSearch, usePlanningSearch,
planningContext, planningContext,
effectivePickerQuery,
debouncedSearch, debouncedSearch,
debouncedAi, debouncedAi,
exerciseKindAny, exerciseKindAny,
@ -571,14 +578,14 @@ export default function ExercisePickerModal({
<div style={{ display: 'grid', gap: '0.65rem' }}> <div style={{ display: 'grid', gap: '0.65rem' }}>
<div> <div>
<label className="form-label"> <label className="form-label">
{usePlanningSearch ? 'Planungs-Suche' : 'Volltextsuche'} {usePlanningSearch ? 'Planungs-Anfrage' : 'Volltextsuche'}
</label> </label>
<input <input
type="search" type="search"
className="form-input" className="form-input"
placeholder={ placeholder={
usePlanningSearch usePlanningSearch
? 'z. B. nächste Übung, Vertiefung, Reaktion mit Partner …' ? 'z. B. Schlage mir die nächste Übung vor, Vertiefung, Reaktion mit Partner …'
: 'Stichwort, Titelfragment…' : 'Stichwort, Titelfragment…'
} }
value={searchInput} value={searchInput}
@ -588,17 +595,30 @@ export default function ExercisePickerModal({
</div> </div>
<div> <div>
<label className="form-label"> <label className="form-label">
Semantisch /{' '} {usePlanningSearch ? 'Planungs-Anfrage (Zusatz, optional)' : 'Semantisch / '}
<span title="aktuell gleiche Datenbanksuche wie Volltext; später KI-Verfeinerung möglich">KI-Feld</span> {!usePlanningSearch ? (
<span title="aktuell gleiche Datenbanksuche wie Volltext; später KI-Verfeinerung möglich">
KI-Feld
</span>
) : null}
</label> </label>
<input <input
type="search" type="search"
className="form-input" className="form-input"
placeholder="zweites Suchkonzept oder Umschreibung…" placeholder={
usePlanningSearch
? 'Alternative Formulierung — wird mit oben kombiniert'
: 'zweites Suchkonzept oder Umschreibung…'
}
value={aiSearchInput} value={aiSearchInput}
onChange={(e) => setAiSearchInput(e.target.value)} onChange={(e) => setAiSearchInput(e.target.value)}
autoComplete="off" autoComplete="off"
/> />
{usePlanningSearch ? (
<p style={{ margin: '4px 0 0', fontSize: '11px', color: 'var(--text3)' }}>
Beide Felder bilden eine gemeinsame Planungs-Anfrage.
</p>
) : null}
</div> </div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center' }}> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center' }}>
<button type="button" className="btn btn-secondary" onClick={() => setFilterOpen(!filterOpen)}> <button type="button" className="btn btn-secondary" onClick={() => setFilterOpen(!filterOpen)}>
@ -755,7 +775,7 @@ export default function ExercisePickerModal({
) : list.length === 0 ? ( ) : list.length === 0 ? (
showQuickCreateOffer ? ( showQuickCreateOffer ? (
<ExerciseAiQuickCreateOffer <ExerciseAiQuickCreateOffer
searchLabel={debouncedSearch} searchLabel={effectivePickerQuery}
title={quickTitle} title={quickTitle}
onTitleChange={setQuickTitle} onTitleChange={setQuickTitle}
sketch={quickSketch} sketch={quickSketch}
@ -770,9 +790,13 @@ export default function ExercisePickerModal({
/> />
) : ( ) : (
<p style={{ color: 'var(--text2)', textAlign: 'center' }}> <p style={{ color: 'var(--text2)', textAlign: 'center' }}>
{debouncedSearch.length >= 3 {usePlanningSearch
? 'Keine Treffer.' ? effectivePickerQuery
: 'Suchbegriff eingeben (mind. 3 Zeichen) …'} ? 'Keine KI-Vorschläge für diese Anfrage.'
: 'Keine Vorschläge — Einheit speichern und Planungskontext prüfen, oder Anfrage eingeben.'
: effectivePickerQuery.length >= 3
? 'Keine Treffer.'
: 'Suchbegriff eingeben (mind. 3 Zeichen) …'}
</p> </p>
) )
) : ( ) : (