diff --git a/frontend/src/components/auth/UserMenu.tsx b/frontend/src/components/auth/UserMenu.tsx index 2a37dbc3..abea2f62 100644 --- a/frontend/src/components/auth/UserMenu.tsx +++ b/frontend/src/components/auth/UserMenu.tsx @@ -5,7 +5,7 @@ * * Avatar-кнопка с инициалом из username + dropdown: * - текущий username - * - role badge («Админ» / «Пилот») + * - role badge («Админ» / «Пилот» / «Аналитик») * - кнопка «Выйти» — invalidate Caddy basic_auth cached creds в browser * * Используется в `TopNav.rightSlot`. Если /me грузится или 401 (dev без @@ -24,7 +24,18 @@ import { useEffect, useRef, useState } from "react"; import { LogOut } from "lucide-react"; -import { useMe } from "@/lib/useMe"; +import { useMe, type Role } from "@/lib/useMe"; + +/** + * Локализация ролей для badge. Должен покрывать все варианты `Role` из + * `lib/useMe.ts` — TypeScript-проверка через `Record` + * сломает build, если backend добавит новую роль без обновления map. + */ +const ROLE_LABELS: Record = { + admin: "Админ", + pilot: "Пилот", + analyst: "Аналитик", +}; /** * Logout — invalidate basic_auth cache + reload. @@ -89,7 +100,7 @@ export function UserMenu() { if (isLoading || error || !data) return null; const initial = data.username[0]?.toUpperCase() ?? "?"; - const roleLabel = data.role === "admin" ? "Админ" : "Пилот"; + const roleLabel = ROLE_LABELS[data.role]; return (
diff --git a/frontend/src/lib/useMe.ts b/frontend/src/lib/useMe.ts index 951edb04..e0a90e30 100644 --- a/frontend/src/lib/useMe.ts +++ b/frontend/src/lib/useMe.ts @@ -4,7 +4,7 @@ * `useMe()` — TanStack-хук для GET /api/v1/me. * * Backend возвращает `UserScope` (см. `backend/app/core/auth.py`): - * { username, role: "admin"|"pilot", allowed_paths: string[], deny_paths: string[] } + * { username, role: "admin"|"pilot"|"analyst", allowed_paths: string[], deny_paths: string[] } * * Failure modes: * - 401 — нет X-Authenticated-User (локально без Caddy basic_auth). Фронт @@ -26,7 +26,10 @@ import { apiFetchWithStatus, HTTPError } from "@/lib/api"; // статус через `error instanceof HTTPError` — это работает в runtime независимо // от type-level decl. -export type Role = "admin" | "pilot"; +// Должен совпадать с `Role` в `backend/app/core/auth.py` (Literal["admin", "pilot", "analyst"]). +// Источник правды — `auth/roles.yaml`; при добавлении новой роли расширить тут и +// `ROLE_LABELS` в `components/auth/UserMenu.tsx`. +export type Role = "admin" | "pilot" | "analyst"; export interface UserScope { username: string;