resolve issues with new features

This commit is contained in:
Matthias Hochmeister
2026-03-12 10:21:26 +01:00
parent 31f1414e06
commit d5be68ca63
13 changed files with 275 additions and 86 deletions

View File

@@ -17,29 +17,38 @@ import {
IconButton,
Typography,
CircularProgress,
Select,
MenuItem,
FormControl,
InputLabel,
} from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import AddIcon from '@mui/icons-material/Add';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { adminApi } from '../../services/admin';
import { useNotification } from '../../contexts/NotificationContext';
import type { PingResult } from '../../types/admin.types';
function ServiceManagerTab() {
const queryClient = useQueryClient();
const { showError } = useNotification();
const [dialogOpen, setDialogOpen] = useState(false);
const [newName, setNewName] = useState('');
const [newUrl, setNewUrl] = useState('');
const [refreshInterval, setRefreshInterval] = useState(15000);
const { data: services, isLoading: servicesLoading } = useQuery({
queryKey: ['admin', 'services'],
queryFn: adminApi.getServices,
refetchInterval: 15000,
refetchInterval: refreshInterval || false,
placeholderData: (previousData: any) => previousData,
});
const { data: pingResults, isLoading: pingLoading } = useQuery({
queryKey: ['admin', 'services', 'ping'],
queryFn: adminApi.pingAll,
refetchInterval: 15000,
refetchInterval: refreshInterval || false,
placeholderData: (previousData: any) => previousData,
});
const createMutation = useMutation({
@@ -50,6 +59,10 @@ function ServiceManagerTab() {
setNewName('');
setNewUrl('');
},
onError: (error: any) => {
const message = error?.response?.data?.message || 'Service konnte nicht erstellt werden';
showError(message);
},
});
const deleteMutation = useMutation({
@@ -57,6 +70,9 @@ function ServiceManagerTab() {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin', 'services'] });
},
onError: () => {
showError('Service konnte nicht gelöscht werden');
},
});
const getPingForUrl = (url: string): PingResult | undefined => {
@@ -102,9 +118,25 @@ function ServiceManagerTab() {
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6">Service Monitor</Typography>
<Button startIcon={<AddIcon />} variant="contained" onClick={() => setDialogOpen(true)}>
Service hinzufuegen
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<FormControl size="small" sx={{ minWidth: 160 }}>
<InputLabel>Aktualisierung</InputLabel>
<Select
value={refreshInterval}
label="Aktualisierung"
onChange={(e) => setRefreshInterval(Number(e.target.value))}
>
<MenuItem value={5000}>5 Sekunden</MenuItem>
<MenuItem value={15000}>15 Sekunden</MenuItem>
<MenuItem value={30000}>30 Sekunden</MenuItem>
<MenuItem value={60000}>60 Sekunden</MenuItem>
<MenuItem value={0}>Aus</MenuItem>
</Select>
</FormControl>
<Button startIcon={<AddIcon />} variant="contained" onClick={() => setDialogOpen(true)}>
Service hinzufuegen
</Button>
</Box>
</Box>
<TableContainer component={Paper}>
@@ -185,6 +217,7 @@ function ServiceManagerTab() {
fullWidth
value={newUrl}
onChange={(e) => setNewUrl(e.target.value)}
helperText="Vollständige URL mit http:// oder https://"
/>
</DialogContent>
<DialogActions>

View File

@@ -19,16 +19,27 @@ function formatBytes(bytes: number): string {
}
function SystemHealthTab() {
const { data: health, isLoading } = useQuery({
const { data: health, isLoading, isError } = useQuery({
queryKey: ['admin', 'system', 'health'],
queryFn: adminApi.getSystemHealth,
refetchInterval: 30000,
});
if (isLoading || !health) {
if (isLoading) {
return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>;
}
if (isError || !health) {
return (
<Box sx={{ p: 4, textAlign: 'center' }}>
<Typography color="error" gutterBottom>Systemdaten konnten nicht geladen werden.</Typography>
<Typography variant="body2" color="text.secondary">
Bitte versuchen Sie es später erneut.
</Typography>
</Box>
);
}
const heapPercent = (health.memoryUsage.heapUsed / health.memoryUsage.heapTotal) * 100;
return (

View File

@@ -40,7 +40,7 @@ function UserOverviewTab() {
const [sortKey, setSortKey] = useState<SortKey>('name');
const [sortDir, setSortDir] = useState<SortDir>('asc');
const { data: users, isLoading } = useQuery({
const { data: users, isLoading, isError } = useQuery({
queryKey: ['admin', 'users'],
queryFn: adminApi.getUsers,
});
@@ -82,6 +82,17 @@ function UserOverviewTab() {
return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>;
}
if (isError) {
return (
<Box sx={{ p: 4, textAlign: 'center' }}>
<Typography color="error" gutterBottom>Benutzerdaten konnten nicht geladen werden.</Typography>
<Typography variant="body2" color="text.secondary">
Bitte versuchen Sie es später erneut.
</Typography>
</Box>
);
}
return (
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>