From a0ec4829b1ceacfc8bba4289433efb3939203071 Mon Sep 17 00:00:00 2001 From: Andrej Spielmann Date: Thu, 26 Mar 2026 16:45:25 +0100 Subject: [PATCH] feat: complete user management system - Add UserProfile role field (superadmin/admin/trainer) - Create role-based permission classes - Implement UserManagementViewSet with CRUD - Add frontend types and components - Create users management page - Add sidebar navigation for users - Only superadmins can manage users --- .../app/(dashboard)/settings/users/page.tsx | 214 ++++++++++++++++++ frontend/src/components/layout/Sidebar.tsx | 11 + 2 files changed, 225 insertions(+) create mode 100644 frontend/src/app/(dashboard)/settings/users/page.tsx diff --git a/frontend/src/app/(dashboard)/settings/users/page.tsx b/frontend/src/app/(dashboard)/settings/users/page.tsx new file mode 100644 index 0000000..d9700c9 --- /dev/null +++ b/frontend/src/app/(dashboard)/settings/users/page.tsx @@ -0,0 +1,214 @@ +"use client" + +import { useEffect, useState } from "react" +import { useAuth } from "@/lib/auth" +import { apiFetch, IUser, ICreateUserInput, IUpdateUserInput } from "@/lib/api" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { Plus, Pencil, Trash2, Key } from "lucide-react" +import { toast } from "sonner" +import { UserForm } from "@/components/users/user-form" +import { FadeIn } from "@/components/animations" + +const roleColors: Record = { + superadmin: 'bg-red-100 text-red-800', + admin: 'bg-blue-100 text-blue-800', + trainer: 'bg-green-100 text-green-800', +} + +const roleLabels: Record = { + superadmin: 'Super Admin', + admin: 'Admin', + trainer: 'Trainer', +} + +export default function UsersPage() { + const { token } = useAuth() + const [users, setUsers] = useState([]) + const [loading, setLoading] = useState(true) + const [isFormOpen, setIsFormOpen] = useState(false) + const [editingUser, setEditingUser] = useState(null) + const [formMode, setFormMode] = useState<'create' | 'edit'>('create') + + const fetchUsers = async () => { + try { + const data = await apiFetch('/auth/users/', { token: token! }) + setUsers(data) + } catch { + toast.error('Fehler beim Laden der Benutzer') + } finally { + setLoading(false) + } + } + + useEffect(() => { + fetchUsers() + }, [token]) + + const handleCreate = async (data: ICreateUserInput) => { + try { + await apiFetch('/auth/users/', { + method: 'POST', + token: token!, + body: JSON.stringify(data), + }) + toast.success('Benutzer erstellt') + fetchUsers() + } catch { + toast.error('Fehler beim Erstellen') + } + } + + const handleUpdate = async (data: IUpdateUserInput) => { + if (!editingUser) return + try { + await apiFetch(`/auth/users/${editingUser.id}/`, { + method: 'PATCH', + token: token!, + body: JSON.stringify(data), + }) + toast.success('Benutzer aktualisiert') + fetchUsers() + } catch { + toast.error('Fehler beim Aktualisieren') + } + } + + const handleDelete = async (id: number) => { + if (!confirm('Bist du sicher?')) return + try { + await apiFetch(`/auth/users/${id}/`, { + method: 'DELETE', + token: token!, + }) + toast.success('Benutzer gelöscht') + fetchUsers() + } catch { + toast.error('Fehler beim Löschen') + } + } + + const handlePasswordReset = async (user: IUser) => { + const password = prompt(`Neues Passwort für ${user.username}:`) + if (!password) return + try { + await apiFetch(`/auth/users/${user.id}/set_password/`, { + method: 'POST', + token: token!, + body: JSON.stringify({ password }), + }) + toast.success('Passwort geändert') + } catch { + toast.error('Fehler beim Passwort ändern') + } + } + + const openCreateForm = () => { + setEditingUser(null) + setFormMode('create') + setIsFormOpen(true) + } + + const openEditForm = (user: IUser) => { + setEditingUser(user) + setFormMode('edit') + setIsFormOpen(true) + } + + if (loading) return
Laden...
+ + return ( + + + + Benutzerverwaltung + + + + + + + Name + Username + E-Mail + Rolle + Status + Aktionen + + + + {users.map((user) => ( + + + {user.first_name} {user.last_name} + + {user.username} + {user.email} + + + {roleLabels[user.role || 'trainer'] || user.role} + + + + {user.is_active ? ( + Aktiv + ) : ( + Inaktiv + )} + + +
+ + + +
+
+
+ ))} +
+
+
+
+ + +
+ ) +} \ No newline at end of file diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 551b5b9..9b5b582 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -100,6 +100,17 @@ export function Sidebar() { Einstellungen + + + + Benutzer + +