mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-25 08:00:37 +00:00
13c4ebb7a6
Shorter, cleaner path. The 'web-' prefix was redundant given the dir sits at the repo root next to web/ and is unambiguously the admin web app. Git tracked the rename so blame + history follow through to the new location. Updated README.md and docs/VENDOR_LOCKIN.md references plus the package README header. No code changes.
83 lines
3.2 KiB
TypeScript
83 lines
3.2 KiB
TypeScript
// User dropdown in the topbar. Shows the logged-in admin's email + a
|
|
// shortcut back to the dashboard. Logout clears the admin token
|
|
// (we don't touch dashboard tokens because they live under a different
|
|
// localStorage key) and bounces back to /auth/login.
|
|
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { ExternalLink, LogOut, User as UserIcon } from "lucide-react";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useMe } from "@/hooks/useMe";
|
|
import { logout as logoutCall } from "@/lib/api/client/auth";
|
|
import { clearToken } from "@/lib/auth/storage";
|
|
import { DASHBOARD_URL } from "@/lib/env";
|
|
|
|
export function UserMenu() {
|
|
const { data: me } = useMe();
|
|
const nav = useNavigate();
|
|
const qc = useQueryClient();
|
|
|
|
async function handleLogout() {
|
|
try {
|
|
await logoutCall();
|
|
} catch {
|
|
/* even if the server call fails, drop our local session */
|
|
} finally {
|
|
clearToken();
|
|
qc.clear();
|
|
nav("/auth/login", { replace: true });
|
|
}
|
|
}
|
|
|
|
const initials = (
|
|
(me?.first_name?.[0] ?? "") + (me?.last_name?.[0] ?? "")
|
|
).toUpperCase() || (me?.email?.[0]?.toUpperCase() ?? "?");
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="sm" className="gap-2 px-2">
|
|
<span className="size-7 rounded-full bg-zinc-900 text-white text-xs font-semibold flex items-center justify-center">
|
|
{initials}
|
|
</span>
|
|
<span className="hidden md:inline text-sm text-muted-foreground">
|
|
{me?.email ?? "signed in"}
|
|
</span>
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-56">
|
|
<DropdownMenuLabel className="font-normal">
|
|
<div className="text-xs text-muted-foreground">Signed in as</div>
|
|
<div className="text-sm truncate">{me?.email ?? "—"}</div>
|
|
</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem asChild>
|
|
<a href={DASHBOARD_URL} target="_blank" rel="noreferrer">
|
|
<ExternalLink className="size-4" />
|
|
Open dashboard
|
|
</a>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem asChild>
|
|
<a href="/settings/encryption">
|
|
<UserIcon className="size-4" />
|
|
Admin settings
|
|
</a>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem onClick={handleLogout} className="text-[var(--admin-danger)] focus:text-[var(--admin-danger)]">
|
|
<LogOut className="size-4" />
|
|
Log out
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
}
|