diff --git a/frontend/components/admin/MenuFormModal.tsx b/frontend/components/admin/MenuFormModal.tsx index f8d80592..ad9edfc4 100644 --- a/frontend/components/admin/MenuFormModal.tsx +++ b/frontend/components/admin/MenuFormModal.tsx @@ -47,12 +47,12 @@ export const MenuFormModal: React.FC = ({ uiTexts, }) => { // console.log("🎯 MenuFormModal λ Œλ”λ§ - Props:", { - // isOpen, - // menuId, - // parentId, - // menuType, - // level, - // parentCompanyCode, + // isOpen, + // menuId, + // parentId, + // menuType, + // level, + // parentCompanyCode, // }); // λ‹€κ΅­μ–΄ ν…μŠ€νŠΈ κ°€μ Έμ˜€κΈ° ν•¨μˆ˜ @@ -75,12 +75,18 @@ export const MenuFormModal: React.FC = ({ }); // ν™”λ©΄ ν• λ‹Ή κ΄€λ ¨ μƒνƒœ - const [urlType, setUrlType] = useState<"direct" | "screen">("screen"); // URL 직접 μž…λ ₯ or ν™”λ©΄ ν• λ‹Ή (κΈ°λ³Έκ°’: ν™”λ©΄ ν• λ‹Ή) + const [urlType, setUrlType] = useState<"direct" | "screen" | "dashboard">("screen"); // URL 직접 μž…λ ₯ or ν™”λ©΄ ν• λ‹Ή or λŒ€μ‹œλ³΄λ“œ ν• λ‹Ή (κΈ°λ³Έκ°’: ν™”λ©΄ ν• λ‹Ή) const [selectedScreen, setSelectedScreen] = useState(null); const [screens, setScreens] = useState([]); const [screenSearchText, setScreenSearchText] = useState(""); const [isScreenDropdownOpen, setIsScreenDropdownOpen] = useState(false); + // λŒ€μ‹œλ³΄λ“œ ν• λ‹Ή κ΄€λ ¨ μƒνƒœ + const [selectedDashboard, setSelectedDashboard] = useState(null); + const [dashboards, setDashboards] = useState([]); + const [dashboardSearchText, setDashboardSearchText] = useState(""); + const [isDashboardDropdownOpen, setIsDashboardDropdownOpen] = useState(false); + const [loading, setLoading] = useState(false); const [isEdit, setIsEdit] = useState(false); const [companies, setCompanies] = useState([]); @@ -93,21 +99,6 @@ export const MenuFormModal: React.FC = ({ try { const response = await screenApi.getScreens({ size: 1000 }); // λͺ¨λ“  ν™”λ©΄ κ°€μ Έμ˜€κΈ° - // console.log("πŸ” ν™”λ©΄ λͺ©λ‘ λ‘œλ“œ 디버깅:", { - // totalScreens: response.data.length, - // firstScreen: response.data[0], - // firstScreenFields: response.data[0] ? Object.keys(response.data[0]) : [], - // firstScreenValues: response.data[0] ? Object.values(response.data[0]) : [], - // allScreenIds: response.data - // .map((s) => ({ - // screenId: s.screenId, - // legacyId: s.id, - // name: s.screenName, - // code: s.screenCode, - // })) - // .slice(0, 5), // 처음 5개만 좜λ ₯ - // }); - setScreens(response.data); console.log("βœ… ν™”λ©΄ λͺ©λ‘ λ‘œλ“œ μ™„λ£Œ:", response.data.length); } catch (error) { @@ -116,15 +107,28 @@ export const MenuFormModal: React.FC = ({ } }; + // λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ + const loadDashboards = async () => { + try { + const { dashboardApi } = await import("@/lib/api/dashboard"); + const response = await dashboardApi.getMyDashboards(); + setDashboards(response.dashboards || []); + console.log("βœ… λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ μ™„λ£Œ:", response.dashboards?.length || 0); + } catch (error) { + console.error("❌ λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ μ‹€νŒ¨:", error); + toast.error("λŒ€μ‹œλ³΄λ“œ λͺ©λ‘μ„ λΆˆλŸ¬μ˜€λŠ”λ° μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."); + } + }; + // ν™”λ©΄ 선택 μ‹œ URL μžλ™ μ„€μ • const handleScreenSelect = (screen: ScreenDefinition) => { // console.log("πŸ–₯️ ν™”λ©΄ 선택 디버깅:", { - // screen, - // screenId: screen.screenId, - // screenIdType: typeof screen.screenId, - // legacyId: screen.id, - // allFields: Object.keys(screen), - // screenValues: Object.values(screen), + // screen, + // screenId: screen.screenId, + // screenIdType: typeof screen.screenId, + // legacyId: screen.id, + // allFields: Object.keys(screen), + // screenValues: Object.values(screen), // }); // ScreenDefinitionμ—μ„œλŠ” screenId ν•„λ“œλ₯Ό μ‚¬μš© @@ -155,24 +159,42 @@ export const MenuFormModal: React.FC = ({ })); // console.log("πŸ–₯️ ν™”λ©΄ 선택 μ™„λ£Œ:", { - // screenId: screen.screenId, - // legacyId: screen.id, - // actualScreenId, - // screenName: screen.screenName, - // menuType: menuType, - // formDataMenuType: formData.menuType, - // isAdminMenu, - // generatedUrl: screenUrl, + // screenId: screen.screenId, + // legacyId: screen.id, + // actualScreenId, + // screenName: screen.screenName, + // menuType: menuType, + // formDataMenuType: formData.menuType, + // isAdminMenu, + // generatedUrl: screenUrl, // }); }; + // λŒ€μ‹œλ³΄λ“œ 선택 μ‹œ URL μžλ™ μ„€μ • + const handleDashboardSelect = (dashboard: any) => { + setSelectedDashboard(dashboard); + setIsDashboardDropdownOpen(false); + + // λŒ€μ‹œλ³΄λ“œ URL 생성 + let dashboardUrl = `/dashboard/${dashboard.id}`; + + // ν˜„μž¬ 메뉴 νƒ€μž…μ΄ κ΄€λ¦¬μžμΈμ§€ 확인 (0 λ˜λŠ” "admin") + const isAdminMenu = menuType === "0" || menuType === "admin" || formData.menuType === "0"; + if (isAdminMenu) { + dashboardUrl += "?mode=admin"; + } + + setFormData((prev) => ({ ...prev, menuUrl: dashboardUrl })); + toast.success(`λŒ€μ‹œλ³΄λ“œκ°€ μ„ νƒλ˜μ—ˆμŠ΅λ‹ˆλ‹€: ${dashboard.title}`); + }; + // URL νƒ€μž… λ³€κ²½ μ‹œ 처리 - const handleUrlTypeChange = (type: "direct" | "screen") => { + const handleUrlTypeChange = (type: "direct" | "screen" | "dashboard") => { // console.log("πŸ”„ URL νƒ€μž… λ³€κ²½:", { - // from: urlType, - // to: type, - // currentSelectedScreen: selectedScreen?.screenName, - // currentUrl: formData.menuUrl, + // from: urlType, + // to: type, + // currentSelectedScreen: selectedScreen?.screenName, + // currentUrl: formData.menuUrl, // }); setUrlType(type); @@ -286,10 +308,10 @@ export const MenuFormModal: React.FC = ({ const screenId = menuUrl.match(/\/screens\/(\d+)/)?.[1]; if (screenId) { // console.log("πŸ” κΈ°μ‘΄ λ©”λ‰΄μ—μ„œ ν™”λ©΄ ID μΆ”μΆœ:", { - // menuUrl, - // screenId, - // hasAdminParam: menuUrl.includes("mode=admin"), - // currentScreensCount: screens.length, + // menuUrl, + // screenId, + // hasAdminParam: menuUrl.includes("mode=admin"), + // currentScreensCount: screens.length, // }); // ν™”λ©΄ μ„€μ • ν•¨μˆ˜ @@ -298,15 +320,15 @@ export const MenuFormModal: React.FC = ({ if (screen) { setSelectedScreen(screen); // console.log("πŸ–₯️ κΈ°μ‘΄ λ©”λ‰΄μ˜ ν• λ‹Ήλœ ν™”λ©΄ μ„€μ •:", { - // screen, - // originalUrl: menuUrl, - // hasAdminParam: menuUrl.includes("mode=admin"), + // screen, + // originalUrl: menuUrl, + // hasAdminParam: menuUrl.includes("mode=admin"), // }); return true; } else { // console.warn("⚠️ ν•΄λ‹Ή ID의 화면을 찾을 수 μ—†μŒ:", { - // screenId, - // availableScreens: screens.map((s) => ({ screenId: s.screenId, id: s.id, name: s.screenName })), + // screenId, + // availableScreens: screens.map((s) => ({ screenId: s.screenId, id: s.id, name: s.screenName })), // }); return false; } @@ -325,30 +347,34 @@ export const MenuFormModal: React.FC = ({ }, 500); } } + } else if (menuUrl.startsWith("/dashboard/")) { + setUrlType("dashboard"); + setSelectedScreen(null); + // λŒ€μ‹œλ³΄λ“œ ID μΆ”μΆœ 및 선택은 useEffectμ—μ„œ 처리됨 } else { setUrlType("direct"); setSelectedScreen(null); } // console.log("μ„€μ •λœ 폼 데이터:", { - // objid: menu.objid || menu.OBJID, - // parentObjId: menu.parent_obj_id || menu.PARENT_OBJ_ID || "0", - // menuNameKor: menu.menu_name_kor || menu.MENU_NAME_KOR || "", - // menuUrl: menu.menu_url || menu.MENU_URL || "", - // menuDesc: menu.menu_desc || menu.MENU_DESC || "", - // seq: menu.seq || menu.SEQ || 1, - // menuType: convertedMenuType, - // status: convertedStatus, - // companyCode: companyCode, - // langKey: langKey, + // objid: menu.objid || menu.OBJID, + // parentObjId: menu.parent_obj_id || menu.PARENT_OBJ_ID || "0", + // menuNameKor: menu.menu_name_kor || menu.MENU_NAME_KOR || "", + // menuUrl: menu.menu_url || menu.MENU_URL || "", + // menuDesc: menu.menu_desc || menu.MENU_DESC || "", + // seq: menu.seq || menu.SEQ || 1, + // menuType: convertedMenuType, + // status: convertedStatus, + // companyCode: companyCode, + // langKey: langKey, // }); } } catch (error: any) { console.error("메뉴 정보 λ‘œλ”© 였λ₯˜:", error); // console.error("였λ₯˜ 상세 정보:", { - // message: error?.message, - // stack: error?.stack, - // response: error?.response, + // message: error?.message, + // stack: error?.stack, + // response: error?.response, // }); toast.error(getText(MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_MENU_INFO)); } finally { @@ -391,11 +417,11 @@ export const MenuFormModal: React.FC = ({ }); // console.log("메뉴 등둝 κΈ°λ³Έκ°’ μ„€μ •:", { - // parentObjId: parentId || "0", - // menuType: defaultMenuType, - // status: "ACTIVE", - // companyCode: "", - // langKey: "", + // parentObjId: parentId || "0", + // menuType: defaultMenuType, + // status: "ACTIVE", + // companyCode: "", + // langKey: "", // }); } }, [menuId, parentId, menuType]); @@ -430,10 +456,11 @@ export const MenuFormModal: React.FC = ({ } }, [isOpen, formData.companyCode]); - // ν™”λ©΄ λͺ©λ‘ λ‘œλ“œ + // ν™”λ©΄ λͺ©λ‘ 및 λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ useEffect(() => { if (isOpen) { loadScreens(); + loadDashboards(); } }, [isOpen]); @@ -449,9 +476,9 @@ export const MenuFormModal: React.FC = ({ if (screen) { setSelectedScreen(screen); // console.log("βœ… κΈ°μ‘΄ λ©”λ‰΄μ˜ ν• λ‹Ήλœ ν™”λ©΄ μžλ™ μ„€μ • μ™„λ£Œ:", { - // screenId, - // screenName: screen.screenName, - // menuUrl, + // screenId, + // screenName: screen.screenName, + // menuUrl, // }); } } @@ -459,6 +486,23 @@ export const MenuFormModal: React.FC = ({ } }, [screens, isEdit, formData.menuUrl, urlType, selectedScreen]); + // λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ μ™„λ£Œ ν›„ κΈ°μ‘΄ λ©”λ‰΄μ˜ ν• λ‹Ήλœ λŒ€μ‹œλ³΄λ“œ μ„€μ • + useEffect(() => { + if (dashboards.length > 0 && isEdit && formData.menuUrl && urlType === "dashboard") { + const menuUrl = formData.menuUrl; + if (menuUrl.startsWith("/dashboard/")) { + const dashboardId = menuUrl.replace("/dashboard/", ""); + if (dashboardId && !selectedDashboard) { + console.log("πŸ”„ λŒ€μ‹œλ³΄λ“œ λͺ©λ‘ λ‘œλ“œ μ™„λ£Œ - κΈ°μ‘΄ ν• λ‹Ή λŒ€μ‹œλ³΄λ“œ μžλ™ μ„€μ •"); + const dashboard = dashboards.find((d) => d.id === dashboardId); + if (dashboard) { + setSelectedDashboard(dashboard); + } + } + } + } + }, [dashboards, isEdit, formData.menuUrl, urlType, selectedDashboard]); + // λ“œλ‘­λ‹€μš΄ μ™ΈλΆ€ 클릭 μ‹œ λ‹«κΈ° useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -471,9 +515,13 @@ export const MenuFormModal: React.FC = ({ setIsScreenDropdownOpen(false); setScreenSearchText(""); } + if (!target.closest(".dashboard-dropdown")) { + setIsDashboardDropdownOpen(false); + setDashboardSearchText(""); + } }; - if (isLangKeyDropdownOpen || isScreenDropdownOpen) { + if (isLangKeyDropdownOpen || isScreenDropdownOpen || isDashboardDropdownOpen) { document.addEventListener("mousedown", handleClickOutside); } @@ -751,6 +799,12 @@ export const MenuFormModal: React.FC = ({ ν™”λ©΄ ν• λ‹Ή +
+ + +