You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
3.1 KiB

"use client";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { type ReactNode, useEffect } from "react";
import { Truck, SignOut } from "@phosphor-icons/react";
import { useAuth } from "@/hooks/use-auth";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
const NAV_ITEMS = [
{ href: "/dashboard", label: "工作台" },
{ href: "/admin/markup", label: "加价配置" },
{ href: "/admin/alerts", label: "预警中心" },
{ href: "/admin/rpa", label: "RPA 状态" },
{ href: "/admin/dashboard", label: "指标看板" },
{ href: "/admin/queues", label: "队列监控" },
];
interface AdminLayoutProps {
children: ReactNode;
}
export function AdminLayout({ children }: AdminLayoutProps) {
const pathname = usePathname();
const router = useRouter();
const { user, token, ready, logout } = useAuth();
useEffect(() => {
if (!ready) return;
if (!token || !user) {
const redirect = encodeURIComponent(pathname);
router.replace(`/login?redirect=${redirect}`);
}
}, [ready, token, user, pathname, router]);
if (!ready) {
return (
<div className="flex min-h-[100dvh] items-center justify-center">
<LoadingSpinner size={32} className="text-primary" />
</div>
);
}
if (!token || !user) {
return null;
}
return (
<div className="min-h-[100dvh] bg-bg">
<header className="border-b border-border bg-surface">
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 md:px-6 lg:px-8">
<div className="flex items-center gap-2">
<Truck size={24} weight="fill" className="text-primary" />
<span className="font-semibold"> · </span>
<span className="rounded-sm bg-primary/10 px-2 py-0.5 text-xs text-primary">
</span>
</div>
<button
type="button"
onClick={() => {
logout();
router.replace("/login");
}}
className="inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary"
>
<SignOut size={16} />
退
</button>
</div>
</header>
<div className="mx-auto flex max-w-7xl gap-6 px-4 py-6 md:px-6 lg:px-8">
<aside className="hidden w-48 shrink-0 md:block">
<nav className="space-y-1">
{NAV_ITEMS.map((item) => {
const active = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={`block rounded-md px-3 py-2 text-sm font-medium transition-colors ${
active
? "bg-primary/10 text-primary"
: "text-text-secondary hover:bg-bg hover:text-text-primary"
}`}
>
{item.label}
</Link>
);
})}
</nav>
</aside>
<main className="min-w-0 flex-1">{children}</main>
</div>
</div>
);
}