메뉴 관리에 대시보드 할당 기능 추가
This commit is contained in:
parent
c5499d2e18
commit
71aaef7acb
|
|
@ -47,12 +47,12 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
});
|
||||
|
||||
// 화면 할당 관련 상태
|
||||
const [urlType, setUrlType] = useState<"direct" | "screen">("screen"); // URL 직접 입력 or 화면 할당 (기본값: 화면 할당)
|
||||
const [urlType, setUrlType] = useState<"direct" | "screen" | "dashboard">("screen"); // URL 직접 입력 or 화면 할당 or 대시보드 할당 (기본값: 화면 할당)
|
||||
const [selectedScreen, setSelectedScreen] = useState<ScreenDefinition | null>(null);
|
||||
const [screens, setScreens] = useState<ScreenDefinition[]>([]);
|
||||
const [screenSearchText, setScreenSearchText] = useState("");
|
||||
const [isScreenDropdownOpen, setIsScreenDropdownOpen] = useState(false);
|
||||
|
||||
// 대시보드 할당 관련 상태
|
||||
const [selectedDashboard, setSelectedDashboard] = useState<any | null>(null);
|
||||
const [dashboards, setDashboards] = useState<any[]>([]);
|
||||
const [dashboardSearchText, setDashboardSearchText] = useState("");
|
||||
const [isDashboardDropdownOpen, setIsDashboardDropdownOpen] = useState(false);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
const [companies, setCompanies] = useState<Company[]>([]);
|
||||
|
|
@ -93,21 +99,6 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
// 대시보드 목록 로드
|
||||
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<MenuFormModalProps> = ({
|
|||
}));
|
||||
|
||||
// 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<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
}, 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<MenuFormModalProps> = ({
|
|||
});
|
||||
|
||||
// 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<MenuFormModalProps> = ({
|
|||
}
|
||||
}, [isOpen, formData.companyCode]);
|
||||
|
||||
// 화면 목록 로드
|
||||
// 화면 목록 및 대시보드 목록 로드
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
loadScreens();
|
||||
loadDashboards();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
|
|
@ -449,9 +476,9 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
}
|
||||
}, [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<MenuFormModalProps> = ({
|
|||
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<MenuFormModalProps> = ({
|
|||
화면 할당
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="dashboard" id="dashboard" />
|
||||
<Label htmlFor="dashboard" className="cursor-pointer">
|
||||
대시보드 할당
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="direct" id="direct" />
|
||||
<Label htmlFor="direct" className="cursor-pointer">
|
||||
|
|
@ -826,10 +880,91 @@ export const MenuFormModal: React.FC<MenuFormModalProps> = ({
|
|||
|
||||
{/* 선택된 화면 정보 표시 */}
|
||||
{selectedScreen && (
|
||||
<div className="rounded-md border bg-accent p-3">
|
||||
<div className="bg-accent rounded-md border p-3">
|
||||
<div className="text-sm font-medium text-blue-900">{selectedScreen.screenName}</div>
|
||||
<div className="text-xs text-primary">코드: {selectedScreen.screenCode}</div>
|
||||
<div className="text-xs text-primary">생성된 URL: {formData.menuUrl}</div>
|
||||
<div className="text-primary text-xs">코드: {selectedScreen.screenCode}</div>
|
||||
<div className="text-primary text-xs">생성된 URL: {formData.menuUrl}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 대시보드 할당 */}
|
||||
{urlType === "dashboard" && (
|
||||
<div className="space-y-2">
|
||||
{/* 대시보드 선택 드롭다운 */}
|
||||
<div className="relative">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="w-full justify-between"
|
||||
onClick={() => setIsDashboardDropdownOpen(!isDashboardDropdownOpen)}
|
||||
>
|
||||
<span className="truncate">{selectedDashboard ? selectedDashboard.title : "대시보드 선택"}</span>
|
||||
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
|
||||
{/* 드롭다운 메뉴 */}
|
||||
{isDashboardDropdownOpen && (
|
||||
<div className="dashboard-dropdown absolute z-50 mt-1 max-h-60 w-full overflow-hidden rounded-md border bg-white shadow-lg">
|
||||
{/* 검색창 */}
|
||||
<div className="border-b p-2">
|
||||
<div className="relative">
|
||||
<Search className="absolute top-2.5 left-2 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="대시보드 검색..."
|
||||
value={dashboardSearchText}
|
||||
onChange={(e) => setDashboardSearchText(e.target.value)}
|
||||
className="pl-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 대시보드 목록 */}
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{dashboards
|
||||
.filter(
|
||||
(dashboard) =>
|
||||
dashboard.title.toLowerCase().includes(dashboardSearchText.toLowerCase()) ||
|
||||
(dashboard.description &&
|
||||
dashboard.description.toLowerCase().includes(dashboardSearchText.toLowerCase())),
|
||||
)
|
||||
.map((dashboard) => (
|
||||
<div
|
||||
key={dashboard.id}
|
||||
onClick={() => handleDashboardSelect(dashboard)}
|
||||
className="cursor-pointer border-b px-3 py-2 last:border-b-0 hover:bg-gray-100"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm font-medium">{dashboard.title}</div>
|
||||
{dashboard.description && (
|
||||
<div className="text-xs text-gray-500">{dashboard.description}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{dashboards.filter(
|
||||
(dashboard) =>
|
||||
dashboard.title.toLowerCase().includes(dashboardSearchText.toLowerCase()) ||
|
||||
(dashboard.description &&
|
||||
dashboard.description.toLowerCase().includes(dashboardSearchText.toLowerCase())),
|
||||
).length === 0 && <div className="px-3 py-2 text-sm text-gray-500">검색 결과가 없습니다.</div>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 선택된 대시보드 정보 표시 */}
|
||||
{selectedDashboard && (
|
||||
<div className="bg-accent rounded-md border p-3">
|
||||
<div className="text-sm font-medium text-blue-900">{selectedDashboard.title}</div>
|
||||
{selectedDashboard.description && (
|
||||
<div className="text-primary text-xs">설명: {selectedDashboard.description}</div>
|
||||
)}
|
||||
<div className="text-primary text-xs">생성된 URL: {formData.menuUrl}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue