; Please enter a commit message to explain why this merge is necessary,
; especially if it merges an updated upstream into a topic branch.
;
; Lines starting with ';' will be ignored, and an empty message aborts
; the commit.
This commit is contained in:
leeheejin
2026-01-12 09:25:13 +09:00
7 changed files with 451 additions and 160 deletions

View File

@@ -281,10 +281,12 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
// 컴포넌트의 columnName에 해당하는 formData 값 추출
const fieldName = (component as any).columnName || component.id;
// modal-repeater-table은 배열 데이터를 다루므로 빈 배열로 초기화
// 다중 레코드를 다루는 컴포넌트는 배열 데이터로 초기화
let currentValue;
if (componentType === "modal-repeater-table" || componentType === "repeat-screen-modal") {
// EditModal에서 전달된 groupedData가 있으면 우선 사용
if (componentType === "modal-repeater-table" ||
componentType === "repeat-screen-modal" ||
componentType === "selected-items-detail-input") {
// EditModal/ScreenModal에서 전달된 groupedData가 있으면 우선 사용
currentValue = props.groupedData || formData?.[fieldName] || [];
} else {
currentValue = formData?.[fieldName] || "";

View File

@@ -42,6 +42,8 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
screenId,
...props
}) => {
// 🆕 groupedData 추출 (DynamicComponentRenderer에서 전달)
const groupedData = (props as any).groupedData || (props as any)._groupedData;
// 🆕 URL 파라미터에서 dataSourceId 읽기
const searchParams = useSearchParams();
const urlDataSourceId = searchParams?.get("dataSourceId") || undefined;
@@ -225,24 +227,32 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
// 🆕 모달 데이터를 ItemData 구조로 변환 (그룹별 구조)
useEffect(() => {
// 🆕 수정 모드: formData에서 데이터 로드 (URL에 mode=edit이 있으면)
// 🆕 수정 모드: groupedData 또는 formData에서 데이터 로드 (URL에 mode=edit이 있으면)
const urlParams = new URLSearchParams(window.location.search);
const mode = urlParams.get("mode");
if (mode === "edit" && formData) {
// 🔧 데이터 소스 우선순위: groupedData > formData (배열) > formData (객체)
const sourceData = groupedData && Array.isArray(groupedData) && groupedData.length > 0
? groupedData
: formData;
if (mode === "edit" && sourceData) {
// 배열인지 단일 객체인지 확인
const isArray = Array.isArray(formData);
const dataArray = isArray ? formData : [formData];
const isArray = Array.isArray(sourceData);
const dataArray = isArray ? sourceData : [sourceData];
if (dataArray.length === 0 || (dataArray.length === 1 && Object.keys(dataArray[0]).length === 0)) {
console.warn("⚠️ [SelectedItemsDetailInput] formData가 비어있음");
console.warn("⚠️ [SelectedItemsDetailInput] 데이터가 비어있음");
return;
}
console.log(
`📝 [SelectedItemsDetailInput] 수정 모드 - ${isArray ? "그룹 레코드" : "단일 레코드"} (${dataArray.length}개)`,
);
console.log("📝 [SelectedItemsDetailInput] formData (JSON):", JSON.stringify(dataArray, null, 2));
console.log("📝 [SelectedItemsDetailInput] 데이터 소스:", {
fromGroupedData: groupedData && Array.isArray(groupedData) && groupedData.length > 0,
dataArray: JSON.stringify(dataArray, null, 2),
});
const groups = componentConfig.fieldGroups || [];
const additionalFields = componentConfig.additionalFields || [];
@@ -423,7 +433,7 @@ export const SelectedItemsDetailInputComponent: React.FC<SelectedItemsDetailInpu
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [modalData, component.id, componentConfig.fieldGroups, formData]); // formData 의존성 추가
}, [modalData, component.id, componentConfig.fieldGroups, formData, groupedData]); // groupedData 의존성 추가
// 🆕 Cartesian Product 생성 함수 (items에서 모든 그룹의 조합을 생성)
const generateCartesianProduct = useCallback(

View File

@@ -51,6 +51,10 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
// 숨김 상태 (props에서 전달받은 값 우선 사용)
const isHidden = props.hidden !== undefined ? props.hidden : component.hidden || componentConfig.hidden || false;
// 수정 모드 여부 확인 (originalData가 있으면 수정 모드)
const originalData = props.originalData || (props as any)._originalData;
const isEditMode = originalData && Object.keys(originalData).length > 0;
// 자동생성된 값 상태
const [autoGeneratedValue, setAutoGeneratedValue] = useState<string>("");
@@ -99,6 +103,16 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
return;
}
// 🆕 수정 모드일 때는 채번 규칙 스킵 (기존 값 유지)
if (isEditMode) {
console.log("⏭️ 수정 모드 - 채번 규칙 스킵:", {
columnName: component.columnName,
originalValue: originalData?.[component.columnName],
});
hasGeneratedRef.current = true; // 생성 완료로 표시하여 재실행 방지
return;
}
if (testAutoGeneration.enabled && testAutoGeneration.type !== "none") {
// 폼 데이터에 이미 값이 있으면 자동생성하지 않음
const currentFormValue = formData?.[component.columnName];
@@ -171,7 +185,7 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
};
generateAutoValue();
}, [testAutoGeneration.enabled, testAutoGeneration.type, component.columnName, isInteractive]);
}, [testAutoGeneration.enabled, testAutoGeneration.type, component.columnName, isInteractive, isEditMode]);
// 실제 화면에서 숨김 처리된 컴포넌트는 렌더링하지 않음
if (isHidden && !isDesignMode) {

View File

@@ -88,6 +88,9 @@ export interface ButtonActionConfig {
// 엑셀 업로드 관련
excelUploadMode?: "insert" | "update" | "upsert"; // 업로드 모드
excelKeyColumn?: string; // 업데이트/Upsert 시 키 컬럼
excelNumberingRuleId?: string; // 채번 규칙 ID (단일 테이블용)
excelNumberingTargetColumn?: string; // 채번 적용 컬럼 (단일 테이블용)
excelAfterUploadFlows?: Array<{ flowId: string; order: number }>; // 업로드 후 제어 실행
// 바코드 스캔 관련
barcodeTargetField?: string; // 스캔 결과를 입력할 필드명
@@ -4838,6 +4841,10 @@ export class ButtonActionExecutor {
userId: context.userId,
tableName: context.tableName,
screenId: context.screenId,
// 채번 설정 디버깅
numberingRuleId: config.excelNumberingRuleId,
numberingTargetColumn: config.excelNumberingTargetColumn,
afterUploadFlows: config.excelAfterUploadFlows,
});
// 🆕 마스터-디테일 구조 확인 (화면에 분할 패널이 있으면 자동 감지)
@@ -4909,7 +4916,7 @@ export class ButtonActionExecutor {
savedSize: localStorage.getItem(storageKey),
});
root.render(
root.render(
React.createElement(ExcelUploadModal, {
open: true,
onOpenChange: (open: boolean) => {
@@ -4931,6 +4938,11 @@ export class ButtonActionExecutor {
isMasterDetail,
masterDetailRelation,
masterDetailExcelConfig,
// 🆕 단일 테이블 채번 설정
numberingRuleId: config.excelNumberingRuleId,
numberingTargetColumn: config.excelNumberingTargetColumn,
// 🆕 업로드 후 제어 실행 설정
afterUploadFlows: config.excelAfterUploadFlows,
onSuccess: () => {
// 성공 메시지는 ExcelUploadModal 내부에서 이미 표시됨
context.onRefresh?.();