[agent-pipeline] pipe-20260330021427-v9fb round-1

This commit is contained in:
DDD1542
2026-03-30 11:25:52 +09:00
parent 4342056de6
commit b9e3f68fce
47 changed files with 144 additions and 144 deletions
@@ -165,7 +165,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "id",
columnLabel: "ID",
dataType: "integer",
webType: "number",
web_type: "number",
widgetType: "number",
inputType: "auto",
isNullable: "N",
@@ -180,7 +180,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "user_name",
columnLabel: "사용자명",
dataType: "character varying",
webType: "text",
web_type: "text",
widgetType: "text",
inputType: "direct",
isNullable: "N",
@@ -195,7 +195,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "email",
columnLabel: "이메일",
dataType: "character varying",
webType: "email",
web_type: "email",
widgetType: "email",
inputType: "direct",
isNullable: "N",
@@ -210,7 +210,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "age",
columnLabel: "나이",
dataType: "integer",
webType: "number",
web_type: "number",
widgetType: "number",
inputType: "direct",
isNullable: "Y",
@@ -224,7 +224,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "birth_date",
columnLabel: "생년월일",
dataType: "date",
webType: "date",
web_type: "date",
widgetType: "date",
inputType: "direct",
isNullable: "Y",
@@ -238,7 +238,7 @@ const TEST_TABLE_COLUMNS: ColumnInfo[] = [
columnName: "phone",
columnLabel: "전화번호",
dataType: "character varying",
webType: "tel",
web_type: "tel",
widgetType: "tel",
inputType: "direct",
isNullable: "Y",
+1 -1
View File
@@ -10,7 +10,7 @@ export async function GET(request: NextRequest) {
const codeLayouts = LayoutRegistry.getAllLayouts().map((layout) => ({
id: layout.id,
name: layout.name,
nameEng: layout.name_eng,
name_eng: layout.name_eng,
description: layout.description,
category: layout.category,
type: "code", // 코드로 생성된 레이아웃
@@ -106,7 +106,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
const [step, setStep] = useState<"basic" | "template" | "advanced">("basic");
const [formData, setFormData] = useState({
name: "",
nameEng: "",
name_eng: "",
description: "",
category: "" as LayoutCategory | "",
zones: 2,
@@ -124,7 +124,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
setStep("basic");
setFormData({
name: "",
nameEng: "",
name_eng: "",
description: "",
category: "",
zones: 2,
@@ -290,8 +290,8 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
<Input
id="nameEng"
placeholder="예: Sidebar, Dashboard, CardGrid"
value={formData.nameEng}
onChange={(e) => setFormData((prev) => ({ ...prev, nameEng: e.target.value }))}
value={formData.name_eng}
onChange={(e) => setFormData((prev) => ({ ...prev, name_eng: e.target.value }))}
/>
</div>
</div>
@@ -119,7 +119,7 @@ export const WebTypeInput: React.FC<WebTypeInputProps> = ({
if (column.columnName === "manager_name" || webType === "entity") {
console.log("🔍 Entity 필드 디버깅:", {
columnName: column.columnName,
webType: webType,
web_type: webType,
tableName: tableName,
effectiveTableName: effectiveTableName,
referenceTable: column.referenceTable,
@@ -120,7 +120,7 @@ const ActionConditionBuilder: React.FC<ActionConditionBuilderProps> = ({
columnName: col.columnName,
connectionId: col.connectionId,
inputType: col.inputType,
webType: col.webType,
web_type: col.webType,
})),
);
console.log("🔍 ActionConditionBuilder - 코드 타입 컬럼들:", codeFields);
@@ -86,7 +86,7 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
columnName: col.columnName,
connectionId: col.connectionId,
inputType: col.inputType,
webType: col.webType,
web_type: col.webType,
})),
);
console.log("🔍 코드 타입 컬럼들:", codeColumns);
@@ -288,7 +288,7 @@ const ControlConditionStep: React.FC<ControlConditionStepProps> = ({ state, acti
conditionField: condition.field,
selectedField: selectedField,
selectedFieldKeys: selectedField ? Object.keys(selectedField) : [],
webType: selectedField?.webType,
web_type: selectedField?.webType,
inputType: selectedField?.inputType,
connectionId: selectedField?.connectionId,
dataType: selectedField?.dataType,
@@ -1308,7 +1308,7 @@ export default function ScreenDesigner({
columnName: col.columnName,
columnLabel: col.columnLabel,
dataType: col.dataType === "string" ? "varchar" : col.dataType === "number" ? "numeric" : col.dataType,
webType: col.dataType === "number" ? "number" : "text",
web_type: col.dataType === "number" ? "number" : "text",
input_type: "text",
widgetType: col.dataType === "number" ? "number" : "text",
isNullable: "YES",
@@ -1399,7 +1399,7 @@ export default function ScreenDesigner({
columnName: col.column_name,
columnLabel: col.display_name || col.column_label || col.column_name,
dataType: col.data_type || col.db_type,
webType: col.web_type,
web_type: col.web_type,
input_type: inputType,
inputType: inputType,
widgetType,
@@ -1484,7 +1484,7 @@ export default function ScreenDesigner({
columnName: col.column_name,
columnLabel: col.display_name || col.column_label || col.column_name,
dataType: col.data_type || col.db_type,
webType: col.web_type,
web_type: col.web_type,
input_type: inputType,
inputType: inputType,
widgetType,
@@ -3279,7 +3279,7 @@ export default function ScreenDesigner({
console.log("🧩 컴포넌트 드롭:", {
componentName: component.name,
webType: component.webType,
web_type: component.webType,
rawPosition: { x: dropX, y: dropY },
boundedPosition: { x: boundedX, y: boundedY },
snappedPosition,
@@ -3289,10 +3289,10 @@ export default function ScreenDesigner({
console.log("🔍 ScreenDesigner handleComponentDrop:", {
componentName: component.name,
componentId: component.id,
webType: component.webType,
web_type: component.webType,
category: component.category,
defaultConfig: component.defaultConfig,
defaultSize: component.defaultSize,
default_config: component.defaultConfig,
default_size: component.defaultSize,
});
// 컴포넌트별 gridColumns 설정 및 크기 계산
@@ -3395,7 +3395,7 @@ export default function ScreenDesigner({
console.log("🎨 최종 컴포넌트 크기:", {
componentId: component.id,
componentName: component.name,
defaultSize: component.defaultSize,
default_size: component.defaultSize,
finalSize: componentSize,
gridColumns,
});
@@ -3465,7 +3465,7 @@ export default function ScreenDesigner({
layerId: activeLayerIdRef.current || 1, // 🆕 현재 활성 레이어에 추가 (ref 사용)
componentConfig: {
type: component.id, // 새 컴포넌트 시스템의 ID 사용
webType: component.webType, // 웹타입 정보 추가
web_type: component.webType, // 웹타입 정보 추가
...enhancedDefaultConfig,
},
webTypeConfig: getDefaultWebTypeConfig(component.webType),
@@ -488,7 +488,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
tableName,
columnName: "id",
columnLabel: "ID",
webType: "number" as WebType,
web_type: "number" as WebType,
dataType: "BIGINT",
isNullable: "NO",
},
@@ -496,7 +496,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
tableName,
columnName: "name",
columnLabel: "이름",
webType: "text" as WebType,
web_type: "text" as WebType,
dataType: "VARCHAR",
isNullable: "NO",
},
@@ -504,7 +504,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
tableName,
columnName: "description",
columnLabel: "설명",
webType: "textarea" as WebType,
web_type: "textarea" as WebType,
dataType: "TEXT",
isNullable: "YES",
},
@@ -512,7 +512,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
tableName,
columnName: "created_date",
columnLabel: "생성일",
webType: "date" as WebType,
web_type: "date" as WebType,
dataType: "TIMESTAMP",
isNullable: "NO",
},
@@ -520,7 +520,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
tableName,
columnName: "updated_date",
columnLabel: "수정일",
webType: "date" as WebType,
web_type: "date" as WebType,
dataType: "TIMESTAMP",
isNullable: "YES",
},
@@ -949,7 +949,7 @@ function TableColumnAccordion({
try {
const settings: ColumnSettings = {
columnLabel: columns.find(c => c.column_name === editingJoin.columnName)?.display_name || editingJoin.columnName,
webType: "entity",
web_type: "entity",
detailSettings: JSON.stringify({}),
codeCategory: "",
codeValue: "",
@@ -2063,7 +2063,7 @@ function OverviewTab({
gridColumns: 4,
componentConfig: {
type: "text-input",
webType: "text-input",
web_type: "text-input",
placeholder: `${newColumn}을(를) 입력하세요`,
},
webTypeConfig: {},
@@ -70,7 +70,7 @@ export default function SimpleScreenDesigner({ selectedScreen, onBackToList }: S
size: { width: 200, height: 80 },
title: `${type}`,
...(type === "widget" && {
webType: "text" as const,
web_type: "text" as const,
label: "라벨",
placeholder: "입력하세요",
}),
@@ -62,8 +62,8 @@ export function ComponentsPanel({
description: "드롭다운, 콤보박스, 라디오, 체크박스 등 다양한 선택 모드 지원",
category: "input" as ComponentCategory,
tags: ["select", "dropdown", "combobox", "v2"],
defaultSize: { width: 300, height: 40 },
defaultConfig: {
default_size: { width: 300, height: 40 },
default_config: {
mode: "dropdown",
source: "static",
multiple: false,
@@ -79,7 +79,7 @@ export function ComponentsPanel({
description: "행 단위로 데이터를 추가/수정/삭제",
category: "data" as ComponentCategory,
tags: ["repeater", "table", "modal", "button", "v2", "v2"],
defaultSize: { width: 600, height: 300 },
default_size: { width: 600, height: 300 },
},
{
id: "v2-bom-tree",
@@ -87,7 +87,7 @@ export function ComponentsPanel({
description: "BOM 구성을 계층 트리 형태로 조회",
category: "data" as ComponentCategory,
tags: ["bom", "tree", "계층", "제조", "v2"],
defaultSize: { width: 900, height: 600 },
default_size: { width: 900, height: 600 },
},
{
id: "v2-bom-item-editor",
@@ -95,7 +95,7 @@ export function ComponentsPanel({
description: "BOM 하위 품목을 트리 구조로 추가/편집/삭제",
category: "data" as ComponentCategory,
tags: ["bom", "tree", "편집", "하위품목", "제조", "v2"],
defaultSize: { width: 900, height: 400 },
default_size: { width: 900, height: 400 },
},
] as unknown as ComponentDefinition[],
[],
@@ -1091,7 +1091,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
type: "component" as const,
component_config: {
type: "button-primary",
webType: "button",
web_type: "button",
...anyComp.component_config,
},
};
@@ -44,7 +44,7 @@ export interface TemplateComponent {
description: string;
category: "table" | "button" | "form" | "layout" | "chart" | "status" | "file" | "area";
icon: React.ReactNode;
defaultSize: { width: number; height: number };
default_size: { width: number; height: number };
components: Array<{
type: "widget" | "container" | "datatable" | "file" | "area";
widgetType?: string;
@@ -93,7 +93,7 @@ const convertTemplateStandardToComponent = (template: TemplateStandard): Templat
description: template.description || "",
category: template.category as TemplateComponent["category"],
icon: getIconByName(template.icon_name),
defaultSize: template.default_size || { width: 300, height: 200 },
default_size: template.default_size || { width: 300, height: 200 },
components: template.layout_config?.components || [],
};
};
@@ -107,7 +107,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "컬럼 설정, 필터링, 페이지네이션이 포함된 완전한 데이터 테이블",
category: "table",
icon: <Table className="h-4 w-4" />,
defaultSize: { width: 1000, height: 680 },
default_size: { width:1000, height: 680 },
components: [
{
type: "datatable",
@@ -133,7 +133,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "컴포넌트들을 그룹화할 수 있는 기본 박스 형태의 영역",
category: "area",
icon: <Square className="h-4 w-4" />,
defaultSize: { width: 400, height: 300 },
default_size: { width:400, height: 300 },
components: [
{
type: "area",
@@ -171,7 +171,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "그림자와 둥근 모서리가 있는 카드 형태의 영역",
category: "area",
icon: <CreditCard className="h-4 w-4" />,
defaultSize: { width: 400, height: 300 },
default_size: { width:400, height: 300 },
components: [
{
type: "area",
@@ -209,7 +209,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "제목 헤더가 포함된 패널 형태의 영역",
category: "area",
icon: <Layout className="h-4 w-4" />,
defaultSize: { width: 500, height: 400 },
default_size: { width:500, height: 400 },
components: [
{
type: "area",
@@ -237,7 +237,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "내부 컴포넌트들을 격자 형태로 배치하는 영역",
category: "area",
icon: <Grid3x3 className="h-4 w-4" />,
defaultSize: { width: 600, height: 400 },
default_size: { width:600, height: 400 },
components: [
{
type: "area",
@@ -281,7 +281,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "내부 컴포넌트들을 가로로 나란히 배치하는 영역",
category: "area",
icon: <Columns className="h-4 w-4" />,
defaultSize: { width: 600, height: 200 },
default_size: { width:600, height: 200 },
components: [
{
type: "area",
@@ -312,7 +312,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "내부 컴포넌트들을 세로로 순차 배치하는 영역",
category: "area",
icon: <Rows className="h-4 w-4" />,
defaultSize: { width: 300, height: 500 },
default_size: { width:300, height: 500 },
components: [
{
type: "area",
@@ -343,7 +343,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "사이드바와 메인 컨텐츠 영역으로 구분된 레이아웃",
category: "area",
icon: <SidebarOpen className="h-4 w-4" />,
defaultSize: { width: 700, height: 400 },
default_size: { width:700, height: 400 },
components: [
{
type: "area",
@@ -372,7 +372,7 @@ const fallbackTemplates: TemplateComponent[] = [
description: "탭으로 구분된 여러 컨텐츠 영역을 제공하는 레이아웃",
category: "area",
icon: <Folder className="h-4 w-4" />,
defaultSize: { width: 600, height: 400 },
default_size: { width:600, height: 400 },
components: [
{
type: "area",
@@ -400,7 +400,7 @@ const fallbackTemplates: TemplateComponent[] = [
// description: "접고 펼칠 수 있는 섹션들로 구성된 영역",
// category: "area",
// icon: <ChevronDown className="h-4 w-4" />,
// defaultSize: { width: 500, height: 600 },
// default_size: { width: 500, height: 600 },
// components: [
// {
// type: "area",
@@ -584,7 +584,7 @@ export const TemplatesPanel: React.FC<TemplatesPanelProps> = ({ onDragStart }) =
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2 text-xs text-muted-foreground/70">
<span className="bg-primary/10 px-3 py-1 rounded-full font-medium text-primary">
{template.defaultSize.width}×{template.defaultSize.height}
{template.default_size.width}×{template.default_size.height}
</span>
</div>
<span className="text-xs font-medium text-primary capitalize bg-primary/5 px-3 py-1 rounded-full border border-primary/20">
@@ -50,7 +50,7 @@ export const numberingRuleTemplate = {
description: "코드 자동 채번 규칙 설정",
category: "admin" as const,
icon: Hash,
defaultSize: { width: 1200, height: 800 },
default_size: { width: 1200, height: 800 },
components: [
{
type: "numbering-rule" as const,
@@ -44,7 +44,7 @@ export const RatingWidget: React.FC<RatingWidgetProps> = ({
setCurrentRating(rating);
onChange?.(rating);
onEvent?.("change", { value: rating, webType: "rating" });
onEvent?.("change", { value: rating, web_type: "rating" });
};
const handleStarHover = (rating: number) => {
@@ -192,7 +192,7 @@ interface ColumnInfo {
label?: string;
dataType?: string;
inputType?: string;
webType?: string;
web_type?: string;
categoryCode?: string;
}
@@ -290,7 +290,7 @@ export const V2AggregationWidgetConfigPanel: React.FC<V2AggregationWidgetConfigP
// 숫자형 컬럼만
const numericColumns = useMemo(() => {
return columns.filter((col) => {
const inputType = (col.inputType || col.webType || "").toLowerCase();
const inputType = (col.inputType || col.web_type || "").toLowerCase();
return inputType === "number" || inputType === "decimal" || inputType === "integer" ||
inputType === "float" || inputType === "currency" || inputType === "percent";
});
@@ -337,12 +337,12 @@ export const V2AggregationWidgetConfigPanel: React.FC<V2AggregationWidgetConfigP
label: col.displayName || col.columnLabel || col.column_label || col.label || col.columnName || col.column_name,
dataType: col.dataType || col.data_type,
inputType: col.inputType || col.input_type,
webType: col.webType || col.web_type,
web_type: col.webType || col.web_type,
categoryCode: col.categoryCode || col.category_code,
}));
setColumns(mapped);
const categoryCols = mapped.filter(
(c: ColumnInfo) => c.inputType === "category" || c.webType === "category"
(c: ColumnInfo) => c.inputType === "category" || c.web_type === "category"
);
if (categoryCols.length > 0) loadCategoryOptions(categoryCols);
} else {
@@ -428,7 +428,7 @@ export const V2AggregationWidgetConfigPanel: React.FC<V2AggregationWidgetConfigP
const isCategoryColumn = useCallback((columnName: string) => {
const col = columns.find((c) => c.columnName === columnName);
return col?.inputType === "category" || col?.webType === "category";
return col?.inputType === "category" || col?.web_type === "category";
}, [columns]);
// ─── 집계 항목 CRUD ───
@@ -896,7 +896,7 @@ export const V2AggregationWidgetConfigPanel: React.FC<V2AggregationWidgetConfigP
onValueChange={(value) => {
updateFilter(filter.id, { columnName: value, staticValue: "" });
const col = columns.find((c) => c.columnName === value);
if (col && (col.inputType === "category" || col.webType === "category")) {
if (col && (col.inputType === "category" || col.web_type === "category")) {
loadCategoryOptions([col]);
}
}}
+40 -40
View File
@@ -41,12 +41,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 입력",
description: "텍스트, 숫자, 비밀번호, 슬라이더, 컬러 등 다양한 입력 타입을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "text" as WebType,
web_type: "text" as WebType,
component: V2Input as any,
tags: ["input", "text", "number", "password", "slider", "color", "v2"],
defaultSize: { width: 200, height: 40 },
configPanel: V2InputConfigPanel as any,
defaultConfig: {
default_size: { width: 200, height: 40 },
config_panel: V2InputConfigPanel as any,
default_config: {
inputType: "text",
format: "none",
placeholder: "",
@@ -57,12 +57,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 선택",
description: "드롭다운, 라디오, 체크박스, 태그, 토글 등 다양한 선택 방식을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "select" as WebType,
web_type: "select" as WebType,
component: V2Select as any,
tags: ["select", "dropdown", "radio", "checkbox", "toggle", "v2"],
defaultSize: { width: 200, height: 40 },
configPanel: V2SelectConfigPanel as any,
defaultConfig: {
default_size: { width: 200, height: 40 },
config_panel: V2SelectConfigPanel as any,
default_config: {
mode: "dropdown",
source: "static",
options: [],
@@ -73,12 +73,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 날짜",
description: "날짜, 시간, 날짜시간, 날짜 범위 등을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "date" as WebType,
web_type: "date" as WebType,
component: V2Date as any,
tags: ["date", "time", "datetime", "datepicker", "v2"],
defaultSize: { width: 200, height: 40 },
configPanel: V2DateConfigPanel as any,
defaultConfig: {
default_size: { width: 200, height: 40 },
config_panel: V2DateConfigPanel as any,
default_config: {
dateType: "date",
format: "YYYY-MM-DD",
},
@@ -88,12 +88,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 목록",
description: "테이블, 카드, 칸반, 리스트 등 다양한 데이터 표시 방식을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "list" as WebType,
web_type: "list" as WebType,
component: V2List as any,
tags: ["list", "table", "card", "kanban", "data", "v2"],
defaultSize: { width: 600, height: 400 },
configPanel: V2ListConfigPanel as any,
defaultConfig: {
default_size: { width: 600, height: 400 },
config_panel: V2ListConfigPanel as any,
default_config: {
viewMode: "table",
source: "static",
columns: [],
@@ -106,12 +106,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 레이아웃",
description: "그리드, 분할 패널, 플렉스 등 다양한 레이아웃 구조를 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "container" as WebType,
web_type: "container" as WebType,
component: V2Layout as any,
tags: ["layout", "grid", "split", "flex", "container", "v2"],
defaultSize: { width: 400, height: 300 },
configPanel: V2LayoutConfigPanel as any,
defaultConfig: {
default_size: { width: 400, height: 300 },
config_panel: V2LayoutConfigPanel as any,
default_config: {
layoutType: "grid",
columns: 2,
gap: "16",
@@ -123,12 +123,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 그룹",
description: "탭, 아코디언, 섹션, 모달 등 그룹 요소를 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "group" as WebType,
web_type: "group" as WebType,
component: V2Group as any,
tags: ["group", "tabs", "accordion", "section", "modal", "v2"],
defaultSize: { width: 400, height: 300 },
configPanel: V2GroupConfigPanel as any,
defaultConfig: {
default_size: { width: 400, height: 300 },
config_panel: V2GroupConfigPanel as any,
default_config: {
groupType: "section",
title: "",
collapsible: false,
@@ -140,12 +140,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 미디어",
description: "이미지, 비디오, 오디오, 파일 업로드 등을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "file" as WebType,
web_type: "file" as WebType,
component: V2Media as any,
tags: ["media", "image", "video", "audio", "file", "upload", "v2"],
defaultSize: { width: 300, height: 200 },
configPanel: V2MediaConfigPanel as any,
defaultConfig: {
default_size: { width: 300, height: 200 },
config_panel: V2MediaConfigPanel as any,
default_config: {
mediaType: "image",
multiple: false,
preview: true,
@@ -156,12 +156,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 비즈니스",
description: "플로우, 랙, 채번규칙 등 비즈니스 기능을 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "custom" as WebType,
web_type: "custom" as WebType,
component: V2Biz as any,
tags: ["business", "flow", "rack", "numbering", "category", "v2"],
defaultSize: { width: 500, height: 400 },
configPanel: V2BizConfigPanel as any,
defaultConfig: {
default_size: { width: 500, height: 400 },
config_panel: V2BizConfigPanel as any,
default_config: {
bizType: "flow",
},
},
@@ -170,12 +170,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 계층",
description: "트리, 조직도, BOM, 연쇄 선택박스 등 계층 구조를 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "tree" as WebType,
web_type: "tree" as WebType,
component: V2Hierarchy as any,
tags: ["hierarchy", "tree", "org-chart", "bom", "cascading", "v2"],
defaultSize: { width: 400, height: 400 },
configPanel: V2HierarchyConfigPanel as any,
defaultConfig: {
default_size: { width: 400, height: 400 },
config_panel: V2HierarchyConfigPanel as any,
default_config: {
hierarchyType: "tree",
viewMode: "tree",
dataSource: "static",
@@ -186,12 +186,12 @@ const v2ComponentDefinitions: ComponentDefinition[] = [
name: "통합 반복",
description: "인라인 테이블, 모달, 버튼 등 다양한 반복 데이터 관리를 지원하는 통합 컴포넌트",
category: ComponentCategory.V2,
webType: "entity" as WebType,
web_type: "entity" as WebType,
component: V2Repeater as any,
tags: ["repeater", "table", "modal", "button", "data", "v2"],
defaultSize: { width: 600, height: 300 },
configPanel: V2RepeaterConfigPanel as any,
defaultConfig: {
default_size: { width: 600, height: 300 },
config_panel: V2RepeaterConfigPanel as any,
default_config: {
renderMode: "inline",
dataSource: {
tableName: "",
@@ -1076,7 +1076,7 @@ export const RepeaterConfigPanel: React.FC<RepeaterConfigPanelProps> = ({
columnName: column.columnName,
input_type: col.input_type,
inputType: col.inputType,
webType: col.webType,
web_type: col.webType,
widgetType: col.widgetType,
finalType: fieldType,
});
+1 -1
View File
@@ -150,7 +150,7 @@ export const entityJoinApi = {
tableName: string,
columnName: string,
settings: {
webType: string;
web_type: string;
referenceTable?: string;
referenceColumn?: string;
displayColumn?: string;
+10 -10
View File
@@ -102,7 +102,7 @@ export interface ColumnInfo {
displayName: string;
dataType: string;
dbType: string;
webType: string;
web_type: string;
isNullable: boolean;
isPrimaryKey: boolean;
defaultValue?: string;
@@ -203,7 +203,7 @@ export const getColumnsFromConnection = async (connectionId: number, tableName:
return {
...col,
columnName: columnName,
webType: isCodeColumn ? "code" : col.web_type || mapDataTypeToWebType(col.data_type),
web_type: isCodeColumn ? "code" : col.web_type || mapDataTypeToWebType(col.data_type),
codeCategory: isCodeColumn ? inferCodeCategory(columnName) : col.code_category,
};
})
@@ -248,7 +248,7 @@ export const getColumnsFromConnection = async (connectionId: number, tableName:
displayName: col.column_comment || col.display_name || columnName,
dataType: dataType,
dbType: dataType,
webType: isCodeColumn ? "code" : mapDataTypeToWebType(dataType),
web_type: isCodeColumn ? "code" : mapDataTypeToWebType(dataType),
isNullable: col.is_nullable === "YES",
columnDefault: col.column_default,
description: col.column_comment || col.description,
@@ -298,7 +298,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "id",
displayName: "ID",
dataType: "NUMBER",
webType: "number",
web_type: "number",
isNullable: false,
isPrimaryKey: true,
columnComment: "고유 식별자",
@@ -307,7 +307,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "name",
displayName: "이름",
dataType: "VARCHAR",
webType: "text",
web_type: "text",
isNullable: false,
isPrimaryKey: false,
columnComment: "이름",
@@ -316,7 +316,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "status",
displayName: "상태",
dataType: "VARCHAR",
webType: "code",
web_type: "code",
isNullable: true,
isPrimaryKey: false,
columnComment: "상태 코드",
@@ -326,7 +326,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "created_date",
displayName: "생성일시",
dataType: "TIMESTAMP",
webType: "datetime",
web_type: "datetime",
isNullable: true,
isPrimaryKey: false,
columnComment: "생성일시",
@@ -335,7 +335,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "updated_date",
displayName: "수정일시",
dataType: "TIMESTAMP",
webType: "datetime",
web_type: "datetime",
isNullable: true,
isPrimaryKey: false,
columnComment: "수정일시",
@@ -348,7 +348,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "email",
displayName: "이메일",
dataType: "VARCHAR",
webType: "email",
web_type: "email",
isNullable: true,
isPrimaryKey: false,
columnComment: "이메일 주소",
@@ -360,7 +360,7 @@ const getMockColumnsForTable = (tableName: string): ColumnInfo[] => {
columnName: "price",
displayName: "가격",
dataType: "DECIMAL",
webType: "decimal",
web_type: "decimal",
isNullable: true,
isPrimaryKey: false,
columnComment: "상품 가격",
+1 -1
View File
@@ -544,7 +544,7 @@ export const tableTypeApi = {
detailSettings?: Record<string, any>,
): Promise<void> => {
await apiClient.put(`/table-management/tables/${tableName}/columns/${columnName}/web-type`, {
webType,
web_type: webType,
detailSettings,
});
},
+2 -2
View File
@@ -208,7 +208,7 @@ export class ComponentRegistry {
count,
})),
by_web_type: Array.from(webTypeMap.entries()).map(([webType, count]) => ({
webType,
web_type: webType,
count,
})),
byAuthor: Array.from(authorMap.entries()).map(([author, count]) => ({
@@ -414,7 +414,7 @@ Hot Reload 제어 (비동기):
// React 컴포넌트는 직렬화할 수 없으므로 제외
component: definition.component.name,
renderer: definition.renderer?.name,
configPanel: definition.config_panel?.name,
config_panel: definition.config_panel?.name,
},
})),
};
@@ -523,7 +523,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
tableName,
columnName,
inputType: "category",
webType: "category",
web_type: "category",
};
const catStyle = catNeedsExternalHorizLabel
@@ -64,7 +64,7 @@ export const DynamicWebTypeRenderer: React.FC<DynamicComponentProps> = ({
return {
...props,
webTypeConfig: mergedConfig,
webType: webType,
web_type: webType,
onEvent: onEvent,
};
}, [props, mergedConfig, webType, onEvent]);
+1 -1
View File
@@ -256,7 +256,7 @@ export class WebTypeRegistry {
...def,
// 함수/컴포넌트는 제외하고 메타데이터만 내보내기
component: def.component.name || "Unknown",
configPanel: def.configPanel.name || "Unknown",
config_panel: def.configPanel.name || "Unknown",
},
]),
),
@@ -1138,7 +1138,7 @@ export const SplitPanelLayoutConfigPanel: React.FC<SplitPanelLayoutConfigPanelPr
columnName: col.column_name,
columnLabel: col.display_name || col.column_label || col.column_name,
dataType: col.data_type,
webType: col.web_type,
web_type: col.web_type,
input_type: col.input_type,
widgetType: col.widget_type || col.web_type,
isNullable: col.is_nullable,
@@ -1182,7 +1182,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
cached.columns.forEach((col: any) => {
labels[col.column_name] = col.display_name || col.comment || col.column_name;
meta[col.column_name] = {
webType: col.web_type,
web_type: col.web_type,
codeCategory: col.code_category,
inputType: inputTypeMap[col.column_name], // 캐시된 inputType 사용!
};
@@ -1214,7 +1214,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
columns.forEach((col: any) => {
labels[col.column_name] = col.display_name || col.comment || col.column_name;
meta[col.column_name] = {
webType: col.web_type,
web_type: col.web_type,
codeCategory: col.code_category,
inputType: inputTypeMap[col.column_name],
};
@@ -541,7 +541,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
columnName: column.columnName,
found: !!tableColumn,
inputType: tableColumn?.input_type,
webType: tableColumn?.web_type,
web_type: tableColumn?.web_type,
});
// 엔티티 타입인 경우 isEntityJoin 플래그 설정 (input_type 또는 web_type 확인)
@@ -3032,7 +3032,7 @@ PopComponentRegistry.registerComponent({
category: "action",
icon: "MousePointerClick",
component: PopButtonComponent,
configPanel: PopButtonConfigPanel,
config_panel: PopButtonConfigPanel,
preview: PopButtonPreviewComponent,
defaultProps: {
label: "버튼",
@@ -37,7 +37,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "LayoutGrid",
component: PopCardListV2Component,
configPanel: PopCardListV2ConfigPanel,
config_panel: PopCardListV2ConfigPanel,
preview: PopCardListV2PreviewComponent,
defaultProps: defaultConfig,
connectionMeta: {
@@ -55,7 +55,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "LayoutGrid",
component: PopCardListComponent,
configPanel: PopCardListConfigPanel,
config_panel: PopCardListConfigPanel,
preview: PopCardListPreviewComponent,
defaultProps: defaultConfig,
connectionMeta: {
@@ -19,7 +19,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "BarChart3",
component: PopDashboardComponent,
configPanel: PopDashboardConfigPanel,
config_panel: PopDashboardConfigPanel,
preview: PopDashboardPreviewComponent,
defaultProps: {
items: [],
@@ -57,7 +57,7 @@ PopComponentRegistry.registerComponent({
category: "input",
icon: "TextCursorInput",
component: PopFieldComponent,
configPanel: PopFieldConfigPanel,
config_panel: PopFieldConfigPanel,
preview: PopFieldPreviewComponent,
defaultProps: DEFAULT_FIELD_CONFIG,
connectionMeta: {
@@ -971,7 +971,7 @@ PopComponentRegistry.registerComponent({
category: "action",
icon: "MousePointer",
component: PopIconComponent,
configPanel: PopIconConfigPanel,
config_panel: PopIconConfigPanel,
preview: PopIconPreviewComponent,
defaultProps: {
iconType: "quick",
@@ -319,7 +319,7 @@ PopComponentRegistry.registerComponent({
category: "action",
icon: "UserCircle",
component: PopProfileComponent,
configPanel: PopProfileConfigPanel,
config_panel: PopProfileConfigPanel,
preview: PopProfilePreview,
defaultProps: {
avatarSize: "md",
@@ -678,7 +678,7 @@ PopComponentRegistry.registerComponent({
category: "input",
icon: "ScanLine",
component: PopScannerComponent,
configPanel: PopScannerConfigPanel,
config_panel: PopScannerConfigPanel,
preview: PopScannerPreview,
defaultProps: DEFAULT_SCANNER_CONFIG,
connectionMeta: {
@@ -31,7 +31,7 @@ PopComponentRegistry.registerComponent({
category: "input",
icon: "Search",
component: PopSearchComponent,
configPanel: PopSearchConfigPanel,
config_panel: PopSearchConfigPanel,
preview: PopSearchPreviewComponent,
defaultProps: DEFAULT_SEARCH_CONFIG,
connectionMeta: {
@@ -52,7 +52,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "BarChart3",
component: PopStatusBarComponent,
configPanel: PopStatusBarConfigPanel,
config_panel: PopStatusBarConfigPanel,
preview: PopStatusBarPreviewComponent,
defaultProps: DEFAULT_STATUS_BAR_CONFIG,
connectionMeta: {
@@ -30,7 +30,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "List",
component: PopStringListComponent,
configPanel: PopStringListConfigPanel,
config_panel: PopStringListConfigPanel,
preview: PopStringListPreviewComponent,
defaultProps: defaultConfig,
connectionMeta: {
@@ -836,7 +836,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "FileText",
component: PopTextComponent,
configPanel: PopTextConfigPanel,
config_panel: PopTextConfigPanel,
preview: PopTextPreviewComponent,
defaultProps: { textType: "text", fontSize: "base" },
touchOptimized: true,
@@ -19,7 +19,7 @@ PopComponentRegistry.registerComponent({
category: "display",
icon: "ClipboardCheck",
component: PopWorkDetailComponent,
configPanel: PopWorkDetailConfigPanel,
config_panel: PopWorkDetailConfigPanel,
preview: PopWorkDetailPreviewComponent,
defaultProps: defaultConfig,
connectionMeta: {
+4 -4
View File
@@ -15,9 +15,9 @@ export interface WebTypeDefinition {
/** 렌더링 컴포넌트 */
component: React.ComponentType<any>;
/** 설정 패널 컴포넌트 */
configPanel: React.ComponentType<WebTypeConfigPanelProps>;
config_panel: React.ComponentType<WebTypeConfigPanelProps>;
/** 기본 설정값 */
defaultConfig: Record<string, any>;
default_config: Record<string, any>;
/** 활성화 상태 */
isActive: boolean;
/** 아이콘 (선택사항) */
@@ -41,7 +41,7 @@ export interface ButtonActionDefinition {
/** 핸들러 함수 */
handler: (context: ButtonActionContext) => void | Promise<void>;
/** 기본 설정값 */
defaultConfig: Record<string, any>;
default_config: Record<string, any>;
/** 활성화 상태 */
isActive: boolean;
/** 아이콘 (선택사항) */
@@ -136,7 +136,7 @@ export type ButtonActionCategory = "save" | "delete" | "navigate" | "export" | "
*/
export interface DynamicComponentProps {
/** 웹타입 ID */
webType: string;
web_type: string;
/** 컴포넌트 속성 */
props: Record<string, any>;
/** 컴포넌트 설정 */
@@ -356,7 +356,7 @@ export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> =
columnName: col.columnName || col.column_name,
columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
dataType: col.dataType || col.data_type || col.dbType,
webType: col.webType || col.web_type,
web_type: col.webType || col.web_type,
input_type: col.inputType || col.input_type,
widgetType: col.widgetType || col.widget_type || col.webType || col.web_type,
isNullable: col.isNullable || col.is_nullable,
+1 -1
View File
@@ -191,7 +191,7 @@ export function convertV2ToLegacy(v2Layout: LayoutV2 | null): LegacyLayoutData |
hidden: overrides.hidden,
codeCategory: overrides.codeCategory,
inputType: overrides.inputType,
webType: overrides.webType,
web_type: overrides.webType,
autoFill: overrides.autoFill,
style: overrides.style || {},
webTypeConfig: overrides.webTypeConfig || {},
+1 -1
View File
@@ -247,7 +247,7 @@ export function createV2ConfigFromColumn(column: {
const componentConfig: Record<string, any> = {
...mapping.config,
webType: column.widgetType, // 원본 웹타입 보존
web_type: column.widgetType, // 원본 웹타입 보존
// DB의 input_type이 있으면 사용, 없으면 매핑에서 가져온 값 유지
inputType: column.inputType || mapping.config.inputType || "text",
placeholder: parsedDetailSettings.placeholder || column.columnLabel || column.columnName,
@@ -95,7 +95,7 @@ export class APIIntegrationTestSuite {
displayName: col.displayName,
dataType: col.dataType,
dbType: col.dbType,
webType: isWebType(col.webType) ? (col.webType as WebType) : "text",
web_type: isWebType(col.webType) ? (col.webType as WebType) : "text",
inputType: col.inputType || "direct",
detailSettings: col.detailSettings ? JSON.parse(col.detailSettings) : {},
description: col.description || "",
+2 -2
View File
@@ -329,7 +329,7 @@ export class TypeSafetyTestSuite {
displayName: "사용자명",
dataType: "varchar",
dbType: "character varying(100)",
webType: "text", // string 타입 (백엔드)
web_type: "text", // string 타입 (백엔드)
inputType: "direct",
detailSettings: JSON.stringify({ maxLength: 100 }),
description: "사용자의 이름을 저장하는 컬럼",
@@ -345,7 +345,7 @@ export class TypeSafetyTestSuite {
displayName: backendColumnInfo.displayName,
dataType: backendColumnInfo.dataType,
dbType: backendColumnInfo.dbType,
webType: isWebType(backendColumnInfo.webType) ? (backendColumnInfo.webType as WebType) : "text", // 안전한 타입 변환
web_type: isWebType(backendColumnInfo.webType) ? (backendColumnInfo.webType as WebType) : "text", // 안전한 타입 변환
inputType: backendColumnInfo.inputType,
detailSettings: JSON.parse(backendColumnInfo.detailSettings || "{}"),
description: backendColumnInfo.description,