From 52f25030a457e6113895abe79f5bd4c70de1ba65 Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 9 Mar 2026 23:25:17 +0900 Subject: [PATCH] feat: Implement automatic master data loading for detail rows in EditModal - Added a new function `loadMasterDataForDetailRow` to automatically fetch master table data based on foreign key relationships when editing detail rows. - Integrated this functionality to merge master data into the form data after it is set, enhancing the user experience by ensuring relevant data is pre-filled. - Included error handling and logging for the data loading process to improve debugging and user feedback. These changes significantly enhance the EditModal by streamlining the data entry process and reducing manual input errors. --- frontend/components/screen/EditModal.tsx | 113 ++++++++++++++++++++++- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/frontend/components/screen/EditModal.tsx b/frontend/components/screen/EditModal.tsx index ec36096d..0127af40 100644 --- a/frontend/components/screen/EditModal.tsx +++ b/frontend/components/screen/EditModal.tsx @@ -18,7 +18,7 @@ import { useAuth } from "@/hooks/useAuth"; import { ConditionalZone, LayerDefinition } from "@/types/screen-management"; import { convertV2ToLegacy, isValidV2Layout } from "@/lib/utils/layoutV2Converter"; import { ScreenContextProvider } from "@/contexts/ScreenContext"; - +import { entityJoinApi } from "@/lib/api/entityJoin"; interface EditModalState { isOpen: boolean; screenId: number | null; @@ -244,6 +244,92 @@ export const EditModal: React.FC = ({ className }) => { } }; + /** + * 디테일 행의 FK를 통해 마스터 테이블 데이터를 자동 조회 + * - entity join 설정에서 FK 관계 탐지 + * - FK 값으로 마스터 테이블 전체 row 조회 + * - editData에 없는 필드만 병합 (디테일 데이터를 덮어쓰지 않음) + */ + const loadMasterDataForDetailRow = async ( + editData: Record, + targetScreenId: number, + eventTableName?: string, + ): Promise> => { + try { + let detailTableName = eventTableName; + if (!detailTableName) { + const screenInfo = await screenApi.getScreen(targetScreenId); + detailTableName = screenInfo?.tableName; + } + + if (!detailTableName) { + console.log("[EditModal:MasterLoad] 테이블명을 알 수 없음 - 스킵"); + return {}; + } + + console.log("[EditModal:MasterLoad] 시작:", { detailTableName, editDataKeys: Object.keys(editData) }); + + const entityJoinRes = await entityJoinApi.getEntityJoinConfigs(detailTableName); + const joinConfigs = entityJoinRes?.joinConfigs || []; + + if (joinConfigs.length === 0) { + console.log("[EditModal:MasterLoad] entity join 없음 - 스킵"); + return {}; + } + + console.log("[EditModal:MasterLoad] entity join:", joinConfigs.map((c) => `${c.sourceColumn} → ${c.referenceTable}`)); + + const masterDataResult: Record = {}; + const processedTables = new Set(); + const { apiClient } = await import("@/lib/api/client"); + + for (const joinConfig of joinConfigs) { + const { sourceColumn, referenceTable, referenceColumn } = joinConfig; + if (processedTables.has(referenceTable)) continue; + + const fkValue = editData[sourceColumn]; + if (!fkValue) continue; + + try { + const response = await apiClient.post( + `/table-management/tables/${referenceTable}/data`, + { + search: { [referenceColumn || "id"]: fkValue }, + size: 1, + page: 1, + autoFilter: true, + }, + ); + + const rows = response.data?.data?.data || response.data?.data?.rows || []; + if (rows.length > 0) { + const masterRow = rows[0]; + for (const [col, val] of Object.entries(masterRow)) { + if (val !== undefined && val !== null && editData[col] === undefined) { + masterDataResult[col] = val; + } + } + console.log("[EditModal:MasterLoad] 조회 성공:", { + table: referenceTable, + fk: `${sourceColumn}=${fkValue}`, + loadedFields: Object.keys(masterDataResult), + }); + } + } catch (queryError) { + console.warn("[EditModal:MasterLoad] 조회 실패:", referenceTable, queryError); + } + + processedTables.add(referenceTable); + } + + console.log("[EditModal:MasterLoad] 최종 결과:", Object.keys(masterDataResult)); + return masterDataResult; + } catch (error) { + console.warn("[EditModal:MasterLoad] 전체 오류:", error); + return {}; + } + }; + // 전역 모달 이벤트 리스너 useEffect(() => { const handleOpenEditModal = async (event: CustomEvent) => { @@ -294,6 +380,8 @@ export const EditModal: React.FC = ({ className }) => { } }); } + + // editData로 formData를 즉시 세팅 (채번 컴포넌트가 빈 formData로 마운트되어 새 번호 생성하는 것 방지) setFormData(enriched); // originalData: changedData 계산(PATCH)에만 사용 // INSERT/UPDATE 판단에는 사용하지 않음 @@ -302,6 +390,21 @@ export const EditModal: React.FC = ({ className }) => { // isCreateMode=true(복사/등록) → INSERT, false/undefined(수정) → UPDATE setIsCreateModeFlag(!!isCreateMode); + // 마스터 데이터 자동 조회 (수정 모드일 때, formData 세팅 이후 비동기로 병합) + // 디테일 행 선택 시 마스터 테이블의 컬럼 데이터를 자동으로 가져와서 추가 + if (!isCreateMode && editData && screenId) { + loadMasterDataForDetailRow(editData, screenId, tableName) + .then((masterData) => { + if (Object.keys(masterData).length > 0) { + setFormData((prev) => ({ ...prev, ...masterData })); + console.log("[EditModal] 마스터 데이터 비동기 병합 완료:", Object.keys(masterData)); + } + }) + .catch((masterError) => { + console.warn("[EditModal] 마스터 데이터 자동 조회 중 오류 (무시):", masterError); + }); + } + console.log("[EditModal] 모달 열림:", { mode: isCreateMode ? "INSERT (생성/복사)" : "UPDATE (수정)", hasEditData: !!editData, @@ -1529,7 +1632,10 @@ export const EditModal: React.FC = ({ className }) => { const shouldUseEditModalSave = !hasTableSectionData && (groupData.length > 0 || !hasUniversalFormModal); const enrichedFormData = { - ...(groupData.length > 0 ? groupData[0] : formData), + // 마스터 데이터(formData)를 기본으로 깔고, groupData[0]으로 덮어쓰기 + // → 디테일 행 수정 시에도 마스터 폼 필드가 표시됨 + ...formData, + ...(groupData.length > 0 ? groupData[0] : {}), tableName: screenData.screenInfo?.tableName, screenId: modalState.screenId, }; @@ -1589,7 +1695,8 @@ export const EditModal: React.FC = ({ className }) => { }; const enrichedFormData = { - ...(groupData.length > 0 ? groupData[0] : formData), + ...formData, + ...(groupData.length > 0 ? groupData[0] : {}), tableName: screenData.screenInfo?.tableName, screenId: modalState.screenId, }; -- 2.43.0