feat: menu wrapper 생성
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { BaseLayout } from "@common/components/ui";
|
||||
import type { MenuItem } from "@common/components/api";
|
||||
import { useMenuStore } from "@/shared/lib/use-menu-store";
|
||||
|
||||
import { BaseLayout } from "@common/components/ui";
|
||||
import { MenuWrapper } from "@/entities/menu";
|
||||
import { useMenuStore } from "@/shared/lib";
|
||||
export default function WithBaseLayout({
|
||||
children,
|
||||
}: {
|
||||
@@ -21,7 +21,7 @@ export default function WithBaseLayout({
|
||||
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleMenuClick}>
|
||||
{children}
|
||||
<MenuWrapper>{children}</MenuWrapper>
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { LoginApi } from "./api/login-service";
|
||||
export { AuthWrapper } from "./ui/auth-wrapper";
|
||||
export { AuthWrapper } from "./lib/auth-wrapper";
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { client } from "@common/components/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { MenuItem, MenuService } from "../model/menu-interface";
|
||||
|
||||
export class MenuApiService implements MenuService {
|
||||
async getMenuRoles(siteId: string): Promise<MenuItem[]> {
|
||||
const response = await client.get<MenuItem[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_ROLES}/${siteId}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuApi = new MenuApiService();
|
||||
@@ -0,0 +1 @@
|
||||
export { MenuWrapper } from "./lib/menu-wrapper";
|
||||
@@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ENDPOINT, SITE_ID } from "@/shared/config";
|
||||
import { useMenuStore } from "@/shared/lib";
|
||||
import { MenuApi } from "../api/menu-service";
|
||||
import { getCurrentMenu } from "./utils";
|
||||
|
||||
const MenuWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
const pathname = usePathname();
|
||||
const { data: menuRoles } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => MenuApi.getMenuRoles(SITE_ID),
|
||||
});
|
||||
const { setCurrentMenuInfo } = useMenuStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
if (!menuRoles) return;
|
||||
const currentMenu = getCurrentMenu(menuRoles, pathname ?? "");
|
||||
|
||||
if (!currentMenu) return;
|
||||
setCurrentMenuInfo(currentMenu);
|
||||
}, [menuRoles]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export { MenuWrapper };
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { MenuItem } from "../model/menu-interface";
|
||||
|
||||
export const getCurrentMenu = (
|
||||
menus: MenuItem[],
|
||||
pathname: string,
|
||||
): MenuItem | null => {
|
||||
return (
|
||||
menus.find((menu) => {
|
||||
if (menu.children) return getCurrentMenu(menu.children, pathname);
|
||||
return menu.urlPath === pathname;
|
||||
}) ?? null
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
export interface MenuItem {
|
||||
menuCd: number;
|
||||
menuKornNm: string;
|
||||
menuEngNm: string;
|
||||
parentId: number | null;
|
||||
sortSeq: number;
|
||||
iconNm?: string;
|
||||
children: MenuItem[] | null;
|
||||
levelNo: number;
|
||||
urlPath: string;
|
||||
menuLnkgTypeCd: string;
|
||||
menuLnkgSn: number;
|
||||
menuPath: string;
|
||||
qrcd: string;
|
||||
useYn: string;
|
||||
otptYn: string;
|
||||
newWndwYn: string;
|
||||
requiredUrlPath: boolean;
|
||||
}
|
||||
|
||||
export interface MenuService {
|
||||
getMenuRoles(siteId: string): Promise<MenuItem[]>;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ function MenuPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageLayout pageTitle="메뉴 관리">
|
||||
<PageLayout>
|
||||
<div className="card !mb-0 flex !min-h-[calc(100vh-16.8rem)] gap-2">
|
||||
<div className="card card-border w-[24rem]">
|
||||
<Select value={siteId} onValueChange={handleSiteIdChange}>
|
||||
|
||||
@@ -38,7 +38,7 @@ function CreatePage() {
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLayout pageTitle="사이트 관리">
|
||||
<PageLayout>
|
||||
<SiteForm form={form} handleSave={createSite} />
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
@@ -63,7 +63,7 @@ function ModifyPage({ siteId }: { siteId: string }) {
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLayout pageTitle="사이트 관리">
|
||||
<PageLayout>
|
||||
<SiteForm
|
||||
form={form}
|
||||
siteId={siteId}
|
||||
|
||||
@@ -44,7 +44,7 @@ function SitePage() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PageLayout pageTitle="사이트 관리">
|
||||
<PageLayout>
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
|
||||
@@ -14,6 +14,7 @@ export const ENDPOINT = {
|
||||
IPS: "/cms-service/api/v1/site-ips",
|
||||
DOMAINS: "/cms-service/api/v1/site-dmns",
|
||||
MENUS: "/cms-service/api/v1/menus",
|
||||
MENU_ROLES: "/cms-service/api/v1/menu-roles",
|
||||
CONTENTS: "/cms-service/api/v1/contents",
|
||||
USER_OGNZ: "/cms-service/api/v1/user-ognz",
|
||||
},
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { getCookie, setCookie, removeCookie } from "./cookie";
|
||||
export { useMenuStore } from "./use-menu-store";
|
||||
export { useUserStore } from "./use-user-store";
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import { PageTitle } from "@common/components";
|
||||
import { useMenuStore } from "../lib/use-menu-store";
|
||||
|
||||
function PageLayout({
|
||||
children,
|
||||
pageTitle,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
pageTitle: string;
|
||||
}) {
|
||||
function PageLayout({ children }: { children: React.ReactNode }) {
|
||||
const { currentMenuInfo } = useMenuStore((state) => state);
|
||||
|
||||
const menuPaths = currentMenuInfo?.menuPath
|
||||
@@ -16,7 +10,10 @@ function PageLayout({
|
||||
|
||||
return (
|
||||
<div className="h-auto min-h-full bg-container">
|
||||
<PageTitle title={pageTitle} menuPaths={menuPaths ?? []} />
|
||||
<PageTitle
|
||||
title={currentMenuInfo?.menuKornNm ?? ""}
|
||||
menuPaths={menuPaths ?? []}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ export interface MenuItem {
|
||||
qrcd: string;
|
||||
useYn: string;
|
||||
otptYn: string;
|
||||
newWndwYn: string;
|
||||
requiredUrlPath: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./utils";
|
||||
export * from "./cookie";
|
||||
export * from "./utils";
|
||||
export * from "./use-mobile";
|
||||
|
||||
@@ -37,7 +37,7 @@ export function LNB({
|
||||
const [openMenuIds, setOpenMenuIds] = useState<number[]>([]);
|
||||
|
||||
function isPathMatched(path: string) {
|
||||
return currentPath === path || currentPath?.startsWith(`${path}/`);
|
||||
return currentPath === path;
|
||||
}
|
||||
|
||||
function MenuItem({ item }: { item: MenuItem }) {
|
||||
@@ -57,7 +57,7 @@ export function LNB({
|
||||
<SidebarMenuButton
|
||||
className={cn(
|
||||
"h-auto py-4 text-[1.4rem] text-label transition-colors hover:bg-accent",
|
||||
isMatched && "bg-accent text-primary",
|
||||
isMatched && "bg-accent !text-primary",
|
||||
)}
|
||||
onClick={handleClick}
|
||||
>
|
||||
@@ -90,15 +90,15 @@ export function LNB({
|
||||
<Collapsible open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger asChild>
|
||||
<div className="group flex items-center justify-between rounded-sm py-4 pr-2 hover:bg-accent data-[state=open]:text-primary">
|
||||
<div className="group flex items-center justify-between rounded-sm py-4 pr-2 hover:bg-accent">
|
||||
<SidebarMenuButton className="group/menu-button text-[1.4rem] group-data-[state=open]:bg-transparent">
|
||||
<i
|
||||
className={cn(
|
||||
"diveicon ml-2 text-label group-data-[state=open]:text-primary",
|
||||
"diveicon ml-2 text-label",
|
||||
`di-${item.iconNm}`,
|
||||
)}
|
||||
/>
|
||||
<span className="text-label group-hover/menu-button:text-foreground group-data-[state=open]:text-primary">
|
||||
<span className="text-label group-hover/menu-button:text-foreground">
|
||||
{item.menuKornNm}
|
||||
</span>
|
||||
</SidebarMenuButton>
|
||||
|
||||
@@ -14,7 +14,7 @@ const PageTitle = ({
|
||||
<div className="flex items-center gap-3">
|
||||
<i className="diveicon di-home-fill text-foreground" />
|
||||
{menuPaths?.map((path, i) => (
|
||||
<React.Fragment key={path}>
|
||||
<React.Fragment key={`${i}-${path}`}>
|
||||
{i !== 0 && (
|
||||
<i className="diveicon di-arrow-forward text-foreground" />
|
||||
)}
|
||||
|
||||
@@ -3,8 +3,7 @@ import { Slot } from "@radix-ui/react-slot";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { PanelLeft } from "lucide-react";
|
||||
|
||||
import { useIsMobile } from "../../lib/use-mobile";
|
||||
import { cn } from "../../lib/utils";
|
||||
import { cn, useIsMobile } from "../../lib";
|
||||
import { Button } from "../button";
|
||||
import { Input } from "../input";
|
||||
import { Separator } from "../separator";
|
||||
|
||||
Reference in New Issue
Block a user