Compare commits
4 Commits
4495e414b5
...
52964739f8
| Author | SHA1 | Date |
|---|---|---|
|
|
52964739f8 | |
|
|
ba5ee357ca | |
|
|
ef32de3087 | |
|
|
fad1748591 |
|
|
@ -937,11 +937,17 @@ export class DynamicFormService {
|
||||||
})
|
})
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
// 🆕 JSONB 타입 값은 JSON 문자열로 변환
|
// 🆕 JSONB 타입 값은 JSON 문자열로 변환, 빈 문자열은 null로 변환
|
||||||
const values: any[] = Object.keys(changedFields).map((key) => {
|
const values: any[] = Object.keys(changedFields).map((key) => {
|
||||||
const value = changedFields[key];
|
const value = changedFields[key];
|
||||||
const dataType = columnTypes[key];
|
const dataType = columnTypes[key];
|
||||||
|
|
||||||
|
// 🔧 빈 문자열은 null로 변환 (날짜 필드 등에서 값을 지울 때 필요)
|
||||||
|
if (value === "" || value === undefined) {
|
||||||
|
console.log(`🔄 빈 값 → null 변환: ${key}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// JSONB/JSON 타입이고 배열/객체인 경우 JSON 문자열로 변환
|
// JSONB/JSON 타입이고 배열/객체인 경우 JSON 문자열로 변환
|
||||||
if (
|
if (
|
||||||
(dataType === "jsonb" || dataType === "json") &&
|
(dataType === "jsonb" || dataType === "json") &&
|
||||||
|
|
|
||||||
|
|
@ -1039,8 +1039,15 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 🆕 _tableSection_ 데이터가 있는지 확인 (TableSectionRenderer 사용 시)
|
||||||
|
// _tableSection_ 데이터가 있으면 buttonActions.ts의 handleUniversalFormModalTableSectionSave가 처리
|
||||||
|
const hasTableSectionData = Object.keys(formData).some(k =>
|
||||||
|
k.startsWith("_tableSection_") || k.startsWith("__tableSection_")
|
||||||
|
);
|
||||||
|
|
||||||
// 🆕 그룹 데이터가 있으면 EditModal.handleSave 사용 (일괄 저장)
|
// 🆕 그룹 데이터가 있으면 EditModal.handleSave 사용 (일괄 저장)
|
||||||
const shouldUseEditModalSave = groupData.length > 0 || !hasUniversalFormModal;
|
// 단, _tableSection_ 데이터가 있으면 EditModal.handleSave 사용하지 않음 (buttonActions.ts가 처리)
|
||||||
|
const shouldUseEditModalSave = !hasTableSectionData && (groupData.length > 0 || !hasUniversalFormModal);
|
||||||
|
|
||||||
// 🔑 첨부파일 컴포넌트가 행(레코드) 단위로 파일을 저장할 수 있도록 tableName 추가
|
// 🔑 첨부파일 컴포넌트가 행(레코드) 단위로 파일을 저장할 수 있도록 tableName 추가
|
||||||
const enrichedFormData = {
|
const enrichedFormData = {
|
||||||
|
|
|
||||||
|
|
@ -951,23 +951,43 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
// 추가 dataFilter 적용
|
// 추가 dataFilter 적용
|
||||||
let filteredData = result.data || [];
|
let filteredData = result.data || [];
|
||||||
const dataFilter = componentConfig.rightPanel?.dataFilter;
|
const dataFilter = componentConfig.rightPanel?.dataFilter;
|
||||||
if (dataFilter?.enabled && dataFilter.conditions?.length > 0) {
|
// 🔧 filters 또는 conditions 배열 모두 지원
|
||||||
|
const filterConditions = dataFilter?.filters || dataFilter?.conditions || [];
|
||||||
|
if (dataFilter?.enabled && filterConditions.length > 0) {
|
||||||
|
console.log(`🔍 [기본탭] dataFilter 설정:`, JSON.stringify(dataFilter, null, 2));
|
||||||
|
console.log(`🔍 [기본탭] 필터 전 데이터 수:`, filteredData.length);
|
||||||
filteredData = filteredData.filter((item: any) => {
|
filteredData = filteredData.filter((item: any) => {
|
||||||
return dataFilter.conditions.every((cond: any) => {
|
return filterConditions.every((cond: any) => {
|
||||||
const value = item[cond.column];
|
// 🔧 columnName 또는 column 필드 모두 지원
|
||||||
|
const columnName = cond.columnName || cond.column;
|
||||||
|
const value = item[columnName];
|
||||||
const condValue = cond.value;
|
const condValue = cond.value;
|
||||||
|
let result = true;
|
||||||
switch (cond.operator) {
|
switch (cond.operator) {
|
||||||
case "equals":
|
case "equals":
|
||||||
return value === condValue;
|
result = value === condValue;
|
||||||
|
break;
|
||||||
case "notEquals":
|
case "notEquals":
|
||||||
return value !== condValue;
|
result = value !== condValue;
|
||||||
|
break;
|
||||||
case "contains":
|
case "contains":
|
||||||
return String(value).includes(String(condValue));
|
result = String(value).includes(String(condValue));
|
||||||
|
break;
|
||||||
|
case "is_null":
|
||||||
|
case "NULL":
|
||||||
|
result = value === null || value === undefined || value === "";
|
||||||
|
break;
|
||||||
|
case "is_not_null":
|
||||||
|
case "NOT NULL":
|
||||||
|
result = value !== null && value !== undefined && value !== "";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
console.log(`🔍 [기본탭] 필터 후 데이터 수:`, filteredData.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRightData(filteredData);
|
setRightData(filteredData);
|
||||||
|
|
@ -1080,23 +1100,48 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
|
|
||||||
// 데이터 필터 적용
|
// 데이터 필터 적용
|
||||||
const dataFilter = tabConfig.dataFilter;
|
const dataFilter = tabConfig.dataFilter;
|
||||||
if (dataFilter?.enabled && dataFilter.conditions?.length > 0) {
|
console.log(`🔍 [추가탭 ${tabIndex}] dataFilter 설정:`, JSON.stringify(dataFilter, null, 2));
|
||||||
|
// 🔧 filters 또는 conditions 배열 모두 지원 (DataFilterConfigPanel은 filters 사용)
|
||||||
|
const filterConditions = dataFilter?.filters || dataFilter?.conditions || [];
|
||||||
|
console.log(`🔍 [추가탭 ${tabIndex}] filterConditions:`, filterConditions);
|
||||||
|
console.log(`🔍 [추가탭 ${tabIndex}] 필터 전 데이터 수:`, resultData.length);
|
||||||
|
if (dataFilter?.enabled && filterConditions.length > 0) {
|
||||||
|
const beforeCount = resultData.length;
|
||||||
resultData = resultData.filter((item: any) => {
|
resultData = resultData.filter((item: any) => {
|
||||||
return dataFilter.conditions.every((cond: any) => {
|
return filterConditions.every((cond: any) => {
|
||||||
const value = item[cond.column];
|
// 🔧 columnName 또는 column 필드 모두 지원
|
||||||
|
const columnName = cond.columnName || cond.column;
|
||||||
|
const value = item[columnName];
|
||||||
const condValue = cond.value;
|
const condValue = cond.value;
|
||||||
|
let result = true;
|
||||||
switch (cond.operator) {
|
switch (cond.operator) {
|
||||||
case "equals":
|
case "equals":
|
||||||
return value === condValue;
|
result = value === condValue;
|
||||||
|
break;
|
||||||
case "notEquals":
|
case "notEquals":
|
||||||
return value !== condValue;
|
result = value !== condValue;
|
||||||
|
break;
|
||||||
case "contains":
|
case "contains":
|
||||||
return String(value).includes(String(condValue));
|
result = String(value).includes(String(condValue));
|
||||||
|
break;
|
||||||
|
case "is_null":
|
||||||
|
case "NULL":
|
||||||
|
result = value === null || value === undefined || value === "";
|
||||||
|
break;
|
||||||
|
case "is_not_null":
|
||||||
|
case "NOT NULL":
|
||||||
|
result = value !== null && value !== undefined && value !== "";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
console.log(`🔍 [필터 체크] ${columnName}=${JSON.stringify(value)}, operator=${cond.operator}, result=${result}`);
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
console.log(`🔍 [추가탭 ${tabIndex}] 필터 후 데이터 수: ${beforeCount} → ${resultData.length}`);
|
||||||
|
} else {
|
||||||
|
console.log(`🔍 [추가탭 ${tabIndex}] 필터 비활성화 또는 조건 없음 (enabled=${dataFilter?.enabled}, conditions=${filterConditions.length})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 중복 제거 적용
|
// 중복 제거 적용
|
||||||
|
|
@ -1557,6 +1602,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
// 추가 버튼 핸들러
|
// 추가 버튼 핸들러
|
||||||
const handleAddClick = useCallback(
|
const handleAddClick = useCallback(
|
||||||
(panel: "left" | "right") => {
|
(panel: "left" | "right") => {
|
||||||
|
console.log("🆕 [추가모달] handleAddClick 호출:", { panel, activeTabIndex });
|
||||||
setAddModalPanel(panel);
|
setAddModalPanel(panel);
|
||||||
|
|
||||||
// 우측 패널 추가 시, 좌측에서 선택된 항목의 조인 컬럼 값을 자동으로 채움
|
// 우측 패널 추가 시, 좌측에서 선택된 항목의 조인 컬럼 값을 자동으로 채움
|
||||||
|
|
@ -1567,16 +1613,19 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
componentConfig.rightPanel?.rightColumn
|
componentConfig.rightPanel?.rightColumn
|
||||||
) {
|
) {
|
||||||
const leftColumnValue = selectedLeftItem[componentConfig.leftPanel.leftColumn];
|
const leftColumnValue = selectedLeftItem[componentConfig.leftPanel.leftColumn];
|
||||||
setAddModalFormData({
|
const initialData = {
|
||||||
[componentConfig.rightPanel.rightColumn]: leftColumnValue,
|
[componentConfig.rightPanel.rightColumn]: leftColumnValue,
|
||||||
});
|
};
|
||||||
|
console.log("🆕 [추가모달] 초기 데이터 설정:", initialData);
|
||||||
|
setAddModalFormData(initialData);
|
||||||
} else {
|
} else {
|
||||||
|
console.log("🆕 [추가모달] 빈 데이터로 초기화");
|
||||||
setAddModalFormData({});
|
setAddModalFormData({});
|
||||||
}
|
}
|
||||||
|
|
||||||
setShowAddModal(true);
|
setShowAddModal(true);
|
||||||
},
|
},
|
||||||
[selectedLeftItem, componentConfig],
|
[selectedLeftItem, componentConfig, activeTabIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 수정 버튼 핸들러
|
// 수정 버튼 핸들러
|
||||||
|
|
@ -1681,10 +1730,44 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
|
||||||
// 기존 자동 편집 모드 (인라인 편집 모달)
|
// 기존 자동 편집 모드 (인라인 편집 모달)
|
||||||
setEditModalPanel(panel);
|
setEditModalPanel(panel);
|
||||||
setEditModalItem(item);
|
setEditModalItem(item);
|
||||||
setEditModalFormData({ ...item });
|
|
||||||
|
// 🔧 우측 패널(추가탭 포함) 수정 시 selectedLeftItem의 FK 값 병합
|
||||||
|
let mergedItem = { ...item };
|
||||||
|
if (panel === "right" && selectedLeftItem) {
|
||||||
|
// 현재 활성 탭의 relation 설정 가져오기
|
||||||
|
const currentTabConfig =
|
||||||
|
activeTabIndex === 0
|
||||||
|
? componentConfig.rightPanel
|
||||||
|
: componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1];
|
||||||
|
|
||||||
|
const relationKeys = currentTabConfig?.relation?.keys;
|
||||||
|
const leftColumn = currentTabConfig?.relation?.leftColumn;
|
||||||
|
const rightColumn = currentTabConfig?.relation?.foreignKey || currentTabConfig?.relation?.rightColumn;
|
||||||
|
|
||||||
|
if (relationKeys && relationKeys.length > 0) {
|
||||||
|
// 복합키인 경우
|
||||||
|
relationKeys.forEach((key: any) => {
|
||||||
|
if (key.leftColumn && key.rightColumn && selectedLeftItem[key.leftColumn] !== undefined) {
|
||||||
|
// item에 해당 FK 값이 없거나 빈 값이면 selectedLeftItem에서 가져옴
|
||||||
|
if (mergedItem[key.rightColumn] === undefined || mergedItem[key.rightColumn] === null || mergedItem[key.rightColumn] === "") {
|
||||||
|
mergedItem[key.rightColumn] = selectedLeftItem[key.leftColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (leftColumn && rightColumn) {
|
||||||
|
// 단일키인 경우
|
||||||
|
if (selectedLeftItem[leftColumn] !== undefined) {
|
||||||
|
if (mergedItem[rightColumn] === undefined || mergedItem[rightColumn] === null || mergedItem[rightColumn] === "") {
|
||||||
|
mergedItem[rightColumn] = selectedLeftItem[leftColumn];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditModalFormData(mergedItem);
|
||||||
setShowEditModal(true);
|
setShowEditModal(true);
|
||||||
},
|
},
|
||||||
[componentConfig],
|
[componentConfig, selectedLeftItem, activeTabIndex],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 수정 모달 저장
|
// 수정 모달 저장
|
||||||
|
|
|
||||||
|
|
@ -449,6 +449,12 @@ export function UniversalFormModalComponent({
|
||||||
event.detail.formData[normalizedKey] = value;
|
event.detail.formData[normalizedKey] = value;
|
||||||
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key} → ${normalizedKey}, ${value.length}개 항목`);
|
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key} → ${normalizedKey}, ${value.length}개 항목`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🆕 원본 테이블 섹션 데이터도 병합 (삭제 추적용)
|
||||||
|
if (key.startsWith("_originalTableSectionData_") && Array.isArray(value)) {
|
||||||
|
event.detail.formData[key] = value;
|
||||||
|
console.log(`[UniversalFormModal] 원본 테이블 섹션 데이터 병합: ${key}, ${value.length}개 항목`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 수정 모드: 원본 그룹 데이터 전달 (UPDATE/DELETE 추적용)
|
// 🆕 수정 모드: 원본 그룹 데이터 전달 (UPDATE/DELETE 추적용)
|
||||||
|
|
@ -938,17 +944,19 @@ export function UniversalFormModalComponent({
|
||||||
newFormData[tableSectionKey] = items;
|
newFormData[tableSectionKey] = items;
|
||||||
console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${tableSectionKey}]에 저장됨`);
|
console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${tableSectionKey}]에 저장됨`);
|
||||||
|
|
||||||
// 🆕 원본 그룹 데이터 저장 (삭제 추적용)
|
// 🆕 테이블 섹션 원본 데이터 저장 (삭제 추적용)
|
||||||
// groupedDataInitializedRef가 false일 때만 설정 (true면 _groupedData useEffect에서 이미 처리됨)
|
// 각 테이블 섹션별로 별도의 키에 원본 데이터 저장 (groupedDataInitializedRef와 무관하게 항상 저장)
|
||||||
// DB에서 로드한 데이터를 originalGroupedData에 저장해야 삭제 시 비교 가능
|
const originalTableSectionKey = `_originalTableSectionData_${section.id}`;
|
||||||
|
newFormData[originalTableSectionKey] = JSON.parse(JSON.stringify(items));
|
||||||
|
console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`);
|
||||||
|
|
||||||
|
// 기존 originalGroupedData에도 추가 (하위 호환성)
|
||||||
if (!groupedDataInitializedRef.current) {
|
if (!groupedDataInitializedRef.current) {
|
||||||
setOriginalGroupedData((prev) => {
|
setOriginalGroupedData((prev) => {
|
||||||
const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))];
|
const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))];
|
||||||
console.log(`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`);
|
console.log(`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`);
|
||||||
return newOriginal;
|
return newOriginal;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
console.log(`[initializeForm] 테이블 섹션 ${section.id}: _groupedData로 이미 초기화됨, originalGroupedData 설정 스킵`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -522,21 +522,7 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
console.log("✅ [handleSave] 필수 항목 검증 통과");
|
console.log("✅ [handleSave] 필수 항목 검증 통과");
|
||||||
|
|
||||||
// 🆕 EditModal 등에서 전달된 onSave 콜백이 있으면 우선 사용
|
// 🆕 저장 전 이벤트 먼저 발생 (UniversalFormModal의 __tableSection_ 데이터 병합을 위해)
|
||||||
if (onSave) {
|
|
||||||
console.log("✅ [handleSave] onSave 콜백 발견 - 콜백 실행");
|
|
||||||
try {
|
|
||||||
await onSave();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("❌ [handleSave] onSave 콜백 실행 오류:", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("⚠️ [handleSave] onSave 콜백 없음 - 기본 저장 로직 실행");
|
|
||||||
|
|
||||||
// 🆕 저장 전 이벤트 발생 (SelectedItemsDetailInput 등에서 최신 데이터 수집)
|
|
||||||
// context.formData를 이벤트 detail에 포함하여 직접 수정 가능하게 함
|
// context.formData를 이벤트 detail에 포함하여 직접 수정 가능하게 함
|
||||||
// skipDefaultSave 플래그를 통해 기본 저장 로직을 건너뛸 수 있음
|
// skipDefaultSave 플래그를 통해 기본 저장 로직을 건너뛸 수 있음
|
||||||
const beforeSaveEventDetail = {
|
const beforeSaveEventDetail = {
|
||||||
|
|
@ -554,6 +540,8 @@ export class ButtonActionExecutor {
|
||||||
// 약간의 대기 시간을 주어 이벤트 핸들러가 formData를 업데이트할 수 있도록 함
|
// 약간의 대기 시간을 주어 이벤트 핸들러가 formData를 업데이트할 수 있도록 함
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
console.log("📦 [handleSave] beforeFormSave 이벤트 후 formData keys:", Object.keys(context.formData || {}));
|
||||||
|
|
||||||
// 검증 실패 시 저장 중단
|
// 검증 실패 시 저장 중단
|
||||||
if (beforeSaveEventDetail.validationFailed) {
|
if (beforeSaveEventDetail.validationFailed) {
|
||||||
console.log("❌ [handleSave] 검증 실패로 저장 중단:", beforeSaveEventDetail.validationErrors);
|
console.log("❌ [handleSave] 검증 실패로 저장 중단:", beforeSaveEventDetail.validationErrors);
|
||||||
|
|
@ -566,7 +554,30 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("📦 [handleSave] beforeFormSave 이벤트 후 formData:", context.formData);
|
// 🆕 _tableSection_ 데이터가 있는지 확인 (TableSectionRenderer 사용 시)
|
||||||
|
// beforeFormSave 이벤트 후에 체크해야 UniversalFormModal에서 병합된 데이터를 확인할 수 있음
|
||||||
|
const hasTableSectionData = Object.keys(context.formData || {}).some(k =>
|
||||||
|
k.startsWith("_tableSection_") || k.startsWith("__tableSection_")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasTableSectionData) {
|
||||||
|
console.log("📋 [handleSave] _tableSection_ 데이터 감지 - onSave 콜백 건너뛰고 테이블 섹션 저장 로직 사용");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🆕 EditModal 등에서 전달된 onSave 콜백이 있으면 우선 사용
|
||||||
|
// 단, _tableSection_ 데이터가 있으면 건너뛰기 (handleUniversalFormModalTableSectionSave가 처리)
|
||||||
|
if (onSave && !hasTableSectionData) {
|
||||||
|
console.log("✅ [handleSave] onSave 콜백 발견 - 콜백 실행 (테이블 섹션 데이터 없음)");
|
||||||
|
try {
|
||||||
|
await onSave();
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ [handleSave] onSave 콜백 실행 오류:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("⚠️ [handleSave] 기본 저장 로직 실행 (onSave 콜백 없음 또는 _tableSection_ 데이터 있음)");
|
||||||
|
|
||||||
// 🆕 렉 구조 컴포넌트 일괄 저장 감지
|
// 🆕 렉 구조 컴포넌트 일괄 저장 감지
|
||||||
let rackStructureLocations: any[] | undefined;
|
let rackStructureLocations: any[] | undefined;
|
||||||
|
|
@ -2246,9 +2257,24 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3️⃣ 삭제된 품목 DELETE (원본에는 있지만 현재에는 없는 항목)
|
// 3️⃣ 삭제된 품목 DELETE (원본에는 있지만 현재에는 없는 항목)
|
||||||
|
// 🆕 테이블 섹션별 원본 데이터 사용 (우선), 없으면 전역 originalGroupedData 사용
|
||||||
|
const sectionOriginalKey = `_originalTableSectionData_${sectionId}`;
|
||||||
|
const sectionOriginalData: any[] = modalData[sectionOriginalKey] || formData[sectionOriginalKey] || [];
|
||||||
|
|
||||||
|
// 섹션별 원본 데이터가 있으면 사용, 없으면 전역 originalGroupedData 사용
|
||||||
|
const originalDataForDelete = sectionOriginalData.length > 0 ? sectionOriginalData : originalGroupedData;
|
||||||
|
|
||||||
|
console.log(`🔍 [DELETE 비교] 섹션 ${sectionId}:`, {
|
||||||
|
sectionOriginalKey,
|
||||||
|
sectionOriginalCount: sectionOriginalData.length,
|
||||||
|
globalOriginalCount: originalGroupedData.length,
|
||||||
|
usingData: sectionOriginalData.length > 0 ? "섹션별 원본" : "전역 원본",
|
||||||
|
currentCount: currentItems.length
|
||||||
|
});
|
||||||
|
|
||||||
// ⚠️ id 타입 통일: 문자열로 변환하여 비교 (숫자 vs 문자열 불일치 방지)
|
// ⚠️ id 타입 통일: 문자열로 변환하여 비교 (숫자 vs 문자열 불일치 방지)
|
||||||
const currentIds = new Set(currentItems.map((item) => String(item.id)).filter(Boolean));
|
const currentIds = new Set(currentItems.map((item) => String(item.id)).filter(Boolean));
|
||||||
const deletedItems = originalGroupedData.filter((orig) => orig.id && !currentIds.has(String(orig.id)));
|
const deletedItems = originalDataForDelete.filter((orig) => orig.id && !currentIds.has(String(orig.id)));
|
||||||
|
|
||||||
for (const deletedItem of deletedItems) {
|
for (const deletedItem of deletedItems) {
|
||||||
console.log(`🗑️ [DELETE] 품목 삭제: id=${deletedItem.id}, tableName=${saveTableName}`);
|
console.log(`🗑️ [DELETE] 품목 삭제: id=${deletedItem.id}, tableName=${saveTableName}`);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue