"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}

)}
); }