"use client";
import * as React from "react";
import { usePathname, useRouter } from "next/navigation";
import { useSession, getSession } from "next-auth/react";
import { SideNav, NavGroup, NavItem } from "@/components/layout/side-nav";
import { Header } from "@/components/layout/header";
import { NotificationContainer } from "@/components/ui/notification-container";
import { useUserStore } from "@/lib/stores/user-store";
import {
LayoutDashboard,
Sparkles,
BookOpen,
BarChart3,
Swords,
Share2,
Heart,
ScanSearch,
TrendingUp,
Code2,
Settings,
} from "lucide-react";
// ─── Nav Config ───────────────────────────────────────────────────────────────
const NAV_GROUPS: NavGroup[] = [
{
id: "menu",
title: "菜单",
items: [
{
id: "dashboard",
label: "仪表盘",
href: "/dashboard",
icon: ,
},
{
id: "content",
label: "内容管理",
href: "/dashboard/content",
icon: ,
},
{
id: "knowledge",
label: "知识库",
href: "/dashboard/knowledge",
icon: ,
},
{
id: "analytics",
label: "品牌监测",
href: "/dashboard/monitoring",
icon: ,
},
{
id: "competitors",
label: "竞品分析",
href: "/dashboard/competitors",
icon: ,
},
{
id: "health-score",
label: "健康评分",
href: "/dashboard/health-score",
icon: ,
},
{
id: "distribution",
label: "内容分发",
href: "/dashboard/distribution",
icon: ,
},
{
id: "detection",
label: "检测任务",
href: "/dashboard/detection",
icon: ,
},
{
id: "trends",
label: "趋势洞察",
href: "/dashboard/trends",
icon: ,
},
{
id: "schema",
label: "Schema 建议",
href: "/dashboard/schema",
icon: ,
},
],
},
{
id: "settings",
title: "设置",
items: [
{
id: "settings",
label: "设置",
href: "/dashboard/settings",
icon: ,
},
],
},
];
// Flatten all items for activeId lookup
const ALL_NAV_ITEMS: NavItem[] = NAV_GROUPS.flatMap((g) => g.items);
// ─── Layout ───────────────────────────────────────────────────────────────────
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const { status, update } = useSession();
const pathname = usePathname();
const router = useRouter();
const [verifying, setVerifying] = React.useState(false);
// 同步 session 到 user store
const syncFromSession = useUserStore((s) => s.syncFromSession);
React.useEffect(() => {
if (status === "authenticated") {
getSession().then((session) => {
syncFromSession(session);
});
} else if (status === "unauthenticated") {
syncFromSession(null);
}
}, [status, syncFromSession]);
// 当 useSession 返回 unauthenticated 时,用 getSession 双重确认,
// 避免 SessionProvider 缓存未更新导致的误判重定向
React.useEffect(() => {
if (status === "unauthenticated") {
setVerifying(true);
getSession().then((session) => {
setVerifying(false);
if (session) {
// SessionProvider 缓存未更新,强制刷新
update();
} else {
router.replace("/login");
}
});
}
}, [status, router, update]);
// Compute activeId: match longest href prefix
const activeId = React.useMemo(() => {
const sorted = [...ALL_NAV_ITEMS]
.filter((item) => item.href)
.sort((a, b) => (b.href?.length ?? 0) - (a.href?.length ?? 0));
return sorted.find((item) => item.href && pathname.startsWith(item.href))?.id ?? "dashboard";
}, [pathname]);
// Handle nav click: navigate to href
const handleNavClick = React.useCallback(
(item: NavItem) => {
if (item.href) {
router.push(item.href);
}
},
[router]
);
if (status === "loading" || verifying) {
return (
);
}
if (status === "unauthenticated") {
return null;
}
return (
{/* Fixed SideNav - left side */}
{/* Main content area - offset by sidebar width */}
{/* Fixed Header at top */}
{/* Global Toast/Notification */}
{/* Scrollable content area */}
{children}
);
}