UserStatus.jsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. "use client";
  2. import React from "react";
  3. import Link from "next/link";
  4. import { usePathname } from "next/navigation";
  5. import { LifeBuoy, LogOut, User, Users } from "lucide-react";
  6. import { cn } from "@/lib/utils";
  7. import { useAuth } from "@/components/auth/authContext";
  8. import { logout } from "@/lib/frontend/apiClient";
  9. import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
  10. import { canManageUsers as canManageUsersRole } from "@/lib/frontend/auth/roles";
  11. import { buildSupportMailto } from "@/lib/frontend/support/supportMailto";
  12. import { Button } from "@/components/ui/button";
  13. import {
  14. DropdownMenu,
  15. DropdownMenuContent,
  16. DropdownMenuItem,
  17. DropdownMenuLabel,
  18. DropdownMenuSeparator,
  19. DropdownMenuTrigger,
  20. } from "@/components/ui/dropdown-menu";
  21. import {
  22. Tooltip,
  23. TooltipContent,
  24. TooltipTrigger,
  25. } from "@/components/ui/tooltip";
  26. function formatRole(role) {
  27. if (role === "branch") return "Niederlassung";
  28. if (role === "admin") return "Admin";
  29. if (role === "superadmin") return "Superadmin";
  30. if (role === "dev") return "Entwicklung";
  31. return role ? String(role) : "Unbekannt";
  32. }
  33. export default function UserStatus() {
  34. const pathname = usePathname() || "/";
  35. const { status, user } = useAuth();
  36. const isAuthenticated = status === "authenticated" && user;
  37. const canManageUsers = isAuthenticated && canManageUsersRole(user.role);
  38. let text = "Nicht geladen";
  39. if (status === "loading") text = "Lädt…";
  40. if (isAuthenticated) {
  41. const roleLabel = formatRole(user.role);
  42. text = user.branchId ? `${roleLabel} (${user.branchId})` : roleLabel;
  43. }
  44. if (status === "unauthenticated") text = "Abgemeldet";
  45. if (status === "error") text = "Fehler";
  46. const currentUrl = typeof window !== "undefined" ? window.location.href : "";
  47. const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "";
  48. const supportMailto = buildSupportMailto({
  49. user,
  50. currentUrl,
  51. pathname,
  52. userAgent,
  53. });
  54. const isProfileActive =
  55. pathname === "/profile" || pathname.startsWith("/profile/");
  56. async function handleLogout() {
  57. try {
  58. await logout();
  59. } catch (err) {
  60. console.error("[UserStatus] logout failed:", err);
  61. }
  62. const loginUrl = buildLoginUrl({ reason: LOGIN_REASONS.LOGGED_OUT });
  63. window.location.replace(loginUrl);
  64. }
  65. return (
  66. <DropdownMenu>
  67. <Tooltip>
  68. <TooltipTrigger asChild>
  69. <DropdownMenuTrigger asChild>
  70. <Button
  71. type="button"
  72. variant="ghost"
  73. size="sm"
  74. aria-label="Benutzermenü öffnen"
  75. className={cn(
  76. "gap-2",
  77. "px-2 md:px-3",
  78. isProfileActive ? "bg-accent" : "",
  79. )}
  80. >
  81. <User className="h-4 w-4" aria-hidden="true" />
  82. <span className="hidden text-xs md:inline">{text}</span>
  83. </Button>
  84. </DropdownMenuTrigger>
  85. </TooltipTrigger>
  86. <TooltipContent side="bottom">Benutzermenü</TooltipContent>
  87. </Tooltip>
  88. <DropdownMenuContent align="end" className="min-w-56">
  89. <DropdownMenuLabel>Benutzer</DropdownMenuLabel>
  90. <div className="px-2 pb-2 text-xs text-muted-foreground">
  91. {isAuthenticated
  92. ? `Angemeldet als: ${text}`
  93. : "Keine aktive Sitzung."}
  94. </div>
  95. <DropdownMenuSeparator />
  96. <DropdownMenuItem asChild disabled={!isAuthenticated}>
  97. <Link href="/profile" className="flex w-full items-center gap-2">
  98. <User className="h-4 w-4" aria-hidden="true" />
  99. Profil
  100. </Link>
  101. </DropdownMenuItem>
  102. {canManageUsers ? (
  103. <DropdownMenuItem asChild>
  104. <Link
  105. href="/admin/users"
  106. className="flex w-full items-center gap-2"
  107. >
  108. <Users className="h-4 w-4" aria-hidden="true" />
  109. Benutzerverwaltung
  110. </Link>
  111. </DropdownMenuItem>
  112. ) : null}
  113. <DropdownMenuItem asChild>
  114. <a href={supportMailto} className="flex w-full items-center gap-2">
  115. <LifeBuoy className="h-4 w-4" aria-hidden="true" />
  116. Support
  117. </a>
  118. </DropdownMenuItem>
  119. <DropdownMenuSeparator />
  120. <DropdownMenuItem
  121. variant="destructive"
  122. disabled={!isAuthenticated}
  123. onSelect={(e) => {
  124. e.preventDefault();
  125. handleLogout();
  126. }}
  127. >
  128. <LogOut className="h-4 w-4" aria-hidden="true" />
  129. Abmelden
  130. </DropdownMenuItem>
  131. </DropdownMenuContent>
  132. </DropdownMenu>
  133. );
  134. }