update FDISK sync
This commit is contained in:
129
frontend/src/components/admin/FdiskSyncTab.tsx
Normal file
129
frontend/src/components/admin/FdiskSyncTab.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import {
|
||||
Box, Button, Card, CardContent, Chip, CircularProgress, Typography,
|
||||
} from '@mui/material';
|
||||
import SyncIcon from '@mui/icons-material/Sync';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { adminApi } from '../../services/admin';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
|
||||
function FdiskSyncTab() {
|
||||
const queryClient = useQueryClient();
|
||||
const { showSuccess, showError } = useNotification();
|
||||
const logBoxRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['admin', 'fdisk-sync', 'logs'],
|
||||
queryFn: adminApi.fdiskSyncLogs,
|
||||
refetchInterval: 5000,
|
||||
});
|
||||
|
||||
// Auto-scroll log box to bottom when new lines arrive
|
||||
useEffect(() => {
|
||||
if (logBoxRef.current) {
|
||||
logBoxRef.current.scrollTop = logBoxRef.current.scrollHeight;
|
||||
}
|
||||
}, [data?.logs.length]);
|
||||
|
||||
const triggerMutation = useMutation({
|
||||
mutationFn: adminApi.fdiskSyncTrigger,
|
||||
onSuccess: () => {
|
||||
showSuccess('Sync gestartet');
|
||||
queryClient.invalidateQueries({ queryKey: ['admin', 'fdisk-sync', 'logs'] });
|
||||
},
|
||||
onError: (err: unknown) => {
|
||||
const msg = (err as { response?: { status?: number } })?.response?.status === 409
|
||||
? 'Sync läuft bereits'
|
||||
: 'Sync konnte nicht gestartet werden';
|
||||
showError(msg);
|
||||
},
|
||||
});
|
||||
|
||||
const running = data?.running ?? false;
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<Card>
|
||||
<CardContent sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h6" gutterBottom>FDISK Mitglieder-Sync</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Synchronisiert Mitgliederdaten und Ausbildungen aus FDISK in die Datenbank.
|
||||
Läuft automatisch täglich um Mitternacht.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Chip
|
||||
label={running ? 'Läuft…' : 'Bereit'}
|
||||
color={running ? 'warning' : 'success'}
|
||||
size="small"
|
||||
icon={running ? <CircularProgress size={12} color="inherit" /> : undefined}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<SyncIcon />}
|
||||
onClick={() => triggerMutation.mutate()}
|
||||
disabled={running || triggerMutation.isPending}
|
||||
>
|
||||
Jetzt synchronisieren
|
||||
</Button>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="subtitle2" gutterBottom>Protokoll (letzte 500 Zeilen)</Typography>
|
||||
{isLoading && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
||||
<CircularProgress size={28} />
|
||||
</Box>
|
||||
)}
|
||||
{isError && (
|
||||
<Typography color="error" variant="body2">
|
||||
Sync-Dienst nicht erreichbar. Läuft der fdisk-sync Container?
|
||||
</Typography>
|
||||
)}
|
||||
{!isLoading && !isError && (
|
||||
<Box
|
||||
ref={logBoxRef}
|
||||
sx={{
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.75rem',
|
||||
bgcolor: 'grey.900',
|
||||
color: 'grey.100',
|
||||
borderRadius: 1,
|
||||
p: 1.5,
|
||||
maxHeight: 500,
|
||||
overflowY: 'auto',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-all',
|
||||
}}
|
||||
>
|
||||
{(data?.logs ?? []).length === 0 ? (
|
||||
<Typography variant="caption" color="grey.500">Noch keine Logs vorhanden.</Typography>
|
||||
) : (
|
||||
(data?.logs ?? []).map((entry, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
component="span"
|
||||
sx={{
|
||||
display: 'block',
|
||||
color: entry.line.includes('ERROR') || entry.line.includes('WARN')
|
||||
? (entry.line.includes('ERROR') ? 'error.light' : 'warning.light')
|
||||
: 'inherit',
|
||||
}}
|
||||
>
|
||||
{entry.line}
|
||||
</Box>
|
||||
))
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default FdiskSyncTab;
|
||||
Reference in New Issue
Block a user