From 18d237c95e8638e2d0be68ae6664295d609f62bd Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Sun, 29 Mar 2026 13:32:26 +0900 Subject: [PATCH] [agent-pipeline] pipe-20260329010534-qgv9 round-2 --- frontend/components/admin/CodeDetailPanel.tsx | 34 ++--- .../components/common/ExcelUploadModal.tsx | 2 +- .../components/screen/CopyScreenModal.tsx | 126 +++++++++--------- frontend/components/screen/EditModal.tsx | 10 +- .../EnhancedInteractiveScreenViewer.tsx | 2 +- .../screen/FileAttachmentDetailModal.tsx | 22 +-- .../screen/InteractiveDataTable.tsx | 16 +-- .../screen/InteractiveScreenViewer.tsx | 14 +- .../screen/InteractiveScreenViewerDynamic.tsx | 12 +- frontend/components/screen/SaveModal.tsx | 4 +- frontend/components/screen/ScreenList.tsx | 10 +- .../components/screen/ScreenSettingModal.tsx | 78 +++++------ frontend/lib/api/data.ts | 2 +- frontend/lib/api/dataflowSave.ts | 12 +- frontend/lib/api/dynamicForm.ts | 28 ++-- frontend/lib/api/file.ts | 30 ++--- frontend/lib/api/layout.ts | 25 ++-- frontend/lib/api/mail.ts | 2 +- frontend/lib/api/tableCategoryValue.ts | 8 +- .../ConditionalContainerConfigPanel.tsx | 20 +-- .../RepeatScreenModalConfigPanel.tsx | 6 +- .../SplitPanelLayoutComponent.tsx | 4 +- .../SplitPanelLayoutConfigPanel.tsx | 38 +++--- .../v2-file-upload/FileUploadComponent.tsx | 10 +- .../SplitPanelLayoutComponent.tsx | 36 ++--- .../SplitPanelLayoutConfigPanel.tsx | 122 ++++++++--------- .../config-panels/LeftPanelConfigTab.tsx | 8 +- .../config-panels/RightPanelConfigTab.tsx | 8 +- .../config-panels/SharedComponents.tsx | 20 +-- .../v2-table-list/TableListComponent.tsx | 2 +- .../registry/pop-components/pop-button.tsx | 6 +- .../PopCardListV2Component.tsx | 6 +- .../pop-card-list-v2/PopCardListV2Config.tsx | 60 ++++----- frontend/lib/services/enhancedFormService.ts | 4 +- frontend/lib/utils/buttonActions.ts | 71 +++++----- 35 files changed, 426 insertions(+), 432 deletions(-) diff --git a/frontend/components/admin/CodeDetailPanel.tsx b/frontend/components/admin/CodeDetailPanel.tsx index 3110a5ee..bab3ad9f 100644 --- a/frontend/components/admin/CodeDetailPanel.tsx +++ b/frontend/components/admin/CodeDetailPanel.tsx @@ -61,8 +61,8 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { // 코드 맵 생성 codes.forEach((code) => { - const codeValue = code.codeValue || code.code_value || ""; - const parentValue = code.parentCodeValue || code.parent_code_value; + const codeValue = code.code_value || ""; + const parentValue = code.parent_code_value; codeMap.set(codeValue, code); if (parentValue) { @@ -77,14 +77,14 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { const traverse = (parentValue: string | null, depth: number) => { const children = parentValue ? childrenMap.get(parentValue) || [] - : codes.filter((c) => !c.parentCodeValue && !c.parent_code_value); + : codes.filter((c) => !c.parent_code_value); // 정렬 순서로 정렬 children - .sort((a, b) => (a.sortOrder || a.sort_order || 0) - (b.sortOrder || b.sort_order || 0)) + .sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0)) .forEach((code) => { result.push(code); - const codeValue = code.codeValue || code.code_value || ""; + const codeValue = code.code_value || ""; traverse(codeValue, depth + 1); }); }; @@ -118,7 +118,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { const childrenMap = useMemo(() => { const map = new Map(); codes.forEach((code) => { - const parentValue = code.parentCodeValue || code.parent_code_value; + const parentValue = code.parent_code_value; if (parentValue) { if (!map.has(parentValue)) { map.set(parentValue, []); @@ -144,14 +144,14 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { // 특정 코드가 표시되어야 하는지 확인 (부모가 접혀있으면 숨김) const isCodeVisible = (code: CodeInfo): boolean => { - const parentValue = code.parentCodeValue || code.parent_code_value; + const parentValue = code.parent_code_value; if (!parentValue) return true; // 최상위 코드는 항상 표시 // 부모가 접혀있으면 숨김 if (collapsedCodes.has(parentValue)) return false; // 부모의 부모도 확인 (재귀적으로) - const parentCode = codes.find((c) => (c.codeValue || c.code_value) === parentValue); + const parentCode = codes.find((c) => c.code_value === parentValue); if (parentCode) { return isCodeVisible(parentCode); } @@ -176,7 +176,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { })), }); }, - getItemId: (code: CodeInfo) => code.codeValue || code.code_value, + getItemId: (code: CodeInfo) => code.code_value, }); // 새 코드 생성 @@ -196,7 +196,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { // 하위 코드 추가 const handleAddChild = (parentCode: CodeInfo) => { setEditingCode(null); - setDefaultParentCode(parentCode.codeValue || parentCode.code_value || ""); + setDefaultParentCode(parentCode.code_value || ""); setShowFormModal(true); }; @@ -213,7 +213,7 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { try { await deleteCodeMutation.mutateAsync({ categoryCode, - codeValue: deletingCode.codeValue || deletingCode.code_value, + codeValue: deletingCode.code_value, }); setShowDeleteModal(false); @@ -298,11 +298,11 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) { <> code.codeValue || code.code_value)} + items={visibleCodes.map((code) => code.code_value)} strategy={verticalListSortingStrategy} > {visibleCodes.map((code, index) => { - const codeValue = code.codeValue || code.code_value || ""; + const codeValue = code.code_value || ""; const children = childrenMap.get(codeValue) || []; const hasChildren = children.length > 0; const isExpanded = !collapsedCodes.has(codeValue); @@ -334,17 +334,17 @@ export function CodeDetailPanel({ categoryCode }: CodeDetailPanelProps) {
-

{activeCode.codeName || activeCode.code_name}

+

{activeCode.code_name}

- {activeCode.isActive === "Y" || activeCode.is_active === "Y" ? "활성" : "비활성"} + {activeCode.is_active === "Y" ? "활성" : "비활성"}

- {activeCode.codeValue || activeCode.code_value} + {activeCode.code_value}

{activeCode.description && (

{activeCode.description}

diff --git a/frontend/components/common/ExcelUploadModal.tsx b/frontend/components/common/ExcelUploadModal.tsx index 8698b270..087df420 100644 --- a/frontend/components/common/ExcelUploadModal.tsx +++ b/frontend/components/common/ExcelUploadModal.tsx @@ -1272,7 +1272,7 @@ export const ExcelUploadModal: React.FC = ({ } } else if (uploadMode === "insert" || uploadMode === "upsert") { // 신규 등록 (insert, upsert 모드) - const formData = { screenId: 0, tableName, data: dataToSave }; + const formData = { screen_id: 0, table_name: tableName, data: dataToSave }; console.log(`📝 [행 ${rowIdx + 1}] 신규 등록 시도 (mode: ${uploadMode}):`, dataToSave); const result = await DynamicFormApi.saveFormData(formData); if (result.success) { diff --git a/frontend/components/screen/CopyScreenModal.tsx b/frontend/components/screen/CopyScreenModal.tsx index 01b91536..8bbe80a6 100644 --- a/frontend/components/screen/CopyScreenModal.tsx +++ b/frontend/components/screen/CopyScreenModal.tsx @@ -167,14 +167,14 @@ export default function CopyScreenModal({ if (isOpen && mode === "screen" && sourceScreen) { // 단일 화면 복제 모드 - setScreenName(`${sourceScreen.screen_name ?? sourceScreen.screenName} (복사본)`); + setScreenName(`${sourceScreen.screen_name} (복사본)`); setDescription(sourceScreen.description || ""); // 대상 회사 코드 설정 if (isSuperAdmin) { - setTargetCompanyCode(sourceScreen.company_code ?? sourceScreen.companyCode); + setTargetCompanyCode(sourceScreen.company_code); } else { - setTargetCompanyCode(sourceScreen.company_code ?? sourceScreen.companyCode); + setTargetCompanyCode(sourceScreen.company_code); } // 대상 그룹 초기화 (전달받은 값 또는 null) @@ -230,7 +230,7 @@ export default function CopyScreenModal({ if (useBulkRename) { // 일괄 수정 사용 시: (복사본) 텍스트 제거 - const newMainName = applyBulkRename(sourceScreen.screen_name ?? sourceScreen.screenName); + const newMainName = applyBulkRename(sourceScreen.screen_name); setScreenName(newMainName); // 모달 화면명 업데이트 @@ -242,8 +242,8 @@ export default function CopyScreenModal({ ); } else { // 일괄 수정 미사용 시: (복사본) 텍스트 추가 - setScreenName(`${sourceScreen.screen_name ?? sourceScreen.screenName} (복사본)`); - + setScreenName(`${sourceScreen.screen_name} (복사본)`); + setLinkedScreens((prev) => prev.map((screen) => ({ ...screen, @@ -259,7 +259,7 @@ export default function CopyScreenModal({ const sourceCompanyCode = mode === "group" ? sourceGroup?.company_code - : sourceScreen?.company_code ?? sourceScreen?.companyCode; + : sourceScreen?.company_code; // 원본 회사와 같은 회사가 선택되어 있으면 다른 회사로 변경 if (sourceCompanyCode && targetCompanyCode === sourceCompanyCode) { @@ -317,8 +317,8 @@ export default function CopyScreenModal({ const data = response.data.data || response.data || []; console.log("📋 회사 목록 데이터:", data); const mappedCompanies = data.map((c: any) => ({ - companyCode: c.company_code || c.companyCode, - companyName: c.company_name || c.companyName, + companyCode: c.company_code, + companyName: c.company_name, })); console.log("📋 매핑된 회사 목록:", mappedCompanies); setCompanies(mappedCompanies); @@ -336,8 +336,8 @@ export default function CopyScreenModal({ try { setLoadingLinkedScreens(true); - console.log("📡 API 호출: detectLinkedModals", sourceScreen.screen_id ?? sourceScreen.screenId); - const linked = await screenApi.detectLinkedModals(sourceScreen.screen_id ?? sourceScreen.screenId); + console.log("📡 API 호출: detectLinkedModals", sourceScreen.screen_id); + const linked = await screenApi.detectLinkedModals(sourceScreen.screen_id); console.log("✅ 연결된 모달 화면 감지 결과:", linked); // 초기 newScreenName 설정 @@ -444,8 +444,8 @@ export default function CopyScreenModal({ return { main: { - original: sourceScreen.screen_name ?? sourceScreen.screenName, - preview: applyBulkRename(sourceScreen.screen_name ?? sourceScreen.screenName), // (복사본) 없음 + original: sourceScreen.screen_name, + preview: applyBulkRename(sourceScreen.screen_name), // (복사본) 없음 }, modals: linkedScreens.map((screen) => ({ original: screen.screenName, @@ -561,7 +561,7 @@ export default function CopyScreenModal({ setIsCopying(true); // 화면명 중복 체크 - const companyCode = targetCompanyCode || sourceScreen.company_code || sourceScreen.companyCode; + const companyCode = targetCompanyCode || sourceScreen.company_code; // 메인 화면명 중복 체크 const isMainDuplicate = await screenApi.checkDuplicateScreenName( @@ -591,7 +591,7 @@ export default function CopyScreenModal({ } // 메인 화면 + 모달 화면들 일괄 복사 - const result = await screenApi.copyScreenWithModals(sourceScreen.screen_id ?? sourceScreen.screenId, { + const result = await screenApi.copyScreenWithModals(sourceScreen.screen_id, { targetCompanyCode: targetCompanyCode || undefined, // 최고 관리자: 대상 회사 전달 mainScreen: { screenName: screenName.trim(), @@ -608,14 +608,14 @@ export default function CopyScreenModal({ console.log("✅ 복사 완료:", result); // 대상 그룹이 선택된 경우 복제된 메인 화면을 그룹에 추가 - if (selectedTargetGroupId && (result.mainScreen?.screen_id ?? result.mainScreen?.screenId)) { + if (selectedTargetGroupId && result.mainScreen?.screen_id) { try { await addScreenToGroup({ group_id: selectedTargetGroupId, - screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId, + screen_id: result.mainScreen.screen_id, screen_role: "MAIN", display_order: 1, - target_company_code: targetCompanyCode || sourceScreen.company_code || sourceScreen.companyCode, // 대상 회사 코드 전달 + target_company_code: targetCompanyCode || sourceScreen.company_code, // 대상 회사 코드 전달 }); console.log(`✅ 복제된 화면을 그룹(${selectedTargetGroupId})에 추가 완료`); } catch (groupError) { @@ -625,7 +625,7 @@ export default function CopyScreenModal({ } // 추가 복사 옵션 처리 (단일 화면 복제용) - const sourceCompanyCode = sourceScreen.company_code ?? sourceScreen.companyCode; + const sourceCompanyCode = sourceScreen.company_code; const copyTargetCompanyCode = targetCompanyCode || sourceCompanyCode; let additionalCopyMessages: string[] = []; @@ -634,8 +634,8 @@ export default function CopyScreenModal({ try { console.log("📋 단일 화면: 채번규칙 복제 시작..."); const numberingResult = await apiClient.post("/api/screen-management/copy-numbering-rules", { - sourceCompanyCode, - targetCompanyCode: copyTargetCompanyCode + source_company_code: sourceCompanyCode, + target_company_code: copyTargetCompanyCode }); if (numberingResult.data.success) { additionalCopyMessages.push(`채번규칙 ${numberingResult.data.copiedCount || 0}개`); @@ -651,8 +651,8 @@ export default function CopyScreenModal({ try { console.log("📋 단일 화면: 카테고리 값 복제 시작..."); const categoryResult = await apiClient.post("/api/screen-management/copy-category-mapping", { - sourceCompanyCode, - targetCompanyCode: copyTargetCompanyCode + source_company_code: sourceCompanyCode, + target_company_code: copyTargetCompanyCode }); if (categoryResult.data.success) { additionalCopyMessages.push(`카테고리 값 ${categoryResult.data.copiedValues || 0}개`); @@ -668,8 +668,8 @@ export default function CopyScreenModal({ try { console.log("📋 단일 화면: 테이블 타입 컬럼 복제 시작..."); const tableTypeResult = await apiClient.post("/api/screen-management/copy-table-type-columns", { - sourceCompanyCode, - targetCompanyCode: copyTargetCompanyCode + source_company_code: sourceCompanyCode, + target_company_code: copyTargetCompanyCode }); if (tableTypeResult.data.success) { additionalCopyMessages.push(`테이블 타입 컬럼 ${tableTypeResult.data.copiedCount || 0}개`); @@ -771,7 +771,7 @@ export default function CopyScreenModal({ const tableName = typeof s === 'object' ? s.table_name : ''; // allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체 - let screenData = allScreens.find((sc) => (sc.screen_id ?? sc.screenId) === screenId); + let screenData = allScreens.find((sc) => sc.screen_id === screenId); if (!screenData && screenId && screenName) { // allScreens에 없는 경우 (다른 회사 화면) - 그룹의 screens 정보로 최소한의 데이터 구성 screenData = { @@ -799,13 +799,13 @@ export default function CopyScreenModal({ setCopyProgress({ current: stats.screens + 1, total: totalScreenCount, - message: `화면 복제 중: ${screen.screen_name ?? screen.screenName}` + message: `화면 복제 중: ${screen.screen_name}` }); - const transformedScreenName = transformName(screen.screen_name ?? screen.screenName, false, sourceGroupData.company_code); - console.log(` 📄 화면 복제: ${screen.screen_name ?? screen.screenName} → ${transformedScreenName}`); + const transformedScreenName = transformName(screen.screen_name, false, sourceGroupData.company_code); + console.log(` 📄 화면 복제: ${screen.screen_name} → ${transformedScreenName}`); - const result = await screenApi.copyScreenWithModals(screen.screen_id ?? screen.screenId, { + const result = await screenApi.copyScreenWithModals(screen.screen_id, { targetCompanyCode: targetCompany, mainScreen: { screenName: transformedScreenName, // 일괄 이름 변경 적용 @@ -815,22 +815,22 @@ export default function CopyScreenModal({ modalScreens: [], }); - if (result.mainScreen?.screen_id ?? result.mainScreen?.screenId) { + if (result.mainScreen?.screen_id) { // 원본 화면 ID -> 새 화면 ID 매핑 기록 - screenIdMap[screen.screen_id ?? screen.screenId] = result.mainScreen.screen_id ?? result.mainScreen.screenId; + screenIdMap[screen.screen_id] = result.mainScreen.screen_id; await addScreenToGroup({ group_id: newGroup.id, - screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId, + screen_id: result.mainScreen.screen_id, screen_role: screenRole || "MAIN", display_order: displayOrder, // 원본 정렬순서 유지 target_company_code: targetCompany, // 대상 회사 코드 전달 }); stats.screens++; - console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screen_name ?? result.mainScreen.screenName} (${screen.screen_id ?? screen.screenId} → ${result.mainScreen.screen_id ?? result.mainScreen.screenId})`); + console.log(` ✅ 화면 복제 완료: ${result.mainScreen.screen_name} (${screen.screen_id} → ${result.mainScreen.screen_id})`); } } catch (screenError) { - console.error(` ❌ 화면 복제 실패 (${screen.screen_code ?? screen.screenCode}):`, screenError); + console.error(` ❌ 화면 복제 실패 (${screen.screen_code}):`, screenError); } } } @@ -951,7 +951,7 @@ export default function CopyScreenModal({ const tableName = typeof s === 'object' ? s.table_name : ''; // allScreens에서 먼저 찾고, 없으면 그룹의 screens 정보로 대체 - let screenData = allScreens.find((sc) => (sc.screen_id ?? sc.screenId) === screenId); + let screenData = allScreens.find((sc) => sc.screen_id === screenId); const foundInAllScreens = !!screenData; if (!screenData && screenId && screenName) { @@ -966,7 +966,7 @@ export default function CopyScreenModal({ companyCode: sourceGroup.company_code || '', } as any; } else if (screenData) { - console.log(` ✅ allScreens에서 찾음: ${screenId} - ${screenData.screen_name ?? screenData.screenName}`); + console.log(` ✅ allScreens에서 찾음: ${screenId} - ${screenData.screen_name}`); } else { console.log(` ❌ 화면 정보 없음: screenId=${screenId}, screenName=${screenName}`); } @@ -974,7 +974,7 @@ export default function CopyScreenModal({ }).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만 console.log(`🔍 매핑 완료: ${screensWithOrder.length}개 화면 복사 예정`); - screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screenName}`)); + screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screen_name}`)); // display_order 순으로 정렬 screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0)); @@ -989,13 +989,13 @@ export default function CopyScreenModal({ setCopyProgress({ current: stats.screens + 1, total: totalScreenCount, - message: `화면 복제 중: ${screen.screen_name ?? screen.screenName}` + message: `화면 복제 중: ${screen.screen_name}` }); - const transformedScreenName = transformName(screen.screen_name ?? screen.screenName, false, sourceGroup.company_code); - console.log(`📄 화면 복제: ${screen.screen_name ?? screen.screenName} → ${transformedScreenName}`); + const transformedScreenName = transformName(screen.screen_name, false, sourceGroup.company_code); + console.log(`📄 화면 복제: ${screen.screen_name} → ${transformedScreenName}`); - const result = await screenApi.copyScreenWithModals(screen.screen_id ?? screen.screenId, { + const result = await screenApi.copyScreenWithModals(screen.screen_id, { targetCompanyCode: finalCompanyCode, mainScreen: { screenName: transformedScreenName, // 일괄 이름 변경 적용 @@ -1005,22 +1005,22 @@ export default function CopyScreenModal({ modalScreens: [], }); - if (result.mainScreen?.screen_id ?? result.mainScreen?.screenId) { + if (result.mainScreen?.screen_id) { // 원본 화면 ID -> 새 화면 ID 매핑 기록 - screenIdMap[screen.screen_id ?? screen.screenId] = result.mainScreen.screen_id ?? result.mainScreen.screenId; + screenIdMap[screen.screen_id] = result.mainScreen.screen_id; await addScreenToGroup({ group_id: newRootGroup.id, - screen_id: result.mainScreen.screen_id ?? result.mainScreen.screenId, + screen_id: result.mainScreen.screen_id, screen_role: screenRole || "MAIN", display_order: displayOrder, // 원본 정렬순서 유지 target_company_code: finalCompanyCode, // 대상 회사 코드 전달 }); stats.screens++; - console.log(`✅ 화면 복제 완료: ${result.mainScreen.screen_name ?? result.mainScreen.screenName} (${screen.screen_id ?? screen.screenId} → ${result.mainScreen.screen_id ?? result.mainScreen.screenId})`); + console.log(`✅ 화면 복제 완료: ${result.mainScreen.screen_name} (${screen.screen_id} → ${result.mainScreen.screen_id})`); } } catch (screenError) { - console.error(`화면 복제 실패 (${screen.screen_code ?? screen.screenCode}):`, screenError); + console.error(`화면 복제 실패 (${screen.screen_code}):`, screenError); } } } @@ -1068,7 +1068,7 @@ export default function CopyScreenModal({ // 7-1. 메뉴 동기화 (화면 그룹 → 메뉴) - 항상 실행 const syncResponse = await apiClient.post("/screen-groups/sync/screen-to-menu", { - targetCompanyCode: finalCompanyCode, + target_company_code: finalCompanyCode, }); if (syncResponse.data?.success) { @@ -1079,9 +1079,9 @@ export default function CopyScreenModal({ console.log("📋 화면-메뉴 할당 복제 시작..."); const menuAssignResponse = await apiClient.post("/screen-management/copy-menu-assignments", { - sourceCompanyCode: sourceGroup.company_code, - targetCompanyCode: finalCompanyCode, - screenIdMap, + source_company_code: sourceGroup.company_code, + target_company_code: finalCompanyCode, + screen_id_map: screenIdMap, }); if (menuAssignResponse.data?.success) { @@ -1096,8 +1096,8 @@ export default function CopyScreenModal({ console.log("📋 채번규칙 복제 시작..."); const numberingResponse = await apiClient.post("/numbering-rules/copy-for-company", { - sourceCompanyCode: sourceGroup.company_code, - targetCompanyCode: finalCompanyCode, + source_company_code: sourceGroup.company_code, + target_company_code: finalCompanyCode, }); if (numberingResponse.data?.success) { @@ -1124,8 +1124,8 @@ export default function CopyScreenModal({ console.log("📋 카테고리 값 복제 시작..."); const response = await apiClient.post("/screen-management/copy-category-mapping", { - sourceCompanyCode: sourceGroup.company_code, - targetCompanyCode: finalCompanyCode, + source_company_code: sourceGroup.company_code, + target_company_code: finalCompanyCode, }); if (response.data?.success) { @@ -1148,8 +1148,8 @@ export default function CopyScreenModal({ console.log("📋 테이블 타입 컬럼 복제 시작..."); const response = await apiClient.post("/screen-management/copy-table-type-columns", { - sourceCompanyCode: sourceGroup.company_code, - targetCompanyCode: finalCompanyCode, + source_company_code: sourceGroup.company_code, + target_company_code: finalCompanyCode, }); if (response.data?.success) { @@ -1169,9 +1169,9 @@ export default function CopyScreenModal({ try { await apiClient.post("/audit-log", { action: "COPY", - resourceType: "SCREEN", - resourceId: String(sourceGroup.id), - resourceName: sourceGroup.group_name, + resource_type: "SCREEN", + resource_id: String(sourceGroup.id), + resource_name: sourceGroup.group_name, summary: `그룹 "${sourceGroup.group_name}" → "${rootGroupName}" 복제 (그룹 ${stats.groups}개, 화면 ${stats.screens}개)${finalCompanyCode !== sourceGroup.company_code ? ` [${sourceGroup.company_code} → ${finalCompanyCode}]` : ""}`, changes: { after: { @@ -1663,7 +1663,7 @@ export default function CopyScreenModal({ 화면 복제 - "{sourceScreen?.screen_name ?? sourceScreen?.screenName}" 화면을 복제합니다. + "{sourceScreen?.screen_name}" 화면을 복제합니다. {linkedScreens.length > 0 && ` (모달 ${linkedScreens.length}개 포함)`} @@ -1758,7 +1758,7 @@ export default function CopyScreenModal({ {companies - .filter((company) => company.companyCode !== (sourceScreen?.company_code ?? sourceScreen?.companyCode)) + .filter((company) => company.companyCode !== sourceScreen?.company_code) .map((company) => ( {company.companyName} @@ -1768,7 +1768,7 @@ export default function CopyScreenModal({ {sourceScreen && (

- * 원본 회사({sourceScreen.company_code ?? sourceScreen.companyCode})로는 복제할 수 없습니다 + * 원본 회사({sourceScreen.company_code})로는 복제할 수 없습니다

)}
diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index 4dfa1597..c7ef75c5 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -948,8 +948,8 @@ export const EditModal: React.FC = ({ className }) => { try { const response = await dynamicFormApi.saveFormData({ - screenId: modalState.screenId || 0, - tableName: screenData.screenInfo.table_name, + screen_id: modalState.screenId || 0, + table_name: screenData.screenInfo.table_name, data: insertData, }); @@ -1332,8 +1332,8 @@ export const EditModal: React.FC = ({ className }) => { console.log("[EditModal] 최종 저장 데이터:", masterDataToSave); const response = await dynamicFormApi.saveFormData({ - screenId: modalState.screenId!, - tableName: screenData.screenInfo.table_name, + screen_id: modalState.screenId!, + table_name: screenData.screenInfo.table_name, data: masterDataToSave, }); @@ -1470,7 +1470,7 @@ export const EditModal: React.FC = ({ className }) => { }); const response = await dynamicFormApi.updateFormData(recordId, { - tableName: screenData.screenInfo.table_name, + table_name: screenData.screenInfo.table_name, data: dataToSave, }); diff --git a/frontend/components/screen/EnhancedInteractiveScreenViewer.tsx b/frontend/components/screen/EnhancedInteractiveScreenViewer.tsx index 7d474e13..cc30f551 100644 --- a/frontend/components/screen/EnhancedInteractiveScreenViewer.tsx +++ b/frontend/components/screen/EnhancedInteractiveScreenViewer.tsx @@ -464,7 +464,7 @@ export const EnhancedInteractiveScreenViewer: React.FC
테이블 - {screenInfo.tableName} + {screenInfo.table_name}
필드 diff --git a/frontend/components/screen/FileAttachmentDetailModal.tsx b/frontend/components/screen/FileAttachmentDetailModal.tsx index 383aea9a..d77f7e1e 100644 --- a/frontend/components/screen/FileAttachmentDetailModal.tsx +++ b/frontend/components/screen/FileAttachmentDetailModal.tsx @@ -162,24 +162,24 @@ export const FileAttachmentDetailModal: React.FC if (response.success && response.data) { const newFiles: FileInfo[] = response.data.map((file: any) => ({ objid: file.objid || `temp_${Date.now()}_${Math.random()}`, - savedFileName: file.saved_file_name || file.savedFileName, - realFileName: file.real_file_name || file.realFileName, - fileSize: file.file_size || file.fileSize, - fileExt: file.file_ext || file.fileExt, - filePath: file.file_path || file.filePath, + savedFileName: file.saved_file_name, + realFileName: file.real_file_name, + fileSize: file.file_size, + fileExt: file.file_ext, + filePath: file.file_path, docType: fileConfig.docType, docTypeName: fileConfig.docTypeName, - targetObjid: file.target_objid || file.targetObjid || recordId || screenId || '', - parentTargetObjid: file.parent_target_objid || file.parentTargetObjid, - companyCode: file.company_code || file.companyCode || 'DEFAULT', + targetObjid: file.target_objid || recordId || screenId || '', + parentTargetObjid: file.parent_target_objid, + companyCode: file.company_code || 'DEFAULT', writer: file.writer || 'user', regdate: file.regdate || new Date().toISOString(), status: file.status || 'ACTIVE', // 호환성 속성들 - path: file.file_path || file.filePath, - name: file.real_file_name || file.realFileName, + path: file.file_path, + name: file.real_file_name, id: file.objid, - size: file.file_size || file.fileSize, + size: file.file_size, type: fileConfig.docType, uploadedAt: file.regdate || new Date().toISOString(), })); diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index 21a66d58..88f846e8 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -879,16 +879,16 @@ export const InteractiveDataTable: React.FC = ({ } // 없으면 테이블 타입 관리에서 설정된 값 찾기 - const tableColumn = tableColumns.find((col) => col.columnName === columnName); + const tableColumn = tableColumns.find((col) => col.column_name === columnName); // input_type 우선 사용 (category 등) - const inputType = (tableColumn as any)?.input_type || (tableColumn as any)?.inputType; + const inputType = tableColumn?.input_type; if (inputType) { return inputType; } - // 없으면 webType 사용 - return tableColumn?.webType || "text"; + // 없으면 web_type 사용 + return tableColumn?.web_type || "text"; }, [component.columns, tableColumns], ); @@ -903,9 +903,9 @@ export const InteractiveDataTable: React.FC = ({ } // 없으면 테이블 타입 관리에서 설정된 값 찾기 - const tableColumn = tableColumns.find((col) => col.columnName === columnName); + const tableColumn = tableColumns.find((col) => col.column_name === columnName); try { - return tableColumn?.detailSettings ? JSON.parse(tableColumn.detailSettings) : {}; + return tableColumn?.detail_settings ? JSON.parse(tableColumn.detail_settings) : {}; } catch { return {}; } @@ -1009,13 +1009,13 @@ export const InteractiveDataTable: React.FC = ({ setTableColumns(columns); // 🆕 전체 컬럼 목록 설정 - const columnNames = columns.map((col) => col.columnName); + const columnNames = columns.map((col) => col.column_name); setAllAvailableColumns(columnNames); // 🆕 컬럼명 -> 라벨 매핑 생성 const labels: Record = {}; columns.forEach((col) => { - labels[col.columnName] = col.display_name || col.columnName; + labels[col.column_name] = col.display_name || col.column_name; }); setColumnLabels(labels); diff --git a/frontend/components/screen/InteractiveScreenViewer.tsx b/frontend/components/screen/InteractiveScreenViewer.tsx index 7933a002..08c81b66 100644 --- a/frontend/components/screen/InteractiveScreenViewer.tsx +++ b/frontend/components/screen/InteractiveScreenViewer.tsx @@ -410,17 +410,17 @@ export const InteractiveScreenViewer: React.FC = ( console.log("📊 팝업 화면 로드 완료:", { componentsCount: layout.components?.length || 0, screenInfo: { - screenId: screen.screenId, - tableName: screen.tableName + screenId: screen.screen_id, + tableName: screen.table_name }, popupFormData: {} }); - + setPopupLayout(layout.components || []); - setPopupScreenResolution(layout.screenResolution || null); + setPopupScreenResolution(layout.screen_resolution || null); setPopupScreenInfo({ id: popupScreen.screenId, - tableName: screen.tableName + tableName: screen.table_name }); // 팝업 formData 초기화 @@ -1778,8 +1778,8 @@ export const InteractiveScreenViewer: React.FC = ( }); const saveData: DynamicFormData = { - screenId: screenInfo.id, - tableName: tableName, + screen_id: screenInfo.id, + table_name: tableName, data: masterDataWithUserInfo, }; diff --git a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx index fe61d5cc..da3b1646 100644 --- a/frontend/components/screen/InteractiveScreenViewerDynamic.tsx +++ b/frontend/components/screen/InteractiveScreenViewerDynamic.tsx @@ -320,12 +320,12 @@ export const InteractiveScreenViewerDynamic: React.FC col.columnName || col.column_name || col.name); + targetTableColumns = columnsData.map((col: any) => col.column_name || col.name); console.log("📍 대상 테이블 컬럼 목록:", targetTableColumns); } } catch (error) { @@ -1033,7 +1033,7 @@ export const InteractiveScreenViewerDynamic: React.FC = ({ const tableName = screenData.table_name || components.find((c) => c.columnName)?.table_name || "dynamic_form_data"; const saveData: DynamicFormData = { - screenId: screenId, - tableName: tableName, + screen_id: screenId, + table_name: tableName, data: masterDataWithUserInfo, }; diff --git a/frontend/components/screen/ScreenList.tsx b/frontend/components/screen/ScreenList.tsx index c48822f7..93c140a9 100644 --- a/frontend/components/screen/ScreenList.tsx +++ b/frontend/components/screen/ScreenList.tsx @@ -368,8 +368,8 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr if (response.success && response.data) { // tableName과 displayName 매핑 (백엔드에서 displayName으로 라벨을 반환함) const tableList = response.data.map((table: any) => ({ - tableName: table.tableName, - tableLabel: table.displayName || table.tableName, + tableName: table.table_name, + tableLabel: table.display_name || table.table_name, })); setTables(tableList); } @@ -679,7 +679,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr setTimeout(async () => { try { // 화면 레이아웃 로드 - const layoutData = await screenApi.getLayout(screen.screenId); + const layoutData = await screenApi.getLayout(screen.screen_id); console.log("📊 미리보기 레이아웃 로드:", layoutData); setPreviewLayout(layoutData); } catch (error) { @@ -1923,8 +1923,8 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr isSelected={false} isDesignMode={false} onClick={() => {}} - screenId={screenToPreview!.screenId} - tableName={screenToPreview?.tableName} + screenId={screenToPreview!.screen_id} + tableName={screenToPreview?.table_name} formData={previewFormData} onFormDataChange={(fieldName, value) => { setPreviewFormData((prev) => ({ diff --git a/frontend/components/screen/ScreenSettingModal.tsx b/frontend/components/screen/ScreenSettingModal.tsx index 91573177..a3d2b171 100644 --- a/frontend/components/screen/ScreenSettingModal.tsx +++ b/frontend/components/screen/ScreenSettingModal.tsx @@ -1365,10 +1365,10 @@ function TableColumnAccordion({ 조인 - {editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? "연결 편집" : (hasJoinSetting ? "연결 정보" : "연결 설정")} + {editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? "연결 편집" : (hasJoinSetting ? "연결 정보" : "연결 설정")}
- {editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? ( + {editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? (
- {editingJoin && editingJoin.columnName === (selectedColumn?.columnName || editingField) ? ( + {editingJoin && editingJoin.columnName === (selectedColumn?.column_name || editingField) ? (
대상 테이블: - {isJoinKey && joinRef ? joinRef.refTable : selectedColumn?.referenceTable} + {isJoinKey && joinRef ? joinRef.refTable : selectedColumn?.reference_table}
연결 컬럼: - {isJoinKey && joinRef ? joinRef.refColumn : selectedColumn?.referenceColumn} + {isJoinKey && joinRef ? joinRef.refColumn : selectedColumn?.reference_column}
) : ( @@ -1499,9 +1499,9 @@ function JoinSettingEditor({ const [refColSearchOpen, setRefColSearchOpen] = useState(false); const [displayColSearchOpen, setDisplayColSearchOpen] = useState(false); - const selectedTable = allTables.find(t => t.tableName === editingJoin.referenceTable); - const selectedRefCol = refTableColumns.find(c => c.columnName === editingJoin.referenceColumn); - const selectedDisplayCol = refTableColumns.find(c => c.columnName === editingJoin.displayColumn); + const selectedTable = allTables.find(t => t.table_name === editingJoin.referenceTable); + const selectedRefCol = refTableColumns.find(c => c.column_name === editingJoin.referenceColumn); + const selectedDisplayCol = refTableColumns.find(c => c.column_name === editingJoin.displayColumn); return (
@@ -1515,7 +1515,7 @@ function JoinSettingEditor({ size="sm" className="w-full justify-between h-7 text-xs font-normal" > - {selectedTable?.displayName || editingJoin.referenceTable || "테이블 선택"} + {selectedTable?.display_name || editingJoin.referenceTable || "테이블 선택"} @@ -1527,11 +1527,11 @@ function JoinSettingEditor({ {allTables.map((t, idx) => ( { - setEditingJoin({ ...editingJoin, referenceTable: t.tableName, referenceColumn: "", displayColumn: "" }); - loadRefTableColumns(t.tableName); + setEditingJoin({ ...editingJoin, referenceTable: t.table_name, referenceColumn: "", displayColumn: "" }); + loadRefTableColumns(t.table_name); setTableSearchOpen(false); }} className="text-xs" @@ -1539,10 +1539,10 @@ function JoinSettingEditor({ - {t.displayName || t.tableName} + {t.display_name || t.table_name} ))} @@ -1563,7 +1563,7 @@ function JoinSettingEditor({ className="w-full justify-between h-7 text-xs font-normal" disabled={!editingJoin.referenceTable || loadingRefColumns} > - {loadingRefColumns ? "로딩중..." : (selectedRefCol?.displayName || editingJoin.referenceColumn || "컬럼 선택")} + {loadingRefColumns ? "로딩중..." : (selectedRefCol?.display_name || editingJoin.referenceColumn || "컬럼 선택")} @@ -1575,10 +1575,10 @@ function JoinSettingEditor({ {refTableColumns.map(c => ( { - setEditingJoin({ ...editingJoin, referenceColumn: c.columnName }); + setEditingJoin({ ...editingJoin, referenceColumn: c.column_name }); setRefColSearchOpen(false); }} className="text-xs" @@ -1586,10 +1586,10 @@ function JoinSettingEditor({ - {c.displayName || c.columnName} + {c.display_name || c.column_name} ))} @@ -1610,7 +1610,7 @@ function JoinSettingEditor({ className="w-full justify-between h-7 text-xs font-normal" disabled={!editingJoin.referenceTable || loadingRefColumns} > - {selectedDisplayCol?.displayName || editingJoin.displayColumn || "컬럼 선택"} + {selectedDisplayCol?.display_name || editingJoin.displayColumn || "컬럼 선택"} @@ -1622,10 +1622,10 @@ function JoinSettingEditor({ {refTableColumns.map(c => ( { - setEditingJoin({ ...editingJoin, displayColumn: c.columnName }); + setEditingJoin({ ...editingJoin, displayColumn: c.column_name }); setDisplayColSearchOpen(false); }} className="text-xs" @@ -1633,10 +1633,10 @@ function JoinSettingEditor({ - {c.displayName || c.columnName} + {c.display_name || c.column_name} ))} @@ -2627,7 +2627,7 @@ function FieldMappingTab({ const columnDisplayMap = useMemo(() => { const map: Record = {}; tableColumns.forEach((tc) => { - map[tc.columnName] = tc.displayName || tc.columnName; + map[tc.column_name] = tc.display_name || tc.column_name; }); return map; }, [tableColumns]); @@ -2806,8 +2806,8 @@ function FieldMappingTab({ ) : ( tableColumns.map((tableCol) => ( { toast.info(`컬럼 변경: ${col} → ${value}`, { description: "저장 기능은 아직 구현 중입니다." @@ -2820,17 +2820,17 @@ function FieldMappingTab({
- {tableCol.displayName || tableCol.columnName} + {tableCol.display_name || tableCol.column_name} - {tableCol.displayName && tableCol.displayName !== tableCol.columnName && ( + {tableCol.display_name && tableCol.display_name !== tableCol.column_name && ( - {tableCol.columnName} + {tableCol.column_name} )}
- {tableCol.columnName === col && ( + {tableCol.column_name === col && ( )}
diff --git a/frontend/lib/api/data.ts b/frontend/lib/api/data.ts index e1652bb0..e1f14b14 100644 --- a/frontend/lib/api/data.ts +++ b/frontend/lib/api/data.ts @@ -40,7 +40,7 @@ export const dataApi = { const page = raw.page ?? params?.page ?? 1; const size = raw.size ?? params?.size ?? items.length; const total = raw.total ?? items.length; - const totalPages = raw.total_pages ?? raw.totalPages ?? Math.max(1, Math.ceil(total / (size || 1))); + const totalPages = raw.total_pages ?? Math.max(1, Math.ceil(total / (size || 1))); return { data: items, total, page, size, totalPages }; }, diff --git a/frontend/lib/api/dataflowSave.ts b/frontend/lib/api/dataflowSave.ts index ccfa9106..49007659 100644 --- a/frontend/lib/api/dataflowSave.ts +++ b/frontend/lib/api/dataflowSave.ts @@ -85,11 +85,11 @@ export const loadDataflowRelationship = async (diagramId: number) => { // 기존 구조를 redesigned 구조로 변환 relationshipsData = { description: firstRelation.note || "", - connectionType: firstRelation.connectionType || "data_save", + connectionType: firstRelation.connection_type || firstRelation.connectionType || "data_save", fromConnection: { id: 0, name: "메인 데이터베이스 (현재 시스템)" }, // 기본값 toConnection: { id: 0, name: "메인 데이터베이스 (현재 시스템)" }, // 기본값 - fromTable: firstRelation.fromTable, - toTable: firstRelation.toTable, + fromTable: firstRelation.from_table || firstRelation.fromTable, + toTable: firstRelation.to_table || firstRelation.toTable, actionType: "insert", // 기본값 controlConditions: [], actionConditions: [], @@ -108,9 +108,9 @@ export const loadDataflowRelationship = async (diagramId: number) => { const plan = diagram.plan.find((p) => p.id === firstRelation.id); if (plan && plan.actions && plan.actions.length > 0) { const firstAction = plan.actions[0]; - relationshipsData.actionType = firstAction.actionType || "insert"; - relationshipsData.fieldMappings = firstAction.fieldMappings || []; - relationshipsData.actionConditions = firstAction.conditions || []; + relationshipsData.actionType = firstAction.action_type || firstAction.actionType || "insert"; + relationshipsData.fieldMappings = firstAction.field_mappings || firstAction.fieldMappings || []; + relationshipsData.actionConditions = firstAction.action_conditions || firstAction.conditions || []; } } } diff --git a/frontend/lib/api/dynamicForm.ts b/frontend/lib/api/dynamicForm.ts index d216d2ed..3e0146a4 100644 --- a/frontend/lib/api/dynamicForm.ts +++ b/frontend/lib/api/dynamicForm.ts @@ -2,8 +2,8 @@ import { apiClient, ApiResponse } from "./client"; // 동적 폼 데이터 타입 export interface DynamicFormData { - screenId: number; - tableName: string; + screen_id?: number; + table_name: string; data: Record; } @@ -145,7 +145,7 @@ export class DynamicFormApi { }); const response = await apiClient.patch(`/dynamic-form/${id}/partial`, { - tableName, + table_name: tableName, originalData, newData, }); @@ -375,7 +375,7 @@ export class DynamicFormApi { console.log("✅ 폼 데이터 검증 요청:", { tableName, data }); const response = await apiClient.post(`/dynamic-form/validate`, { - tableName, + table_name: tableName, data, }); @@ -440,8 +440,8 @@ export class DynamicFormApi { filters?: Record; autoFilter?: { enabled: boolean; - filterColumn?: string; - userField?: string; + filter_column?: string; + user_field?: string; }; }, ): Promise> { @@ -455,8 +455,8 @@ export class DynamicFormApi { size: params?.pageSize || params?.size || 100, // 기본값 100 autoFilter: params?.autoFilter ?? { enabled: true, - filterColumn: "company_code", - userField: "company_code", + filter_column: "company_code", + user_field: "company_code", }, }; @@ -675,7 +675,7 @@ export class DynamicFormApi { masterFieldValues: Record, numberingRuleId?: string, afterUploadFlowId?: string, - afterUploadFlows?: Array<{ flowId: string; order: number }> + afterUploadFlows?: Array<{ flow_id: string; order: number }> ): Promise> { try { console.log("📤 마스터-디테일 간단 모드 업로드:", { @@ -688,11 +688,11 @@ export class DynamicFormApi { const response = await apiClient.post(`/data/master-detail/upload-simple`, { screen_id: screenId, - detailData, - masterFieldValues, - numberingRuleId, - afterUploadFlowId, - afterUploadFlows, + detail_data: detailData, + master_field_values: masterFieldValues, + numbering_rule_id: numberingRuleId, + after_upload_flow_id: afterUploadFlowId, + after_upload_flows: afterUploadFlows, }); return { diff --git a/frontend/lib/api/file.ts b/frontend/lib/api/file.ts index d02b7a14..3e7390f9 100644 --- a/frontend/lib/api/file.ts +++ b/frontend/lib/api/file.ts @@ -55,20 +55,20 @@ export const uploadFiles = async (params: { // 추가 파라미터들 추가 if (params.tableName) formData.append("table_name", params.tableName); - if (params.fieldName) formData.append("fieldName", params.fieldName); - if (params.recordId) formData.append("recordId", String(params.recordId)); - if (params.docType) formData.append("docType", params.docType); - if (params.docTypeName) formData.append("docTypeName", params.docTypeName); - if (params.targetObjid) formData.append("targetObjid", params.targetObjid); - if (params.parentTargetObjid) formData.append("parentTargetObjid", params.parentTargetObjid); - if (params.linkedTable) formData.append("linkedTable", params.linkedTable); - if (params.linkedField) formData.append("linkedField", params.linkedField); - if (params.autoLink !== undefined) formData.append("autoLink", params.autoLink.toString()); - if (params.columnName) formData.append("columnName", params.columnName); - if (params.isVirtualFileColumn !== undefined) formData.append("isVirtualFileColumn", params.isVirtualFileColumn.toString()); + if (params.fieldName) formData.append("field_name", params.fieldName); + if (params.recordId) formData.append("record_id", String(params.recordId)); + if (params.docType) formData.append("doc_type", params.docType); + if (params.docTypeName) formData.append("doc_type_name", params.docTypeName); + if (params.targetObjid) formData.append("target_objid", params.targetObjid); + if (params.parentTargetObjid) formData.append("parent_target_objid", params.parentTargetObjid); + if (params.linkedTable) formData.append("linked_table", params.linkedTable); + if (params.linkedField) formData.append("linked_field", params.linkedField); + if (params.autoLink !== undefined) formData.append("auto_link", params.autoLink.toString()); + if (params.columnName) formData.append("column_name", params.columnName); + if (params.isVirtualFileColumn !== undefined) formData.append("is_virtual_file_column", params.isVirtualFileColumn.toString()); if (params.companyCode) formData.append("company_code", params.companyCode); // 🔒 멀티테넌시 // 🆕 레코드 모드 플래그 추가 (백엔드에서 attachments 컬럼 자동 업데이트용) - if (params.isRecordMode !== undefined) formData.append("isRecordMode", params.isRecordMode.toString()); + if (params.isRecordMode !== undefined) formData.append("is_record_mode", params.isRecordMode.toString()); const response = await apiClient.post("/files/upload", formData, { headers: { @@ -145,10 +145,10 @@ export const getFileInfo = async (fileId: string, serverFilename: string) => { */ export const getComponentFiles = async (params: { screen_id: number; - componentId: string; + component_id: string; table_name?: string; - recordId?: string; - columnName?: string; + record_id?: string; + column_name?: string; }): Promise<{ success: boolean; templateFiles: FileInfo[]; diff --git a/frontend/lib/api/layout.ts b/frontend/lib/api/layout.ts index 24f511fa..c6d1798c 100644 --- a/frontend/lib/api/layout.ts +++ b/frontend/lib/api/layout.ts @@ -12,13 +12,13 @@ export interface GetLayoutsParams { page?: number; size?: number; category?: LayoutCategory; - layoutType?: LayoutType; - searchTerm?: string; - includePublic?: boolean; + layout_type?: LayoutType; + search_term?: string; + include_public?: boolean; } export interface DuplicateLayoutRequest { - newName: string; + new_name: string; } class LayoutApiService { @@ -84,9 +84,9 @@ class LayoutApiService { */ async searchLayouts( searchTerm: string, - params: Omit = {}, + params: Omit = {}, ): Promise { - return this.getLayouts({ ...params, searchTerm }); + return this.getLayouts({ ...params, search_term: searchTerm }); } /** @@ -104,23 +104,23 @@ class LayoutApiService { */ async getLayoutsByType( layoutType: LayoutType, - params: Omit = {}, + params: Omit = {}, ): Promise { - return this.getLayouts({ ...params, layoutType }); + return this.getLayouts({ ...params, layout_type: layoutType }); } /** * 공개 레이아웃만 조회 */ - async getPublicLayouts(params: Omit = {}): Promise { - return this.getLayouts({ ...params, includePublic: true }); + async getPublicLayouts(params: Omit = {}): Promise { + return this.getLayouts({ ...params, include_public: true }); } /** * 개인 레이아웃만 조회 */ - async getPrivateLayouts(params: Omit = {}): Promise { - return this.getLayouts({ ...params, includePublic: false }); + async getPrivateLayouts(params: Omit = {}): Promise { + return this.getLayouts({ ...params, include_public: false }); } } @@ -129,4 +129,3 @@ export const layoutApi = new LayoutApiService(); // 기본 내보내기 export default layoutApi; - diff --git a/frontend/lib/api/mail.ts b/frontend/lib/api/mail.ts index 41b8516f..4a9e2d97 100644 --- a/frontend/lib/api/mail.ts +++ b/frontend/lib/api/mail.ts @@ -347,7 +347,7 @@ export async function previewMailTemplate( ): Promise<{ html: string }> { return fetchApi<{ html: string }>(`/mail/templates-file/${id}/preview`, { method: 'POST', - data: { sampleData }, + data: { sample_data: sampleData }, }); } diff --git a/frontend/lib/api/tableCategoryValue.ts b/frontend/lib/api/tableCategoryValue.ts index 253e66d0..fd1c020d 100644 --- a/frontend/lib/api/tableCategoryValue.ts +++ b/frontend/lib/api/tableCategoryValue.ts @@ -316,10 +316,10 @@ export async function getSecondLevelMenus() { const response = await apiClient.get<{ success: boolean; data: Array<{ - menuObjid: number; - menuName: string; - parentMenuName: string; - screenCode?: string; + menu_objid: number; + menu_name: string; + parent_menu_name: string; + screen_code?: string; }>; }>("/table-categories/second-level-menus"); return response.data; diff --git a/frontend/lib/registry/components/conditional-container/ConditionalContainerConfigPanel.tsx b/frontend/lib/registry/components/conditional-container/ConditionalContainerConfigPanel.tsx index dd5da5ae..f2b85d1b 100644 --- a/frontend/lib/registry/components/conditional-container/ConditionalContainerConfigPanel.tsx +++ b/frontend/lib/registry/components/conditional-container/ConditionalContainerConfigPanel.tsx @@ -58,7 +58,7 @@ export function ConditionalContainerConfigPanel({ const [screensLoading, setScreensLoading] = useState(false); // 🆕 메뉴 기반 카테고리 관련 상태 - const [availableMenus, setAvailableMenus] = useState>([]); + const [availableMenus, setAvailableMenus] = useState>([]); const [menusLoading, setMenusLoading] = useState(false); const [selectedMenuObjid, setSelectedMenuObjid] = useState(null); const [menuPopoverOpen, setMenuPopoverOpen] = useState(false); @@ -309,8 +309,8 @@ export function ConditionalContainerConfigPanel({ ) : selectedMenuObjid ? ( (() => { - const menu = availableMenus.find((m) => m.menuObjid === selectedMenuObjid); - return menu ? `${menu.parentMenuName} > ${menu.menuName}` : `메뉴 ${selectedMenuObjid}`; + const menu = availableMenus.find((m) => m.menu_objid === selectedMenuObjid); + return menu ? `${menu.parent_menu_name} > ${menu.menu_name}` : `메뉴 ${selectedMenuObjid}`; })() ) : ( "메뉴 선택..." @@ -326,10 +326,10 @@ export function ConditionalContainerConfigPanel({ {availableMenus.map((menu) => ( { - setSelectedMenuObjid(menu.menuObjid); + setSelectedMenuObjid(menu.menu_objid); setSelectedCategoryColumn(""); setSelectedCategoryTableName(""); setMenuPopoverOpen(false); @@ -339,14 +339,14 @@ export function ConditionalContainerConfigPanel({
- {menu.parentMenuName} > {menu.menuName} - {menu.screenCode && ( + {menu.parent_menu_name} > {menu.menu_name} + {menu.screen_code && ( - {menu.screenCode} + {menu.screen_code} )}
diff --git a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx index c2d6e652..b8f66d29 100644 --- a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx +++ b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel.tsx @@ -1674,9 +1674,9 @@ function RowNumberingConfigSection({ const response = await getNumberingRules(); if (response.success && response.data) { setNumberingRules(response.data.map((rule: any, index: number) => ({ - id: String(rule.ruleId || rule.id || `rule-${index}`), - name: rule.ruleName || rule.name || "이름 없음", - code: rule.ruleId || rule.code || "", + id: String(rule.rule_id || `rule-${index}`), + name: rule.rule_name || "이름 없음", + code: rule.rule_id || "", }))); } } catch (error) { diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index edc94ffa..abe2d49e 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -1347,8 +1347,8 @@ export const SplitPanelLayoutComponent: React.FC const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`); if (response.data.success && response.data.data && response.data.data.length > 0) { return response.data.data.map((item: any) => ({ - value: item.valueCode, - label: item.valueLabel, + value: item.value_code, + label: item.value_label, })); } } catch { diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutConfigPanel.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutConfigPanel.tsx index 854f078f..6e305501 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutConfigPanel.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutConfigPanel.tsx @@ -198,7 +198,7 @@ interface AdditionalTabConfigPanelProps { tabIndex: number; config: SplitPanelLayoutConfig; updateRightPanel: (updates: Partial) => void; - availableRightTables: TableInfo[]; + availableRightTables: any[]; leftTableColumns: ColumnInfo[]; menuObjid?: number; // 공유 컬럼 로드 상태 @@ -323,17 +323,17 @@ const AdditionalTabConfigPanel: React.FC = ({ {availableRightTables.map((table) => ( updateTab({ tableName: table.tableName, columns: [] })} + key={table.table_name} + value={`${table.display_name || ""} ${table.table_name}`} + onSelect={() => updateTab({ tableName: table.table_name, columns: [] })} > - {table.displayName || table.tableName} + {table.display_name || table.table_name} ))} @@ -1431,8 +1431,8 @@ export const SplitPanelLayoutConfigPanel: React.FC {config.leftPanel?.tableName - ? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.displayName || + ? allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.table_label || + allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.display_name || config.leftPanel?.tableName : "테이블을 선택하세요"} @@ -1466,8 +1466,8 @@ export const SplitPanelLayoutConfigPanel: React.FC - {allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName || + {allTables.find((t) => t.table_name === screenTableName)?.table_label || + allTables.find((t) => t.table_name === screenTableName)?.display_name || screenTableName}
@@ -1476,10 +1476,10 @@ export const SplitPanelLayoutConfigPanel: React.FC {allTables - .filter((t) => (t.tableName || t.table_name) !== screenTableName) + .filter((t) => t.table_name !== screenTableName) .map((table) => { - const tableName = table.tableName || table.table_name; - const displayName = table.tableLabel || table.displayName || tableName; + const tableName = table.table_name; + const displayName = table.table_label || table.display_name || tableName; return ( {availableRightTables.map((table) => ( { - updateRightPanel({ tableName: table.tableName }); + updateRightPanel({ tableName: table.table_name }); setRightTableOpen(false); }} > - {table.displayName || table.tableName} - {table.displayName && ({table.tableName})} + {table.display_name || table.table_name} + {table.display_name && ({table.table_name})} ))} diff --git a/frontend/lib/registry/components/v2-file-upload/FileUploadComponent.tsx b/frontend/lib/registry/components/v2-file-upload/FileUploadComponent.tsx index 959ccd69..dedd6194 100644 --- a/frontend/lib/registry/components/v2-file-upload/FileUploadComponent.tsx +++ b/frontend/lib/registry/components/v2-file-upload/FileUploadComponent.tsx @@ -672,11 +672,11 @@ const FileUploadComponent: React.FC = ({ const fileData = response.files || (response as any).data || []; const chunkFiles = fileData.map((file: any) => ({ objid: file.objid || file.id, - savedFileName: file.saved_file_name || file.savedFileName, - realFileName: file.real_file_name || file.realFileName || file.name, - fileSize: file.file_size || file.fileSize || file.size, - fileExt: file.file_ext || file.fileExt || file.extension, - filePath: file.file_path || file.filePath || file.path, + savedFileName: file.saved_file_name, + realFileName: file.real_file_name || file.name, + fileSize: file.file_size || file.size, + fileExt: file.file_ext || file.extension, + filePath: file.file_path || file.path, docType: file.doc_type, docTypeName: file.doc_type_name, targetObjid: file.target_objid, diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx index aa293ce6..dcb4b23f 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx @@ -2012,8 +2012,8 @@ export const SplitPanelLayoutComponent: React.FC // 컬럼명을 라벨로 변환하는 함수 const getColumnLabel = useCallback( (columnName: string) => { - const column = rightTableColumns.find((col) => col.columnName === columnName || col.column_name === columnName); - return column?.columnLabel || column?.column_label || column?.displayName || columnName; + const column = rightTableColumns.find((col) => col.column_name === columnName); + return column?.column_label || column?.display_name || columnName; }, [rightTableColumns], ); @@ -2030,8 +2030,8 @@ export const SplitPanelLayoutComponent: React.FC const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`); if (response.data.success && response.data.data && response.data.data.length > 0) { return response.data.data.map((item: any) => ({ - value: item.valueCode, - label: item.valueLabel, + value: item.value_code, + label: item.value_label, })); } } catch { @@ -2203,8 +2203,8 @@ export const SplitPanelLayoutComponent: React.FC const columnsResponse = await tableTypeApi.getColumns(leftTableName); const labels: Record = {}; columnsResponse.forEach((col: any) => { - const columnName = col.columnName || col.column_name; - const label = col.columnLabel || col.column_label || col.displayName || columnName; + const columnName = col.column_name; + const label = col.column_label || col.display_name || columnName; if (columnName) { labels[columnName] = label; } @@ -2231,8 +2231,8 @@ export const SplitPanelLayoutComponent: React.FC // 우측 컬럼 라벨도 함께 로드 const labels: Record = {}; columnsResponse.forEach((col: any) => { - const columnName = col.columnName || col.column_name; - const label = col.columnLabel || col.column_label || col.displayName || columnName; + const columnName = col.column_name; + const label = col.column_label || col.display_name || columnName; if (columnName) { labels[columnName] = label; } @@ -2251,9 +2251,9 @@ export const SplitPanelLayoutComponent: React.FC try { const inputTypesResponse = await tableTypeApi.getColumnInputTypes(tbl); inputTypesResponse.forEach((col: any) => { - const colName = col.columnName || col.column_name; + const colName = col.column_name; if (colName) { - inputTypes[colName] = col.inputType || "text"; + inputTypes[colName] = col.input_type || "text"; } }); } catch { @@ -2293,10 +2293,10 @@ export const SplitPanelLayoutComponent: React.FC for (const tableName of tablesToLoad) { try { const columnsResponse = await tableTypeApi.getColumns(tableName); - const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category"); + const categoryColumns = columnsResponse.filter((col: any) => col.input_type === "category"); for (const col of categoryColumns) { - const columnName = col.columnName || col.column_name; + const columnName = col.column_name; try { const response = await apiClient.get( `/table-categories/${tableName}/${columnName}/values?includeInactive=true`, @@ -2305,8 +2305,8 @@ export const SplitPanelLayoutComponent: React.FC if (response.data.success && response.data.data) { const valueMap: Record = {}; response.data.data.forEach((item: any) => { - valueMap[item.value_code || item.valueCode] = { - label: item.value_label || item.valueLabel, + valueMap[item.value_code] = { + label: item.value_label, color: item.color, }; }); @@ -2379,11 +2379,11 @@ export const SplitPanelLayoutComponent: React.FC try { // 1. 컬럼 메타 정보 조회 const columnsResponse = await tableTypeApi.getColumns(tableName); - const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category"); + const categoryColumns = columnsResponse.filter((col: any) => col.input_type === "category"); // 2. 각 카테고리 컬럼에 대한 값 조회 for (const col of categoryColumns) { - const columnName = col.columnName || col.column_name; + const columnName = col.column_name; try { const response = await apiClient.get( `/table-categories/${tableName}/${columnName}/values?includeInactive=true`, @@ -2392,8 +2392,8 @@ export const SplitPanelLayoutComponent: React.FC if (response.data.success && response.data.data) { const valueMap: Record = {}; response.data.data.forEach((item: any) => { - valueMap[item.value_code || item.valueCode] = { - label: item.value_label || item.valueLabel, + valueMap[item.value_code] = { + label: item.value_label, color: item.color, }; }); diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutConfigPanel.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutConfigPanel.tsx index e2895037..517fd5be 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutConfigPanel.tsx @@ -312,15 +312,15 @@ const GroupByColumnsSelector: React.FC<{ ) : (
{columns.map((col) => ( -
+
toggleColumn(col.columnName)} + id={`groupby-${col.column_name}`} + checked={selectedColumns.includes(col.column_name)} + onCheckedChange={() => toggleColumn(col.column_name)} /> -
))} @@ -343,7 +343,7 @@ const ScreenSelector: React.FC<{ onChange: (screenId?: number) => void; }> = ({ value, onChange }) => { const [open, setOpen] = useState(false); - const [screens, setScreens] = useState>([]); + const [screens, setScreens] = useState>([]); const [loading, setLoading] = useState(false); useEffect(() => { @@ -353,7 +353,7 @@ const ScreenSelector: React.FC<{ const { screenApi } = await import("@/lib/api/screen"); const response = await screenApi.getScreens({ page: 1, size: 1000 }); setScreens( - response.data.map((s) => ({ screenId: s.screenId, screenName: s.screenName, screenCode: s.screenCode })), + response.data.map((s) => ({ screen_id: s.screen_id, screen_name: s.screen_name, screen_code: s.screen_code })), ); } catch (error) { console.error("화면 목록 로드 실패:", error); @@ -364,7 +364,7 @@ const ScreenSelector: React.FC<{ loadScreens(); }, []); - const selectedScreen = screens.find((s) => s.screenId === value); + const selectedScreen = screens.find((s) => s.screen_id === value); return ( @@ -376,7 +376,7 @@ const ScreenSelector: React.FC<{ className="h-8 w-full justify-between text-xs" disabled={loading} > - {loading ? "로딩 중..." : selectedScreen ? selectedScreen.screenName : "화면 선택"} + {loading ? "로딩 중..." : selectedScreen ? selectedScreen.screen_name : "화면 선택"} @@ -388,18 +388,18 @@ const ScreenSelector: React.FC<{ {screens.map((screen) => ( { - onChange(screen.screenId === value ? undefined : screen.screenId); + onChange(screen.screen_id === value ? undefined : screen.screen_id); setOpen(false); }} className="text-xs" > - +
- {screen.screenName} - {screen.screenCode} + {screen.screen_name} + {screen.screen_code}
))} @@ -546,16 +546,16 @@ const AdditionalTabConfigPanel: React.FC = ({ {availableRightTables.map((table) => ( updateTab({ tableName: table.tableName, columns: [] })} + key={table.table_name} + value={`${table.display_name || ""} ${table.table_name}`} + onSelect={() => updateTab({ tableName: table.table_name, columns: [] })} > - {table.displayName || table.tableName} - {table.displayName && ( - ({table.tableName}) + {table.display_name || table.table_name} + {table.display_name && ( + ({table.table_name}) )} ))} @@ -1645,23 +1645,23 @@ export const SplitPanelLayoutConfigPanel: React.FC ({ - tableName: col.tableName || tableName, - columnName: col.columnName || col.column_name, - columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name, - dataType: col.dataType || col.data_type || col.dbType, - webType: col.webType || col.web_type, - input_type: col.inputType || col.input_type, - widgetType: col.widgetType || col.widget_type || col.webType || col.web_type, - isNullable: col.isNullable || col.is_nullable, - required: col.required !== undefined ? col.required : col.isNullable === "NO" || col.is_nullable === "NO", - columnDefault: col.columnDefault || col.column_default, - characterMaximumLength: col.characterMaximumLength || col.character_maximum_length, + tableName: col.table_name || tableName, + columnName: col.column_name, + columnLabel: col.display_name || col.column_label || col.column_name, + dataType: col.data_type, + webType: col.web_type, + input_type: col.input_type, + widgetType: col.widget_type || col.web_type, + isNullable: col.is_nullable, + required: col.required !== undefined ? col.required : col.is_nullable === "NO", + columnDefault: col.column_default, + characterMaximumLength: col.character_maximum_length, isPrimaryKey: col.isPrimaryKey || false, // PK 여부 추가 - codeCategory: col.codeCategory || col.code_category, - codeValue: col.codeValue || col.code_value, - referenceTable: col.referenceTable || col.reference_table, // 🆕 참조 테이블 - referenceColumn: col.referenceColumn || col.reference_column, // 🆕 참조 컬럼 - displayColumn: col.displayColumn || col.display_column, // 🆕 표시 컬럼 + codeCategory: col.code_category, + codeValue: col.code_value, + referenceTable: col.reference_table, // 🆕 참조 테이블 + referenceColumn: col.reference_column, // 🆕 참조 컬럼 + displayColumn: col.display_column, // 🆕 표시 컬럼 })); console.log(`✅ 테이블 ${tableName} 컬럼 ${columns.length}개 로드됨:`, columns); @@ -1733,11 +1733,11 @@ export const SplitPanelLayoutConfigPanel: React.FC ({ - tableName: col.tableName || refTableName, - columnName: col.columnName || col.column_name, - columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name, - dataType: col.dataType || col.data_type || col.dbType, - input_type: col.inputType || col.input_type, + tableName: col.table_name || refTableName, + columnName: col.column_name, + columnLabel: col.display_name || col.column_label || col.column_name, + dataType: col.data_type, + input_type: col.input_type, })); referenceTableData.push({ tableName: refTableName, columns: refColumns }); @@ -2061,10 +2061,10 @@ export const SplitPanelLayoutConfigPanel: React.FC {config.leftPanel?.tableName - ? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName) - ?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName) - ?.displayName || + ? allTables.find((t) => t.table_name === config.leftPanel?.tableName) + ?.table_label || + allTables.find((t) => t.table_name === config.leftPanel?.tableName) + ?.display_name || config.leftPanel?.tableName : "테이블을 선택하세요"} @@ -2098,8 +2098,8 @@ export const SplitPanelLayoutConfigPanel: React.FC - {allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName || + {allTables.find((t) => t.table_name === screenTableName)?.table_label || + allTables.find((t) => t.table_name === screenTableName)?.display_name || screenTableName} @@ -2108,10 +2108,10 @@ export const SplitPanelLayoutConfigPanel: React.FC {allTables - .filter((t) => (t.tableName || t.table_name) !== screenTableName) + .filter((t) => t.table_name !== screenTableName) .map((table) => { - const tableName = table.tableName || table.table_name; - const displayName = table.tableLabel || table.displayName || tableName; + const tableName = table.table_name; + const displayName = table.table_label || table.display_name || tableName; return ( {availableRightTables.map((table) => ( { - updateRightPanel({ tableName: table.tableName }); + updateRightPanel({ tableName: table.table_name }); setRightTableOpen(false); }} > - {table.displayName || table.tableName} - {table.displayName && ( - ({table.tableName}) + {table.display_name || table.table_name} + {table.display_name && ( + ({table.table_name}) )} ))} diff --git a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/LeftPanelConfigTab.tsx b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/LeftPanelConfigTab.tsx index 289eb9bf..7af5117d 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/LeftPanelConfigTab.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/LeftPanelConfigTab.tsx @@ -75,8 +75,8 @@ export const LeftPanelConfigTab: React.FC = ({ {config.leftPanel?.tableName - ? allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === config.leftPanel?.tableName)?.displayName || + ? allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.table_label || + allTables.find((t) => t.table_name === config.leftPanel?.tableName)?.display_name || config.leftPanel?.tableName : "테이블을 선택하세요"} @@ -108,8 +108,8 @@ export const LeftPanelConfigTab: React.FC = ({ )} /> - {allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel || - allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName || + {allTables.find((t) => t.table_name === screenTableName)?.table_label || + allTables.find((t) => t.table_name === screenTableName)?.display_name || screenTableName}
diff --git a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/RightPanelConfigTab.tsx b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/RightPanelConfigTab.tsx index 417e1f97..7a838d38 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/RightPanelConfigTab.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/RightPanelConfigTab.tsx @@ -99,11 +99,11 @@ export const RightPanelConfigTab: React.FC = ({ 테이블을 찾을 수 없습니다. {availableRightTables.map((table) => { - const tableName = (table as any).tableName || (table as any).table_name; + const tableName = (table as any).table_name; return ( { updateRightPanel({ tableName }); setRightTableOpen(false); @@ -115,8 +115,8 @@ export const RightPanelConfigTab: React.FC = ({ config.rightPanel?.tableName === tableName ? "opacity-100" : "opacity-0", )} /> - {(table as any).displayName || tableName} - {(table as any).displayName && ({tableName})} + {(table as any).display_name || tableName} + {(table as any).display_name && ({tableName})} ); })} diff --git a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx index 5c0c1af0..2e3257ea 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/config-panels/SharedComponents.tsx @@ -201,7 +201,7 @@ export const ScreenSelector: React.FC<{ onChange: (screenId?: number) => void; }> = ({ value, onChange }) => { const [open, setOpen] = useState(false); - const [screens, setScreens] = useState>([]); + const [screens, setScreens] = useState>([]); const [loading, setLoading] = useState(false); useEffect(() => { @@ -211,7 +211,7 @@ export const ScreenSelector: React.FC<{ const { screenApi } = await import("@/lib/api/screen"); const response = await screenApi.getScreens({ page: 1, size: 1000 }); setScreens( - response.data.map((s) => ({ screenId: s.screen_id, screenName: s.screen_name, screenCode: s.screen_code })), + response.data.map((s) => ({ screen_id: s.screen_id, screen_name: s.screen_name, screen_code: s.screen_code })), ); } catch (error) { console.error("화면 목록 로드 실패:", error); @@ -222,7 +222,7 @@ export const ScreenSelector: React.FC<{ loadScreens(); }, []); - const selectedScreen = screens.find((s) => s.screenId === value); + const selectedScreen = screens.find((s) => s.screen_id === value); return ( @@ -234,7 +234,7 @@ export const ScreenSelector: React.FC<{ className="h-8 w-full justify-between text-xs" disabled={loading} > - {loading ? "로딩 중..." : selectedScreen ? selectedScreen.screenName : "화면 선택"} + {loading ? "로딩 중..." : selectedScreen ? selectedScreen.screen_name : "화면 선택"} @@ -246,18 +246,18 @@ export const ScreenSelector: React.FC<{ {screens.map((screen) => ( { - onChange(screen.screenId === value ? undefined : screen.screenId); + onChange(screen.screen_id === value ? undefined : screen.screen_id); setOpen(false); }} className="text-xs" > - +
- {screen.screenName} - {screen.screenCode} + {screen.screen_name} + {screen.screen_code}
))} diff --git a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx index b3c1c9e2..ebe3846a 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx @@ -1515,7 +1515,7 @@ export const TableListComponent: React.FC = ({ try { // 🔥 FIX: 캐시 키에 회사 코드 포함 (멀티테넌시 지원) const currentUser = JSON.parse(localStorage.getItem("currentUser") || "{}"); - const companyCode = currentUser.companyCode || "UNKNOWN"; + const companyCode = currentUser.company_code || "UNKNOWN"; const cacheKey = `columns_${tableConfig.selectedTable}_${companyCode}`; const cached = tableColumnCache.get(cacheKey); if (cached && Date.now() - cached.timestamp < TABLE_CACHE_TTL) { diff --git a/frontend/lib/registry/pop-components/pop-button.tsx b/frontend/lib/registry/pop-components/pop-button.tsx index c5fa646e..25147c0f 100644 --- a/frontend/lib/registry/pop-components/pop-button.tsx +++ b/frontend/lib/registry/pop-components/pop-button.tsx @@ -853,13 +853,13 @@ export function PopButtonComponent({ config, label, isDesignMode, screenId, comp action: "inbound-confirm", data: { items: selectedItems, - fieldValues, + field_values: fieldValues, }, mappings: { - cardList: cardListMapping, + card_list: cardListMapping, field: fieldMapping, }, - statusChanges: statusChangeRules, + status_changes: statusChangeRules, }); if (result.data?.success) { diff --git a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx index 8c3c6447..12fdb70b 100644 --- a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx +++ b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Component.tsx @@ -518,7 +518,7 @@ export function PopCardListV2Component({ const result = await apiClient.post("/pop/execute-action", { tasks, - data: { items: [{ ...row, id: targetId }], fieldValues: {} }, + data: { items: [{ ...row, id: targetId }], field_values: {} }, mappings: {}, }); if (result.data?.success) successCount++; @@ -1284,7 +1284,7 @@ function CardV2({ try { const result = await apiClient.post("/pop/execute-action", { tasks, - data: { items: [targetRow], fieldValues: {} }, + data: { items: [targetRow], field_values: {} }, mappings: {}, }); if (result.data?.success) { @@ -1546,7 +1546,7 @@ function CardV2({ : processId ? { ...actionRow, id: processId } : actionRow; const result = await apiClient.post("/pop/execute-action", { tasks, - data: { items: [targetRow], fieldValues: {} }, + data: { items: [targetRow], field_values: {} }, mappings: {}, }); if (result.data?.success) { diff --git a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Config.tsx b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Config.tsx index 9fc1339a..b9f996b4 100644 --- a/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Config.tsx +++ b/frontend/lib/registry/pop-components/pop-card-list-v2/PopCardListV2Config.tsx @@ -331,7 +331,7 @@ function TabData({ const ds = cfg.dataSource; const selectedDisplay = ds.tableName - ? tables.find((t) => t.tableName === ds.tableName)?.displayName || ds.tableName + ? tables.find((t) => t.table_name === ds.tableName)?.display_name || ds.tableName : ""; const toggleColumn = (colName: string) => { @@ -383,16 +383,16 @@ function TabData({ {tables.map((t) => ( { onTableChange(t.tableName); setTableOpen(false); }} + key={t.table_name} + value={`${t.table_name} ${t.display_name || ""}`} + onSelect={() => { onTableChange(t.table_name); setTableOpen(false); }} className="text-xs" > - +
- {t.displayName || t.tableName} - {t.displayName && t.displayName !== t.tableName && ( - {t.tableName} + {t.display_name || t.table_name} + {t.display_name && t.display_name !== t.table_name && ( + {t.table_name} )}
@@ -596,7 +596,7 @@ function JoinItemV2({ targetColumns.some((tc) => tc.name === mc.name && tc.type === mc.type) ); - const selectableTables = tables.filter((t) => t.tableName !== mainTableName); + const selectableTables = tables.filter((t) => t.table_name !== mainTableName); const hasJoinCondition = join.sourceColumn !== "" && join.targetColumn !== ""; const selectedTargetCols = join.selectedTargetColumns || []; const pickableTargetCols = targetColumns.filter((tc) => tc.name !== join.targetColumn); @@ -633,16 +633,16 @@ function JoinItemV2({ {selectableTables.map((t) => ( { - onUpdate({ targetTable: t.tableName, sourceColumn: "", targetColumn: "", selectedTargetColumns: [] }); + onUpdate({ targetTable: t.table_name, sourceColumn: "", targetColumn: "", selectedTargetColumns: [] }); setTableOpen(false); }} className="text-[10px]" > - - {t.tableName} + + {t.table_name} ))} @@ -1635,16 +1635,16 @@ function TimelineConfigEditor({ {tables.map((t) => ( { - updateSource({ processTable: t.tableName, foreignKey: "", seqColumn: "", nameColumn: "", statusColumn: "" }); + updateSource({ processTable: t.table_name, foreignKey: "", seqColumn: "", nameColumn: "", statusColumn: "" }); setTableOpen(false); }} className="text-[10px]" > - - {t.displayName || t.tableName} + + {t.display_name || t.table_name} ))} @@ -2726,15 +2726,15 @@ function DbTableCombobox({ const q = search.toLowerCase(); return tables.filter( (t) => - t.tableName.toLowerCase().includes(q) || - (t.tableComment || "").toLowerCase().includes(q), + t.table_name.toLowerCase().includes(q) || + (t.description || "").toLowerCase().includes(q), ); }, [tables, search]); const selectedLabel = useMemo(() => { if (!value) return "DB 테이블 검색..."; - const found = tables.find((t) => t.tableName === value); - return found ? `${found.tableName}${found.tableComment ? ` (${found.tableComment})` : ""}` : value; + const found = tables.find((t) => t.table_name === value); + return found ? `${found.table_name}${found.description ? ` (${found.description})` : ""}` : value; }, [value, tables]); return ( @@ -2769,19 +2769,19 @@ function DbTableCombobox({ {filtered.map((t) => ( { - onSelect(t.tableName); + onSelect(t.table_name); setOpen(false); setSearch(""); }} className="text-[10px]" > - - {t.tableName} - {t.tableComment && ( - ({t.tableComment}) + + {t.table_name} + {t.description && ( + ({t.description}) )} ))} diff --git a/frontend/lib/services/enhancedFormService.ts b/frontend/lib/services/enhancedFormService.ts index 78d2839b..ea6be5eb 100644 --- a/frontend/lib/services/enhancedFormService.ts +++ b/frontend/lib/services/enhancedFormService.ts @@ -385,8 +385,8 @@ export class EnhancedFormService { ): Promise<{ success: boolean; message?: string; data?: any }> { try { const result = await dynamicFormApi.saveData({ - screenId, - tableName, + screen_id: screenId, + table_name: tableName, data: formData, }); diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index 2f29b755..570d5333 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -667,13 +667,8 @@ export class ButtonActionExecutor { firstItem.location_name && firstItem.row_num !== undefined && firstItem.level_num !== undefined; - const isOldFormat = - firstItem.locationCode && - firstItem.locationName && - firstItem.rowNum !== undefined && - firstItem.levelNum !== undefined; - if (isNewFormat || isOldFormat) { + if (isNewFormat) { rackStructureLocations = value; rackStructureFieldKey = key; break; @@ -937,8 +932,8 @@ export class ButtonActionExecutor { delete dataWithMeta.id; const insertResult = await DynamicFormApi.saveFormData({ - screenId: context.screenId || 0, - tableName: repeaterTargetTable, + screen_id: context.screenId || 0, + table_name: repeaterTargetTable, data: dataWithMeta as Record, }); } else if (item.id && _existingRecord === true) { @@ -1075,7 +1070,7 @@ export class ButtonActionExecutor { // 전체 업데이트 (originalData 없이 id로 UPDATE 판단된 경우) saveResult = await DynamicFormApi.updateFormData(primaryKeyValue, { - tableName, + table_name: tableName, data: formData, }); } @@ -1533,8 +1528,8 @@ export class ButtonActionExecutor { saveResult = { success: true, message: "RepeaterFieldGroup/RepeatScreenModal/V2Repeater에서 처리" }; } else { saveResult = await DynamicFormApi.saveFormData({ - screenId, - tableName, + screen_id: screenId, + table_name: tableName, data: dataWithUserInfo, }); } @@ -2095,7 +2090,7 @@ export class ButtonActionExecutor { // 저장 전 중복 체크 const firstLocation = locations[0]; - const warehouseCode = firstLocation.warehouse_code || firstLocation.warehouse_id || firstLocation.warehouseCode; + const warehouseCode = firstLocation.warehouse_code || firstLocation.warehouse_id; const floor = firstLocation.floor; const zone = firstLocation.zone; @@ -2124,14 +2119,14 @@ export class ButtonActionExecutor { const existingSet = new Set(existingData.map((loc: any) => `${loc.row_num}-${loc.level_num}`)); const duplicates = locations.filter((loc) => { - const key = `${loc.row_num || loc.rowNum}-${loc.level_num || loc.levelNum}`; + const key = `${loc.row_num}-${loc.level_num}`; return existingSet.has(key); }); if (duplicates.length > 0) { const duplicateInfo = duplicates .slice(0, 5) - .map((d) => `${d.row_num || d.rowNum}열 ${d.level_num || d.levelNum}단`) + .map((d) => `${d.row_num}열 ${d.level_num}단`) .join(", "); const moreCount = duplicates.length > 5 ? ` 외 ${duplicates.length - 5}개` : ""; @@ -2153,17 +2148,17 @@ export class ButtonActionExecutor { // 새로운 형식(스네이크 케이스)과 기존 형식(카멜 케이스) 모두 지원 const record: Record = { // 렉 구조에서 생성된 필드 (이미 테이블 컬럼명과 동일) - location_code: loc.location_code || loc.locationCode, - location_name: loc.location_name || loc.locationName, - row_num: loc.row_num || String(loc.rowNum), - level_num: loc.level_num || String(loc.levelNum), + location_code: loc.location_code, + location_name: loc.location_name, + row_num: loc.row_num, + level_num: loc.level_num, // 창고 정보 (렉 구조 컴포넌트에서 전달) - DB 컬럼명은 warehouse_code - warehouse_code: loc.warehouse_code || loc.warehouse_id || loc.warehouseCode, - warehouse_name: loc.warehouse_name || loc.warehouseName, + warehouse_code: loc.warehouse_code || loc.warehouse_id, + warehouse_name: loc.warehouse_name, // 위치 정보 (렉 구조 컴포넌트에서 전달) floor: loc.floor, zone: loc.zone, - location_type: loc.location_type || loc.locationType, + location_type: loc.location_type, status: loc.status || "사용", // 사용자 정보 추가 writer: userId, @@ -2183,8 +2178,8 @@ export class ButtonActionExecutor { const record = recordsToInsert[i]; try { const result = await DynamicFormApi.saveFormData({ - screenId, - tableName, + screen_id: screenId, + table_name: tableName, data: record, }); @@ -2474,15 +2469,15 @@ export class ButtonActionExecutor { if (isMainUpdate) { mainSaveResult = await DynamicFormApi.updateFormData(existingMainId, { - tableName: tableName!, + table_name: tableName!, data: mainRowToSave, }); mainRecordId = existingMainId; } else { console.log("➕ [handleUniversalFormModalTableSectionSave] 메인 테이블 INSERT 실행"); mainSaveResult = await DynamicFormApi.saveFormData({ - screenId: screenId!, - tableName: tableName!, + screen_id: screenId!, + table_name: tableName!, data: mainRowToSave, }); mainRecordId = mainSaveResult.data?.id || null; @@ -2546,8 +2541,8 @@ export class ButtonActionExecutor { console.log("➕ [INSERT] 신규 품목:", { tableName: saveTableName, data: rowToSave }); const saveResult = await DynamicFormApi.saveFormData({ - screenId: screenId!, - tableName: saveTableName, + screen_id: screenId!, + table_name: saveTableName, data: rowToSave, }); @@ -2583,7 +2578,7 @@ export class ButtonActionExecutor { }); const updateResult = await DynamicFormApi.updateFormData(item.id, { - tableName: saveTableName, + table_name: saveTableName, data: rowToUpdate, }); @@ -2937,8 +2932,8 @@ export class ButtonActionExecutor { // INSERT 실행 const { DynamicFormApi } = await import("@/lib/api/dynamicForm"); const saveResult = await DynamicFormApi.saveFormData({ - screenId, - tableName, + screen_id: screenId, + table_name: tableName, data: dataWithUserInfo, }); @@ -4725,8 +4720,8 @@ export class ButtonActionExecutor { } const result = await DynamicFormApi.saveFormData({ - screenId: 0, // 임시값 - tableName: context.tableName, + screen_id: 0, // 임시값 + table_name: context.tableName, data: saveData, }); @@ -4817,7 +4812,7 @@ export class ButtonActionExecutor { } const result = await DynamicFormApi.updateFormData(updateId, { - tableName: context.tableName, + table_name: context.tableName, data: updateData, }); @@ -4955,8 +4950,8 @@ export class ButtonActionExecutor { const targetTable = action.fieldMappings?.[0]?.targetTable || action.targetTable || "test_project_info"; const formDataPayload = { - screenId: 0, // 제어 관리에서는 screenId가 없으므로 0 사용 - tableName: targetTable, // 필드 매핑에서 대상 테이블명 가져오기 + screen_id: 0, // 제어 관리에서는 screen_id가 없으므로 0 사용 + table_name: targetTable, // 필드 매핑에서 대상 테이블명 가져오기 data: insertData, }; @@ -5445,8 +5440,8 @@ export class ButtonActionExecutor { // valueCode → valueLabel 매핑 categoryMap[columnName] = {}; valuesResponse.data.forEach((catValue: any) => { - const code = catValue.valueCode || catValue.category_value_id; - const label = catValue.valueLabel || catValue.label || code; + const code = catValue.value_code || catValue.category_value_id; + const label = catValue.value_label || catValue.label || code; if (code) { categoryMap[columnName][code] = label; }