Files
frontend/apps/dm/components/TopNav.tsx
T
2026-04-30 18:27:55 +09:00

211 lines
7.0 KiB
TypeScript

"use client";
import Link from "next/link";
import Image from "next/image";
import { usePathname, useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import LoginModal from "@/components/LoginModal";
import { fetchAnonymousMenu, isMenuActive, type MenuItem } from "@/app/store/common";
import { useDmStore } from "@/app/store";
// const LS_TOKEN_KEY = "reuse_accessToken";
const LS_USER_KEY = "user";
const DEFAULT_NAV_ITEMS: Pick<MenuItem, "menuCd" | "menuKornNm" | "urlPath" | "newWndwYn">[] = [
{ menuCd: 1, menuKornNm: "물 재이용 소개", urlPath: "/intro", newWndwYn: "N" },
{ menuCd: 2, menuKornNm: "물 재이용 현황", urlPath: "/status", newWndwYn: "N" },
{ menuCd: 3, menuKornNm: "물 재이용 사례", urlPath: "/example", newWndwYn: "N" },
{ menuCd: 4, menuKornNm: "수요신청", urlPath: "/apply/dmdco", newWndwYn: "N" },
];
export default function TopNav() {
const router = useRouter();
const pathname = usePathname();
const { actionLogout } = useDmStore((state) => state);
const [loginOpen, setLoginOpen] = useState(false);
const [userName, setUserName] = useState<string | null>(null);
const [topMenus, setTopMenus] = useState<MenuItem[]>([]);
useEffect(() => {
const sessionUser = sessionStorage.getItem(LS_USER_KEY);
if (sessionUser) {
const parsed = JSON.parse(sessionUser) as { dmdCoNm?: string };
setUserName(parsed.dmdCoNm ?? "수요처");
} else {
setUserName(null);
}
}, []);
useEffect(() => {
let isMounted = true;
(async () => {
try {
const loaded = await fetchAnonymousMenu();
if (!isMounted) return;
const level1 = loaded.filter((menu) => menu.levelNo === 1 || menu.parentId === null);
setTopMenus(level1);
} catch (error) {
console.error("Failed to load DN top menu", error);
if (!isMounted) return;
setTopMenus([]);
}
})();
return () => {
isMounted = false;
};
}, []);
const navItems = useMemo(() => {
if (topMenus.length > 0) return topMenus;
return DEFAULT_NAV_ITEMS.map((item) => ({
...item,
parentId: null,
sortSeq: 0,
children: [],
levelNo: 1,
useYn: "Y",
otptYn: "Y",
}));
}, [topMenus]);
const openLogin = () => setLoginOpen(true);
const logout = async () => {
// localStorage.removeItem(LS_TOKEN_KEY);
// localStorage.removeItem(LS_USER_KEY);
setUserName(null);
await actionLogout();
router.push("/main");
};
return (
<>
<header className="fixed top-0 left-0 right-0 z-50 bg-white backdrop-blur border-b shadow-md min-w-[1800px]">
{/* ✅ 좌/중/우 정렬이 되도록 컨테이너 구성 */}
<div className="px-6">
<div className="relative flex h-[83px] items-center justify-between">
{/* 좌측: 로고 */}
<div className="flex items-center gap-3">
<Link href="/main" className="flex items-center">
<Image
src="/images/landing/logo.png"
alt="HASUON"
width={229}
height={46}
className="h-9 w-auto"
priority
/>
</Link>
</div>
{/* 중앙: 상단 메뉴 */}
<nav className="absolute left-1/2 flex -translate-x-1/2 items-center gap-[40px] text-sm font-medium">
{navItems.map((item) => {
const requiresLogin = (item.urlPath)? item.urlPath.startsWith("/apply") : false;
const active = isMenuActive(pathname, item);
const className = [
"relative px-3 py-2 text-lg font-bold",
active ? "text-sky-700" : "text-gray-800 hover:text-sky-700",
].join(" ");
if (requiresLogin && !userName) {
return (
<button
key={item.menuCd}
type="button"
onClick={openLogin}
className={className}
>
{item.menuKornNm}
</button>
);
}
if (item.newWndwYn === "Y") {
return (
<button
key={item.menuCd}
type="button"
onClick={() => window.open(item.urlPath, "_blank")}
className={className}
>
{item.menuKornNm}
</button>
);
}
return (
<Link key={item.menuCd} href={item.urlPath || "#"} className={className}>
{item.menuKornNm}
</Link>
);
})}
</nav>
{/* ✅ 우측: 로그인 버튼 / 로그인 정보 + 로그아웃 */}
<div className="flex items-center gap-3">
{!userName ? (
<button
type="button"
onClick={openLogin}
className="inline-flex items-center gap-2 rounded-md bg-sky-500 px-4 py-2 text-base text-white hover:bg-sky-600"
>
로그인
<span aria-hidden>
<Image
src="/images/landing/icon_login.png"
alt="login icon"
width={24}
height={24}
className="ml-1 h-6 w-6 object-contain"
/>
</span>
</button>
) : (
<>
<span className="text-base text-gray-700">
<strong className="text-xl text-emerald-600">{userName}</strong>님 환영합니다.
</span>
<button
type="button"
onClick={logout}
className="inline-flex items-center gap-2 rounded-md bg-red-500 px-4 py-2 text-base text-white hover:bg-red-600"
>
로그아웃
<span aria-hidden>
<Image
src="/images/landing/icon_logout.png"
alt="logout icon"
width={24}
height={24}
className="h-6 w-6 object-contain"
/>
</span>
</button>
</>
)}
</div>
</div>
</div>
</header>
<LoginModal
open={loginOpen}
onClose={() => setLoginOpen(false)}
onSuccess={({ userName: nextUserName }) => {
// localStorage.setItem(LS_USER_KEY, nextUserName);
// if (accessToken) localStorage.setItem(LS_TOKEN_KEY, accessToken);
setUserName(nextUserName);
setLoginOpen(false);
router.refresh();
}}
/>
</>
);
}