fix bookstack display error
This commit is contained in:
@@ -78,7 +78,23 @@ const BookStackRecentWidget: React.FC = () => {
|
|||||||
const configured = data?.configured ?? true;
|
const configured = data?.configured ?? true;
|
||||||
const pages = data?.data ?? [];
|
const pages = data?.data ?? [];
|
||||||
|
|
||||||
if (!configured) return null;
|
if (!configured) {
|
||||||
|
return (
|
||||||
|
<Card sx={{ height: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
|
||||||
|
<MenuBook color="disabled" />
|
||||||
|
<Typography variant="h6" sx={{ flexGrow: 1 }} color="text.secondary">
|
||||||
|
BookStack — Neueste Seiten
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ py: 2, textAlign: 'center' }}>
|
||||||
|
BookStack nicht eingerichtet
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
|
Skeleton,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Search, MenuBook } from '@mui/icons-material';
|
import { Search, MenuBook } from '@mui/icons-material';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { bookstackApi } from '../../services/bookstack';
|
import { bookstackApi } from '../../services/bookstack';
|
||||||
import type { BookStackSearchResult } from '../../types/bookstack.types';
|
import type { BookStackSearchResult } from '../../types/bookstack.types';
|
||||||
|
|
||||||
@@ -63,29 +65,47 @@ const ResultRow: React.FC<{ result: BookStackSearchResult; showDivider: boolean
|
|||||||
const BookStackSearchWidget: React.FC = () => {
|
const BookStackSearchWidget: React.FC = () => {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [results, setResults] = useState<BookStackSearchResult[]>([]);
|
const [results, setResults] = useState<BookStackSearchResult[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [configured, setConfigured] = useState(true);
|
|
||||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
const latestQueryRef = useRef<string>('');
|
||||||
|
|
||||||
|
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(() => {
|
useEffect(() => {
|
||||||
if (debounceRef.current) clearTimeout(debounceRef.current);
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
||||||
|
|
||||||
if (!query.trim()) {
|
if (!query.trim()) {
|
||||||
setResults([]);
|
setResults([]);
|
||||||
setLoading(false);
|
setSearching(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setSearching(true);
|
||||||
|
const thisQuery = query.trim();
|
||||||
|
latestQueryRef.current = thisQuery;
|
||||||
|
|
||||||
debounceRef.current = setTimeout(async () => {
|
debounceRef.current = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await bookstackApi.search(query.trim());
|
const response = await bookstackApi.search(thisQuery);
|
||||||
setConfigured(response.configured);
|
if (latestQueryRef.current === thisQuery) {
|
||||||
setResults(response.data);
|
setResults(response.data);
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
if (latestQueryRef.current === thisQuery) {
|
||||||
setResults([]);
|
setResults([]);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
if (latestQueryRef.current === thisQuery) {
|
||||||
|
setSearching(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 400);
|
}, 400);
|
||||||
|
|
||||||
@@ -94,7 +114,37 @@ const BookStackSearchWidget: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
if (!configured) return null;
|
if (configured === undefined) {
|
||||||
|
return (
|
||||||
|
<Card sx={{ height: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
|
||||||
|
<MenuBook color="primary" />
|
||||||
|
<Skeleton variant="text" width={160} height={32} />
|
||||||
|
</Box>
|
||||||
|
<Skeleton variant="rectangular" height={40} sx={{ borderRadius: 1 }} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configured === false) {
|
||||||
|
return (
|
||||||
|
<Card sx={{ height: '100%' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 2 }}>
|
||||||
|
<MenuBook color="disabled" />
|
||||||
|
<Typography variant="h6" sx={{ flexGrow: 1 }} color="text.secondary">
|
||||||
|
BookStack — Suche
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ py: 2, textAlign: 'center' }}>
|
||||||
|
BookStack nicht eingerichtet
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -121,13 +171,13 @@ const BookStackSearchWidget: React.FC = () => {
|
|||||||
InputProps={{
|
InputProps={{
|
||||||
startAdornment: (
|
startAdornment: (
|
||||||
<InputAdornment position="start">
|
<InputAdornment position="start">
|
||||||
{loading ? <CircularProgress size={16} /> : <Search fontSize="small" />}
|
{searching ? <CircularProgress size={16} /> : <Search fontSize="small" />}
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!loading && query.trim() && results.length === 0 && (
|
{!searching && query.trim() && results.length === 0 && (
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ py: 2, textAlign: 'center' }}>
|
<Typography variant="body2" color="text.secondary" sx={{ py: 2, textAlign: 'center' }}>
|
||||||
Keine Ergebnisse für „{query}"
|
Keine Ergebnisse für „{query}"
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ export { default as StatsCard } from './StatsCard';
|
|||||||
export { default as DashboardLayout } from './DashboardLayout';
|
export { default as DashboardLayout } from './DashboardLayout';
|
||||||
export { default as PersonalWarningsBanner } from './PersonalWarningsBanner';
|
export { default as PersonalWarningsBanner } from './PersonalWarningsBanner';
|
||||||
export { default as UpcomingEventsWidget } from './UpcomingEventsWidget';
|
export { default as UpcomingEventsWidget } from './UpcomingEventsWidget';
|
||||||
|
export { default as BookStackRecentWidget } from './BookStackRecentWidget';
|
||||||
|
export { default as BookStackSearchWidget } from './BookStackSearchWidget';
|
||||||
|
|||||||
Reference in New Issue
Block a user