Files
landing_page/refer_landing_page/app/console/layout.tsx
T
Godopu 5285e3fd02 feat(console): hide AppHeader/Footer on /console and fix sidebar to full-height absolute/fixed position
- Hide Header and Footer components automatically when navigating to /console routes
- Set AdminLayout sidebar to fixed full-height navigation (md:fixed md:top-0 md:left-0 md:h-screen)
- Offset main dashboard content (md:ml-64) with independent scroll container
- Passed all 12 E2E and quality gates cleanly
2026-08-25 15:19:32 +09:00

146 lines
4.9 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { getClientToken, removeClientToken } from "./_components/AdminComponents";
import { AdminLoginForm } from "./_components/AdminLoginForm";
import { adminVerifyToken } from "../../lib/adminApi";
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const router = useRouter();
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
useEffect(() => {
let isMounted = true;
async function checkAuth() {
const token = getClientToken();
if (!token) {
if (isMounted) setIsAuthenticated(false);
return;
}
const isValid = await adminVerifyToken(token);
if (!isMounted) return;
if (!isValid) {
removeClientToken();
setIsAuthenticated(false);
} else {
setIsAuthenticated(true);
}
}
checkAuth();
return () => {
isMounted = false;
};
}, [pathname]);
if (pathname === "/console/login") {
return (
<div className="min-h-screen bg-ivory text-ink flex items-center justify-center p-4">
<AdminLoginForm
onLoginSuccess={() => {
setIsAuthenticated(true);
router.push("/console");
}}
/>
</div>
);
}
if (isAuthenticated === null) {
const token = typeof window !== "undefined" ? getClientToken() : null;
if (!token) {
return (
<div className="min-h-screen bg-ivory text-ink flex items-center justify-center p-4">
<AdminLoginForm onLoginSuccess={() => setIsAuthenticated(true)} />
</div>
);
}
return (
<div className="min-h-screen bg-ivory text-ink flex flex-col items-center justify-center space-y-4">
<div className="text-sm text-ink/70">Checking authentication...</div>
</div>
);
}
if (isAuthenticated === false) {
return (
<div className="min-h-screen bg-ivory text-ink flex items-center justify-center p-4">
<AdminLoginForm onLoginSuccess={() => setIsAuthenticated(true)} />
</div>
);
}
const navItems = [
{ href: "/console", label: "Dashboard Overview" },
{ href: "/console/research-projects", label: "Research Projects" },
{ href: "/console/research-areas", label: "Research Areas" },
{ href: "/console/members", label: "Members & Alumni" },
{ href: "/console/publications", label: "Publications & Patents" },
{ href: "/console/lectures", label: "Lectures & Courses" },
{ href: "/console/standardization", label: "Standardization" },
];
const handleLogout = () => {
removeClientToken();
setIsAuthenticated(false);
};
return (
<div className="min-h-screen bg-ivory text-ink flex flex-col md:flex-row">
{/* Fixed Sidebar */}
<aside className="w-full md:w-64 md:fixed md:top-0 md:left-0 md:bottom-0 md:h-screen bg-paper border-r border-line flex flex-col shrink-0 z-30 overflow-y-auto">
<div className="p-6 border-b border-line shrink-0">
<div className="flex items-center justify-between">
<span className="font-bold text-lg text-cobalt tracking-tight">ANL Console</span>
<span className="text-xs bg-cobalt/10 text-cobalt font-medium px-2 py-0.5 rounded">Admin</span>
</div>
<p className="text-xs text-ink/60 mt-1">Lab Data Management</p>
</div>
<nav className="p-4 space-y-1 flex-1 overflow-y-auto">
{navItems.map((item) => {
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={`block px-3 py-2 text-sm font-medium rounded-md transition-colors ${
isActive
? "bg-cobalt text-white"
: "text-ink/80 hover:bg-ivory hover:text-ink"
}`}
>
{item.label}
</Link>
);
})}
</nav>
<div className="p-4 border-t border-line space-y-2 shrink-0 bg-paper">
<Link
href="/"
className="block w-full text-center px-3 py-2 text-xs font-medium rounded border border-line hover:bg-ivory text-ink/80"
>
&larr; Return to Public Site
</Link>
<button
onClick={handleLogout}
className="block w-full text-center px-3 py-2 text-xs font-medium rounded bg-red-50 hover:bg-red-100 text-red-600 border border-red-200 dark:bg-red-950/30 dark:hover:bg-red-950/60 dark:text-red-400 dark:border-red-900"
>
Logout
</button>
</div>
</aside>
{/* Main Content Area with offset for fixed sidebar */}
<main className="flex-1 md:ml-64 p-6 md:p-10 min-h-screen overflow-x-auto">{children}</main>
</div>
);
}