feat(PROJ-25): User-Profil & Einstellungen — Passwort, E-Mail, 2FA
Backend: - PATCH /api/auth/password — Passwort ändern (bcrypt, LDAP-Guard, Audit-Log) - PATCH /api/auth/email — E-Mail ändern (Unique-Check, LDAP-Guard, Audit-Log) - userstore: UpdatePassword, UpdateEmail, GetPasswordHash Frontend: - UserNav.tsx: Dropdown-Menü (Profil & Einstellungen, Abmelden) - navbar.tsx: UserNav eingebunden - /settings: Passwort ändern, E-Mail ändern, 2FA verwalten (QR-Code + Deaktivieren) - api.ts: changePassword, changeEmail, getTOTPSetup, confirmTOTPSetup, disableTOTP Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { logout } from "@/lib/api";
|
||||
|
||||
interface UserNavProps {
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export function UserNav({ username, role }: UserNavProps) {
|
||||
const router = useRouter();
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
await logout();
|
||||
} catch {
|
||||
// ignore logout errors
|
||||
}
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex items-center gap-2"
|
||||
aria-label="Benutzermenu"
|
||||
>
|
||||
<span className="text-sm font-medium">{username}</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{role}
|
||||
</Badge>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-48">
|
||||
<DropdownMenuItem onClick={() => router.push("/settings")}>
|
||||
Profil & Einstellungen
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={handleLogout} className="text-red-600">
|
||||
Abmelden
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user