"use client"; import { useState, useEffect, useCallback, useMemo, useRef } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { useUnsavedChangesGuard, UnsavedChangesDialog } from "@/components/common/UnsavedChangesGuard"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Loader2, Search, FileText } from "lucide-react"; import { menuApi } from "@/lib/api/menu"; import { MenuItem } from "@/types/menu"; import { cn } from "@/lib/utils"; interface MenuSelectModalProps { isOpen: boolean; onClose: () => void; onConfirm: (menuObjids: number[]) => void; selectedMenuObjids?: number[]; } interface FlatMenuEntry { objid: string; menuNameKor: string; parentNameKor: string; } export function MenuSelectModal({ isOpen, onClose, onConfirm, selectedMenuObjids = [] }: MenuSelectModalProps) { const [menus, setMenus] = useState([]); const [isLoading, setIsLoading] = useState(false); const [searchText, setSearchText] = useState(""); const [selectedIds, setSelectedIds] = useState>(new Set(selectedMenuObjids)); const initialSelectionRef = useRef(""); const hasChanges = useCallback(() => { const currentSelection = JSON.stringify(Array.from(selectedIds).sort()); return currentSelection !== initialSelectionRef.current; }, [selectedIds]); const guard = useUnsavedChangesGuard({ hasChanges, onClose, description: "변경된 선택 내용이 저장되지 않습니다. 정말 닫으시겠습니까?", }); useEffect(() => { if (isOpen) { setSelectedIds(new Set(selectedMenuObjids)); initialSelectionRef.current = JSON.stringify(Array.from(new Set(selectedMenuObjids)).sort()); } }, [isOpen, selectedMenuObjids]); useEffect(() => { if (isOpen) { fetchMenus(); } }, [isOpen]); const fetchMenus = async () => { setIsLoading(true); try { const response = await menuApi.getUserMenus(); if (response.success && response.data) { setMenus(response.data as MenuItem[]); } } catch (error) { console.error("메뉴 로드 오류:", error); } finally { setIsLoading(false); } }; const level2List = useMemo(() => { const byObjid = new Map(); menus.forEach((menu: any) => { const objid = String(menu.objid || menu.OBJID || ""); byObjid.set(objid, menu); }); return menus .filter((menu: any) => Number(menu.lev ?? menu.LEV ?? 0) === 2) .map((menu: any) => { const objid = String(menu.objid || menu.OBJID || ""); const parentObjId = String(menu.parent_obj_id || menu.PARENT_OBJ_ID || ""); const parent = byObjid.get(parentObjId); const parentNameKor = parent ? parent.menu_name_kor || parent.MENU_NAME_KOR || parent.translated_name || parent.TRANSLATED_NAME || "" : ""; const menuNameKor = menu.menu_name_kor || menu.MENU_NAME_KOR || menu.translated_name || menu.TRANSLATED_NAME || ""; return { objid, menuNameKor, parentNameKor }; }) .sort((a, b) => a.menuNameKor.localeCompare(b.menuNameKor, "ko")); }, [menus]); const filteredList = useMemo(() => { if (!searchText.trim()) return level2List; const q = searchText.toLowerCase(); return level2List.filter( (m) => m.menuNameKor.toLowerCase().includes(q) || m.parentNameKor.toLowerCase().includes(q), ); }, [level2List, searchText]); const toggleSelect = useCallback((objid: string) => { const numericId = Number(objid); setSelectedIds((prev) => { const next = new Set(prev); if (next.has(numericId)) { next.delete(numericId); } else { next.add(numericId); } return next; }); }, []); const handleConfirm = () => { onConfirm(Array.from(selectedIds)); onClose(); }; return ( <> 사용 메뉴 선택 (대분류) 이 리포트가 속할 2레벨(대분류) 메뉴를 선택하세요. 선택한 대분류의 하위 메뉴에서 이 리포트를 사용할 수 있습니다. 아무 것도 선택하지 않으면 모든 메뉴에서 보입니다.
setSearchText(e.target.value)} className="pl-10" />
{selectedIds.size}개 메뉴 선택됨
{isLoading ? (
메뉴 로드 중...
) : filteredList.length === 0 ? (
{searchText ? "검색 결과가 없습니다." : "표시할 2레벨 메뉴가 없습니다."}
) : (
{filteredList.map((node) => { const isSelected = selectedIds.has(Number(node.objid)); return (
toggleSelect(node.objid)} > toggleSelect(node.objid)} onClick={(e) => e.stopPropagation()} /> {node.menuNameKor} {node.parentNameKor && ( {node.parentNameKor} )}
); })}
)}
); }