import { useCallback, useEffect, useState, createContext, useContext } from "react"; import { NavLink, Routes, Route, Navigate } from "react-router-dom"; import { LogOut, Sun, Moon, ChevronDown } from "lucide-react"; import { routes, defaultPath } from "../routes"; import { tenantCurrent, myTenants, switchTenant, type AuthUser, type TenantCtx, type MyTenant } from "../api"; import { useTheme } from "../theme"; import { cn, useToast } from "../ui"; // 租户上下文共享给各页面:当前租户/角色/余额 + 主动刷新(切换组织、邀请成员后要立即反映)。 interface TenantState { ctx: TenantCtx | null; tenants: MyTenant[]; active: string; refresh: () => void; } const TenantStateCtx = createContext({ ctx: null, tenants: [], active: "", refresh: () => {} }); export const useTenant = () => useContext(TenantStateCtx); export function AppShell({ user, onLogout }: { user: AuthUser; onLogout: () => void }) { const toast = useToast(); const { theme, toggle } = useTheme(); const [ctx, setCtx] = useState(null); const [tenants, setTenants] = useState([]); const [active, setActive] = useState(""); const [switcherOpen, setSwitcherOpen] = useState(false); const refresh = useCallback(() => { tenantCurrent().then(setCtx).catch(() => {}); myTenants() .then((r) => { setTenants(r.tenants); setActive(r.active); }) .catch(() => {}); }, []); useEffect(() => { refresh(); const id = setInterval(refresh, 20000); return () => clearInterval(id); }, [refresh]); const onSwitch = async (id: string) => { setSwitcherOpen(false); if (id === active) return; try { await switchTenant(id); refresh(); } catch (e) { toast.push("error", (e as Error).message); } }; return (
{/* 顶栏:品牌 + 组织切换 + 主题 + 用户 */}
sundynix 组织与账单 {/* 组织切换器 */}
{switcherOpen && (
{tenants.map((t) => ( ))}
)}
{user.name || user.email}
{/* 左导航(由路由注册表派生) */}
{routes.map((r) => { const Page = r.page; return } />; })} } />
); }