# User Management Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Implement user management system with roles (superadmin, admin, trainer) where superadmins can create, edit, delete users and assign roles via Settings page. **Architecture:** Extend Django User model with Role model, create UserManagementViewSet with permission-based access control, build React Settings page with user CRUD operations. **Tech Stack:** Django + DRF (backend), Next.js + Shadcn UI (frontend), JWT Auth --- ## File Structure | File | Purpose | |------|---------| | `backend/auth_app/models.py` | Add Role model | | `backend/auth_app/serializers.py` | User management serializers | | `backend/auth_app/views.py` | UserManagementViewSet | | `backend/auth_app/permissions.py` | Role-based permissions | | `backend/auth_app/urls.py` | Add user management routes | | `frontend/src/app/(dashboard)/settings/users/page.tsx` | Users management page | | `frontend/src/components/users/user-form.tsx` | Create/Edit user form | | `frontend/src/components/users/user-table.tsx` | Users table with actions | | `frontend/src/lib/api.ts` | Add user management types | --- ## Task 1: Create Role Model **Files:** - Modify: `backend/auth_app/models.py` - [ ] **Step 1: Add Role model with choices** ```python from django.db import models from django.contrib.auth.models import User class UserRole(models.Model): ROLE_CHOICES = [ ('superadmin', 'Super Admin'), ('admin', 'Admin'), ('trainer', 'Trainer'), ] user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='role') role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='trainer') class Meta: verbose_name = 'User Role' verbose_name_plural = 'User Roles' def __str__(self): return f"{self.user.username} - {self.get_role_display()}" ``` - [ ] **Step 2: Create and run migration** ```bash cd /Volumes/T3/Opencode/WrestleDesk/backend python manage.py makemigrations auth_app python manage.py migrate ``` - [ ] **Step 3: Commit** ```bash git add backend/auth_app/models.py backend/auth_app/migrations/ git commit -m "feat(auth): add UserRole model with superadmin/admin/trainer roles" ``` --- ## Task 2: Create Role-Based Permissions **Files:** - Create: `backend/auth_app/permissions.py` - [ ] **Step 1: Create permission classes** ```python from rest_framework import permissions class IsSuperAdmin(permissions.BasePermission): def has_permission(self, request, view): return request.user.is_authenticated and hasattr(request.user, 'role') and request.user.role.role == 'superadmin' class IsAdminOrSuperAdmin(permissions.BasePermission): def has_permission(self, request, view): if not request.user.is_authenticated: return False if not hasattr(request.user, 'role'): return False return request.user.role.role in ['admin', 'superadmin'] class HasUserManagementAccess(permissions.BasePermission): def has_permission(self, request, view): if not request.user.is_authenticated: return False if not hasattr(request.user, 'role'): return False return request.user.role.role == 'superadmin' ``` - [ ] **Step 2: Commit** ```bash git add backend/auth_app/permissions.py git commit -m "feat(auth): add role-based permission classes" ``` --- ## Task 3: Create User Management Serializers **Files:** - Modify: `backend/auth_app/serializers.py` (create if not exists) - [ ] **Step 1: Create user management serializers** ```python from rest_framework import serializers from django.contrib.auth.models import User from .models import UserRole class UserRoleSerializer(serializers.ModelSerializer): class Meta: model = UserRole fields = ['role'] class UserListSerializer(serializers.ModelSerializer): role = serializers.SerializerMethodField() class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_active', 'role', 'date_joined'] def get_role(self, obj): if hasattr(obj, 'role'): return obj.role.role return 'trainer' class UserCreateSerializer(serializers.ModelSerializer): password = serializers.CharField(write_only=True) role = serializers.ChoiceField(choices=UserRole.ROLE_CHOICES, default='trainer') class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'password', 'role'] def create(self, validated_data): role = validated_data.pop('role', 'trainer') user = User.objects.create_user(**validated_data) UserRole.objects.create(user=user, role=role) return user class UserUpdateSerializer(serializers.ModelSerializer): role = serializers.ChoiceField(choices=UserRole.ROLE_CHOICES, required=False) class Meta: model = User fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_active', 'role'] def update(self, instance, validated_data): role = validated_data.pop('role', None) user = super().update(instance, validated_data) if role and hasattr(user, 'role'): user.role.role = role user.role.save() return user class PasswordChangeSerializer(serializers.Serializer): password = serializers.CharField(write_only=True, required=True) ``` - [ ] **Step 2: Commit** ```bash git add backend/auth_app/serializers.py git commit -m "feat(auth): add user management serializers" ``` --- ## Task 4: Create User Management ViewSet **Files:** - Modify: `backend/auth_app/views.py` - [ ] **Step 1: Add UserManagementViewSet** ```python from rest_framework import viewsets, status from rest_framework.decorators import action from rest_framework.response import Response from django.contrib.auth.models import User from .models import UserRole from .serializers import ( UserListSerializer, UserCreateSerializer, UserUpdateSerializer, PasswordChangeSerializer ) from .permissions import IsSuperAdmin, HasUserManagementAccess class UserManagementViewSet(viewsets.ModelViewSet): queryset = User.objects.all().select_related('role') permission_classes = [HasUserManagementAccess] def get_serializer_class(self): if self.action == 'create': return UserCreateSerializer elif self.action in ['update', 'partial_update']: return UserUpdateSerializer return UserListSerializer def get_queryset(self): return User.objects.all().select_related('role').order_by('-date_joined') @action(detail=True, methods=['post']) def set_password(self, request, pk=None): user = self.get_object() serializer = PasswordChangeSerializer(data=request.data) if serializer.is_valid(): user.set_password(serializer.validated_data['password']) user.save() return Response({'status': 'password set'}) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) ``` - [ ] **Step 2: Commit** ```bash git add backend/auth_app/views.py git commit -m "feat(auth): add UserManagementViewSet with CRUD and password change" ``` --- ## Task 5: Add URL Routes **Files:** - Modify: `backend/auth_app/urls.py` (create if not exists) - [ ] **Step 1: Add user management routes** ```python from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import UserManagementViewSet router = DefaultRouter() router.register(r'users', UserManagementViewSet, basename='usermanagement') urlpatterns = [ path('', include(router.urls)), ] ``` - [ ] **Step 2: Include in main urls.py** Modify `backend/wrestleDesk/urls.py`: ```python path('api/v1/auth/', include('auth_app.urls')), ``` - [ ] **Step 3: Commit** ```bash git add backend/auth_app/urls.py backend/wrestleDesk/urls.py git commit -m "feat(auth): add user management URL routes" ``` --- ## Task 6: Add User Management Types to Frontend **Files:** - Modify: `frontend/src/lib/api.ts` - [ ] **Step 1: Add types** ```typescript export interface IUserRole { role: 'superadmin' | 'admin' | 'trainer' } export interface IUser { id: number username: string email: string first_name: string last_name: string is_active: boolean role: string date_joined: string } export interface ICreateUserInput { username: string email: string first_name: string last_name: string password: string role: 'superadmin' | 'admin' | 'trainer' } export interface IUpdateUserInput { username?: string email?: string first_name?: string last_name?: string is_active?: boolean role?: 'superadmin' | 'admin' | 'trainer' } ``` - [ ] **Step 2: Commit** ```bash git add frontend/src/lib/api.ts git commit -m "feat(api): add user management types" ``` --- ## Task 7: Create User Form Component **Files:** - Create: `frontend/src/components/users/user-form.tsx` - [ ] **Step 1: Create user form component** ```tsx "use client" import { useState } from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { ICreateUserInput, IUpdateUserInput, IUser } from "@/lib/api" interface UserFormProps { open: boolean onOpenChange: (open: boolean) => void onSubmit: (data: ICreateUserInput | IUpdateUserInput) => Promise user?: IUser mode: 'create' | 'edit' } const roles = [ { value: 'superadmin', label: 'Super Admin' }, { value: 'admin', label: 'Admin' }, { value: 'trainer', label: 'Trainer' }, ] export function UserForm({ open, onOpenChange, onSubmit, user, mode }: UserFormProps) { const [loading, setLoading] = useState(false) const [formData, setFormData] = useState({ username: user?.username || '', email: user?.email || '', first_name: user?.first_name || '', last_name: user?.last_name || '', password: '', role: (user?.role as any) || 'trainer', }) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setLoading(true) try { await onSubmit(formData) onOpenChange(false) } finally { setLoading(false) } } return ( {mode === 'create' ? 'Neuer Benutzer' : 'Benutzer bearbeiten'} {mode === 'create' ? 'Erstelle einen neuen Benutzer mit Rolle' : 'Bearbeite die Benutzerdaten'}
setFormData({ ...formData, first_name: e.target.value })} required />
setFormData({ ...formData, last_name: e.target.value })} required />
setFormData({ ...formData, username: e.target.value })} required />
setFormData({ ...formData, email: e.target.value })} required />
{mode === 'create' && (
setFormData({ ...formData, password: e.target.value })} required={mode === 'create'} />
)}
) } ``` - [ ] **Step 2: Commit** ```bash git add frontend/src/components/users/user-form.tsx git commit -m "feat(users): add user form component" ``` --- ## Task 8: Create Users Page **Files:** - Create: `frontend/src/app/(dashboard)/settings/users/page.tsx` - [ ] **Step 1: Create users management page** ```tsx "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] || user.role} {user.is_active ? ( Aktiv ) : ( Inaktiv )}
))}
) } ``` - [ ] **Step 2: Commit** ```bash git add frontend/src/app/(dashboard)/settings/users/page.tsx git commit -m "feat(users): add user management page" ``` --- ## Task 9: Add Sidebar Navigation **Files:** - Modify: `frontend/src/components/layout/Sidebar.tsx` - [ ] **Step 1: Add Users link to Settings section** Füge unter der Settings Section hinzu: ```tsx { title: "Einstellungen", icon: Settings, href: "/settings", subItems: [ { title: "Benutzer", href: "/settings/users" }, ], } ``` - [ ] **Step 2: Commit** ```bash git add frontend/src/components/layout/Sidebar.tsx git commit -m "feat(nav): add users link to settings sidebar" ``` --- ## Task 10: Final Verification - [ ] **Step 1: Test Backend API** ```bash curl -X GET http://localhost:8000/api/v1/auth/users/ \ -H "Authorization: Bearer " ``` - [ ] **Step 2: Build Frontend** ```bash cd frontend && npm run build ``` - [ ] **Step 3: Final Commit and Push** ```bash git push origin feature/pwa ``` --- ## Summary **Was implementiert wird:** 1. UserRole Model (superadmin, admin, trainer) 2. Permission classes für Rollen 3. UserManagementViewSet (CRUD + Passwort ändern) 4. Frontend User Form und Table 5. Settings/Users Page 6. Sidebar Navigation **Rollen-Berechtigungen:** - **superadmin**: Kann alle User verwalten, Rollen zuweisen - **admin**: Kann User sehen, aber nicht verwalten (optional für später) - **trainer**: Kein Zugriff auf Settings (nur superadmin hat Zugriff) Nach dem Deploy: Nur User mit `role='superadmin'` können die Users-Seite sehen!