Merge branch 'feature/v2-renewal' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-unified-renewal

This commit is contained in:
kjs
2026-02-06 11:07:56 +09:00
12 changed files with 834 additions and 95 deletions

View File

@@ -618,7 +618,36 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
if (currentValue !== originalValue) {
console.log(`🔍 [품목 수정 감지] ${key}: ${originalValue}${currentValue}`);
// 날짜 필드는 정규화된 값 사용, 나머지는 원본 값 사용
changedData[key] = dateFields.includes(key) ? currentValue : currentData[key];
let finalValue = dateFields.includes(key) ? currentValue : currentData[key];
// 🔧 배열이면 쉼표 구분 문자열로 변환 (리피터 데이터 제외)
if (Array.isArray(finalValue)) {
const isRepeaterData = finalValue.length > 0 &&
typeof finalValue[0] === "object" &&
finalValue[0] !== null &&
("_targetTable" in finalValue[0] || "_isNewItem" in finalValue[0] || "_existingRecord" in finalValue[0]);
if (!isRepeaterData) {
// 🔧 손상된 값 필터링 헬퍼
const isValidValue = (v: any): boolean => {
if (typeof v === "number" && !isNaN(v)) return true;
if (typeof v !== "string") return false;
if (!v || v.trim() === "") return false;
if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false;
return true;
};
const validValues = finalValue
.map((v: any) => typeof v === "number" ? String(v) : v)
.filter(isValidValue);
const stringValue = validValues.join(",");
console.log(`🔧 [EditModal 그룹UPDATE] 배열→문자열 변환: ${key}`, { original: finalValue.length, valid: validValues.length, converted: stringValue });
finalValue = stringValue;
}
}
changedData[key] = finalValue;
}
});
@@ -827,12 +856,39 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
}
// 🆕 리피터 데이터(배열)를 마스터 저장에서 제외 (V2Repeater가 별도로 저장)
// 🔧 단, 다중 선택 배열은 쉼표 구분 문자열로 변환하여 저장
const masterDataToSave: Record<string, any> = {};
Object.entries(dataToSave).forEach(([key, value]) => {
if (!Array.isArray(value)) {
masterDataToSave[key] = value;
} else {
console.log(`🔄 [EditModal] 리피터 데이터 제외 (별도 저장): ${key}, ${value.length}개 항목`);
// 리피터 데이터인지 확인 (객체 배열이고 _targetTable 또는 _isNewItem이 있으면 리피터)
const isRepeaterData = value.length > 0 &&
typeof value[0] === "object" &&
value[0] !== null &&
("_targetTable" in value[0] || "_isNewItem" in value[0] || "_existingRecord" in value[0]);
if (isRepeaterData) {
console.log(`🔄 [EditModal] 리피터 데이터 제외 (별도 저장): ${key}, ${value.length}개 항목`);
} else {
// 🔧 손상된 값 필터링 헬퍼 (중괄호, 따옴표, 백슬래시 포함 시 무효)
const isValidValue = (v: any): boolean => {
if (typeof v === "number" && !isNaN(v)) return true;
if (typeof v !== "string") return false;
if (!v || v.trim() === "") return false;
if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false;
return true;
};
// 🔧 다중 선택 배열 → 쉼표 구분 문자열로 변환 (손상된 값 필터링)
const validValues = value
.map((v: any) => typeof v === "number" ? String(v) : v)
.filter(isValidValue);
const stringValue = validValues.join(",");
console.log(`🔧 [EditModal CREATE] 배열→문자열 변환: ${key}`, { original: value.length, valid: validValues.length, converted: stringValue });
masterDataToSave[key] = stringValue;
}
}
});
@@ -916,7 +972,47 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
const changedData: Record<string, any> = {};
Object.keys(formData).forEach((key) => {
if (formData[key] !== originalData[key]) {
changedData[key] = formData[key];
let value = formData[key];
// 🔧 배열이면 쉼표 구분 문자열로 변환 (리피터 데이터 제외)
if (Array.isArray(value)) {
// 리피터 데이터인지 확인 (객체 배열이고 _targetTable 또는 _isNewItem이 있으면 리피터)
const isRepeaterData = value.length > 0 &&
typeof value[0] === "object" &&
value[0] !== null &&
("_targetTable" in value[0] || "_isNewItem" in value[0] || "_existingRecord" in value[0]);
if (!isRepeaterData) {
// 🔧 손상된 값 필터링 헬퍼 (중괄호, 따옴표, 백슬래시 포함 시 무효)
const isValidValue = (v: any): boolean => {
if (typeof v === "number" && !isNaN(v)) return true;
if (typeof v !== "string") return false;
if (!v || v.trim() === "") return false;
// 손상된 PostgreSQL 배열 형식 감지
if (v.includes("{") || v.includes("}") || v.includes('"') || v.includes("\\")) return false;
return true;
};
// 🔧 다중 선택 배열 → 쉼표 구분 문자열로 변환 (손상된 값 필터링)
const validValues = value
.map((v: any) => typeof v === "number" ? String(v) : v)
.filter(isValidValue);
if (validValues.length !== value.length) {
console.warn(`⚠️ [EditModal UPDATE] 손상된 값 필터링: ${key}`, {
before: value.length,
after: validValues.length,
removed: value.filter((v: any) => !isValidValue(v))
});
}
const stringValue = validValues.join(",");
console.log(`🔧 [EditModal UPDATE] 배열→문자열 변환: ${key}`, { original: value.length, valid: validValues.length, converted: stringValue });
value = stringValue;
}
}
changedData[key] = value;
}
});

View File

@@ -6321,6 +6321,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU
onDragStart={(e) => startComponentDrag(component, e)}
onDragEnd={endDrag}
selectedScreen={selectedScreen}
tableName={selectedScreen?.tableName} // 🆕 디자인 모드에서도 옵션 로딩을 위해 전달
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
// onZoneComponentDrop 제거
onZoneClick={handleZoneClick}
@@ -6487,6 +6488,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU
onDragStart={(e) => startComponentDrag(child, e)}
onDragEnd={endDrag}
selectedScreen={selectedScreen}
tableName={selectedScreen?.tableName} // 🆕 디자인 모드에서도 옵션 로딩을 위해 전달
// onZoneComponentDrop 제거
onZoneClick={handleZoneClick}
// 설정 변경 핸들러 (자식 컴포넌트용)
@@ -6707,6 +6709,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList, onScreenU
component={relativeButton}
isDesignMode={true}
formData={{}}
tableName={selectedScreen?.tableName}
onDataflowComplete={() => {}}
/>
</div>