"use client"; import React, { useState, useEffect } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { toast } from "sonner"; import { Search, Monitor, Settings, X, Plus } from "lucide-react"; import { menuScreenApi } from "@/lib/api/screen"; import { apiClient } from "@/lib/api/client"; import type { MenuItem } from "@/lib/api/menu"; import { ScreenDefinition } from "@/types/screen"; interface MenuAssignmentModalProps { isOpen: boolean; onClose: () => void; screenInfo: ScreenDefinition | null; onAssignmentComplete?: () => void; onBackToList?: () => void; // 화면 목록으로 돌아가는 콜백 추가 } export const MenuAssignmentModal: React.FC = ({ isOpen, onClose, screenInfo, onAssignmentComplete, onBackToList, }) => { const [menus, setMenus] = useState([]); const [selectedMenuId, setSelectedMenuId] = useState(""); const [selectedMenu, setSelectedMenu] = useState(null); const [searchTerm, setSearchTerm] = useState(""); const [loading, setLoading] = useState(false); const [assigning, setAssigning] = useState(false); const [existingScreens, setExistingScreens] = useState([]); const [showReplaceDialog, setShowReplaceDialog] = useState(false); const [assignmentSuccess, setAssignmentSuccess] = useState(false); const [assignmentMessage, setAssignmentMessage] = useState(""); // 메뉴 목록 로드 (관리자 메뉴만) const loadMenus = async () => { try { setLoading(true); // 화면관리는 관리자 전용 기능이므로 관리자 메뉴만 가져오기 const adminResponse = await apiClient.get("/admin/menus", { params: { menuType: "0" } }); const adminMenus = adminResponse.data?.data || []; // 관리자 메뉴 정규화 const normalizedAdminMenus = adminMenus.map((menu: any) => ({ objid: menu.objid || menu.OBJID, parent_obj_id: menu.parent_obj_id || menu.PARENT_OBJ_ID, menu_name_kor: menu.menu_name_kor || menu.MENU_NAME_KOR, menu_url: menu.menu_url || menu.MENU_URL, menu_desc: menu.menu_desc || menu.MENU_DESC, seq: menu.seq || menu.SEQ, menu_type: "0", // 관리자 메뉴 status: menu.status || menu.STATUS, lev: menu.lev || menu.LEV, company_code: menu.company_code || menu.COMPANY_CODE, company_name: menu.company_name || menu.COMPANY_NAME, })); console.log("로드된 관리자 메뉴 목록:", { total: normalizedAdminMenus.length, sample: normalizedAdminMenus.slice(0, 3), }); setMenus(normalizedAdminMenus); } catch (error) { console.error("메뉴 목록 로드 실패:", error); toast.error("메뉴 목록을 불러오는데 실패했습니다."); } finally { setLoading(false); } }; // 모달이 열릴 때 메뉴 목록 로드 useEffect(() => { if (isOpen) { loadMenus(); setSelectedMenuId(""); setSelectedMenu(null); setSearchTerm(""); setAssignmentSuccess(false); setAssignmentMessage(""); } }, [isOpen]); // 메뉴 선택 처리 const handleMenuSelect = async (menuId: string) => { // 유효하지 않은 메뉴 ID인 경우 처리하지 않음 if (!menuId || menuId === "no-menu") { setSelectedMenuId(""); setSelectedMenu(null); setExistingScreens([]); return; } setSelectedMenuId(menuId); const menu = menus.find((m) => m.objid?.toString() === menuId); setSelectedMenu(menu || null); // 선택된 메뉴에 할당된 화면들 확인 if (menu) { try { const menuObjid = parseInt(menu.objid?.toString() || "0"); if (menuObjid > 0) { const screens = await menuScreenApi.getScreensByMenu(menuObjid); setExistingScreens(screens); console.log(`메뉴 "${menu.menu_name_kor}"에 할당된 화면:`, screens); } } catch (error) { console.error("할당된 화면 조회 실패:", error); setExistingScreens([]); } } }; // 화면 할당 처리 const handleAssignScreen = async () => { if (!selectedMenu || !screenInfo) { toast.error("메뉴와 화면 정보가 필요합니다."); return; } // 기존에 할당된 화면이 있는지 확인 if (existingScreens.length > 0) { // 이미 같은 화면이 할당되어 있는지 확인 const alreadyAssigned = existingScreens.some((screen) => screen.screenId === screenInfo.screenId); if (alreadyAssigned) { toast.info("이미 해당 메뉴에 할당된 화면입니다."); return; } // 다른 화면이 할당되어 있으면 교체 확인 setShowReplaceDialog(true); return; } // 기존 화면이 없으면 바로 할당 await performAssignment(); }; // 실제 할당 수행 const performAssignment = async (replaceExisting = false) => { if (!selectedMenu || !screenInfo) return; try { setAssigning(true); const menuObjid = parseInt(selectedMenu.objid?.toString() || "0"); if (menuObjid === 0) { toast.error("유효하지 않은 메뉴 ID입니다."); return; } // 기존 화면 교체인 경우 기존 화면들 먼저 제거 if (replaceExisting && existingScreens.length > 0) { console.log("기존 화면들 제거 중...", existingScreens); for (const existingScreen of existingScreens) { try { await menuScreenApi.unassignScreenFromMenu(existingScreen.screenId, menuObjid); console.log(`기존 화면 "${existingScreen.screenName}" 제거 완료`); } catch (error) { console.error(`기존 화면 "${existingScreen.screenName}" 제거 실패:`, error); } } } // 새 화면 할당 await menuScreenApi.assignScreenToMenu(screenInfo.screenId, menuObjid); const successMessage = replaceExisting ? `기존 화면을 제거하고 "${screenInfo.screenName}" 화면이 "${selectedMenu.menu_name_kor}" 메뉴에 할당되었습니다.` : `"${screenInfo.screenName}" 화면이 "${selectedMenu.menu_name_kor}" 메뉴에 성공적으로 할당되었습니다.`; // 성공 상태 설정 setAssignmentSuccess(true); setAssignmentMessage(successMessage); // 할당 완료 콜백 호출 if (onAssignmentComplete) { onAssignmentComplete(); } // 3초 후 자동으로 화면 목록으로 이동 setTimeout(() => { if (onBackToList) { onBackToList(); } else { onClose(); } }, 3000); } catch (error: any) { console.error("화면 할당 실패:", error); const errorMessage = error.response?.data?.message || "화면 할당에 실패했습니다."; toast.error(errorMessage); } finally { setAssigning(false); } }; // "나중에 할당" 처리 - 시각적 효과 포함 const handleAssignLater = () => { if (!screenInfo) return; // 성공 상태 설정 (나중에 할당 메시지) setAssignmentSuccess(true); setAssignmentMessage(`"${screenInfo.screenName}" 화면이 저장되었습니다. 나중에 메뉴에 할당할 수 있습니다.`); // 할당 완료 콜백 호출 if (onAssignmentComplete) { onAssignmentComplete(); } // 3초 후 자동으로 화면 목록으로 이동 setTimeout(() => { if (onBackToList) { onBackToList(); } else { onClose(); } }, 3000); }; // 필터된 메뉴 목록 const filteredMenus = menus.filter((menu) => { if (!searchTerm) return true; const searchLower = searchTerm.toLowerCase(); return ( menu.menu_name_kor?.toLowerCase().includes(searchLower) || menu.menu_url?.toLowerCase().includes(searchLower) || menu.menu_desc?.toLowerCase().includes(searchLower) ); }); // 메뉴 옵션 생성 (계층 구조 표시) const getMenuOptions = (): JSX.Element[] => { if (loading) { return [ 메뉴 로딩 중... , ]; } if (filteredMenus.length === 0) { return [ {searchTerm ? `"${searchTerm}"에 대한 검색 결과가 없습니다` : "메뉴가 없습니다"} , ]; } return filteredMenus .filter((menu) => menu.objid && menu.objid.toString().trim() !== "") // objid가 유효한 메뉴만 필터링 .map((menu) => { const indent = " ".repeat(Math.max(0, menu.lev || 0)); const menuId = menu.objid!.toString(); // 이미 필터링했으므로 non-null assertion 사용 return ( {indent} {menu.menu_name_kor} ); }); }; return ( <> {assignmentSuccess ? ( // 성공 화면 <>
{assignmentMessage.includes("나중에") ? "화면 저장 완료" : "화면 할당 완료"}
{assignmentMessage.includes("나중에") ? "화면이 성공적으로 저장되었습니다. 나중에 메뉴에 할당할 수 있습니다." : "화면이 성공적으로 메뉴에 할당되었습니다."}

{assignmentMessage}

3초 후 자동으로 화면 목록으로 이동합니다...

) : ( // 기본 할당 화면 <> 메뉴에 화면 할당 저장된 화면을 메뉴에 할당하여 사용자가 접근할 수 있도록 설정합니다. {screenInfo && (
{screenInfo.screenName} {screenInfo.screenCode}
{screenInfo.description &&

{screenInfo.description}

}
)}
{/* 메뉴 선택 (검색 기능 포함) */}
{ e.stopPropagation(); // 이벤트 전파 방지 setSearchTerm(e.target.value); }} onKeyDown={(e) => { e.stopPropagation(); // 키보드 이벤트 전파 방지 }} onClick={(e) => { e.stopPropagation(); // 클릭 이벤트 전파 방지 }} className="h-8 pr-8 pl-10 text-sm" /> {searchTerm && ( )}
{/* 메뉴 옵션들 */}
{getMenuOptions()}
{/* 선택된 메뉴 정보 */} {selectedMenu && (

{selectedMenu.menu_name_kor}

관리자 {selectedMenu.status === "active" ? "활성" : "비활성"}
{selectedMenu.menu_url &&

URL: {selectedMenu.menu_url}

} {selectedMenu.menu_desc &&

설명: {selectedMenu.menu_desc}

} {selectedMenu.company_name &&

회사: {selectedMenu.company_name}

}
{/* 기존 할당된 화면 정보 */} {existingScreens.length > 0 && (

⚠️ 이미 할당된 화면 ({existingScreens.length}개)

{existingScreens.map((screen) => (
{screen.screenName} {screen.screenCode}
))}

새 화면을 할당하면 기존 화면들이 제거됩니다.

)}
)} )}
{/* 화면 교체 확인 대화상자 */} 화면 교체 확인 선택한 메뉴에 이미 할당된 화면이 있습니다.
{/* 기존 화면 목록 */}

제거될 화면 ({existingScreens.length}개):

{existingScreens.map((screen) => (
{screen.screenName} {screen.screenCode}
))}
{/* 새로 할당될 화면 */} {screenInfo && (

새로 할당될 화면:

{screenInfo.screenName} {screenInfo.screenCode}
)}

주의: 기존 화면들이 메뉴에서 제거되고 새 화면으로 교체됩니다. 이 작업은 되돌릴 수 없습니다.

); };