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:
@@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user