new features
This commit is contained in:
106
frontend/src/components/admin/DebugTab.tsx
Normal file
106
frontend/src/components/admin/DebugTab.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Box, Paper, Typography, Button, Autocomplete, TextField,
|
||||
Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions,
|
||||
CircularProgress,
|
||||
} from '@mui/material';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { adminApi } from '../../services/admin';
|
||||
import { useNotification } from '../../contexts/NotificationContext';
|
||||
import type { UserOverview } from '../../types/admin.types';
|
||||
|
||||
export default function DebugTab() {
|
||||
const { showSuccess, showError } = useNotification();
|
||||
|
||||
const { data: users = [], isLoading: usersLoading } = useQuery<UserOverview[]>({
|
||||
queryKey: ['admin', 'users'],
|
||||
queryFn: adminApi.getUsers,
|
||||
});
|
||||
|
||||
const [selectedUser, setSelectedUser] = useState<UserOverview | null>(null);
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
|
||||
const handleDelete = async () => {
|
||||
if (!selectedUser) return;
|
||||
setDeleting(true);
|
||||
try {
|
||||
await adminApi.deleteUserProfile(selectedUser.id);
|
||||
showSuccess(`Profildaten fuer ${selectedUser.name || selectedUser.email} geloescht`);
|
||||
setSelectedUser(null);
|
||||
} catch (err: unknown) {
|
||||
const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || 'Fehler beim Loeschen';
|
||||
showError(msg);
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
setConfirmOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: 600 }}>
|
||||
<Typography variant="h6" sx={{ mb: 1 }}>Debug-Werkzeuge</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
Werkzeuge fuer Fehlersuche und Datenbereinigung.
|
||||
</Typography>
|
||||
|
||||
<Paper sx={{ p: 3 }}>
|
||||
<Typography variant="subtitle1" fontWeight={600} sx={{ mb: 1 }}>
|
||||
Profildaten loeschen
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Loescht die synchronisierten Profildaten (mitglieder_profile) eines Benutzers.
|
||||
Beim naechsten Login werden die Daten erneut von Authentik und FDISK synchronisiert.
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Autocomplete
|
||||
options={users}
|
||||
loading={usersLoading}
|
||||
value={selectedUser}
|
||||
onChange={(_e, v) => setSelectedUser(v)}
|
||||
getOptionLabel={(u) => `${u.name || 'Kein Name'} (${u.email})`}
|
||||
isOptionEqualToValue={(a, b) => a.id === b.id}
|
||||
sx={{ minWidth: 320, flex: 1 }}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label="Benutzer waehlen" size="small" />
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
disabled={!selectedUser || deleting}
|
||||
startIcon={deleting ? <CircularProgress size={16} /> : <DeleteIcon />}
|
||||
onClick={() => setConfirmOpen(true)}
|
||||
>
|
||||
Profildaten loeschen
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Dialog open={confirmOpen} onClose={() => !deleting && setConfirmOpen(false)}>
|
||||
<DialogTitle>Profildaten loeschen?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
Profildaten fuer <strong>{selectedUser?.name || selectedUser?.email}</strong> werden geloescht.
|
||||
Beim naechsten Login werden die Daten erneut synchronisiert.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setConfirmOpen(false)} disabled={deleting}>Abbrechen</Button>
|
||||
<Button
|
||||
onClick={handleDelete}
|
||||
color="error"
|
||||
variant="contained"
|
||||
disabled={deleting}
|
||||
startIcon={deleting ? <CircularProgress size={16} /> : <DeleteIcon />}
|
||||
>
|
||||
{deleting ? 'Wird geloescht...' : 'Loeschen'}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
ExpandLess,
|
||||
LocalShipping,
|
||||
Store,
|
||||
BugReport,
|
||||
} from '@mui/icons-material';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@@ -65,6 +66,7 @@ const adminSubItems: SubItem[] = [
|
||||
{ text: 'Berechtigungen', path: '/admin?tab=7' },
|
||||
{ text: 'Bestellungen', path: '/admin?tab=8' },
|
||||
{ text: 'Datenverwaltung', path: '/admin?tab=9' },
|
||||
{ text: 'Debug', path: '/admin?tab=10' },
|
||||
];
|
||||
|
||||
const baseNavigationItems: NavigationItem[] = [
|
||||
@@ -124,8 +126,24 @@ const baseNavigationItems: NavigationItem[] = [
|
||||
text: 'Shop',
|
||||
icon: <Store />,
|
||||
path: '/shop',
|
||||
subItems: [
|
||||
{ text: 'Katalog', path: '/shop?tab=0' },
|
||||
{ text: 'Meine Anfragen', path: '/shop?tab=1' },
|
||||
{ text: 'Alle Anfragen', path: '/shop?tab=2' },
|
||||
{ text: 'Übersicht', path: '/shop?tab=3' },
|
||||
],
|
||||
permission: 'shop:view',
|
||||
},
|
||||
{
|
||||
text: 'Issues',
|
||||
icon: <BugReport />,
|
||||
path: '/issues',
|
||||
subItems: [
|
||||
{ text: 'Meine Issues', path: '/issues?tab=0' },
|
||||
{ text: 'Alle Issues', path: '/issues?tab=1' },
|
||||
],
|
||||
permission: 'issues:create',
|
||||
},
|
||||
];
|
||||
|
||||
const adminItem: NavigationItem = {
|
||||
|
||||
Reference in New Issue
Block a user