diff --git a/src/App.jsx b/src/App.jsx index f4280b3..98f8666 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,6 +8,7 @@ import RegisterPage from './pages/RegisterPage'; import UsersPage from './pages/UsersPage'; import PostsPage from './pages/PostsPage'; import CreateNewsPage from './pages/CreateNewsPage'; +import OrganizationPage from './pages/OrganizationPage'; import PushPage from './pages/PushPage'; const theme = createTheme({ @@ -68,6 +69,14 @@ export default function App() { } /> + + + + } + /> , roles: ['ADMIN'] }, { label: 'Posts', path: '/posts', icon: , roles: ['ADMIN', 'REPORTER'] }, { label: 'Push', path: '/push', icon: , roles: ['ADMIN'] }, + { label: 'Organization', path: '/organizations', icon: , roles: ['ADMIN', 'REPORTER'] }, ]; export default function Layout() { diff --git a/src/pages/OrganizationPage.jsx b/src/pages/OrganizationPage.jsx new file mode 100644 index 0000000..bec8c0d --- /dev/null +++ b/src/pages/OrganizationPage.jsx @@ -0,0 +1,201 @@ +import { useEffect, useState } from 'react'; +import { + Box, Typography, Alert, IconButton, Tooltip, + Dialog, DialogTitle, DialogContent, DialogActions, + Button, Stack, TextField, CircularProgress +} from '@mui/material'; +import { DataGrid } from '@mui/x-data-grid'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import AddIcon from '@mui/icons-material/Add'; +import { Chip } from '@mui/material'; +import axiosInstance from '../api/axiosInstance'; + +export default function OrganizationPage() { + const [rows, setRows] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + + const [open, setOpen] = useState(false); + const [creating, setCreating] = useState(false); + const [creatingError, setCreatingError] = useState(''); + + const [createForm, setCreateForm] = useState({ + name: '', + email: '' + }); + + const loadOrganizations = async () => { + try { + const { data } = await axiosInstance.get('/api/organization'); + setRows(data); + } catch { + setError('Fehler beim Laden der Organisationen'); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadOrganizations(); + }, []); + + const handleFormChange = (e) => { + const { name, value } = e.target; + setCreateForm((prev) => ({ ...prev, [name]: value })); + }; + + const handleCreate = async () => { + setCreating(true); + setCreatingError(''); + + try { + const jsonPayload = { + name: createForm.name, + ownerEmail: createForm.email + }; + + await axiosInstance.post('/api/organization', jsonPayload); + + await loadOrganizations(); // Liste neu laden + handleCreateClose(); + + } catch (err) { + setCreatingError(err.response?.data?.message ?? 'Erstellen fehlgeschlagen'); + } finally { + setCreating(false); + } + }; + + const handleCreateClose = () => { + setOpen(false); + setCreateForm({ name: '', email: '' }); + setCreatingError(''); + }; + + const handleDelete = async (id) => { + if (!window.confirm('Organisation wirklich löschen?')) return; + + try { + await axiosInstance.delete(`/api/organization/${id}`); + setRows((prev) => prev.filter((r) => r.id !== id)); + } catch { + alert('Löschen fehlgeschlagen'); + } + }; + + const columns = [ + { field: 'id', headerName: 'ID', width: 70 }, + { field: 'name', headerName: 'Name', flex: 1.5 }, + { + field: 'ownerEmail', + headerName: 'Verwalter', + flex: 2, + renderCell: ({ value }) => ( + + {value} + + ), + }, + { + field: 'active', + headerName: 'Status', + width: 150, + renderCell: ({ value }) => ( + + ), + }, + { + field: 'actions', + headerName: '', + width: 60, + sortable: false, + renderCell: ({ row }) => ( + + handleDelete(row.id)}> + + + + ), + }, + ]; + + return ( + + {/* Header */} + + Organisationen + + + + {error && {error}} + + {/* Tabelle */} + + + + + {/* Create Dialog */} + + Organisation erstellen + + + + {creatingError && {creatingError}} + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/pages/UsersPage.jsx b/src/pages/UsersPage.jsx index 1a00a8d..82e12fb 100644 --- a/src/pages/UsersPage.jsx +++ b/src/pages/UsersPage.jsx @@ -2,12 +2,13 @@ import { useEffect, useState } from 'react'; import { Box, Typography, Alert, IconButton, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, - TextField, Button, CircularProgress, Stack, Divider, - Snackbar, + TextField, Button, CircularProgress, Stack, + Snackbar, Select, MenuItem, FormControl, InputLabel, } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import LockResetIcon from '@mui/icons-material/LockReset'; +import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import axiosInstance from '../api/axiosInstance'; export default function UsersPage() { @@ -20,8 +21,11 @@ export default function UsersPage() { const [saving, setSaving] = useState(false); const [saveError, setSaveError] = useState(''); - const [resetting, setResetting] = useState(null); // user id der gerade resettet wird - const [snackbar, setSnackbar] = useState(''); // Erfolgsmeldung + const [resetting, setResetting] = useState(null); + const [snackbar, setSnackbar] = useState(''); + + const [deleteUser, setDeleteUser] = useState(null); + const [deleting, setDeleting] = useState(false); useEffect(() => { axiosInstance.get('/api/users') @@ -35,7 +39,7 @@ export default function UsersPage() { setEditForm({ email: user.email ?? '', nickname: user.nickname ?? '', - role: user.role ?? '', + role: user.role?.replace('ROLE_', '') ?? '', }); setSaveError(''); }; @@ -77,6 +81,20 @@ export default function UsersPage() { } }; + const handleDeleteConfirm = async () => { + setDeleting(true); + try { + await axiosInstance.delete(`/api/users/${deleteUser.id}`); + setRows((prev) => prev.filter((r) => r.id !== deleteUser.id)); + setSnackbar(`Nutzer ${deleteUser.nickname} wurde gelöscht.`); + setDeleteUser(null); + } catch (err) { + setSnackbar(err.response?.data?.message ?? 'Löschen fehlgeschlagen'); + } finally { + setDeleting(false); + } + }; + const columns = [ { field: 'id', headerName: 'ID', width: 80 }, { field: 'nickname', headerName: 'Anzeigename', flex: 1 }, @@ -91,7 +109,7 @@ export default function UsersPage() { { field: 'actions', headerName: '', - width: 100, + width: 130, sortable: false, renderCell: ({ row }) => ( @@ -115,6 +133,11 @@ export default function UsersPage() { + + setDeleteUser(row)}> + + + ), }, @@ -159,19 +182,19 @@ export default function UsersPage() { onChange={handleFormChange} fullWidth /> - - - - - + + Berechtigung + + @@ -187,7 +210,29 @@ export default function UsersPage() { - {/* Snackbar für Reset-Feedback */} + {/* Delete Bestätigungs-Dialog */} + setDeleteUser(null)} maxWidth="xs" fullWidth> + Nutzer löschen + + + Soll {deleteUser?.nickname} ({deleteUser?.email}) wirklich gelöscht werden? Diese Aktion kann nicht rückgängig gemacht werden. + + + + + + + + + {/* Snackbar */}