214 lines
6.4 KiB
TypeScript
214 lines
6.4 KiB
TypeScript
"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/ui/animations"
|
|
|
|
const roleColors: Record<string, string> = {
|
|
superadmin: 'bg-red-100 text-red-800',
|
|
admin: 'bg-blue-100 text-blue-800',
|
|
trainer: 'bg-green-100 text-green-800',
|
|
}
|
|
|
|
const roleLabels: Record<string, string> = {
|
|
superadmin: 'Super Admin',
|
|
admin: 'Admin',
|
|
trainer: 'Trainer',
|
|
}
|
|
|
|
export default function UsersPage() {
|
|
const { token } = useAuth()
|
|
const [users, setUsers] = useState<IUser[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [isFormOpen, setIsFormOpen] = useState(false)
|
|
const [editingUser, setEditingUser] = useState<IUser | null>(null)
|
|
const [formMode, setFormMode] = useState<'create' | 'edit'>('create')
|
|
|
|
const fetchUsers = async () => {
|
|
try {
|
|
const data = await apiFetch<IUser[]>('/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<IUser>('/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<IUser>(`/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 <div>Laden...</div>
|
|
|
|
return (
|
|
<FadeIn>
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<CardTitle>Benutzerverwaltung</CardTitle>
|
|
<Button onClick={openCreateForm}>
|
|
<Plus className="w-4 h-4 mr-2" />
|
|
Neuer Benutzer
|
|
</Button>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
<TableHead>Username</TableHead>
|
|
<TableHead>E-Mail</TableHead>
|
|
<TableHead>Rolle</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead className="text-right">Aktionen</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{users.map((user) => (
|
|
<TableRow key={user.id}>
|
|
<TableCell>
|
|
{user.first_name} {user.last_name}
|
|
</TableCell>
|
|
<TableCell>{user.username}</TableCell>
|
|
<TableCell>{user.email}</TableCell>
|
|
<TableCell>
|
|
<Badge className={roleColors[user.role || 'trainer'] || 'bg-gray-100'}>
|
|
{roleLabels[user.role || 'trainer'] || user.role}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
{user.is_active ? (
|
|
<span className="text-green-600">Aktiv</span>
|
|
) : (
|
|
<span className="text-red-600">Inaktiv</span>
|
|
)}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
<div className="flex justify-end gap-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => handlePasswordReset(user)}
|
|
title="Passwort ändern"
|
|
>
|
|
<Key className="w-4 h-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => openEditForm(user)}
|
|
>
|
|
<Pencil className="w-4 h-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => handleDelete(user.id!)}
|
|
className="text-red-600"
|
|
>
|
|
<Trash2 className="w-4 h-4" />
|
|
</Button>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<UserForm
|
|
open={isFormOpen}
|
|
onOpenChange={setIsFormOpen}
|
|
onSubmit={formMode === 'create' ? ((data: ICreateUserInput) => handleCreate(data)) : ((data: IUpdateUserInput) => handleUpdate(data))}
|
|
user={editingUser || undefined}
|
|
mode={formMode}
|
|
/>
|
|
</FadeIn>
|
|
)
|
|
} |