feat: 화면 관리 및 메뉴 동기화 기능 개선
- 화면 그룹 컨트롤러 기능 확장 - 메뉴 복사 서비스 개선 - 메뉴-화면 동기화 서비스 추가 - 번호 규칙 서비스 개선 - 화면 관리 서비스 확장 - CopyScreenModal 기능 개선 - DataFlowPanel, FieldJoinPanel 수정
This commit is contained in:
@@ -33,9 +33,10 @@ import {
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import { Loader2, Copy, Link as LinkIcon, Trash2, AlertCircle, AlertTriangle, Check, ChevronsUpDown, FolderTree } from "lucide-react";
|
||||
import { Loader2, Copy, Link as LinkIcon, Trash2, AlertCircle, AlertTriangle, Check, ChevronsUpDown, FolderTree, Hash, Code, Table, Settings, Database } from "lucide-react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { ScreenDefinition } from "@/types/screen";
|
||||
import { screenApi } from "@/lib/api/screen";
|
||||
import { screenApi, updateTabScreenReferences } from "@/lib/api/screen";
|
||||
import { ScreenGroup, addScreenToGroup, createScreenGroup } from "@/lib/api/screenGroup";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { toast } from "sonner";
|
||||
@@ -135,6 +136,15 @@ export default function CopyScreenModal({
|
||||
// 그룹 복제 모드: "all" (전체), "folder_only" (폴더만), "screen_only" (화면만)
|
||||
const [groupCopyMode, setGroupCopyMode] = useState<"all" | "folder_only" | "screen_only">("all");
|
||||
|
||||
// 채번규칙 복제 옵션 (체크 시: 복제 → 메뉴 동기화 → 채번규칙 복제 순서로 실행)
|
||||
const [copyNumberingRules, setCopyNumberingRules] = useState(false);
|
||||
|
||||
// 추가 복사 옵션들
|
||||
const [copyCodeCategory, setCopyCodeCategory] = useState(false); // 코드 카테고리 + 코드 복사
|
||||
const [copyCategoryMapping, setCopyCategoryMapping] = useState(false); // 카테고리 매핑 + 값 복사
|
||||
const [copyTableTypeColumns, setCopyTableTypeColumns] = useState(false); // 테이블 타입관리 입력타입 설정 복사
|
||||
const [copyCascadingRelation, setCopyCascadingRelation] = useState(false); // 연쇄관계 설정 복사
|
||||
|
||||
// 복사 중 상태
|
||||
const [isCopying, setIsCopying] = useState(false);
|
||||
const [copyProgress, setCopyProgress] = useState({ current: 0, total: 0, message: "" });
|
||||
@@ -584,6 +594,7 @@ export default function CopyScreenModal({
|
||||
screen_id: result.mainScreen.screenId,
|
||||
screen_role: "MAIN",
|
||||
display_order: 1,
|
||||
target_company_code: finalCompanyCode, // 대상 회사 코드 전달
|
||||
});
|
||||
console.log(`✅ 복제된 화면을 그룹(${selectedTargetGroupId})에 추가 완료`);
|
||||
} catch (groupError) {
|
||||
@@ -609,7 +620,7 @@ export default function CopyScreenModal({
|
||||
};
|
||||
|
||||
// 이름 변환 헬퍼 함수 (일괄 이름 변경 적용)
|
||||
const transformName = (originalName: string, isRootGroup: boolean = false): string => {
|
||||
const transformName = (originalName: string, isRootGroup: boolean = false, sourceCompanyCode?: string): string => {
|
||||
// 루트 그룹은 사용자가 직접 입력한 이름 사용
|
||||
if (isRootGroup) {
|
||||
return newGroupName.trim();
|
||||
@@ -621,7 +632,12 @@ export default function CopyScreenModal({
|
||||
return originalName.replace(new RegExp(groupFindText, "g"), groupReplaceText);
|
||||
}
|
||||
|
||||
// 기본: "(복제)" 붙이기
|
||||
// 다른 회사로 복제하는 경우: 원본 이름 그대로 사용 (중복될 일 없음)
|
||||
if (sourceCompanyCode && sourceCompanyCode !== targetCompanyCode) {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
// 같은 회사 내 복제: "(복제)" 붙이기 (중복 방지)
|
||||
return `${originalName} (복제)`;
|
||||
};
|
||||
|
||||
@@ -633,17 +649,19 @@ export default function CopyScreenModal({
|
||||
screenCodes: string[], // 미리 생성된 화면 코드 배열
|
||||
codeIndex: { current: number }, // 현재 사용할 코드 인덱스 (참조로 전달)
|
||||
stats: { groups: number; screens: number },
|
||||
totalScreenCount: number // 전체 화면 수 (진행률 표시용)
|
||||
totalScreenCount: number, // 전체 화면 수 (진행률 표시용)
|
||||
screenIdMap: { [key: number]: number } // 원본 화면 ID -> 새 화면 ID 매핑
|
||||
): Promise<void> => {
|
||||
// 1. 현재 그룹 생성 (원본 display_order 유지)
|
||||
const timestamp = Date.now();
|
||||
const randomSuffix = Math.floor(Math.random() * 1000);
|
||||
const newGroupCode = `${targetCompany}_GROUP_${timestamp}_${randomSuffix}`;
|
||||
|
||||
console.log(`📁 그룹 생성: ${sourceGroupData.group_name} (복제)`);
|
||||
const transformedGroupName = transformName(sourceGroupData.group_name, false, sourceGroupData.company_code);
|
||||
console.log(`📁 그룹 생성: ${transformedGroupName}`);
|
||||
|
||||
const newGroupResponse = await createScreenGroup({
|
||||
group_name: transformName(sourceGroupData.group_name), // 일괄 이름 변경 적용
|
||||
group_name: transformedGroupName, // 일괄 이름 변경 적용
|
||||
group_code: newGroupCode,
|
||||
parent_group_id: parentGroupId,
|
||||
target_company_code: targetCompany,
|
||||
@@ -663,13 +681,29 @@ export default function CopyScreenModal({
|
||||
const sourceScreensInfo = sourceGroupData.screens || [];
|
||||
|
||||
// 화면 정보와 display_order를 함께 매핑
|
||||
// allScreens에서 못 찾으면 그룹의 screens 정보를 직접 사용 (다른 회사 폴더 복사 시)
|
||||
const screensWithOrder = sourceScreensInfo.map((s: any) => {
|
||||
const screenId = typeof s === 'object' ? s.screen_id : s;
|
||||
const displayOrder = typeof s === 'object' ? s.display_order : 0;
|
||||
const screenRole = typeof s === 'object' ? s.screen_role : 'MAIN';
|
||||
const screenData = allScreens.find((sc) => sc.screenId === screenId);
|
||||
const screenName = typeof s === 'object' ? s.screen_name : '';
|
||||
const tableName = typeof s === 'object' ? s.table_name : '';
|
||||
|
||||
// allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체
|
||||
let screenData = allScreens.find((sc) => sc.screenId === screenId);
|
||||
if (!screenData && screenId && screenName) {
|
||||
// allScreens에 없는 경우 (다른 회사 화면) - 그룹의 screens 정보로 최소한의 데이터 구성
|
||||
screenData = {
|
||||
screenId: screenId,
|
||||
screenName: screenName,
|
||||
screenCode: `SCREEN_${screenId}`, // 임시 코드 (실제 복사 시 새 코드 생성)
|
||||
tableName: tableName || '',
|
||||
description: '',
|
||||
companyCode: sourceGroupData.company_code || '',
|
||||
} as any;
|
||||
}
|
||||
return { screenId, displayOrder, screenRole, screenData };
|
||||
}).filter(item => item.screenData); // 화면 데이터가 있는 것만
|
||||
}).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만
|
||||
|
||||
// display_order 순으로 정렬
|
||||
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
||||
@@ -687,12 +721,13 @@ export default function CopyScreenModal({
|
||||
message: `화면 복제 중: ${screen.screenName}`
|
||||
});
|
||||
|
||||
console.log(` 📄 화면 복제: ${screen.screenName} → ${newScreenCode}`);
|
||||
const transformedScreenName = transformName(screen.screenName, false, sourceGroupData.company_code);
|
||||
console.log(` 📄 화면 복제: ${screen.screenName} → ${transformedScreenName}`);
|
||||
|
||||
const result = await screenApi.copyScreenWithModals(screen.screenId, {
|
||||
targetCompanyCode: targetCompany,
|
||||
mainScreen: {
|
||||
screenName: transformName(screen.screenName), // 일괄 이름 변경 적용
|
||||
screenName: transformedScreenName, // 일괄 이름 변경 적용
|
||||
screenCode: newScreenCode,
|
||||
description: screen.description || "",
|
||||
},
|
||||
@@ -700,14 +735,18 @@ export default function CopyScreenModal({
|
||||
});
|
||||
|
||||
if (result.mainScreen?.screenId) {
|
||||
// 원본 화면 ID -> 새 화면 ID 매핑 기록
|
||||
screenIdMap[screen.screenId] = result.mainScreen.screenId;
|
||||
|
||||
await addScreenToGroup({
|
||||
group_id: newGroup.id,
|
||||
screen_id: result.mainScreen.screenId,
|
||||
screen_role: screenRole || "MAIN",
|
||||
display_order: displayOrder, // 원본 정렬순서 유지
|
||||
target_company_code: targetCompany, // 대상 회사 코드 전달
|
||||
});
|
||||
stats.screens++;
|
||||
console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screenName}`);
|
||||
console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screenName} (${screen.screenId} → ${result.mainScreen.screenId})`);
|
||||
}
|
||||
} catch (screenError) {
|
||||
console.error(` ❌ 화면 복제 실패 (${screen.screenCode}):`, screenError);
|
||||
@@ -730,7 +769,8 @@ export default function CopyScreenModal({
|
||||
screenCodes,
|
||||
codeIndex,
|
||||
stats,
|
||||
totalScreenCount
|
||||
totalScreenCount,
|
||||
screenIdMap // screenIdMap 전달
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -769,6 +809,7 @@ export default function CopyScreenModal({
|
||||
|
||||
const finalCompanyCode = targetCompanyCode || sourceGroup.company_code;
|
||||
const stats = { groups: 0, screens: 0 };
|
||||
const screenIdMap: { [key: number]: number } = {}; // 원본 화면 ID -> 새 화면 ID 매핑
|
||||
|
||||
console.log("🔄 그룹 복제 시작 (재귀적):", {
|
||||
sourceGroup: sourceGroup.group_name,
|
||||
@@ -795,7 +836,7 @@ export default function CopyScreenModal({
|
||||
|
||||
// 일괄 이름 변경이 활성화된 경우 원본 이름에 변환 적용
|
||||
const rootGroupName = useGroupBulkRename && groupFindText
|
||||
? transformName(sourceGroup.group_name)
|
||||
? transformName(sourceGroup.group_name, false, sourceGroup.company_code)
|
||||
: newGroupName.trim();
|
||||
|
||||
const newGroupResponse = await createScreenGroup({
|
||||
@@ -818,14 +859,41 @@ export default function CopyScreenModal({
|
||||
if (groupCopyMode !== "folder_only") {
|
||||
const sourceScreensInfo = sourceGroup.screens || [];
|
||||
|
||||
// 화면 정보와 display_order를 함께 매핑
|
||||
const screensWithOrder = sourceScreensInfo.map((s: any) => {
|
||||
const screenId = typeof s === 'object' ? s.screen_id : s;
|
||||
const displayOrder = typeof s === 'object' ? s.display_order : 0;
|
||||
const screenRole = typeof s === 'object' ? s.screen_role : 'MAIN';
|
||||
const screenData = allScreens.find((sc) => sc.screenId === screenId);
|
||||
return { screenId, displayOrder, screenRole, screenData };
|
||||
}).filter(item => item.screenData);
|
||||
// 화면 정보와 display_order를 함께 매핑
|
||||
// allScreens에서 못 찾으면 그룹의 screens 정보를 직접 사용 (다른 회사 폴더 복사 시)
|
||||
console.log(`🔍 루트 그룹 화면 매핑 시작: ${sourceScreensInfo.length}개 화면, allScreens: ${allScreens.length}개`);
|
||||
const screensWithOrder = sourceScreensInfo.map((s: any) => {
|
||||
const screenId = typeof s === 'object' ? s.screen_id : s;
|
||||
const displayOrder = typeof s === 'object' ? s.display_order : 0;
|
||||
const screenRole = typeof s === 'object' ? s.screen_role : 'MAIN';
|
||||
const screenName = typeof s === 'object' ? s.screen_name : '';
|
||||
const tableName = typeof s === 'object' ? s.table_name : '';
|
||||
|
||||
// allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체
|
||||
let screenData = allScreens.find((sc) => sc.screenId === screenId);
|
||||
const foundInAllScreens = !!screenData;
|
||||
|
||||
if (!screenData && screenId && screenName) {
|
||||
// allScreens에 없는 경우 (다른 회사 화면) - 그룹의 screens 정보로 최소한의 데이터 구성
|
||||
console.log(` ⚠️ allScreens에서 못 찾음, 그룹 정보 사용: ${screenId} - ${screenName}`);
|
||||
screenData = {
|
||||
screenId: screenId,
|
||||
screenName: screenName,
|
||||
screenCode: `SCREEN_${screenId}`, // 임시 코드 (실제 복사 시 새 코드 생성)
|
||||
tableName: tableName || '',
|
||||
description: '',
|
||||
companyCode: sourceGroup.company_code || '',
|
||||
} as any;
|
||||
} else if (screenData) {
|
||||
console.log(` ✅ allScreens에서 찾음: ${screenId} - ${screenData.screenName}`);
|
||||
} else {
|
||||
console.log(` ❌ 화면 정보 없음: screenId=${screenId}, screenName=${screenName}`);
|
||||
}
|
||||
return { screenId, displayOrder, screenRole, screenData };
|
||||
}).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만
|
||||
|
||||
console.log(`🔍 매핑 완료: ${screensWithOrder.length}개 화면 복사 예정`);
|
||||
screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screenName}`));
|
||||
|
||||
// display_order 순으로 정렬
|
||||
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
|
||||
@@ -843,12 +911,13 @@ export default function CopyScreenModal({
|
||||
message: `화면 복제 중: ${screen.screenName}`
|
||||
});
|
||||
|
||||
console.log(`📄 화면 복제: ${screen.screenName} → ${newScreenCode}`);
|
||||
const transformedScreenName = transformName(screen.screenName, false, sourceGroup.company_code);
|
||||
console.log(`📄 화면 복제: ${screen.screenName} → ${transformedScreenName}`);
|
||||
|
||||
const result = await screenApi.copyScreenWithModals(screen.screenId, {
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
mainScreen: {
|
||||
screenName: transformName(screen.screenName), // 일괄 이름 변경 적용
|
||||
screenName: transformedScreenName, // 일괄 이름 변경 적용
|
||||
screenCode: newScreenCode,
|
||||
description: screen.description || "",
|
||||
},
|
||||
@@ -856,14 +925,18 @@ export default function CopyScreenModal({
|
||||
});
|
||||
|
||||
if (result.mainScreen?.screenId) {
|
||||
// 원본 화면 ID -> 새 화면 ID 매핑 기록
|
||||
screenIdMap[screen.screenId] = result.mainScreen.screenId;
|
||||
|
||||
await addScreenToGroup({
|
||||
group_id: newRootGroup.id,
|
||||
screen_id: result.mainScreen.screenId,
|
||||
screen_role: screenRole || "MAIN",
|
||||
display_order: displayOrder, // 원본 정렬순서 유지
|
||||
target_company_code: finalCompanyCode, // 대상 회사 코드 전달
|
||||
});
|
||||
stats.screens++;
|
||||
console.log(`✅ 화면 복제 완료: ${result.mainScreen.screenName}`);
|
||||
console.log(`✅ 화면 복제 완료: ${result.mainScreen.screenName} (${screen.screenId} → ${result.mainScreen.screenId})`);
|
||||
}
|
||||
} catch (screenError) {
|
||||
console.error(`화면 복제 실패 (${screen.screenCode}):`, screenError);
|
||||
@@ -886,11 +959,180 @@ export default function CopyScreenModal({
|
||||
screenCodes,
|
||||
codeIndex,
|
||||
stats,
|
||||
totalScreenCount
|
||||
totalScreenCount,
|
||||
screenIdMap // screenIdMap 전달
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 탭 컴포넌트의 screenId 참조 일괄 업데이트
|
||||
console.log("🔍 screenIdMap 상태:", screenIdMap, "키 개수:", Object.keys(screenIdMap).length);
|
||||
if (Object.keys(screenIdMap).length > 0) {
|
||||
console.log("🔗 탭 screenId 참조 업데이트 중...", screenIdMap);
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "탭 참조 업데이트 중..." });
|
||||
|
||||
const targetScreenIds = Object.values(screenIdMap);
|
||||
try {
|
||||
const updateResult = await updateTabScreenReferences(targetScreenIds, screenIdMap);
|
||||
console.log(`✅ 탭 screenId 참조 업데이트 완료: ${updateResult.updated}개 레이아웃`);
|
||||
} catch (tabUpdateError) {
|
||||
console.warn("탭 screenId 참조 업데이트 실패 (무시):", tabUpdateError);
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 채번규칙 복제 옵션이 선택된 경우 (복제 → 메뉴 동기화 → 채번규칙 복제)
|
||||
if (copyNumberingRules) {
|
||||
try {
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "메뉴 동기화 중..." });
|
||||
console.log("📋 메뉴 동기화 시작 (채번규칙 복제 준비)...");
|
||||
|
||||
// 7-1. 메뉴 동기화 (화면 그룹 → 메뉴)
|
||||
const syncResponse = await apiClient.post("/screen-groups/sync/screen-to-menu", {
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (syncResponse.data?.success) {
|
||||
console.log("✅ 메뉴 동기화 완료:", syncResponse.data.data);
|
||||
|
||||
// 7-2. 채번규칙 복제
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "채번규칙 복제 중..." });
|
||||
console.log("📋 채번규칙 복제 시작...");
|
||||
|
||||
const numberingResponse = await apiClient.post("/numbering-rules/copy-for-company", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (numberingResponse.data?.success) {
|
||||
console.log("✅ 채번규칙 복제 완료:", numberingResponse.data.data);
|
||||
toast.success(`채번규칙 ${numberingResponse.data.data?.copiedCount || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("채번규칙 복제 실패:", numberingResponse.data?.error);
|
||||
toast.warning("채번규칙 복제에 실패했습니다. 수동으로 복제해주세요.");
|
||||
}
|
||||
|
||||
// 7-3. 화면-메뉴 할당 복제 (screen_menu_assignments)
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "화면-메뉴 할당 복제 중..." });
|
||||
console.log("📋 화면-메뉴 할당 복제 시작...");
|
||||
|
||||
const menuAssignResponse = await apiClient.post("/screen-management/copy-menu-assignments", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
screenIdMap,
|
||||
});
|
||||
|
||||
if (menuAssignResponse.data?.success) {
|
||||
console.log("✅ 화면-메뉴 할당 복제 완료:", menuAssignResponse.data.data);
|
||||
toast.success(`화면-메뉴 할당 ${menuAssignResponse.data.data?.copiedCount || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("화면-메뉴 할당 복제 실패:", menuAssignResponse.data?.error);
|
||||
}
|
||||
} else {
|
||||
console.warn("메뉴 동기화 실패:", syncResponse.data?.error);
|
||||
toast.warning("메뉴 동기화에 실패했습니다. 채번규칙이 복제되지 않았습니다.");
|
||||
}
|
||||
} catch (numberingError) {
|
||||
console.error("채번규칙 복제 중 오류:", numberingError);
|
||||
toast.warning("채번규칙 복제 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 코드 카테고리 + 코드 복제
|
||||
if (copyCodeCategory) {
|
||||
try {
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "코드 카테고리/코드 복제 중..." });
|
||||
console.log("📋 코드 카테고리/코드 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-code-category", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
console.log("✅ 코드 카테고리/코드 복제 완료:", response.data.data);
|
||||
toast.success(`코드 카테고리 ${response.data.data?.copiedCategories || 0}개, 코드 ${response.data.data?.copiedCodes || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("코드 카테고리/코드 복제 실패:", response.data?.error);
|
||||
toast.warning("코드 카테고리/코드 복제에 실패했습니다.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("코드 카테고리/코드 복제 중 오류:", error);
|
||||
toast.warning("코드 카테고리/코드 복제 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
// 9. 카테고리 매핑 + 값 복제
|
||||
if (copyCategoryMapping) {
|
||||
try {
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "카테고리 매핑/값 복제 중..." });
|
||||
console.log("📋 카테고리 매핑/값 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-category-mapping", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
console.log("✅ 카테고리 매핑/값 복제 완료:", response.data.data);
|
||||
toast.success(`카테고리 매핑 ${response.data.data?.copiedMappings || 0}개, 값 ${response.data.data?.copiedValues || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("카테고리 매핑/값 복제 실패:", response.data?.error);
|
||||
toast.warning("카테고리 매핑/값 복제에 실패했습니다.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("카테고리 매핑/값 복제 중 오류:", error);
|
||||
toast.warning("카테고리 매핑/값 복제 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
// 10. 테이블 타입관리 입력타입 설정 복제
|
||||
if (copyTableTypeColumns) {
|
||||
try {
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "테이블 타입 컬럼 복제 중..." });
|
||||
console.log("📋 테이블 타입 컬럼 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-table-type-columns", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
console.log("✅ 테이블 타입 컬럼 복제 완료:", response.data.data);
|
||||
toast.success(`테이블 타입 컬럼 ${response.data.data?.copiedCount || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("테이블 타입 컬럼 복제 실패:", response.data?.error);
|
||||
toast.warning("테이블 타입 컬럼 복제에 실패했습니다.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("테이블 타입 컬럼 복제 중 오류:", error);
|
||||
toast.warning("테이블 타입 컬럼 복제 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
// 11. 연쇄관계 설정 복제
|
||||
if (copyCascadingRelation) {
|
||||
try {
|
||||
setCopyProgress({ current: stats.screens, total: totalScreenCount, message: "연쇄관계 설정 복제 중..." });
|
||||
console.log("📋 연쇄관계 설정 복제 시작...");
|
||||
|
||||
const response = await apiClient.post("/screen-management/copy-cascading-relation", {
|
||||
sourceCompanyCode: sourceGroup.company_code,
|
||||
targetCompanyCode: finalCompanyCode,
|
||||
});
|
||||
|
||||
if (response.data?.success) {
|
||||
console.log("✅ 연쇄관계 설정 복제 완료:", response.data.data);
|
||||
toast.success(`연쇄관계 설정 ${response.data.data?.copiedCount || 0}개가 복제되었습니다.`);
|
||||
} else {
|
||||
console.warn("연쇄관계 설정 복제 실패:", response.data?.error);
|
||||
toast.warning("연쇄관계 설정 복제에 실패했습니다.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("연쇄관계 설정 복제 중 오류:", error);
|
||||
toast.warning("연쇄관계 설정 복제 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
toast.success(
|
||||
`그룹 복제가 완료되었습니다! (그룹 ${stats.groups}개 + 화면 ${stats.screens}개)`
|
||||
);
|
||||
@@ -1045,6 +1287,89 @@ export default function CopyScreenModal({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 추가 복사 옵션 (선택사항) */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs sm:text-sm font-medium">추가 복사 옵션 (선택사항):</Label>
|
||||
|
||||
{/* 코드 카테고리 + 코드 복사 */}
|
||||
<div className="flex items-center space-x-2 p-2 bg-muted/30 rounded-md ml-2">
|
||||
<Checkbox
|
||||
id="copyCodeCategory"
|
||||
checked={copyCodeCategory}
|
||||
onCheckedChange={(checked) => setCopyCodeCategory(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="copyCodeCategory" className="text-xs sm:text-sm cursor-pointer flex items-center gap-2">
|
||||
<Code className="h-4 w-4 text-muted-foreground" />
|
||||
코드 카테고리 + 코드 복사
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* 채번규칙 복제 */}
|
||||
<div className="flex items-center space-x-2 p-2 bg-muted/30 rounded-md ml-2">
|
||||
<Checkbox
|
||||
id="copyNumberingRules"
|
||||
checked={copyNumberingRules}
|
||||
onCheckedChange={(checked) => setCopyNumberingRules(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="copyNumberingRules" className="text-xs sm:text-sm cursor-pointer flex items-center gap-2">
|
||||
<Hash className="h-4 w-4 text-muted-foreground" />
|
||||
채번 규칙 복사
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* 카테고리 매핑 + 값 복사 */}
|
||||
<div className="flex items-center space-x-2 p-2 bg-muted/30 rounded-md ml-2">
|
||||
<Checkbox
|
||||
id="copyCategoryMapping"
|
||||
checked={copyCategoryMapping}
|
||||
onCheckedChange={(checked) => setCopyCategoryMapping(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="copyCategoryMapping" className="text-xs sm:text-sm cursor-pointer flex items-center gap-2">
|
||||
<Table className="h-4 w-4 text-muted-foreground" />
|
||||
카테고리 매핑 + 값 복사
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* 테이블 타입관리 입력타입 설정 복사 */}
|
||||
<div className="flex items-center space-x-2 p-2 bg-muted/30 rounded-md ml-2">
|
||||
<Checkbox
|
||||
id="copyTableTypeColumns"
|
||||
checked={copyTableTypeColumns}
|
||||
onCheckedChange={(checked) => setCopyTableTypeColumns(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="copyTableTypeColumns" className="text-xs sm:text-sm cursor-pointer flex items-center gap-2">
|
||||
<Settings className="h-4 w-4 text-muted-foreground" />
|
||||
테이블 타입관리 입력타입 설정 복사
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{/* 연쇄관계 설정 복사 */}
|
||||
<div className="flex items-center space-x-2 p-2 bg-muted/30 rounded-md ml-2">
|
||||
<Checkbox
|
||||
id="copyCascadingRelation"
|
||||
checked={copyCascadingRelation}
|
||||
onCheckedChange={(checked) => setCopyCascadingRelation(checked === true)}
|
||||
/>
|
||||
<Label htmlFor="copyCascadingRelation" className="text-xs sm:text-sm cursor-pointer flex items-center gap-2">
|
||||
<Database className="h-4 w-4 text-muted-foreground" />
|
||||
연쇄관계 설정 복사
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 기본 복사 항목 안내 */}
|
||||
<div className="p-3 bg-muted/50 rounded-lg">
|
||||
<p className="text-xs font-medium mb-1">기본 복사 항목:</p>
|
||||
<ul className="text-[10px] sm:text-xs text-muted-foreground space-y-0.5 list-disc list-inside">
|
||||
<li>메뉴 구조 (하위 메뉴 포함)</li>
|
||||
<li>화면 + 레이아웃 (모달, 조건부 컨테이너)</li>
|
||||
<li>플로우 제어 (스텝, 연결)</li>
|
||||
</ul>
|
||||
<p className="text-[10px] text-muted-foreground mt-2 italic">
|
||||
* 코드, 채번규칙, 카테고리는 위 옵션 선택 시 복사됩니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 새 그룹명 + 정렬 순서 */}
|
||||
<div className="flex gap-3">
|
||||
<div className="flex-1">
|
||||
|
||||
@@ -462,3 +462,5 @@ export default function DataFlowPanel({ groupId, screenId, screens = [] }: DataF
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -414,3 +414,5 @@ export default function FieldJoinPanel({ screenId, componentId, layoutId }: Fiel
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user