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';