refactor: update loading state management and enhance search functionality in ExercisesListPage
Some checks failed
Deploy Development / deploy (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Failing after 1m55s

- Replaced loading state with listFetching for clearer intent in managing exercise list fetching.
- Introduced search title suggestions for improved user experience during exercise searches.
- Updated UI elements to reflect changes in loading states and added datalist for search inputs, enhancing usability.
- Adjusted text for loading indicators to provide a more localized experience.
This commit is contained in:
Lars 2026-04-28 15:24:05 +02:00
parent 756263bad4
commit d5fbc2cd5c

View File

@ -34,7 +34,7 @@ function ExercisesListPage() {
skills: [],
})
const [catalogsReady, setCatalogsReady] = useState(false)
const [loading, setLoading] = useState(true)
const [listFetching, setListFetching] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const [offset, setOffset] = useState(0)
const [hasMore, setHasMore] = useState(false)
@ -222,6 +222,12 @@ function ExercisesListPage() {
statusOptions,
])
/** Für Browser-/datalist-Vorschläge (aktuelle Treffer-Titel, begrenzt) */
const searchTitleSuggestions = useMemo(() => {
const titles = exercises.map((e) => (e.title || '').trim()).filter(Boolean)
return [...new Set(titles)].slice(0, 80)
}, [exercises])
const queryBase = useMemo(() => {
const q = {}
const n = (v) => (v === '' || v == null ? undefined : Number(v))
@ -284,7 +290,7 @@ function ExercisesListPage() {
if (!catalogsReady) return
let cancelled = false
const run = async () => {
setLoading(true)
setListFetching(true)
setOffset(0)
try {
const batch = await api.listExercises({ ...queryBase, limit: PAGE_SIZE, offset: 0 })
@ -298,7 +304,7 @@ function ExercisesListPage() {
alert('Fehler beim Laden: ' + err.message)
}
} finally {
if (!cancelled) setLoading(false)
if (!cancelled) setListFetching(false)
}
}
run()
@ -334,11 +340,11 @@ function ExercisesListPage() {
const resetAllFilters = useCallback(() => setFilters({ ...INITIAL_FILTERS }), [])
if (!catalogsReady || loading) {
if (!catalogsReady) {
return (
<div style={{ padding: '2rem', textAlign: 'center' }}>
<div className="spinner"></div>
<p>Laden...</p>
<p>Lade Kataloge…</p>
</div>
)
}
@ -363,13 +369,21 @@ function ExercisesListPage() {
<div className="card exercise-search-bar" style={{ marginBottom: '12px' }}>
<label className="form-label">Volltextsuche (Titel, Ziel, …)</label>
<datalist id="exercise-search-titles">
{searchTitleSuggestions.map((t) => (
<option key={t} value={t} />
))}
</datalist>
<input
type="search"
className="form-input"
placeholder="Suchbegriffe…"
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
autoComplete="off"
autoComplete="on"
name="exercise-fulltext-search"
list="exercise-search-titles"
enterKeyHint="search"
style={{ marginBottom: '10px' }}
/>
<label className="form-label">Ergänzende Suche / KI-Vorbereitung (Beta)</label>
@ -379,7 +393,10 @@ function ExercisesListPage() {
placeholder="zweiter Begriff — zusätzliche Volltextsuche (ODER)"
value={aiSearchInput}
onChange={(e) => setAiSearchInput(e.target.value)}
autoComplete="off"
autoComplete="on"
name="exercise-ai-search"
list="exercise-search-titles"
enterKeyHint="search"
/>
<div className="exercise-search-bar__actions">
<button type="button" className="btn btn-secondary exercise-filter-trigger" onClick={() => setFilterModalOpen(true)}>
@ -416,7 +433,8 @@ function ExercisesListPage() {
</div>
) : null}
<p style={{ fontSize: '12px', color: 'var(--text3)', marginTop: '10px', marginBottom: 0 }}>
Vereins-/Trainerfilter folgen später. Fachliche Filter über „Filter“ – zwischen Feldern UND, Auswahl mehrerer Werte je Feld mit ODER.
Trefferliste aktualisiert sich kurz nach Eingabe. Titel der aktuellen Liste erscheinen als Vorschläge (Pfeil im
Feld). Fachliche Filter über „Filter“ – zwischen Feldern UND, Auswahl mehrerer Werte je Feld mit ODER.
</p>
</div>
@ -584,7 +602,12 @@ function ExercisesListPage() {
</div>
)}
{exercises.length === 0 ? (
{listFetching && exercises.length === 0 ? (
<div className="card" style={{ textAlign: 'center', padding: '2rem' }}>
<div className="spinner"></div>
<p style={{ color: 'var(--text2)', marginTop: '12px' }}>Lade Übungen…</p>
</div>
) : exercises.length === 0 ? (
<div className="card">
<p style={{ color: 'var(--text2)', textAlign: 'center' }}>
Keine Übungen gefunden.
@ -592,6 +615,9 @@ function ExercisesListPage() {
</div>
) : (
<>
{listFetching ? (
<p style={{ fontSize: '13px', color: 'var(--text3)', marginBottom: '8px' }}>Aktualisiere Treffer…</p>
) : null}
<p style={{ fontSize: '13px', color: 'var(--text2)', marginBottom: '10px' }}>
{exercises.length} angezeigt
{hasMore ? ' · es gibt weitere Einträge' : ''}