feat: V2 레이아웃 동기화 및 컴포넌트 개선
- TableManagementService에서 V2 레이아웃 동기화 로직을 추가하여, 새로운 입력 타입에 따라 화면 레이아웃을 자동으로 업데이트하도록 개선하였습니다. - syncScreenLayoutsV2InputType 메서드를 통해 V2 레이아웃의 컴포넌트 source를 동기화하는 기능을 구현하였습니다. - EditModal에서 배열 데이터를 쉼표 구분 문자열로 변환하는 로직을 추가하여, 손상된 값을 필터링하고 데이터 저장 시 일관성을 높였습니다. - CategorySelectComponent에서 불필요한 스타일 및 높이 관련 props를 제거하여 코드 간결성을 개선하였습니다. - V2Select 및 관련 컴포넌트에서 height 스타일을 통일하여 사용자 경험을 향상시켰습니다.
This commit is contained in:
@@ -8,6 +8,30 @@ import { ImprovedButtonActionExecutor } from "@/lib/utils/improvedButtonActionEx
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import type { ExtendedControlContext } from "@/types/control-management";
|
||||
|
||||
/**
|
||||
* 🔧 formData 내 배열 값을 쉼표 구분 문자열로 변환
|
||||
* PostgreSQL 배열 형식 저장 방지
|
||||
*/
|
||||
function normalizeFormDataArrays(formData: Record<string, any>): Record<string, any> {
|
||||
if (!formData || typeof formData !== "object") return formData;
|
||||
|
||||
const normalized: Record<string, any> = {};
|
||||
for (const [key, value] of Object.entries(formData)) {
|
||||
if (Array.isArray(value)) {
|
||||
// 배열 내 숫자를 문자열로 변환 후 쉼표 구분
|
||||
const stringValue = value
|
||||
.map(v => typeof v === "number" ? String(v) : v)
|
||||
.filter(v => v !== null && v !== undefined && v !== "")
|
||||
.join(",");
|
||||
console.log(`🔧 [normalizeFormDataArrays] 배열→문자열: ${key}`, { original: value, converted: stringValue });
|
||||
normalized[key] = stringValue;
|
||||
} else {
|
||||
normalized[key] = value;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 버튼 액션 타입 정의
|
||||
*/
|
||||
@@ -978,6 +1002,28 @@ export class ButtonActionExecutor {
|
||||
if (isUpdate) {
|
||||
// UPDATE 처리 - 부분 업데이트 사용 (원본 데이터가 있는 경우)
|
||||
|
||||
// 🔧 UPDATE 전 formData 배열 → 쉼표 구분 문자열 변환 (리피터 데이터 제외)
|
||||
for (const key of Object.keys(formData)) {
|
||||
const 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 stringValue = value
|
||||
.map(v => typeof v === "number" ? String(v) : v)
|
||||
.filter(v => v !== null && v !== undefined && v !== "")
|
||||
.join(",");
|
||||
console.log(`🔧 [handleSave UPDATE] 배열→문자열 변환: ${key}`, { original: value, converted: stringValue });
|
||||
formData[key] = stringValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasRealOriginalData) {
|
||||
// 부분 업데이트: 변경된 필드만 업데이트
|
||||
|
||||
@@ -1131,11 +1177,30 @@ export class ButtonActionExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 배열 데이터(리피터 데이터) 제거 - 마스터 테이블에는 배열 데이터를 저장하지 않음
|
||||
// 🆕 배열 데이터 처리 - 마스터 테이블에는 배열 데이터를 저장하지 않음
|
||||
// 리피터 데이터는 별도의 RepeaterFieldGroup/V2Repeater 저장 로직에서 처리됨
|
||||
// 🔧 단순 배열(다중 선택)은 쉼표 구분 문자열로 변환, 리피터 데이터 배열은 제거
|
||||
for (const key of Object.keys(dataWithUserInfo)) {
|
||||
if (Array.isArray(dataWithUserInfo[key])) {
|
||||
delete dataWithUserInfo[key];
|
||||
const value = dataWithUserInfo[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) {
|
||||
// 리피터 데이터는 제거 (별도 저장 로직에서 처리)
|
||||
delete dataWithUserInfo[key];
|
||||
} else {
|
||||
// 🔧 다중 선택 배열 → 쉼표 구분 문자열로 변환
|
||||
const stringValue = value
|
||||
.map(v => typeof v === "number" ? String(v) : v)
|
||||
.filter(v => v !== null && v !== undefined && v !== "")
|
||||
.join(",");
|
||||
console.log(`🔧 [handleSave] 배열→문자열 변환: ${key}`, { original: value, converted: stringValue });
|
||||
dataWithUserInfo[key] = stringValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3863,7 +3928,8 @@ export class ButtonActionExecutor {
|
||||
|
||||
case "form":
|
||||
if (context.formData && Object.keys(context.formData).length > 0) {
|
||||
sourceData = [context.formData];
|
||||
// 🔧 배열 값을 쉼표 구분 문자열로 변환
|
||||
sourceData = [normalizeFormDataArrays(context.formData)];
|
||||
} else {
|
||||
console.warn("⚠️ form 모드이지만 폼 데이터가 없습니다.");
|
||||
}
|
||||
@@ -3908,7 +3974,8 @@ export class ButtonActionExecutor {
|
||||
dataSourceType = "table-selection";
|
||||
}
|
||||
} else if (context.formData && Object.keys(context.formData).length > 0) {
|
||||
sourceData = [context.formData];
|
||||
// 🔧 배열 값을 쉼표 구분 문자열로 변환
|
||||
sourceData = [normalizeFormDataArrays(context.formData)];
|
||||
dataSourceType = "form";
|
||||
}
|
||||
break;
|
||||
@@ -4045,8 +4112,8 @@ export class ButtonActionExecutor {
|
||||
console.log("📦 [executeAfterSaveControl] savedData 필드:", Object.keys(context.savedData));
|
||||
console.log("📦 [executeAfterSaveControl] savedData.sabun:", context.savedData.sabun);
|
||||
} else if (context.formData && Object.keys(context.formData).length > 0) {
|
||||
// 폼 데이터 사용
|
||||
sourceData = [context.formData];
|
||||
// 폼 데이터 사용 (🔧 배열 값을 쉼표 구분 문자열로 변환)
|
||||
sourceData = [normalizeFormDataArrays(context.formData)];
|
||||
console.log("📦 [executeAfterSaveControl] formData 사용:", sourceData);
|
||||
} else if (context.selectedRowsData && context.selectedRowsData.length > 0) {
|
||||
// 테이블 섹션 데이터 (마지막 순위)
|
||||
@@ -4163,8 +4230,8 @@ export class ButtonActionExecutor {
|
||||
sourceData = Array.isArray(context.savedData) ? context.savedData : [context.savedData];
|
||||
console.log("📦 [executeSingleFlowControl] savedData 사용:", sourceData);
|
||||
} else if (context.formData && Object.keys(context.formData).length > 0) {
|
||||
// 폼 데이터 사용
|
||||
sourceData = [context.formData];
|
||||
// 폼 데이터 사용 (🔧 배열 값을 쉼표 구분 문자열로 변환)
|
||||
sourceData = [normalizeFormDataArrays(context.formData)];
|
||||
console.log("📦 [executeSingleFlowControl] formData 사용:", sourceData);
|
||||
} else if (context.selectedRowsData && context.selectedRowsData.length > 0) {
|
||||
// 테이블 섹션 데이터 (마지막 순위)
|
||||
|
||||
Reference in New Issue
Block a user