- Split landing pages into Server/Client Components to enable Next.js metadata exports - Add robots.ts, sitemap.ts, JSON-LD structured data, OpenGraph and viewport config - Fix i18n hydration mismatch: detect locale server-side via cookie/Accept-Language header - Replace localStorage with cookie for locale persistence (SSR-readable) - Dynamic <html lang> based on locale cookie - Optimize images with next/image (avif/webp formats, quality config) - Add /homepage route as always-visible landing page (no auth redirect) - Auth-aware CTA buttons: show "Dashboard"/"进入工作台" for logged-in users - Login page redirects authenticated users to dashboard - Unify logout/401 redirect to "/" instead of "/login" - Fix title template to avoid double "Multica" suffix on homepage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
import { useRouter, usePathname } from "next/navigation";
|
|
import { MulticaIcon } from "@/components/multica-icon";
|
|
import { useNavigationStore } from "@/features/navigation";
|
|
import { SidebarProvider, SidebarInset } from "@/components/ui/sidebar";
|
|
import { useAuthStore } from "@/features/auth";
|
|
import { useWorkspaceStore } from "@/features/workspace";
|
|
import { AppSidebar } from "./_components/app-sidebar";
|
|
|
|
export default function DashboardLayout({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const router = useRouter();
|
|
const pathname = usePathname();
|
|
const user = useAuthStore((s) => s.user);
|
|
const isLoading = useAuthStore((s) => s.isLoading);
|
|
const workspace = useWorkspaceStore((s) => s.workspace);
|
|
|
|
useEffect(() => {
|
|
if (!isLoading && !user) {
|
|
router.push("/");
|
|
}
|
|
}, [user, isLoading, router]);
|
|
|
|
useEffect(() => {
|
|
useNavigationStore.getState().onPathChange(pathname);
|
|
}, [pathname]);
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex h-screen items-center justify-center">
|
|
<MulticaIcon className="size-6" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) return null;
|
|
|
|
return (
|
|
<SidebarProvider className="h-svh">
|
|
<AppSidebar />
|
|
<SidebarInset className="overflow-hidden">
|
|
{workspace ? (
|
|
children
|
|
) : (
|
|
<div className="flex flex-1 items-center justify-center">
|
|
<MulticaIcon className="size-6 animate-pulse" />
|
|
</div>
|
|
)}
|
|
</SidebarInset>
|
|
</SidebarProvider>
|
|
);
|
|
}
|