205 lines
9.8 KiB
TypeScript
205 lines
9.8 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import { useAuth } from "@/lib/auth-context"
|
|
import { type User } from "@/lib/types"
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Input } from "@/components/ui/input"
|
|
import { MoreHorizontal, Shield, ShieldAlert, Trash, KeyRound } from "lucide-react"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@/components/ui/dialog"
|
|
import { Label } from "@/components/ui/label"
|
|
import { useToast } from "@/components/ui/use-toast"
|
|
|
|
export function UserManagement() {
|
|
const { getAllUsers, updateUserRole, deleteUser, adminResetPassword, user: currentUser } = useAuth()
|
|
const { toast } = useToast()
|
|
const [users, setUsers] = useState<User[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
// State for reset password dialog
|
|
const [resetDialogOpen, setResetDialogOpen] = useState(false)
|
|
const [selectedUser, setSelectedUser] = useState<User | null>(null)
|
|
const [newPassword, setNewPassword] = useState("")
|
|
|
|
const loadUsers = async () => {
|
|
setLoading(true)
|
|
const data = await getAllUsers()
|
|
setUsers(data)
|
|
setLoading(false)
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadUsers()
|
|
}, [])
|
|
|
|
const handleRoleChange = async (userId: number, newRole: string) => {
|
|
const success = await updateUserRole(userId, newRole)
|
|
if (success) {
|
|
toast({ title: "Rolle aktualisiert", description: `User ist jetzt ${newRole}` })
|
|
loadUsers()
|
|
} else {
|
|
toast({ title: "Fehler", description: "Rolle konnte nicht geändert werden", variant: "destructive" })
|
|
}
|
|
}
|
|
|
|
const handleDelete = async (userId: number) => {
|
|
if (confirm("Sind Sie sicher, dass Sie diesen Benutzer löschen möchten?")) {
|
|
const success = await deleteUser(userId)
|
|
if (success) {
|
|
toast({ title: "Benutzer gelöscht" })
|
|
loadUsers()
|
|
} else {
|
|
toast({ title: "Fehler", description: "Benutzer konnte nicht gelöscht werden", variant: "destructive" })
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleResetPassword = async () => {
|
|
if (!selectedUser || !newPassword) return
|
|
const success = await adminResetPassword(selectedUser.id, newPassword)
|
|
if (success) {
|
|
toast({ title: "Passwort zurückgesetzt" })
|
|
setResetDialogOpen(false)
|
|
setNewPassword("")
|
|
setSelectedUser(null)
|
|
} else {
|
|
toast({ title: "Fehler", description: "Passwort konnte nicht zurückgesetzt werden", variant: "destructive" })
|
|
}
|
|
}
|
|
|
|
if (loading) return <div>Lade Benutzer...</div>
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="rounded-md border">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
<TableHead>Email</TableHead>
|
|
<TableHead>Rolle</TableHead>
|
|
<TableHead className="w-[70px]"></TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{users.map((user) => (
|
|
<TableRow key={user.id}>
|
|
<TableCell className="font-medium">{user.name}</TableCell>
|
|
<TableCell>{user.email}</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center gap-2">
|
|
{user.role === "ADMIN" && <ShieldAlert className="h-4 w-4 text-destructive" />}
|
|
{user.role === "LEHRKRAFT" && <span className="text-muted-foreground">Lehrkraft</span>}
|
|
{user.role === "RAUMBETREUER" && <span className="text-primary">Raumbetreuer</span>}
|
|
{user.role === "ADMIN" && <span className="font-bold text-destructive">Admin</span>}
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
|
<span className="sr-only">Open menu</span>
|
|
<MoreHorizontal className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Aktionen</DropdownMenuLabel>
|
|
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(user.email)}>
|
|
Email kopieren
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
|
|
{/* Safety: Cannot modify ID 1 and cannot modify self here ideally, but backend blocks ID 1 */}
|
|
{user.id !== 1 && (
|
|
<>
|
|
<DropdownMenuItem onClick={() => {
|
|
setSelectedUser(user)
|
|
setResetDialogOpen(true)
|
|
}}>
|
|
<KeyRound className="mr-2 h-4 w-4" /> Passwort zurücksetzen
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
{user.role !== "ADMIN" && (
|
|
<DropdownMenuItem onClick={() => handleRoleChange(user.id, "ADMIN")}>
|
|
<ShieldAlert className="mr-2 h-4 w-4" /> Zum Admin machen
|
|
</DropdownMenuItem>
|
|
)}
|
|
{user.role !== "LEHRKRAFT" && (
|
|
<DropdownMenuItem onClick={() => handleRoleChange(user.id, "LEHRKRAFT")}>
|
|
Zum Lehrer herabstufen
|
|
</DropdownMenuItem>
|
|
)}
|
|
{user.role !== "RAUMBETREUER" && (
|
|
<DropdownMenuItem onClick={() => handleRoleChange(user.id, "RAUMBETREUER")}>
|
|
<Shield className="mr-2 h-4 w-4" /> Zum Raumbetreuer machen
|
|
</DropdownMenuItem>
|
|
)}
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={() => handleDelete(user.id)} className="text-destructive">
|
|
<Trash className="mr-2 h-4 w-4" /> Löschen
|
|
</DropdownMenuItem>
|
|
</>
|
|
)}
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
<Dialog open={resetDialogOpen} onOpenChange={setResetDialogOpen}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Passwort zurücksetzen für {selectedUser?.name}</DialogTitle>
|
|
<DialogDescription>
|
|
Setzen Sie ein neues Passwort für diesen Benutzer.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="grid gap-4 py-4">
|
|
<div className="grid grid-cols-4 items-center gap-4">
|
|
<Label htmlFor="new-pass" className="text-right">
|
|
Neues Passwort
|
|
</Label>
|
|
<Input
|
|
id="new-pass"
|
|
type="password"
|
|
value={newPassword}
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
className="col-span-3"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => setResetDialogOpen(false)}>Abbrechen</Button>
|
|
<Button onClick={handleResetPassword}>Speichern</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
)
|
|
}
|