Consolidate canonical input migration
Build & Deploy to K8s / build-and-deploy (push) Failing after 11m17s
Build & Deploy to K8s / build-and-deploy (push) Failing after 11m17s
Remove legacy v2 input/select and file/media runtimes, add canonical option/file loaders, and document Codex handoff.
This commit is contained in:
@@ -336,7 +336,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
}, [screenTableName]);
|
||||
|
||||
// 컴포넌트 타입 추출 - 새 시스템에서는 componentType 속성 사용, 레거시는 type 사용
|
||||
// 🆕 V2 레이아웃의 경우 url에서 컴포넌트 타입 추출 (예: "@/lib/registry/components/v2-input" → "v2-input")
|
||||
// 레거시 저장 데이터의 url 마지막 세그먼트를 컴포넌트 타입으로 사용
|
||||
const extractTypeFromUrl = (url: string | undefined): string | undefined => {
|
||||
if (!url) return undefined;
|
||||
// url의 마지막 세그먼트를 컴포넌트 타입으로 사용
|
||||
@@ -349,8 +349,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
|
||||
// 레거시 타입을 v2 컴포넌트로 매핑 (v2 컴포넌트가 없으면 원본 유지)
|
||||
// ★ 2026-04-11: INVYONE 통합 컴포넌트(Phase A~) 는 v2- 매핑에서 제외.
|
||||
// 예: 'input' 을 'v2-input' 으로 리다이렉트하면 새 통합 Input 대신 기존
|
||||
// v2-input 이 렌더되어 설정 변경이 안 반영됨.
|
||||
// ★ 2026-05-12: V2 입력/선택은 완전 폐기 — 매핑/alias/fallback 모두 제거.
|
||||
const INVYONE_UNIFIED_IDS = new Set([
|
||||
"divider",
|
||||
"title",
|
||||
@@ -373,8 +372,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
"v2-button-primary": "button", "button-primary": "button",
|
||||
// search
|
||||
"v2-table-search-widget": "search", "table-search-widget": "search",
|
||||
// input
|
||||
"v2-input": "input", "v2-select": "input",
|
||||
// input (V2 입력/선택은 Phase D.2 에서 완전 폐기 — alias 제거)
|
||||
"text-input": "input", "number-input": "input", "date-input": "input",
|
||||
"select-basic": "input", "checkbox-basic": "input", "textarea-basic": "input",
|
||||
"slider-basic": "input", "radio-basic": "input", "toggle-switch": "input",
|
||||
@@ -415,9 +413,8 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
|
||||
const mappedComponentType = mapToV2ComponentType(rawComponentType);
|
||||
|
||||
// ★ canonical 라우팅: fieldType / dbInputType → v2-input/v2-select 강제 swap 제거됨.
|
||||
// InvField (kind/type/format) 모델이 진실의 원천. mappedComponentType 그대로 사용.
|
||||
// (이전 분기는 brumb 변경 시 InputComponent → V2Input swap 의 원인이었음)
|
||||
// ★ canonical 라우팅: InvField (kind/type/format) 모델이 진실의 원천.
|
||||
// fieldType / dbInputType 기반 강제 swap 분기는 Phase 3 / D.2 에서 제거됨.
|
||||
const componentType = mappedComponentType;
|
||||
|
||||
// 🆕 조건부 렌더링 체크 (conditionalConfig)
|
||||
@@ -478,7 +475,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
}
|
||||
|
||||
// 🆕 모든 v2- 컴포넌트는 ComponentRegistry에서 통합 처리
|
||||
// (v2-input, v2-select, v2-repeat-container 등 모두 동일하게 처리)
|
||||
// (v2-repeat-container 등. V2 입력/선택은 Phase D.2 에서 폐기됨)
|
||||
|
||||
// 🎯 카테고리 타입 우선 처리 (inputType 또는 webType 확인)
|
||||
// DB input_type이 "text" 등 비-카테고리로 변경된 경우 이 분기를 건너뜀
|
||||
@@ -502,117 +499,11 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
// webType도 DB 값으로 대체 (레이아웃에 webType: "category" 하드코딩되어 있을 수 있음)
|
||||
const effectiveWebType = dbFieldInputType || webType;
|
||||
|
||||
const componentMode = (component as any).componentConfig?.mode || (component as any).config?.mode;
|
||||
const isMultipleSelect = (component as any).componentConfig?.multiple;
|
||||
const nonDropdownModes = ["radio", "check", "checkbox", "tag", "tagbox", "toggle", "swap", "combobox"];
|
||||
const isNonDropdownMode = componentMode && nonDropdownModes.includes(componentMode);
|
||||
const shouldUseV2Select =
|
||||
componentType === "select-basic" || componentType === "v2-select" || isNonDropdownMode || isMultipleSelect;
|
||||
|
||||
// DB input_type이 비-카테고리(text 등)로 확인된 경우, 레이아웃에 category가 남아있어도 카테고리 분기 강제 스킵
|
||||
// dbFieldInputType이 있으면(캐시 로드됨) 그 값으로 판단, 없으면 기존 로직 유지
|
||||
const isDbConfirmedNonCategory = dbFieldInputType && !["category", "entity", "select"].includes(dbFieldInputType);
|
||||
|
||||
if (!isDbConfirmedNonCategory && (inputType === "category" || effectiveWebType === "category") && tableName && columnName && shouldUseV2Select) {
|
||||
// V2SelectRenderer로 직접 렌더링 (카테고리 + 고급 모드)
|
||||
try {
|
||||
const { V2SelectRenderer } = require("@/lib/registry/components/v2-select/V2SelectRenderer");
|
||||
const fieldName = columnName || component.id;
|
||||
|
||||
// 수평 라벨 감지
|
||||
const catLabelDisplay = component.style?.label_display ?? (component as any).labelDisplay;
|
||||
const catLabelPosition = component.style?.label_position;
|
||||
const catLabelText =
|
||||
catLabelDisplay === true || catLabelDisplay === "true"
|
||||
? component.style?.label_text || (component as any).label || component.component_config?.label
|
||||
: undefined;
|
||||
const catNeedsExternalHorizLabel = !!(
|
||||
catLabelText &&
|
||||
(catLabelPosition === "left" || catLabelPosition === "right")
|
||||
);
|
||||
|
||||
const selectComponent = {
|
||||
...component,
|
||||
component_config: {
|
||||
...component.component_config,
|
||||
mode: componentMode || "dropdown",
|
||||
source: "category",
|
||||
categoryTable: tableName,
|
||||
categoryColumn: columnName,
|
||||
},
|
||||
tableName,
|
||||
columnName,
|
||||
inputType: "category",
|
||||
web_type: "category",
|
||||
};
|
||||
|
||||
const catStyle = catNeedsExternalHorizLabel
|
||||
? {
|
||||
...(component as any).style,
|
||||
label_display: false,
|
||||
label_position: "top" as const,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
borderWidth: undefined,
|
||||
borderColor: undefined,
|
||||
borderStyle: undefined,
|
||||
border: undefined,
|
||||
borderRadius: undefined,
|
||||
}
|
||||
: (component as any).style;
|
||||
const catSize = catNeedsExternalHorizLabel
|
||||
? { ...(component as any).size, width: undefined }
|
||||
: (component as any).size;
|
||||
|
||||
const rendererProps = {
|
||||
component: selectComponent,
|
||||
formData: props.formData,
|
||||
onFormDataChange: props.onFormDataChange,
|
||||
isDesignMode: props.isDesignMode,
|
||||
isInteractive: props.isInteractive ?? !props.isDesignMode,
|
||||
tableName,
|
||||
style: catStyle,
|
||||
size: catSize,
|
||||
};
|
||||
|
||||
const rendererInstance = new V2SelectRenderer(rendererProps);
|
||||
const renderedCatSelect = rendererInstance.render();
|
||||
|
||||
if (catNeedsExternalHorizLabel) {
|
||||
const labelGap = component.style?.label_gap || "8px";
|
||||
const labelFontSize = component.style?.label_font_size || "14px";
|
||||
const labelColor = getAdaptiveLabelColor(component.style?.label_color);
|
||||
const labelFontWeight = component.style?.label_font_weight || "500";
|
||||
const isRequired =
|
||||
component.required || (component as any).required || isColumnRequiredByMeta(tableName, columnName);
|
||||
const isLeft = catLabelPosition === "left";
|
||||
return (
|
||||
<div style={{ position: "relative", width: "100%", height: "100%" }}>
|
||||
<label
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
...(isLeft ? { right: "100%", marginRight: labelGap } : { left: "100%", marginLeft: labelGap }),
|
||||
fontSize: labelFontSize,
|
||||
color: labelColor,
|
||||
fontWeight: labelFontWeight,
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
className="text-sm font-medium"
|
||||
>
|
||||
{catLabelText}
|
||||
{isRequired && <span className="ml-0.5 text-amber-500">*</span>}
|
||||
</label>
|
||||
<div style={{ width: "100%", height: "100%" }}>{renderedCatSelect}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return renderedCatSelect;
|
||||
} catch (error) {
|
||||
console.error("❌ V2SelectRenderer 로드 실패:", error);
|
||||
}
|
||||
} else if (!isDbConfirmedNonCategory && (inputType === "category" || effectiveWebType === "category") && tableName && columnName) {
|
||||
if (!isDbConfirmedNonCategory && (inputType === "category" || effectiveWebType === "category") && tableName && columnName) {
|
||||
try {
|
||||
const { CategorySelectComponent } = require("@/lib/registry/components/category-select/CategorySelectComponent");
|
||||
const fieldName = columnName || component.id;
|
||||
@@ -821,14 +712,13 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
};
|
||||
|
||||
// 🆕 엔티티 검색 컴포넌트는 componentConfig.tableName을 사용해야 함 (화면 테이블이 아닌 검색 대상 테이블)
|
||||
// 🆕 v2-input도 포함 (채번 규칙 조회 시 tableName 필요)
|
||||
// (V2 입력 폐기로 채번 규칙 조회용 분기 제거됨 — input canonical 은 자체 props 로 tableName 받음)
|
||||
const useConfigTableName =
|
||||
componentType === "entity-search-input" ||
|
||||
componentType === "autocomplete-search-input" ||
|
||||
componentType === "modal-repeater-table" ||
|
||||
componentType === "v2-input";
|
||||
componentType === "modal-repeater-table";
|
||||
|
||||
// 🆕 v2-input 등의 라벨 표시 로직 (InteractiveScreenViewerDynamic과 동일한 부정형 체크)
|
||||
// 🆕 라벨 표시 로직 (InteractiveScreenViewerDynamic 과 동일한 부정형 체크)
|
||||
const labelDisplay = component.style?.label_display ?? (component as any).labelDisplay;
|
||||
const effectiveLabel =
|
||||
labelDisplay !== false && labelDisplay !== "false"
|
||||
|
||||
Reference in New Issue
Block a user