diff --git a/frontend/components/v2/registerV2Components.ts b/frontend/components/v2/registerV2Components.ts index ffe8cc17..d4db6d53 100644 --- a/frontend/components/v2/registerV2Components.ts +++ b/frontend/components/v2/registerV2Components.ts @@ -20,6 +20,7 @@ import { V2Group } from "./V2Group"; import { V2Media } from "./V2Media"; import { V2Biz } from "./V2Biz"; import { V2Hierarchy } from "./V2Hierarchy"; +import { V2Repeater } from "./V2Repeater"; // 설정 패널 import import { V2InputConfigPanel } from "./config-panels/V2InputConfigPanel"; @@ -31,6 +32,7 @@ import { V2GroupConfigPanel } from "./config-panels/V2GroupConfigPanel"; import { V2MediaConfigPanel } from "./config-panels/V2MediaConfigPanel"; import { V2BizConfigPanel } from "./config-panels/V2BizConfigPanel"; import { V2HierarchyConfigPanel } from "./config-panels/V2HierarchyConfigPanel"; +import { V2RepeaterConfigPanel } from "./config-panels/V2RepeaterConfigPanel"; // V2 컴포넌트 정의 const v2ComponentDefinitions: ComponentDefinition[] = [ @@ -179,6 +181,31 @@ const v2ComponentDefinitions: ComponentDefinition[] = [ dataSource: "static", }, }, + { + id: "v2-repeater", + name: "통합 반복", + description: "인라인 테이블, 모달, 버튼 등 다양한 반복 데이터 관리를 지원하는 통합 컴포넌트", + category: ComponentCategory.V2, + webType: "entity" as WebType, + component: V2Repeater as any, + tags: ["repeater", "table", "modal", "button", "data", "v2"], + defaultSize: { width: 600, height: 300 }, + configPanel: V2RepeaterConfigPanel as any, + defaultConfig: { + renderMode: "inline", + dataSource: { + tableName: "", + foreignKey: "", + referenceKey: "", + }, + columns: [], + features: { + showAddButton: true, + showDeleteButton: true, + inlineEdit: false, + }, + }, + }, ]; /** diff --git a/frontend/lib/registry/DynamicComponentRenderer.tsx b/frontend/lib/registry/DynamicComponentRenderer.tsx index 35e129bb..23b684ac 100644 --- a/frontend/lib/registry/DynamicComponentRenderer.tsx +++ b/frontend/lib/registry/DynamicComponentRenderer.tsx @@ -1,25 +1,11 @@ "use client"; -import React, { useCallback } from "react"; +import React from "react"; import { ComponentData } from "@/types/screen"; import { DynamicLayoutRenderer } from "./DynamicLayoutRenderer"; import { ComponentRegistry } from "./ComponentRegistry"; import { filterDOMProps } from "@/lib/utils/domPropsFilter"; -// V2 컴포넌트 import -import { - V2Input, - V2Select, - V2Date, - V2List, - V2Layout, - V2Group, - V2Media, - V2Biz, - V2Hierarchy, -} from "@/components/v2"; -import { V2Repeater } from "@/components/v2/V2Repeater"; - // 통합 폼 시스템 import import { useV2FormOptional } from "@/components/v2/V2FormContext"; @@ -189,11 +175,13 @@ export const DynamicComponentRenderer: React.FC = // 컴포넌트 타입 추출 - 새 시스템에서는 componentType 속성 사용, 레거시는 type 사용 const rawComponentType = (component as any).componentType || component.type; - // 🆕 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지) + // 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지) const mapToV2ComponentType = (type: string | undefined): string | undefined => { if (!type) return type; - // 이미 v2- 또는 v2- 접두사가 있으면 그대로 반환 - if (type.startsWith("v2-") || type.startsWith("v2-")) return type; + + // 이미 v2- 접두사가 있으면 그대로 반환 + if (type.startsWith("v2-")) return type; + // 레거시 타입을 v2로 매핑 시도 const v2Type = `v2-${type}`; // v2 버전이 등록되어 있는지 확인 @@ -208,306 +196,8 @@ export const DynamicComponentRenderer: React.FC = // 컴포넌트 타입 변환 완료 - // 🆕 V2 폼 시스템 연동 (최상위에서 한 번만 호출) - // eslint-disable-next-line react-hooks/rules-of-hooks - const v2FormContextLocal = useV2FormOptional(); - - // 🆕 V2 컴포넌트 처리 - if (componentType?.startsWith("v2-")) { - const v2Type = componentType as string; - const config = (component as any).componentConfig || {}; - const fieldName = (component as any).columnName || component.id; - - // V2 시스템이 있으면 거기서 값 가져오기, 없으면 props.formData 사용 - const currentValue = v2FormContextLocal - ? v2FormContextLocal.getValue(fieldName) - : props.formData?.[fieldName]; - - // 🆕 통합 onChange 핸들러 - 양쪽 시스템에 전파 - const handleChange = (value: any) => { - // 1. V2 시스템에 전파 - if (v2FormContextLocal) { - v2FormContextLocal.setValue(fieldName, value); - } - // 2. 레거시 콜백도 호출 (호환성) - if (props.onFormDataChange) { - props.onFormDataChange(fieldName, value); - } - }; - - // 공통 props - const commonProps = { - id: component.id, - label: (component as any).label, - required: (component as any).required, - readonly: (component as any).readonly, - // conditionalDisabled가 true이면 비활성화 - disabled: (component as any).disabled || props.disabledFields?.includes(fieldName) || props.conditionalDisabled, - value: currentValue, - onChange: handleChange, - tableName: (component as any).tableName || props.tableName, - columnName: fieldName, - style: component.style, - size: component.size, - position: component.position, - }; - - switch (v2Type) { - // V2 입력 컴포넌트 - case "v2-input": - return ( - - ); - - // V2 선택 컴포넌트 - case "v2-select": - // v2-select는 항상 테이블 컬럼에서 distinct 값을 자동 로드 - return ( - - ); - - // V2 날짜 컴포넌트 - case "v2-date": - return ( - - ); - - case "v2-list": - // 데이터 소스: config.data > props.tableDisplayData > [] - const listData = config.data?.length > 0 ? config.data : props.tableDisplayData || []; - - return ( - { - // 항상 선택된 데이터를 전달 (modalDataStore에 자동 저장됨) - if (props.onSelectedRowsChange) { - props.onSelectedRowsChange( - rows.map((r: any) => r.id || r.objid), - rows, - props.sortBy, - props.sortOrder, - undefined, - props.tableDisplayData, - ); - } - }} - /> - ); - - case "v2-layout": - return ( - - {children} - - ); - - case "v2-group": - return ( - - {children} - - ); - - case "v2-media": - return ( - - ); - - case "v2-biz": - return ( - - ); - - case "v2-hierarchy": - return ( - - ); - - case "v2-repeater": - // 🆕 저장 설정 추출 (useCustomTable, mainTableName, foreignKeyColumn) - const repeaterTargetTable = config.useCustomTable && config.mainTableName - ? config.mainTableName - : config.dataSource?.tableName; - - return ( - { - // 🆕 formData 업데이트 (부모로 데이터 전달) - if (props.onFormDataChange) { - // _targetTable 메타데이터 추가 - const dataWithTargetTable = data.map((item: any) => ({ - ...item, - _targetTable: repeaterTargetTable, - })); - props.onFormDataChange(component.id || "repeaterData", dataWithTargetTable); - } - }} - onRowClick={(row) => { - }} - onButtonClick={(action, row, buttonConfig) => { - }} - /> - ); - - default: - return ( -
-
-
V2 컴포넌트
-
알 수 없는 타입: {v2Type}
-
-
- ); - } - } + // 🆕 모든 v2- 컴포넌트는 ComponentRegistry에서 통합 처리 + // (v2-input, v2-select, v2-repeat-container 등 모두 동일하게 처리) // 🎯 카테고리 타입 우선 처리 (inputType 또는 webType 확인) const inputType = (component as any).componentConfig?.inputType || (component as any).inputType;