[apps/cs] feat: 임시저장 메뉴 미리보기 기능 작업
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { Button } from "@common/components";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
LNB,
|
||||
} from "@common/components";
|
||||
import { MenuApi, menuFormSchema, type MenuFormData } from "@/entities/menu";
|
||||
import { ManagementAuthorityDialog } from "@/entities/menu-authority";
|
||||
import { Form } from "@/shared/ui/form";
|
||||
@@ -33,7 +41,7 @@ function MenuForm({
|
||||
handleDelete,
|
||||
}: MenuFormProps) {
|
||||
const isModifyMode = !!menuCd;
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const { confirm } = useConfirm();
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
@@ -96,55 +104,82 @@ function MenuForm({
|
||||
const isSubmitDisabled = !form.watch("siteId");
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{isModifyMode ? "메뉴 수정" : "메뉴 등록"}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<MenuHistoryDialog menuCd={menuCd} />
|
||||
<ManagementAuthorityDialog menuCd={menuCd} />
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{isModifyMode ? "메뉴 수정" : "메뉴 등록"}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<MenuHistoryDialog menuCd={menuCd} />
|
||||
<ManagementAuthorityDialog menuCd={menuCd} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<BaseMenuForm form={form} menuCd={menuCd} />
|
||||
<TypeMenuForm form={form} menuType={menuType} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<BaseMenuForm form={form} menuCd={menuCd} />
|
||||
<TypeMenuForm form={form} menuType={menuType} />
|
||||
<div className="flex justify-center gap-4 pb-12 pt-10">
|
||||
{isModifyMode && (
|
||||
<Button
|
||||
size="lg"
|
||||
color="white"
|
||||
type="button"
|
||||
onClick={handleLoadTemporaryData}
|
||||
>
|
||||
불러오기
|
||||
</Button>
|
||||
)}
|
||||
{isModifyMode && (
|
||||
<Button
|
||||
size="lg"
|
||||
color="gray"
|
||||
type="button"
|
||||
onClick={handleMenuTemporaryStorage}
|
||||
>
|
||||
임시저장
|
||||
</Button>
|
||||
)}
|
||||
{isModifyMode && (
|
||||
<Button
|
||||
size="lg"
|
||||
color="emerald"
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
미리보기
|
||||
</Button>
|
||||
)}
|
||||
<Button size="lg" type="submit" disabled={isSubmitDisabled}>
|
||||
저장
|
||||
</Button>
|
||||
{isModifyMode && (
|
||||
<Button size="lg" color="red" type="button" onClick={onDelete}>
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center gap-4 pb-12 pt-10">
|
||||
{isModifyMode && (
|
||||
<Button
|
||||
size="lg"
|
||||
color="white"
|
||||
type="button"
|
||||
onClick={handleLoadTemporaryData}
|
||||
>
|
||||
불러오기
|
||||
</Button>
|
||||
)}
|
||||
{isModifyMode && (
|
||||
<Button
|
||||
size="lg"
|
||||
color="gray"
|
||||
type="button"
|
||||
onClick={handleMenuTemporaryStorage}
|
||||
>
|
||||
임시저장
|
||||
</Button>
|
||||
)}
|
||||
<Button size="lg" type="submit" disabled={isSubmitDisabled}>
|
||||
저장
|
||||
</Button>
|
||||
{isModifyMode && (
|
||||
<Button size="lg" color="red" type="button" onClick={onDelete}>
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</form>
|
||||
</Form>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-h-[90vh]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>메뉴 미리보기</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex h-full min-h-[60vh] flex-col gap-4">
|
||||
<LNB
|
||||
currentPath={form.watch("urlPath") ?? ""}
|
||||
propSiteId={form.watch("siteId")}
|
||||
previewMode
|
||||
classNames={{ wrapper: "static h-full border-none p-0" }}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ export interface MenuItem {
|
||||
requiredUrlPath: boolean;
|
||||
}
|
||||
|
||||
export interface TreeMenu extends MenuItem {
|
||||
menuHlpCn: string;
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
|
||||
export const getMenuList = async (siteId: string) => {
|
||||
@@ -29,3 +33,10 @@ export const getMenuList = async (siteId: string) => {
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getPreviewTreeMenuList = async (siteId: string) => {
|
||||
const response = await client.get<TreeMenu[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree/preview`,
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const ENDPOINT = {
|
||||
CMS_SERVICE: {
|
||||
MENUS: "/cms-service/api/v1/menus",
|
||||
MENU_ROLES: "/cms-service/api/v1/menu-roles",
|
||||
SITES: "/cms-service/api/v1/sites",
|
||||
POPUPS: "/cms-service/api/v1/popups",
|
||||
|
||||
@@ -15,6 +15,7 @@ export * from "./icon-finder";
|
||||
export * from "./input";
|
||||
export * from "./label";
|
||||
export * from "./layout";
|
||||
export * from "./lnb";
|
||||
export * from "./loading";
|
||||
export * from "./page-title";
|
||||
export * from "./popover";
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { cn, setCookie } from "../../lib";
|
||||
import { getMenuList, type MenuItem } from "../../api";
|
||||
import { getParentIds } from "./lib";
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
@@ -22,29 +20,47 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "../collapsible";
|
||||
import { getParentIds } from "./lib";
|
||||
import { getCookie } from "../../lib";
|
||||
|
||||
const siteId = getCookie("site-id-key") ?? "";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { cn, getCookie, setCookie } from "../../lib";
|
||||
import { getMenuList, getPreviewTreeMenuList, type MenuItem } from "../../api";
|
||||
|
||||
export function LNB({
|
||||
viewOnly,
|
||||
currentPath,
|
||||
propSiteId,
|
||||
previewMode,
|
||||
classNames,
|
||||
handleMenuClick,
|
||||
}: {
|
||||
viewOnly?: boolean;
|
||||
currentPath?: string;
|
||||
propSiteId?: string;
|
||||
previewMode?: boolean;
|
||||
classNames?: {
|
||||
wrapper?: string;
|
||||
};
|
||||
handleMenuClick?: (item: MenuItem) => void;
|
||||
}) {
|
||||
const { data: menuList } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(siteId),
|
||||
enabled: !viewOnly,
|
||||
});
|
||||
|
||||
const [defaultOpenMenuIds, setDefaultOpenMenuIds] = useState<number[]>([]);
|
||||
const prevPathRef = useRef(currentPath);
|
||||
|
||||
const cookieSiteId = getCookie("site-id-key") ?? "";
|
||||
const siteId = propSiteId ?? cookieSiteId;
|
||||
|
||||
const { data: menuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(siteId),
|
||||
enabled: !viewOnly && !previewMode,
|
||||
});
|
||||
|
||||
const { data: previewTreeMenuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId, "preview"],
|
||||
queryFn: () => getPreviewTreeMenuList(siteId),
|
||||
enabled: previewMode,
|
||||
});
|
||||
|
||||
const menuList = previewMode ? previewTreeMenuListData : menuListData;
|
||||
|
||||
function isPathMatched(path: string) {
|
||||
if (!path) return false;
|
||||
return currentPath?.startsWith(path);
|
||||
@@ -191,7 +207,10 @@ export function LNB({
|
||||
return (
|
||||
<Sidebar
|
||||
collapsible="icon"
|
||||
className="border-border bg-background px-4 py-[6rem]"
|
||||
className={cn(
|
||||
"border-border bg-background px-4 py-[6rem]",
|
||||
classNames?.wrapper,
|
||||
)}
|
||||
>
|
||||
<SidebarContent className="bg-background">
|
||||
<SidebarMenu className="gap-0">
|
||||
|
||||
Reference in New Issue
Block a user