diff --git a/frontend/src/components/dashboard/BookStackRecentWidget.tsx b/frontend/src/components/dashboard/BookStackRecentWidget.tsx index e4678e0..0cb2f53 100644 --- a/frontend/src/components/dashboard/BookStackRecentWidget.tsx +++ b/frontend/src/components/dashboard/BookStackRecentWidget.tsx @@ -78,7 +78,23 @@ const BookStackRecentWidget: React.FC = () => { const configured = data?.configured ?? true; const pages = data?.data ?? []; - if (!configured) return null; + if (!configured) { + return ( + + + + + + BookStack — Neueste Seiten + + + + BookStack nicht eingerichtet + + + + ); + } return ( { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); - const [loading, setLoading] = useState(false); - const [configured, setConfigured] = useState(true); + const [searching, setSearching] = useState(false); const debounceRef = useRef | null>(null); + const latestQueryRef = useRef(''); + + const { data, isLoading: configLoading } = useQuery({ + queryKey: ['bookstack-recent'], + queryFn: () => bookstackApi.getRecent(), + refetchInterval: 5 * 60 * 1000, + retry: 1, + }); + + // undefined while loading, true/false once known + const configured = configLoading ? undefined : (data?.configured ?? false); useEffect(() => { if (debounceRef.current) clearTimeout(debounceRef.current); if (!query.trim()) { setResults([]); - setLoading(false); + setSearching(false); return; } - setLoading(true); + setSearching(true); + const thisQuery = query.trim(); + latestQueryRef.current = thisQuery; + debounceRef.current = setTimeout(async () => { try { - const response = await bookstackApi.search(query.trim()); - setConfigured(response.configured); - setResults(response.data); + const response = await bookstackApi.search(thisQuery); + if (latestQueryRef.current === thisQuery) { + setResults(response.data); + } } catch { - setResults([]); + if (latestQueryRef.current === thisQuery) { + setResults([]); + } } finally { - setLoading(false); + if (latestQueryRef.current === thisQuery) { + setSearching(false); + } } }, 400); @@ -94,7 +114,37 @@ const BookStackSearchWidget: React.FC = () => { }; }, [query]); - if (!configured) return null; + if (configured === undefined) { + return ( + + + + + + + + + + ); + } + + if (configured === false) { + return ( + + + + + + BookStack — Suche + + + + BookStack nicht eingerichtet + + + + ); + } return ( { InputProps={{ startAdornment: ( - {loading ? : } + {searching ? : } ), }} /> - {!loading && query.trim() && results.length === 0 && ( + {!searching && query.trim() && results.length === 0 && ( Keine Ergebnisse für „{query}" diff --git a/frontend/src/components/dashboard/index.ts b/frontend/src/components/dashboard/index.ts index 015289d..2fdd9d4 100644 --- a/frontend/src/components/dashboard/index.ts +++ b/frontend/src/components/dashboard/index.ts @@ -4,3 +4,5 @@ export { default as StatsCard } from './StatsCard'; export { default as DashboardLayout } from './DashboardLayout'; export { default as PersonalWarningsBanner } from './PersonalWarningsBanner'; export { default as UpcomingEventsWidget } from './UpcomingEventsWidget'; +export { default as BookStackRecentWidget } from './BookStackRecentWidget'; +export { default as BookStackSearchWidget } from './BookStackSearchWidget';