"use client";
/**
* MIRROR of main frontend `frontend/src/components/auth/UserMenu.tsx`
* — keep in sync manually. Tradein-mvp бандлится отдельно (basePath=/trade-in)
* и НЕ имеет `lucide-react` в deps, поэтому icon заменён на inline SVG.
*
* UserMenu — личный кабинет в правом верхнем углу Topbar (gendsgn.ru/trade-in/).
*
* Avatar-кнопка с инициалом из username + dropdown:
* - текущий username
* - role badge («Админ» / «Пилот»)
* - кнопка «Выйти» — invalidate Caddy basic_auth cached creds в browser
*
* Logout flow — basic_auth quirk:
* 1. Отправляем fetch с заведомо неправильными Basic creds → 401 от Caddy
* → браузер забывает cached creds для этого origin.
* 2. Hard reload (`window.location.href = "/"`) — basic_auth prompt
* покажется заново.
*/
import { useEffect, useRef, useState } from "react";
import { logout } from "@/lib/logout";
import { useMe } from "@/lib/useMe";
/** Inline LogOut icon (lucide-react `LogOut` SVG path, stroke 1.5). */
function LogOutIcon() {
return (
);
}
export function UserMenu() {
const { data, isLoading, error } = useMe();
const [open, setOpen] = useState(false);
const containerRef = useRef(null);
const buttonRef = useRef(null);
// Click outside → закрыть dropdown.
useEffect(() => {
if (!open) return;
function handleClick(e: MouseEvent) {
if (
containerRef.current &&
!containerRef.current.contains(e.target as Node)
) {
setOpen(false);
}
}
document.addEventListener("mousedown", handleClick);
return () => document.removeEventListener("mousedown", handleClick);
}, [open]);
// Escape key → закрыть + вернуть focus на avatar.
useEffect(() => {
if (!open) return;
function handleKey(e: KeyboardEvent) {
if (e.key === "Escape") {
setOpen(false);
buttonRef.current?.focus();
}
}
document.addEventListener("keydown", handleKey);
return () => document.removeEventListener("keydown", handleKey);
}, [open]);
// Loading / 401 / no data — ничего не рендерим.
if (isLoading || error || !data) return null;
const initial = data.username[0]?.toUpperCase() ?? "?";
const roleLabel = data.role === "admin" ? "Админ" : "Пилот";
return (
{open && (
{data.username}
{roleLabel}
)}
);
}