메뉴얼 팝업 공통화 추가
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user