import { useRef, useState, useEffect } from "react"; import { Check, LogOut, Settings, UserCircle } from "lucide-react"; import type { ApiSettings, AuthInfo, AuthTenantMembership, LoginResponse } from "../types"; import HelpMenu from "./HelpMenu"; import LanguageMenu from "./LanguageMenu"; import LoginModal from "../features/auth/LoginModal"; import DismissibleAlert from "../components/DismissibleAlert"; import { useGuardedNavigate, useUnsavedChanges } from "../components/UnsavedChangesGuard"; import { logout, switchTenant } from "../api/auth"; import { hasAnyScope } from "../utils/permissions"; import { usePlatformLanguage } from "../i18n/LanguageContext"; type Props = { settings: ApiSettings; auth: AuthInfo | null; onSettingsChange: (settings: ApiSettings) => void; onAuthChange: (auth: AuthInfo | null, accessToken?: string) => void; maintenanceMode?: {enabled: boolean;message?: string | null;}; }; export default function Titlebar({ settings, auth, onAuthChange, maintenanceMode }: Props) { const navigate = useGuardedNavigate(); const { requestNavigation } = useUnsavedChanges(); const [accountOpen, setAccountOpen] = useState(false); const [tenantOpen, setTenantOpen] = useState(false); const [loginOpen, setLoginOpen] = useState(false); const [switchingTenantId, setSwitchingTenantId] = useState(null); const [tenantError, setTenantError] = useState(""); const accountRef = useRef(null); const tenantRef = useRef(null); const { translateText } = usePlatformLanguage(); const activeTenant = auth?.active_tenant ?? auth?.tenant ?? null; const tenants = auth?.tenants ?? (activeTenant ? [activeTenant] : []); const displayUserName = auth?.user?.display_name || auth?.user?.email || translateText("i18n:govoplan-core.sign_in.ada2e9e9"); const canSwitchTenant = tenants.length > 1; const canAdministerTenants = hasAnyScope(auth, [ "system:tenants:read", "system:tenants:create", "system:tenants:update", "system:tenants:suspend"] ); const showTenantControl = Boolean(activeTenant && (canSwitchTenant || canAdministerTenants)); useEffect(() => { function onPointerDown(event: MouseEvent) { const target = event.target as Node; if (accountRef.current && !accountRef.current.contains(target)) { setAccountOpen(false); } if (tenantRef.current && !tenantRef.current.contains(target)) { setTenantOpen(false); } } window.addEventListener("mousedown", onPointerDown); return () => window.removeEventListener("mousedown", onPointerDown); }, []); function handleLogin(response: LoginResponse) { onAuthChange(response, ""); } async function performLogout() { try { await logout(settings); } catch { // Logout is best effort; clear local session either way. } onAuthChange(null, ""); setAccountOpen(false); } async function performTenantSelect(tenant: AuthTenantMembership) { setSwitchingTenantId(tenant.id); setTenantError(""); try { const next = await switchTenant(settings, tenant.id); // Keep the current URL. The tenant-keyed route tree reloads in the new // context; permission and resource boundaries then retain the page or // fall back to the nearest accessible parent. onAuthChange(next); setTenantOpen(false); } catch (error) { setTenantError(error instanceof Error ? error.message : translateText("i18n:govoplan-core.tenant_switch_failed.c325b20b")); } finally { setSwitchingTenantId(null); } } function handleLogout() { requestNavigation(() => {void performLogout();}); } function handleTenantSelect(tenant: AuthTenantMembership) { if (!auth || tenant.id === activeTenant?.id || switchingTenantId) { setTenantOpen(false); return; } requestNavigation(() => {void performTenantSelect(tenant);}); } function handleAccountSettings() { setAccountOpen(false); navigate("/settings?section=profile"); } function openMaintenanceSettings() { navigate("/admin?section=system-settings"); } return (
{maintenanceMode?.enabled && } {auth && activeTenant && showTenantControl &&
{translateText("i18n:govoplan-core.tenant_label_prefix")} {canSwitchTenant ? <> {tenantOpen &&
{tenants.map((tenant) => { const active = tenant.id === activeTenant.id; return ( ); })} {tenantError && {tenantError}}
} : {activeTenant.name} }
}
{accountOpen &&
{auth ? <> {auth.user &&
{auth.user.display_name || auth.user.email} {auth.user.email}
} : }
}
{loginOpen && setLoginOpen(false)} onLogin={handleLogin} />}
); }