메뉴얼 팝업 공통화 추가

This commit is contained in:
2025-11-13 18:30:43 +09:00
parent 3d15def3ad
commit ca37374c87
7 changed files with 112 additions and 25 deletions
@@ -0,0 +1,55 @@
"use client";
import { createContext, useContext, useState, useCallback } from "react";
interface HelpDialogContextType {
openHelp: () => void;
closeHelp: () => void;
isOpen: boolean;
setHelpContent: (content: string) => void;
helpContent: string;
}
const HelpDialogContext = createContext<HelpDialogContextType | undefined>(
undefined,
);
export function HelpDialogProvider({
children,
}: {
children: React.ReactNode;
}) {
const [isOpen, setIsOpen] = useState(false);
const [helpContent, setHelpContent] = useState<string>("");
const openHelp = useCallback(() => {
setIsOpen(true);
}, []);
const closeHelp = useCallback(() => {
setIsOpen(false);
}, []);
return (
<HelpDialogContext.Provider
value={{
openHelp,
closeHelp,
isOpen,
setHelpContent,
helpContent,
}}
>
{children}
</HelpDialogContext.Provider>
);
}
export function useHelpDialog() {
const context = useContext(HelpDialogContext);
if (context === undefined) {
throw new Error("useHelpDialog must be used within a HelpDialogProvider");
}
return context;
}
@@ -1,6 +1,7 @@
import type { QueryClient } from "@tanstack/react-query";
import { QueryProvider } from "../../provider/query-provider";
import { PopupProvider } from "../../provider/popup-provider";
import { HelpDialogProvider } from "../../provider/help-dialog-context";
import { ClientIpGuard } from "../../lib/client-ip-guard";
import type { MenuItem } from "../../api";
import { SidebarProvider } from "../sidebar";
@@ -24,7 +25,8 @@ export function BaseLayout({
<QueryProvider queryClient={queryClient}>
<ClientIpGuard currentPath={currentPath}>
<PopupProvider>
<SidebarProvider>
<HelpDialogProvider>
<SidebarProvider>
<div className="flex-1">
<Header />
<div className="flex">
@@ -39,6 +41,7 @@ export function BaseLayout({
</div>
</div>
</SidebarProvider>
</HelpDialogProvider>
</PopupProvider>
</ClientIpGuard>
</QueryProvider>
@@ -1,6 +1,7 @@
import type { QueryClient } from "@tanstack/react-query";
import { QueryProvider } from "../../provider/query-provider";
import { PopupProvider } from "../../provider/popup-provider";
import { HelpDialogProvider } from "../../provider/help-dialog-context";
import { ClientIpGuard } from "../../lib/client-ip-guard";
import type { MenuItem } from "../../api";
import { SidebarProvider } from "../sidebar";
@@ -23,7 +24,8 @@ export function NoFooterLayout({
<QueryProvider queryClient={queryClient}>
<ClientIpGuard currentPath={currentPath}>
<PopupProvider>
<SidebarProvider>
<HelpDialogProvider>
<SidebarProvider>
<div className="flex-1">
<Header />
<div className="flex">
@@ -37,6 +39,7 @@ export function NoFooterLayout({
</div>
</div>
</SidebarProvider>
</HelpDialogProvider>
</PopupProvider>
</ClientIpGuard>
</QueryProvider>
@@ -1,6 +1,7 @@
import type { QueryClient } from "@tanstack/react-query";
import { QueryProvider } from "../../provider/query-provider";
import { PopupProvider } from "../../provider/popup-provider";
import { HelpDialogProvider } from "../../provider/help-dialog-context";
import { ClientIpGuard } from "../../lib/client-ip-guard";
import type { MenuItem } from "../../api";
import { SidebarProvider } from "../sidebar";
@@ -24,7 +25,8 @@ export function NoLNBLayout({
<QueryProvider queryClient={queryClient}>
<ClientIpGuard currentPath={currentPath}>
<PopupProvider>
<SidebarProvider>
<HelpDialogProvider>
<SidebarProvider>
<div className="flex-1">
<Header />
<div className="flex">
@@ -35,6 +37,7 @@ export function NoLNBLayout({
</div>
</div>
</SidebarProvider>
</HelpDialogProvider>
</PopupProvider>
</ClientIpGuard>
</QueryProvider>
@@ -1,4 +1,5 @@
import { QueryProvider } from "../../provider/query-provider";
import { HelpDialogProvider } from "../../provider/help-dialog-context";
import { SidebarProvider } from "../sidebar";
import { LNB } from "../lnb";
import { Header } from "../header";
@@ -16,7 +17,8 @@ export function VBaseLayout({
}) {
return (
<QueryProvider queryClient={queryClient}>
<SidebarProvider>
<HelpDialogProvider>
<SidebarProvider>
<div className="flex-1">
<Header viewOnly />
<div className="flex h-[calc(100vh-50px)]">
@@ -30,6 +32,7 @@ export function VBaseLayout({
</div>
</div>
</SidebarProvider>
</HelpDialogProvider>
</QueryProvider>
);
}
@@ -31,6 +31,7 @@ import {
} from "../dialog";
import { ENDPOINT } from "../../config";
import { cn, getCookie, setCookie } from "../../lib";
import { useHelpDialog } from "../../provider/help-dialog-context";
import {
getMenuList,
getPreviewTreeMenuList,
@@ -63,10 +64,9 @@ export function LNB({
}) {
const [defaultOpenMenuIds, setDefaultOpenMenuIds] = useState<number[]>([]);
const [currentMenuCode, setCurrentMenuCode] = useState<number | null>(null);
const [currentMenuHelp, setCurrentMenuHelp] = useState<string>("");
const [isHelpModalOpen, setIsHelpModalOpen] = useState(false);
const prevPathRef = useRef(currentPath);
const { browser } = useDetectDevice();
const { isOpen: isHelpModalOpen, openHelp, closeHelp, setHelpContent, helpContent } = useHelpDialog();
const siteId = propSiteId ?? appSiteId;
@@ -142,7 +142,7 @@ export function LNB({
if (matchedMenuCode) {
setCurrentMenuCode(matchedMenuCode);
setCurrentMenuHelp(matchedMenuHelp);
setHelpContent(matchedMenuHelp);
}
// 접속 로그 저장
@@ -295,9 +295,10 @@ export function LNB({
{/* 도움말 아이콘 */}
<div className="mt-auto pb-4">
<Dialog open={isHelpModalOpen} onOpenChange={setIsHelpModalOpen}>
<Dialog open={isHelpModalOpen} onOpenChange={(open) => open ? openHelp() : closeHelp()}>
<DialogTrigger asChild>
<button
onClick={openHelp}
className={cn(
"flex h-12 w-full items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
"focus:outline-none",
@@ -313,10 +314,10 @@ export function LNB({
<DialogTitle>도움말</DialogTitle>
</DialogHeader>
<div className="py-4">
{currentMenuHelp ? (
{helpContent ? (
<div
className="text-sm leading-relaxed text-muted-foreground"
dangerouslySetInnerHTML={{ __html: currentMenuHelp }}
dangerouslySetInnerHTML={{ __html: helpContent }}
/>
) : (
<p className="text-sm text-muted-foreground">
@@ -1,4 +1,7 @@
"use client";
import React from "react";
import { useHelpDialog } from "../../provider/help-dialog-context";
const PageTitle = ({
title,
@@ -7,23 +10,39 @@ const PageTitle = ({
title: string;
menuPaths: string[];
}) => {
const { openHelp } = useHelpDialog();
return (
<div className="flex w-full items-center justify-between px-[1.8rem] pt-[2rem]">
<h2 className="text-[2.4rem] font-semibold text-title">{title}</h2>
{menuPaths.length > 0 && (
<div className="flex items-center gap-3">
<i className="diveicon di-home-fill text-foreground" />
{menuPaths?.map((path, i) => (
<React.Fragment key={`${i}-${path}`}>
{i !== 0 && (
<i className="diveicon di-arrow-forward text-foreground" />
)}
<span className="text-xl text-foreground">{path}</span>
</React.Fragment>
))}
</div>
)}
<div className="flex items-center gap-3">메뉴얼</div>
<div className="flex items-center gap-3">
<h2 className="text-[2.4rem] font-semibold text-title">{title}</h2>
<button
onClick={openHelp}
className="flex items-center gap-3 cursor-pointer hover:opacity-80 transition-opacity"
>
<i className="diveicon di-question-circle text-red-500 text-2xl" />
</button>
</div>
<div className="flex items-center gap-3">
{menuPaths.length > 0 && (
<div className="flex items-center gap-3">
<i className="diveicon di-home-fill text-foreground" />
{menuPaths?.map((path, i) => (
<React.Fragment key={`${i}-${path}`}>
{i !== 0 && (
<i className="diveicon di-arrow-forward text-foreground" />
)}
<span className="text-xl text-foreground">{path}</span>
</React.Fragment>
))}
</div>
)}
<button
onClick={openHelp}
className="flex items-center gap-3 cursor-pointer hover:opacity-80 transition-opacity"
>
<i className="diveicon di-question-circle text-red-500 text-2xl" />
</button>
</div>
</div>
);
};