refactor: 코드 정리 및 가독성 향상
- numberingRuleController.ts에서 API 엔드포인트의 코드 스타일을 일관되게 정리하여 가독성을 높였습니다. - 불필요한 줄바꿈을 제거하고, 코드 블록을 명확하게 정리하여 유지보수성을 개선했습니다. - tableManagementService.ts와 ButtonConfigPanel.tsx에서 코드 정리를 통해 일관성을 유지하고, 가독성을 향상시켰습니다. - 전반적으로 코드의 깔끔함을 유지하고, 향후 개발 시 이해하기 쉽게 개선했습니다.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* 렌더링 모드:
|
||||
* - inline: 현재 테이블 컬럼 직접 입력
|
||||
* - modal: 엔티티 선택 (FK 저장) + 추가 입력 컬럼
|
||||
*
|
||||
*
|
||||
* RepeaterTable 및 ItemSelectionModal 재사용
|
||||
*/
|
||||
|
||||
@@ -63,7 +63,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
|
||||
// 🆕 데이터 변경 시 자동으로 컬럼 너비 조정 트리거
|
||||
const [autoWidthTrigger, setAutoWidthTrigger] = useState(0);
|
||||
|
||||
|
||||
// 소스 테이블 컬럼 라벨 매핑
|
||||
const [sourceColumnLabels, setSourceColumnLabels] = useState<Record<string, string>>({});
|
||||
|
||||
@@ -72,10 +72,10 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
|
||||
// 🆕 카테고리 코드 → 라벨 매핑 (RepeaterTable 표시용)
|
||||
const [categoryLabelMap, setCategoryLabelMap] = useState<Record<string, string>>({});
|
||||
|
||||
|
||||
// 현재 테이블 컬럼 정보 (inputType 매핑용)
|
||||
const [currentTableColumnInfo, setCurrentTableColumnInfo] = useState<Record<string, any>>({});
|
||||
|
||||
|
||||
// 동적 데이터 소스 상태
|
||||
const [activeDataSources, setActiveDataSources] = useState<Record<string, string>>({});
|
||||
|
||||
@@ -88,10 +88,9 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 전역 리피터 등록
|
||||
// 🆕 useCustomTable이 설정된 경우 mainTableName 사용 (실제 저장될 테이블)
|
||||
useEffect(() => {
|
||||
const targetTableName = config.useCustomTable && config.mainTableName
|
||||
? config.mainTableName
|
||||
: config.dataSource?.tableName;
|
||||
|
||||
const targetTableName =
|
||||
config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
|
||||
|
||||
if (targetTableName) {
|
||||
if (!window.__v2RepeaterInstances) {
|
||||
window.__v2RepeaterInstances = new Set();
|
||||
@@ -110,22 +109,21 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
useEffect(() => {
|
||||
const handleSaveEvent = async (event: CustomEvent) => {
|
||||
// 🆕 mainTableName이 설정된 경우 우선 사용, 없으면 dataSource.tableName 사용
|
||||
const tableName = config.useCustomTable && config.mainTableName
|
||||
? config.mainTableName
|
||||
: config.dataSource?.tableName;
|
||||
const tableName =
|
||||
config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
|
||||
const eventParentId = event.detail?.parentId;
|
||||
const mainFormData = event.detail?.mainFormData;
|
||||
|
||||
|
||||
// 🆕 마스터 테이블에서 생성된 ID (FK 연결용)
|
||||
const masterRecordId = event.detail?.masterRecordId || mainFormData?.id;
|
||||
|
||||
|
||||
if (!tableName || data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// V2Repeater 저장 시작
|
||||
const saveInfo = {
|
||||
tableName,
|
||||
const saveInfo = {
|
||||
tableName,
|
||||
useCustomTable: config.useCustomTable,
|
||||
mainTableName: config.mainTableName,
|
||||
foreignKeyColumn: config.foreignKeyColumn,
|
||||
@@ -145,10 +143,10 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} catch {
|
||||
console.warn("테이블 컬럼 정보 조회 실패");
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const row = data[i];
|
||||
|
||||
|
||||
// 내부 필드 제거
|
||||
const cleanRow = Object.fromEntries(Object.entries(row).filter(([key]) => !key.startsWith("_")));
|
||||
|
||||
@@ -157,14 +155,14 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
if (config.useCustomTable && config.mainTableName) {
|
||||
// 커스텀 테이블: 리피터 데이터만 저장
|
||||
mergedData = { ...cleanRow };
|
||||
|
||||
|
||||
// 🆕 FK 자동 연결 - foreignKeySourceColumn이 설정된 경우 해당 컬럼 값 사용
|
||||
if (config.foreignKeyColumn) {
|
||||
// foreignKeySourceColumn이 있으면 mainFormData에서 해당 컬럼 값 사용
|
||||
// 없으면 마스터 레코드 ID 사용 (기존 동작)
|
||||
const sourceColumn = config.foreignKeySourceColumn;
|
||||
let fkValue: any;
|
||||
|
||||
|
||||
if (sourceColumn && mainFormData && mainFormData[sourceColumn] !== undefined) {
|
||||
// mainFormData에서 참조 컬럼 값 가져오기
|
||||
fkValue = mainFormData[sourceColumn];
|
||||
@@ -172,18 +170,18 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 기본: 마스터 레코드 ID 사용
|
||||
fkValue = masterRecordId;
|
||||
}
|
||||
|
||||
|
||||
if (fkValue !== undefined && fkValue !== null) {
|
||||
mergedData[config.foreignKeyColumn] = fkValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 기존 방식: 메인 폼 데이터 병합
|
||||
const { id: _mainId, ...mainFormDataWithoutId } = mainFormData || {};
|
||||
const { id: _mainId, ...mainFormDataWithoutId } = mainFormData || {};
|
||||
mergedData = {
|
||||
...mainFormDataWithoutId,
|
||||
...cleanRow,
|
||||
};
|
||||
...mainFormDataWithoutId,
|
||||
...cleanRow,
|
||||
};
|
||||
}
|
||||
|
||||
// 유효하지 않은 컬럼 제거
|
||||
@@ -193,10 +191,9 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
filteredData[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await apiClient.post(`/table-management/tables/${tableName}/add`, filteredData);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("❌ V2Repeater 저장 실패:", error);
|
||||
throw error;
|
||||
@@ -207,14 +204,13 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
const unsubscribe = v2EventBus.subscribe(
|
||||
V2_EVENTS.REPEATER_SAVE,
|
||||
async (payload) => {
|
||||
const tableName = config.useCustomTable && config.mainTableName
|
||||
? config.mainTableName
|
||||
: config.dataSource?.tableName;
|
||||
const tableName =
|
||||
config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
|
||||
if (payload.tableName === tableName) {
|
||||
await handleSaveEvent({ detail: payload } as CustomEvent);
|
||||
}
|
||||
},
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` }
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` },
|
||||
);
|
||||
|
||||
// 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
|
||||
@@ -223,7 +219,14 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
unsubscribe();
|
||||
window.removeEventListener("repeaterSave" as any, handleSaveEvent);
|
||||
};
|
||||
}, [data, config.dataSource?.tableName, config.useCustomTable, config.mainTableName, config.foreignKeyColumn, parentId]);
|
||||
}, [
|
||||
data,
|
||||
config.dataSource?.tableName,
|
||||
config.useCustomTable,
|
||||
config.mainTableName,
|
||||
config.foreignKeyColumn,
|
||||
parentId,
|
||||
]);
|
||||
|
||||
// 현재 테이블 컬럼 정보 로드
|
||||
useEffect(() => {
|
||||
@@ -234,7 +237,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${tableName}/columns`);
|
||||
const columns = response.data?.data?.columns || response.data?.columns || response.data || [];
|
||||
|
||||
|
||||
const columnMap: Record<string, any> = {};
|
||||
columns.forEach((col: any) => {
|
||||
const name = col.columnName || col.column_name || col.name;
|
||||
@@ -320,7 +323,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
try {
|
||||
const response = await apiClient.get(`/table-management/tables/${resolvedSourceTable}/columns`);
|
||||
const columns = response.data?.data?.columns || response.data?.columns || response.data || [];
|
||||
|
||||
|
||||
const labels: Record<string, string> = {};
|
||||
const categoryCols: string[] = [];
|
||||
|
||||
@@ -364,13 +367,13 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
calculated: true,
|
||||
width: col.width === "auto" ? undefined : col.width,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 일반 입력 컬럼
|
||||
let type: "text" | "number" | "date" | "select" | "category" = "text";
|
||||
if (inputType === "number" || inputType === "decimal") type = "number";
|
||||
else if (inputType === "date" || inputType === "datetime") type = "date";
|
||||
else if (inputType === "code") type = "select";
|
||||
if (inputType === "number" || inputType === "decimal") type = "number";
|
||||
else if (inputType === "date" || inputType === "datetime") type = "date";
|
||||
else if (inputType === "code") type = "select";
|
||||
else if (inputType === "category") type = "category"; // 🆕 카테고리 타입
|
||||
|
||||
// 🆕 카테고리 참조 ID 가져오기 (tableName.columnName 형식)
|
||||
@@ -383,19 +386,19 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
categoryRef = `${tableName}.${col.key}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
field: col.key,
|
||||
label: col.title || colInfo?.displayName || col.key,
|
||||
type,
|
||||
editable: col.editable !== false,
|
||||
width: col.width === "auto" ? undefined : col.width,
|
||||
required: false,
|
||||
|
||||
return {
|
||||
field: col.key,
|
||||
label: col.title || colInfo?.displayName || col.key,
|
||||
type,
|
||||
editable: col.editable !== false,
|
||||
width: col.width === "auto" ? undefined : col.width,
|
||||
required: false,
|
||||
categoryRef, // 🆕 카테고리 참조 ID 전달
|
||||
hidden: col.hidden, // 🆕 히든 처리
|
||||
autoFill: col.autoFill, // 🆕 자동 입력 설정
|
||||
};
|
||||
});
|
||||
};
|
||||
});
|
||||
}, [config.columns, sourceColumnLabels, currentTableColumnInfo, resolvedSourceTable, config.dataSource?.tableName]);
|
||||
|
||||
// 🆕 데이터 변경 시 카테고리 라벨 로드 (RepeaterTable 표시용)
|
||||
@@ -451,26 +454,25 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 데이터 변경 핸들러
|
||||
const handleDataChange = useCallback(
|
||||
(newData: any[]) => {
|
||||
setData(newData);
|
||||
|
||||
// 🆕 _targetTable 메타데이터 포함하여 전달 (백엔드에서 테이블 분리용)
|
||||
if (onDataChange) {
|
||||
const targetTable = config.useCustomTable && config.mainTableName
|
||||
? config.mainTableName
|
||||
: config.dataSource?.tableName;
|
||||
|
||||
if (targetTable) {
|
||||
// 각 행에 _targetTable 추가
|
||||
const dataWithTarget = newData.map(row => ({
|
||||
...row,
|
||||
_targetTable: targetTable,
|
||||
}));
|
||||
onDataChange(dataWithTarget);
|
||||
} else {
|
||||
onDataChange(newData);
|
||||
setData(newData);
|
||||
|
||||
// 🆕 _targetTable 메타데이터 포함하여 전달 (백엔드에서 테이블 분리용)
|
||||
if (onDataChange) {
|
||||
const targetTable =
|
||||
config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
|
||||
|
||||
if (targetTable) {
|
||||
// 각 행에 _targetTable 추가
|
||||
const dataWithTarget = newData.map((row) => ({
|
||||
...row,
|
||||
_targetTable: targetTable,
|
||||
}));
|
||||
onDataChange(dataWithTarget);
|
||||
} else {
|
||||
onDataChange(newData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🆕 데이터 변경 시 자동으로 컬럼 너비 조정
|
||||
setAutoWidthTrigger((prev) => prev + 1);
|
||||
},
|
||||
@@ -480,26 +482,25 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 행 변경 핸들러
|
||||
const handleRowChange = useCallback(
|
||||
(index: number, newRow: any) => {
|
||||
const newData = [...data];
|
||||
newData[index] = newRow;
|
||||
setData(newData);
|
||||
|
||||
// 🆕 _targetTable 메타데이터 포함
|
||||
if (onDataChange) {
|
||||
const targetTable = config.useCustomTable && config.mainTableName
|
||||
? config.mainTableName
|
||||
: config.dataSource?.tableName;
|
||||
|
||||
if (targetTable) {
|
||||
const dataWithTarget = newData.map(row => ({
|
||||
...row,
|
||||
_targetTable: targetTable,
|
||||
}));
|
||||
onDataChange(dataWithTarget);
|
||||
} else {
|
||||
onDataChange(newData);
|
||||
const newData = [...data];
|
||||
newData[index] = newRow;
|
||||
setData(newData);
|
||||
|
||||
// 🆕 _targetTable 메타데이터 포함
|
||||
if (onDataChange) {
|
||||
const targetTable =
|
||||
config.useCustomTable && config.mainTableName ? config.mainTableName : config.dataSource?.tableName;
|
||||
|
||||
if (targetTable) {
|
||||
const dataWithTarget = newData.map((row) => ({
|
||||
...row,
|
||||
_targetTable: targetTable,
|
||||
}));
|
||||
onDataChange(dataWithTarget);
|
||||
} else {
|
||||
onDataChange(newData);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[data, onDataChange, config.useCustomTable, config.mainTableName, config.dataSource?.tableName],
|
||||
);
|
||||
@@ -507,16 +508,16 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 행 삭제 핸들러
|
||||
const handleRowDelete = useCallback(
|
||||
(index: number) => {
|
||||
const newData = data.filter((_, i) => i !== index);
|
||||
const newData = data.filter((_, i) => i !== index);
|
||||
handleDataChange(newData); // 🆕 handleDataChange 사용
|
||||
|
||||
// 선택 상태 업데이트
|
||||
const newSelected = new Set<number>();
|
||||
selectedRows.forEach((i) => {
|
||||
if (i < index) newSelected.add(i);
|
||||
else if (i > index) newSelected.add(i - 1);
|
||||
});
|
||||
setSelectedRows(newSelected);
|
||||
|
||||
// 선택 상태 업데이트
|
||||
const newSelected = new Set<number>();
|
||||
selectedRows.forEach((i) => {
|
||||
if (i < index) newSelected.add(i);
|
||||
else if (i > index) newSelected.add(i - 1);
|
||||
});
|
||||
setSelectedRows(newSelected);
|
||||
},
|
||||
[data, selectedRows, handleDataChange],
|
||||
);
|
||||
@@ -535,30 +536,30 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
if (!col.autoFill || col.autoFill.type === "none") return undefined;
|
||||
|
||||
const now = new Date();
|
||||
|
||||
|
||||
switch (col.autoFill.type) {
|
||||
case "currentDate":
|
||||
return now.toISOString().split("T")[0]; // YYYY-MM-DD
|
||||
|
||||
|
||||
case "currentDateTime":
|
||||
return now.toISOString().slice(0, 19).replace("T", " "); // YYYY-MM-DD HH:mm:ss
|
||||
|
||||
|
||||
case "sequence":
|
||||
return rowIndex + 1; // 1부터 시작하는 순번
|
||||
|
||||
|
||||
case "numbering":
|
||||
// 채번은 별도 비동기 처리 필요
|
||||
return null; // null 반환하여 비동기 처리 필요함을 표시
|
||||
|
||||
|
||||
case "fromMainForm":
|
||||
if (col.autoFill.sourceField && mainFormData) {
|
||||
return mainFormData[col.autoFill.sourceField];
|
||||
}
|
||||
return "";
|
||||
|
||||
|
||||
case "fixed":
|
||||
return col.autoFill.fixedValue ?? "";
|
||||
|
||||
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -568,19 +569,22 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
|
||||
// 🆕 채번 API 호출 (비동기)
|
||||
// 🆕 수동 입력 부분 지원을 위해 userInputCode 파라미터 추가
|
||||
const generateNumberingCode = useCallback(async (ruleId: string, userInputCode?: string, formData?: Record<string, any>): Promise<string> => {
|
||||
try {
|
||||
const result = await allocateNumberingCode(ruleId, userInputCode, formData);
|
||||
if (result.success && result.data?.generatedCode) {
|
||||
return result.data.generatedCode;
|
||||
const generateNumberingCode = useCallback(
|
||||
async (ruleId: string, userInputCode?: string, formData?: Record<string, any>): Promise<string> => {
|
||||
try {
|
||||
const result = await allocateNumberingCode(ruleId, userInputCode, formData);
|
||||
if (result.success && result.data?.generatedCode) {
|
||||
return result.data.generatedCode;
|
||||
}
|
||||
console.error("채번 실패:", result.error);
|
||||
return "";
|
||||
} catch (error) {
|
||||
console.error("채번 API 호출 실패:", error);
|
||||
return "";
|
||||
}
|
||||
console.error("채번 실패:", result.error);
|
||||
return "";
|
||||
} catch (error) {
|
||||
console.error("채번 API 호출 실패:", error);
|
||||
return "";
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
// 🆕 행 추가 (inline 모드 또는 모달 열기) - 비동기로 변경
|
||||
const handleAddRow = useCallback(async () => {
|
||||
@@ -589,7 +593,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} else {
|
||||
const newRow: any = { _id: `new_${Date.now()}` };
|
||||
const currentRowCount = data.length;
|
||||
|
||||
|
||||
// 먼저 동기적 자동 입력 값 적용
|
||||
for (const col of config.columns) {
|
||||
const autoValue = generateAutoFillValueSync(col, currentRowCount);
|
||||
@@ -599,10 +603,10 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} else if (autoValue !== undefined) {
|
||||
newRow[col.key] = autoValue;
|
||||
} else {
|
||||
newRow[col.key] = "";
|
||||
newRow[col.key] = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const newData = [...data, newRow];
|
||||
handleDataChange(newData);
|
||||
}
|
||||
@@ -611,23 +615,23 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 모달에서 항목 선택 - 비동기로 변경
|
||||
const handleSelectItems = useCallback(
|
||||
async (items: Record<string, unknown>[]) => {
|
||||
const fkColumn = config.dataSource?.foreignKey;
|
||||
const fkColumn = config.dataSource?.foreignKey;
|
||||
const currentRowCount = data.length;
|
||||
|
||||
// 채번이 필요한 컬럼 찾기
|
||||
const numberingColumns = config.columns.filter(
|
||||
(col) => !col.isSourceDisplay && col.autoFill?.type === "numbering" && col.autoFill.numberingRuleId
|
||||
(col) => !col.isSourceDisplay && col.autoFill?.type === "numbering" && col.autoFill.numberingRuleId,
|
||||
);
|
||||
|
||||
|
||||
const newRows = await Promise.all(
|
||||
items.map(async (item, index) => {
|
||||
const row: any = { _id: `new_${Date.now()}_${Math.random()}` };
|
||||
|
||||
const row: any = { _id: `new_${Date.now()}_${Math.random()}` };
|
||||
|
||||
// FK 값 저장 (resolvedReferenceKey 사용)
|
||||
if (fkColumn && item[resolvedReferenceKey]) {
|
||||
row[fkColumn] = item[resolvedReferenceKey];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 모든 컬럼 처리 (순서대로)
|
||||
for (const col of config.columns) {
|
||||
if (col.isSourceDisplay) {
|
||||
@@ -643,20 +647,28 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
row[col.key] = autoValue;
|
||||
} else if (row[col.key] === undefined) {
|
||||
// 입력 컬럼: 빈 값으로 초기화
|
||||
row[col.key] = "";
|
||||
}
|
||||
row[col.key] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
})
|
||||
|
||||
return row;
|
||||
}),
|
||||
);
|
||||
|
||||
const newData = [...data, ...newRows];
|
||||
|
||||
const newData = [...data, ...newRows];
|
||||
handleDataChange(newData);
|
||||
setModalOpen(false);
|
||||
setModalOpen(false);
|
||||
},
|
||||
[config.dataSource?.foreignKey, resolvedReferenceKey, config.columns, data, handleDataChange, generateAutoFillValueSync, generateNumberingCode],
|
||||
[
|
||||
config.dataSource?.foreignKey,
|
||||
resolvedReferenceKey,
|
||||
config.columns,
|
||||
data,
|
||||
handleDataChange,
|
||||
generateAutoFillValueSync,
|
||||
generateNumberingCode,
|
||||
],
|
||||
);
|
||||
|
||||
// 소스 컬럼 목록 (모달용) - 🆕 columns 배열에서 isSourceDisplay인 것만 필터링
|
||||
@@ -670,19 +682,19 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 🆕 beforeFormSave 이벤트에서 채번 placeholder를 실제 값으로 변환
|
||||
const dataRef = useRef(data);
|
||||
dataRef.current = data;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleBeforeFormSave = async (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const formData = customEvent.detail?.formData;
|
||||
|
||||
|
||||
if (!formData || !dataRef.current.length) return;
|
||||
|
||||
// 채번 placeholder가 있는 행들을 찾아서 실제 값으로 변환
|
||||
const processedData = await Promise.all(
|
||||
dataRef.current.map(async (row) => {
|
||||
const newRow = { ...row };
|
||||
|
||||
|
||||
for (const key of Object.keys(newRow)) {
|
||||
const value = newRow[key];
|
||||
if (typeof value === "string" && value.startsWith("__NUMBERING_RULE__")) {
|
||||
@@ -706,16 +718,16 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return newRow;
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
// 처리된 데이터를 formData에 추가
|
||||
const fieldName = config.fieldName || "repeaterData";
|
||||
formData[fieldName] = processedData;
|
||||
};
|
||||
|
||||
|
||||
// V2 EventBus 구독
|
||||
const unsubscribe = v2EventBus.subscribe(
|
||||
V2_EVENTS.FORM_SAVE_COLLECT,
|
||||
@@ -726,12 +738,12 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} as CustomEvent;
|
||||
await handleBeforeFormSave(fakeEvent);
|
||||
},
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` }
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` },
|
||||
);
|
||||
|
||||
// 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
|
||||
window.addEventListener("beforeFormSave", handleBeforeFormSave);
|
||||
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
window.removeEventListener("beforeFormSave", handleBeforeFormSave);
|
||||
@@ -744,20 +756,20 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
const handleComponentDataTransfer = async (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const { targetComponentId, data: transferData, mappingRules, mode } = customEvent.detail || {};
|
||||
|
||||
|
||||
// 이 컴포넌트가 대상인지 확인
|
||||
if (targetComponentId !== parentId && targetComponentId !== config.fieldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!transferData || transferData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 데이터 매핑 처리
|
||||
const mappedData = transferData.map((item: any, index: number) => {
|
||||
const newRow: any = { _id: `transfer_${Date.now()}_${index}` };
|
||||
|
||||
|
||||
if (mappingRules && mappingRules.length > 0) {
|
||||
// 매핑 규칙이 있으면 적용
|
||||
mappingRules.forEach((rule: any) => {
|
||||
@@ -767,10 +779,10 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
// 매핑 규칙 없으면 그대로 복사
|
||||
Object.assign(newRow, item);
|
||||
}
|
||||
|
||||
|
||||
return newRow;
|
||||
});
|
||||
|
||||
|
||||
// mode에 따라 데이터 처리
|
||||
if (mode === "replace") {
|
||||
handleDataChange(mappedData);
|
||||
@@ -784,20 +796,20 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
handleDataChange([...data, ...mappedData]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// splitPanelDataTransfer: 분할 패널에서 전역 이벤트로 전달
|
||||
const handleSplitPanelDataTransfer = async (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const { data: transferData, mappingRules, mode, sourcePosition } = customEvent.detail || {};
|
||||
|
||||
|
||||
if (!transferData || transferData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 데이터 매핑 처리
|
||||
const mappedData = transferData.map((item: any, index: number) => {
|
||||
const newRow: any = { _id: `transfer_${Date.now()}_${index}` };
|
||||
|
||||
|
||||
if (mappingRules && mappingRules.length > 0) {
|
||||
mappingRules.forEach((rule: any) => {
|
||||
newRow[rule.targetField] = item[rule.sourceField];
|
||||
@@ -805,10 +817,10 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} else {
|
||||
Object.assign(newRow, item);
|
||||
}
|
||||
|
||||
|
||||
return newRow;
|
||||
});
|
||||
|
||||
|
||||
// mode에 따라 데이터 처리
|
||||
if (mode === "replace") {
|
||||
handleDataChange(mappedData);
|
||||
@@ -816,7 +828,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
handleDataChange([...data, ...mappedData]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// V2 EventBus 구독
|
||||
const unsubscribeComponent = v2EventBus.subscribe(
|
||||
V2_EVENTS.COMPONENT_DATA_TRANSFER,
|
||||
@@ -831,7 +843,7 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} as CustomEvent;
|
||||
handleComponentDataTransfer(fakeEvent);
|
||||
},
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` }
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` },
|
||||
);
|
||||
|
||||
const unsubscribeSplitPanel = v2EventBus.subscribe(
|
||||
@@ -846,13 +858,13 @@ export const V2Repeater: React.FC<V2RepeaterProps> = ({
|
||||
} as CustomEvent;
|
||||
handleSplitPanelDataTransfer(fakeEvent);
|
||||
},
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` }
|
||||
{ componentId: `v2-repeater-${config.dataSource?.tableName}` },
|
||||
);
|
||||
|
||||
// 레거시 이벤트도 계속 지원 (점진적 마이그레이션)
|
||||
window.addEventListener("componentDataTransfer", handleComponentDataTransfer as EventListener);
|
||||
window.addEventListener("splitPanelDataTransfer", handleSplitPanelDataTransfer as EventListener);
|
||||
|
||||
|
||||
return () => {
|
||||
unsubscribeComponent();
|
||||
unsubscribeSplitPanel();
|
||||
@@ -928,11 +940,7 @@ V2Repeater.displayName = "V2Repeater";
|
||||
// V2ErrorBoundary로 래핑된 안전한 버전 export
|
||||
export const SafeV2Repeater: React.FC<V2RepeaterProps> = (props) => {
|
||||
return (
|
||||
<V2ErrorBoundary
|
||||
componentId={props.parentId || "v2-repeater"}
|
||||
componentType="V2Repeater"
|
||||
fallbackStyle="compact"
|
||||
>
|
||||
<V2ErrorBoundary componentId={props.parentId || "v2-repeater"} componentType="V2Repeater" fallbackStyle="compact">
|
||||
<V2Repeater {...props} />
|
||||
</V2ErrorBoundary>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user