Merge commit '80be7c5a7600c080f897823837890fbdef58f565' into jskim-node

Made-with: Cursor

# Conflicts:
#	.cursor/agents/pipeline-common-rules.md
#	.cursor/agents/pipeline-frontend.md
#	.cursor/agents/pipeline-ui.md
This commit is contained in:
kjs
2026-03-12 15:53:20 +09:00
125 changed files with 32277 additions and 10461 deletions

View File

@@ -304,9 +304,16 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
return type;
};
const componentType = mapToV2ComponentType(rawComponentType);
const mappedComponentType = mapToV2ComponentType(rawComponentType);
// 컴포넌트 타입 변환 완료
// fieldType 기반 동적 컴포넌트 전환 (통합 필드 설정 패널에서 설정된 값)
const componentType = (() => {
const ft = (component as any).componentConfig?.fieldType;
if (!ft) return mappedComponentType;
if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(ft)) return "v2-input";
if (["select", "category", "entity"].includes(ft)) return "v2-select";
return mappedComponentType;
})();
// 🆕 조건부 렌더링 체크 (conditionalConfig)
// componentConfig 또는 overrides에서 conditionalConfig를 가져와서 formData와 비교
@@ -740,7 +747,21 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
// 컬럼 메타데이터 기반 componentConfig 병합 (DB 최신 설정 우선)
const isEntityJoinColumn = fieldName?.includes(".");
const baseColumnName = isEntityJoinColumn ? undefined : fieldName;
const mergedComponentConfig = mergeColumnMeta(screenTableName, baseColumnName, component.componentConfig || {});
const rawMergedConfig = mergeColumnMeta(screenTableName, baseColumnName, component.componentConfig || {});
// fieldType이 설정된 경우, source/inputType 보조 속성 자동 보완
const mergedComponentConfig = (() => {
const ft = rawMergedConfig?.fieldType;
if (!ft) return rawMergedConfig;
const patch: Record<string, any> = {};
if (["select", "category", "entity"].includes(ft) && !rawMergedConfig.source) {
patch.source = ft === "category" ? "category" : ft === "entity" ? "entity" : "static";
}
if (["text", "number", "password", "textarea", "slider", "color", "numbering"].includes(ft) && !rawMergedConfig.inputType) {
patch.inputType = ft;
}
return Object.keys(patch).length > 0 ? { ...rawMergedConfig, ...patch } : rawMergedConfig;
})();
// NOT NULL 기반 필수 여부를 component.required에 반영
const notNullRequired = isColumnRequiredByMeta(screenTableName, baseColumnName);
@@ -757,17 +778,16 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
onClick,
onDragStart,
onDragEnd,
size: needsExternalHorizLabel
? { ...(component.size || newComponent.defaultSize), width: undefined, height: undefined }
: component.size || newComponent.defaultSize,
position: component.position,
config: mergedComponentConfig,
componentConfig: mergedComponentConfig,
// componentConfig의 모든 속성을 props로 spread (tableName, displayField 등)
// componentConfig spread를 먼저 → 이후 명시적 속성이 override
...(mergedComponentConfig || {}),
// 🔧 style은 맨 마지막에! (componentConfig.style이 있어도 mergedStyle이 우선)
// size/position/style/label은 componentConfig spread 이후에 설정 (덮어쓰기 방지)
size: needsExternalHorizLabel
? { ...(component.size || newComponent.defaultSize), width: undefined }
: component.size || newComponent.defaultSize,
position: component.position,
style: mergedStyle,
// 수평 라벨 → 외부에서 처리하므로 label 전달 안 함
label: needsExternalHorizLabel ? undefined : effectiveLabel,
// NOT NULL 메타데이터 포함된 필수 여부 (V2Hierarchy 등 직접 props.required 참조하는 컴포넌트용)
required: effectiveRequired,

View File

@@ -1,6 +1,6 @@
"use client";
import React from "react";
import React, { useState } from "react";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import {
@@ -13,7 +13,18 @@ import {
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Plus, X } from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Plus, X, Check, ChevronsUpDown } from "lucide-react";
import { cn } from "@/lib/utils";
import { ConfigFieldDefinition, ConfigOption } from "./ConfigPanelTypes";
interface ConfigFieldProps<T = any> {
@@ -29,6 +40,8 @@ export function ConfigField<T>({
onChange,
tableColumns,
}: ConfigFieldProps<T>) {
const [comboboxOpen, setComboboxOpen] = useState(false);
const handleChange = (newValue: any) => {
onChange(field.key, newValue);
};
@@ -41,7 +54,7 @@ export function ConfigField<T>({
value={value ?? ""}
onChange={(e) => handleChange(e.target.value)}
placeholder={field.placeholder}
className="h-8 text-xs"
className="h-7 text-xs"
/>
);
@@ -59,7 +72,7 @@ export function ConfigField<T>({
min={field.min}
max={field.max}
step={field.step}
className="h-8 text-xs"
className="h-7 text-xs"
/>
);
@@ -77,7 +90,7 @@ export function ConfigField<T>({
value={value ?? ""}
onValueChange={handleChange}
>
<SelectTrigger className="h-8 text-xs">
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder={field.placeholder || "선택"} />
</SelectTrigger>
<SelectContent>
@@ -103,25 +116,25 @@ export function ConfigField<T>({
case "color":
return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5">
<input
type="color"
value={value ?? "#000000"}
onChange={(e) => handleChange(e.target.value)}
className="h-8 w-8 cursor-pointer rounded border"
className="h-7 w-7 cursor-pointer rounded border"
/>
<Input
value={value ?? ""}
onChange={(e) => handleChange(e.target.value)}
placeholder="#000000"
className="h-8 flex-1 text-xs"
className="h-7 flex-1 text-xs"
/>
</div>
);
case "slider":
return (
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5">
<Input
type="number"
value={value ?? field.min ?? 0}
@@ -129,17 +142,17 @@ export function ConfigField<T>({
min={field.min}
max={field.max}
step={field.step}
className="h-8 w-20 text-xs"
className="h-7 w-16 text-xs"
/>
<span className="text-muted-foreground text-[10px]">
{field.min ?? 0} ~ {field.max ?? 100}
<span className="text-muted-foreground text-[9px]">
{field.min ?? 0}~{field.max ?? 100}
</span>
</div>
);
case "multi-select":
return (
<div className="space-y-1">
<div className="space-y-0.5">
{(field.options || []).map((opt) => {
const selected = Array.isArray(value) && value.includes(opt.value);
return (
@@ -230,7 +243,7 @@ export function ConfigField<T>({
value={value ?? ""}
onValueChange={handleChange}
>
<SelectTrigger className="h-8 text-xs">
<SelectTrigger className="h-7 text-xs">
<SelectValue placeholder={field.placeholder || "컬럼 선택"} />
</SelectTrigger>
<SelectContent>
@@ -244,21 +257,123 @@ export function ConfigField<T>({
);
}
case "checkbox":
return (
<div className="flex items-center gap-2">
<Checkbox
id={`field-${field.key}`}
checked={!!value}
onCheckedChange={handleChange}
/>
{field.description && (
<label
htmlFor={`field-${field.key}`}
className="cursor-pointer text-xs text-muted-foreground"
>
{field.description}
</label>
)}
</div>
);
case "combobox": {
const options = field.options || [];
const selectedLabel = options.find((opt) => opt.value === value)?.label;
return (
<Popover open={comboboxOpen} onOpenChange={setComboboxOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={comboboxOpen}
className="h-7 w-full justify-between text-xs font-normal"
>
<span className="truncate">
{selectedLabel || field.placeholder || "선택"}
</span>
<ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
style={{ width: "var(--radix-popover-trigger-width)" }}
align="start"
>
<Command>
<CommandInput placeholder="검색..." className="text-xs" />
<CommandList>
<CommandEmpty className="py-2 text-center text-xs">
</CommandEmpty>
<CommandGroup>
{options.map((opt) => (
<CommandItem
key={opt.value}
value={opt.value}
onSelect={(currentValue) => {
handleChange(currentValue === value ? "" : currentValue);
setComboboxOpen(false);
}}
className="text-xs"
>
<Check
className={cn(
"mr-1.5 h-3 w-3",
value === opt.value ? "opacity-100" : "opacity-0",
)}
/>
{opt.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}
default:
return null;
}
};
return (
<div className="space-y-1">
<div className="flex items-center justify-between">
<Label className="text-xs font-medium">{field.label}</Label>
{field.type === "switch" && renderField()}
// textarea, multi-select, key-value는 전체 폭 수직 레이아웃
const isFullWidth = ["textarea", "multi-select", "key-value"].includes(field.type);
// checkbox는 description을 인라인으로 표시하므로 별도 처리
const isCheckbox = field.type === "checkbox";
if (isFullWidth) {
return (
<div className="py-1.5">
<Label className="mb-1 block text-xs text-muted-foreground">{field.label}</Label>
{field.description && !isCheckbox && (
<p className="text-muted-foreground/60 mb-1 text-[9px]">{field.description}</p>
)}
{renderField()}
</div>
);
}
// switch, checkbox: 라벨 왼쪽, 컨트롤 오른쪽 (고정폭 없이)
if (field.type === "switch" || isCheckbox) {
return (
<div className="flex items-center justify-between py-1.5">
<Label className="mr-3 truncate text-xs text-muted-foreground">{field.label}</Label>
{renderField()}
</div>
);
}
// 기본: 수평 property row (라벨 왼쪽, 컨트롤 오른쪽 고정폭)
return (
<div className="flex items-center justify-between py-1.5">
<Label className="mr-3 min-w-0 shrink truncate text-xs text-muted-foreground">
{field.label}
</Label>
<div className="w-[140px] flex-shrink-0">
{renderField()}
</div>
{field.description && (
<p className="text-muted-foreground text-[10px]">{field.description}</p>
)}
{field.type !== "switch" && renderField()}
</div>
);
}

View File

@@ -1,75 +1,144 @@
"use client";
import React from "react";
import { ConfigPanelBuilderProps } from "./ConfigPanelTypes";
import { ConfigPanelBuilderProps, ConfigSectionDefinition } from "./ConfigPanelTypes";
import { ConfigSection } from "./ConfigSection";
import { ConfigField } from "./ConfigField";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
function renderSections<T extends Record<string, any>>(
sections: ConfigSectionDefinition<T>[],
config: T,
onChange: (key: string, value: any) => void,
tableColumns?: any[],
) {
return sections.map((section) => {
if (section.condition && !section.condition(config)) {
return null;
}
const visibleFields = section.fields.filter(
(field) => !field.condition || field.condition(config),
);
if (visibleFields.length === 0) {
return null;
}
return (
<ConfigSection key={section.id} section={section}>
{visibleFields.map((field) => (
<ConfigField
key={field.key}
field={field}
value={(config as any)[field.key]}
onChange={onChange}
tableColumns={tableColumns}
/>
))}
</ConfigSection>
);
});
}
export function ConfigPanelBuilder<T extends Record<string, any>>({
config,
onChange,
onConfigChange,
sections,
presets,
tableColumns,
children,
mode = "flat",
context,
}: ConfigPanelBuilderProps<T>) {
return (
<div className="space-y-3">
{/* 프리셋 버튼 */}
{presets && presets.length > 0 && (
<div className="border-b pb-3">
<h4 className="mb-2 text-xs font-medium text-muted-foreground">
</h4>
<div className="flex flex-wrap gap-1">
{presets.map((preset, idx) => (
<button
key={idx}
onClick={() => {
Object.entries(preset.values).forEach(([key, value]) => {
onChange(key, value);
});
}}
className="rounded-full bg-muted px-2.5 py-1 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-primary hover:text-primary-foreground"
title={preset.description}
>
{preset.label}
</button>
))}
</div>
const effectiveTableColumns = tableColumns || context?.tableColumns;
const presetSection = presets && presets.length > 0 && (
<div className="border-b border-border/40 pb-2.5">
<h4 className="mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
</h4>
<div className="flex flex-wrap gap-1">
{presets.map((preset, idx) => (
<button
key={idx}
onClick={() => {
Object.entries(preset.values).forEach(([key, value]) => {
onChange(key, value);
});
if (onConfigChange) {
onConfigChange({ ...config, ...preset.values } as Record<string, any>);
}
}}
className="rounded border border-border bg-background px-2 py-0.5 text-[10px] font-medium text-muted-foreground transition-colors hover:border-primary hover:text-primary"
title={preset.description}
>
{preset.label}
</button>
))}
</div>
</div>
);
if (mode === "tabs") {
const groupMap = new Map<string, ConfigSectionDefinition<T>[]>();
const ungrouped: ConfigSectionDefinition<T>[] = [];
for (const section of sections) {
if (section.group) {
const existing = groupMap.get(section.group) || [];
existing.push(section);
groupMap.set(section.group, existing);
} else {
ungrouped.push(section);
}
}
const tabGroups = Array.from(groupMap.entries());
if (tabGroups.length === 0) {
return (
<div className="space-y-1">
{presetSection}
{renderSections(sections, config, onChange, effectiveTableColumns)}
{children}
</div>
)}
);
}
{/* 섹션 렌더링 */}
{sections.map((section) => {
if (section.condition && !section.condition(config)) {
return null;
}
const defaultTab = tabGroups[0]?.[0] || "general";
const visibleFields = section.fields.filter(
(field) => !field.condition || field.condition(config),
);
return (
<div className="space-y-1">
{presetSection}
if (visibleFields.length === 0) {
return null;
}
{ungrouped.length > 0 && renderSections(ungrouped, config, onChange, effectiveTableColumns)}
return (
<ConfigSection key={section.id} section={section}>
{visibleFields.map((field) => (
<ConfigField
key={field.key}
field={field}
value={(config as any)[field.key]}
onChange={onChange}
tableColumns={tableColumns}
/>
<Tabs defaultValue={defaultTab} className="w-full">
<TabsList className="h-7 w-full">
{tabGroups.map(([groupName]) => (
<TabsTrigger key={groupName} value={groupName} className="h-6 text-xs">
{groupName}
</TabsTrigger>
))}
</ConfigSection>
);
})}
</TabsList>
{tabGroups.map(([groupName, groupSections]) => (
<TabsContent key={groupName} value={groupName} className="mt-1">
{renderSections(groupSections, config, onChange, effectiveTableColumns)}
</TabsContent>
))}
</Tabs>
{/* 커스텀 children */}
{children}
</div>
);
}
return (
<div className="space-y-1">
{presetSection}
{renderSections(sections, config, onChange, effectiveTableColumns)}
{children}
</div>
);

View File

@@ -10,7 +10,9 @@ export type ConfigFieldType =
| "slider"
| "multi-select"
| "key-value"
| "column-picker";
| "column-picker"
| "checkbox"
| "combobox";
export interface ConfigOption {
label: string;
@@ -40,11 +42,13 @@ export interface ConfigSectionDefinition<T = any> {
defaultOpen?: boolean;
fields: ConfigFieldDefinition<T>[];
condition?: (config: T) => boolean;
group?: string;
}
export interface ConfigPanelBuilderProps<T = any> {
config: T;
onChange: (key: string, value: any) => void;
onConfigChange?: (config: Record<string, any>) => void;
sections: ConfigSectionDefinition<T>[];
presets?: Array<{
label: string;
@@ -53,4 +57,30 @@ export interface ConfigPanelBuilderProps<T = any> {
}>;
tableColumns?: ConfigOption[];
children?: React.ReactNode;
mode?: "flat" | "tabs";
context?: ConfigPanelContext;
}
/**
* 설정 패널에 전달되는 화면/컴포넌트 컨텍스트 정보
*/
export interface ConfigPanelContext {
tables?: any[];
tableColumns?: any[];
screenTableName?: string;
menuObjid?: number;
allComponents?: any[];
currentComponent?: any;
allTables?: any[];
screenComponents?: any[];
currentScreenCompanyCode?: string;
}
/**
* 모든 ConfigPanel이 공통으로 받는 표준 Props
*/
export interface StandardConfigPanelProps {
config: Record<string, any>;
onChange: (config: Record<string, any>) => void;
context?: ConfigPanelContext;
}

View File

@@ -14,39 +14,45 @@ export function ConfigSection({ section, children }: ConfigSectionProps) {
if (section.collapsible) {
return (
<div className="border-b pb-3">
<div className="border-b border-border/40 py-2.5">
<button
onClick={() => setIsOpen(!isOpen)}
className="flex w-full items-center gap-1.5 py-1 text-left"
className="flex w-full items-center justify-between py-0.5 text-left"
>
{isOpen ? (
<ChevronDown className="h-3.5 w-3.5 shrink-0" />
) : (
<ChevronRight className="h-3.5 w-3.5 shrink-0" />
)}
<span className="text-sm font-medium">{section.title}</span>
{section.description && (
<span className="text-muted-foreground ml-auto text-[10px]">
{section.description}
</span>
)}
<span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
{section.title}
</span>
<div className="flex items-center gap-1.5">
{section.description && (
<span className="text-muted-foreground/60 text-[9px]">
{section.description}
</span>
)}
{isOpen ? (
<ChevronDown className="h-3 w-3 shrink-0 text-muted-foreground/50" />
) : (
<ChevronRight className="h-3 w-3 shrink-0 text-muted-foreground/50" />
)}
</div>
</button>
{isOpen && <div className="mt-2 space-y-3">{children}</div>}
{isOpen && <div className="mt-1.5 space-y-1">{children}</div>}
</div>
);
}
return (
<div className="border-b pb-3">
<div className="mb-2">
<h4 className="text-sm font-medium">{section.title}</h4>
<div className="border-b border-border/40 py-2.5">
<div className="mb-1.5 flex items-center justify-between">
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
{section.title}
</h4>
{section.description && (
<p className="text-muted-foreground text-[10px]">
<span className="text-muted-foreground/60 text-[9px]">
{section.description}
</p>
</span>
)}
</div>
<div className="space-y-3">{children}</div>
<div className="space-y-1">{children}</div>
</div>
);
}

View File

@@ -1,11 +1,9 @@
"use client";
import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { SelectedItemsDetailInputWrapper } from "./SelectedItemsDetailInputComponent";
import { SelectedItemsDetailInputConfigPanel } from "./SelectedItemsDetailInputConfigPanel";
import { V2SelectedItemsDetailInputConfigPanel } from "@/components/v2/config-panels/V2SelectedItemsDetailInputConfigPanel";
import { SelectedItemsDetailInputConfig } from "./types";
/**
@@ -33,7 +31,7 @@ export const SelectedItemsDetailInputDefinition = createComponentDefinition({
readonly: false,
} as SelectedItemsDetailInputConfig,
defaultSize: { width: 800, height: 400 },
configPanel: SelectedItemsDetailInputConfigPanel,
configPanel: V2SelectedItemsDetailInputConfigPanel,
icon: "Table",
tags: ["선택", "상세입력", "반복", "테이블", "데이터전달"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { AggregationWidgetWrapper } from "./AggregationWidgetComponent";
import { AggregationWidgetConfigPanel } from "./AggregationWidgetConfigPanel";
import { V2AggregationWidgetConfigPanel } from "@/components/v2/config-panels/V2AggregationWidgetConfigPanel";
import type { AggregationWidgetConfig } from "./types";
/**
@@ -34,7 +34,7 @@ export const V2AggregationWidgetDefinition = createComponentDefinition({
refreshOnFormChange: true, // 폼 변경 시 자동 새로고침
} as Partial<AggregationWidgetConfig>,
defaultSize: { width: 400, height: 60 },
configPanel: AggregationWidgetConfigPanel,
configPanel: V2AggregationWidgetConfigPanel,
icon: "Calculator",
tags: ["집계", "합계", "평균", "개수", "통계", "데이터", "필터"],
version: "1.1.0",

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { ApprovalStepWrapper } from "./ApprovalStepComponent";
import { ApprovalStepConfigPanel } from "./ApprovalStepConfigPanel";
import { V2ApprovalStepConfigPanel as ApprovalStepConfigPanel } from "@/components/v2/config-panels/V2ApprovalStepConfigPanel";
import { ApprovalStepConfig } from "./types";
/**

View File

@@ -1,11 +1,9 @@
"use client";
import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { ButtonPrimaryWrapper } from "./ButtonPrimaryComponent";
import { ButtonPrimaryConfig } from "./types";
import { V2ButtonConfigPanel } from "@/components/v2/config-panels/V2ButtonConfigPanel";
/**
* ButtonPrimary 컴포넌트 정의
@@ -30,7 +28,7 @@ export const V2ButtonPrimaryDefinition = createComponentDefinition({
},
},
defaultSize: { width: 120, height: 40 },
configPanel: undefined, // 상세 설정 패널(ButtonConfigPanel)이 대신 사용됨
configPanel: V2ButtonConfigPanel,
icon: "MousePointer",
tags: ["버튼", "액션", "클릭"],
version: "1.0.0",

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { CardDisplayComponent } from "./CardDisplayComponent";
import { CardDisplayConfigPanel } from "./CardDisplayConfigPanel";
import { V2CardDisplayConfigPanel } from "@/components/v2/config-panels/V2CardDisplayConfigPanel";
import { CardDisplayConfig } from "./types";
/**
@@ -38,7 +38,7 @@ export const V2CardDisplayDefinition = createComponentDefinition({
staticData: [],
},
defaultSize: { width: 800, height: 400 },
configPanel: CardDisplayConfigPanel,
configPanel: V2CardDisplayConfigPanel,
icon: "Grid3x3",
tags: ["card", "display", "table", "grid"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { V2CategoryManagerComponent } from "./V2CategoryManagerComponent";
import { V2CategoryManagerConfigPanel } from "./V2CategoryManagerConfigPanel";
import { V2CategoryManagerConfigPanel } from "@/components/v2/config-panels/V2CategoryManagerConfigPanel";
import { defaultV2CategoryManagerConfig } from "./types";
/**
@@ -32,5 +32,5 @@ export const V2CategoryManagerDefinition = createComponentDefinition({
// 타입 내보내기
export type { V2CategoryManagerConfig, CategoryValue, ViewMode } from "./types";
export { V2CategoryManagerComponent } from "./V2CategoryManagerComponent";
export { V2CategoryManagerConfigPanel } from "./V2CategoryManagerConfigPanel";
export { V2CategoryManagerConfigPanel } from "@/components/v2/config-panels/V2CategoryManagerConfigPanel";

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { DividerLineWrapper } from "./DividerLineComponent";
import { DividerLineConfigPanel } from "./DividerLineConfigPanel";
import { V2DividerLineConfigPanel } from "@/components/v2/config-panels/V2DividerLineConfigPanel";
import { DividerLineConfig } from "./types";
/**
@@ -25,7 +25,7 @@ export const V2DividerLineDefinition = createComponentDefinition({
maxLength: 255,
},
defaultSize: { width: 400, height: 2 },
configPanel: DividerLineConfigPanel,
configPanel: V2DividerLineConfigPanel,
icon: "Layout",
tags: [],
version: "1.0.0",

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { FileUploadComponent } from "./FileUploadComponent";
import { FileUploadConfigPanel } from "./FileUploadConfigPanel";
import { V2FileUploadConfigPanel } from "@/components/v2/config-panels/V2FileUploadConfigPanel";
import { FileUploadConfig } from "./types";
/**
@@ -27,7 +27,7 @@ export const V2FileUploadDefinition = createComponentDefinition({
maxSize: 10 * 1024 * 1024, // 10MB
},
defaultSize: { width: 350, height: 240 },
configPanel: FileUploadConfigPanel,
configPanel: V2FileUploadConfigPanel,
icon: "Upload",
tags: ["file", "upload", "attachment", "v2"],
version: "2.0.0",

View File

@@ -6,7 +6,7 @@
import { ComponentCategory } from "@/types/component";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { V2InputConfigPanel } from "@/components/v2/config-panels/V2InputConfigPanel";
import { V2FieldConfigPanel } from "@/components/v2/config-panels/V2FieldConfigPanel";
import { V2Input } from "@/components/v2/V2Input";
export const V2InputDefinition = createComponentDefinition({
@@ -72,7 +72,7 @@ export const V2InputDefinition = createComponentDefinition({
tags: ["input", "text", "number", "v2"],
// 설정 패널
configPanel: V2InputConfigPanel,
configPanel: V2FieldConfigPanel,
});
export default V2InputDefinition;

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { ItemRoutingComponent } from "./ItemRoutingComponent";
import { ItemRoutingConfigPanel } from "./ItemRoutingConfigPanel";
import { V2ItemRoutingConfigPanel as ItemRoutingConfigPanel } from "@/components/v2/config-panels/V2ItemRoutingConfigPanel";
import { defaultConfig } from "./config";
export const V2ItemRoutingDefinition = createComponentDefinition({

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { LocationSwapSelectorComponent } from "./LocationSwapSelectorComponent";
import { LocationSwapSelectorConfigPanel } from "./LocationSwapSelectorConfigPanel";
import { V2LocationSwapSelectorConfigPanel as LocationSwapSelectorConfigPanel } from "@/components/v2/config-panels/V2LocationSwapSelectorConfigPanel";
/**
* LocationSwapSelector 컴포넌트 정의

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { NumberingRuleWrapper } from "./NumberingRuleComponent";
import { NumberingRuleConfigPanel } from "./NumberingRuleConfigPanel";
import { V2NumberingRuleConfigPanel } from "@/components/v2/config-panels/V2NumberingRuleConfigPanel";
import { defaultConfig } from "./config";
/**
@@ -25,7 +25,7 @@ export const V2NumberingRuleDefinition = createComponentDefinition({
height: 800,
gridColumnSpan: "12",
},
configPanel: NumberingRuleConfigPanel,
configPanel: V2NumberingRuleConfigPanel,
icon: "Hash",
tags: ["코드", "채번", "규칙", "표시", "자동생성"],
version: "1.0.0",

View File

@@ -43,7 +43,7 @@ export type {
// 컴포넌트 내보내기
export { PivotGridComponent } from "./PivotGridComponent";
export { PivotGridConfigPanel } from "./PivotGridConfigPanel";
export { V2PivotGridConfigPanel as PivotGridConfigPanel } from "@/components/v2/config-panels/V2PivotGridConfigPanel";
// 유틸리티
export {

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { ProcessWorkStandardComponent } from "./ProcessWorkStandardComponent";
import { ProcessWorkStandardConfigPanel } from "./ProcessWorkStandardConfigPanel";
import { V2ProcessWorkStandardConfigPanel as ProcessWorkStandardConfigPanel } from "@/components/v2/config-panels/V2ProcessWorkStandardConfigPanel";
import { defaultConfig } from "./config";
export const V2ProcessWorkStandardDefinition = createComponentDefinition({

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { RackStructureWrapper } from "./RackStructureComponent";
import { RackStructureConfigPanel } from "./RackStructureConfigPanel";
import { V2RackStructureConfigPanel as RackStructureConfigPanel } from "@/components/v2/config-panels/V2RackStructureConfigPanel";
import { defaultConfig } from "./config";
/**

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { RepeatContainerWrapper } from "./RepeatContainerComponent";
import { RepeatContainerConfigPanel } from "./RepeatContainerConfigPanel";
import { V2RepeatContainerConfigPanel as RepeatContainerConfigPanel } from "@/components/v2/config-panels/V2RepeatContainerConfigPanel";
import type { RepeatContainerConfig } from "./types";
/**

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { SectionCardComponent } from "./SectionCardComponent";
import { SectionCardConfigPanel } from "./SectionCardConfigPanel";
import { V2SectionCardConfigPanel } from "@/components/v2/config-panels/V2SectionCardConfigPanel";
/**
* Section Card 컴포넌트 정의
@@ -28,7 +28,7 @@ export const V2SectionCardDefinition = createComponentDefinition({
defaultOpen: true,
},
defaultSize: { width: 800, height: 250 },
configPanel: SectionCardConfigPanel,
configPanel: V2SectionCardConfigPanel,
icon: "LayoutPanelTop",
tags: ["섹션", "그룹", "카드", "컨테이너", "제목", "card"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { SectionPaperComponent } from "./SectionPaperComponent";
import { SectionPaperConfigPanel } from "./SectionPaperConfigPanel";
import { V2SectionPaperConfigPanel } from "@/components/v2/config-panels/V2SectionPaperConfigPanel";
/**
* Section Paper 컴포넌트 정의
@@ -25,7 +25,7 @@ export const V2SectionPaperDefinition = createComponentDefinition({
showBorder: false,
},
defaultSize: { width: 800, height: 200 },
configPanel: SectionPaperConfigPanel,
configPanel: V2SectionPaperConfigPanel,
icon: "Square",
tags: ["섹션", "그룹", "배경", "컨테이너", "색종이", "paper"],
version: "1.0.0",

View File

@@ -6,7 +6,7 @@
import { ComponentCategory } from "@/types/component";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { V2SelectConfigPanel } from "@/components/v2/config-panels/V2SelectConfigPanel";
import { V2FieldConfigPanel } from "@/components/v2/config-panels/V2FieldConfigPanel";
import { V2Select } from "@/components/v2/V2Select";
export const V2SelectDefinition = createComponentDefinition({
@@ -82,7 +82,7 @@ export const V2SelectDefinition = createComponentDefinition({
tags: ["select", "dropdown", "combobox", "v2"],
// 설정 패널
configPanel: V2SelectConfigPanel,
configPanel: V2FieldConfigPanel,
});
export default V2SelectDefinition;

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { SplitLineWrapper } from "./SplitLineComponent";
import { SplitLineConfigPanel } from "./SplitLineConfigPanel";
import { V2SplitLineConfigPanel } from "@/components/v2/config-panels/V2SplitLineConfigPanel";
import { SplitLineConfig } from "./types";
/**
@@ -25,7 +25,7 @@ export const V2SplitLineDefinition = createComponentDefinition({
lineWidth: 4,
} as SplitLineConfig,
defaultSize: { width: 8, height: 600 },
configPanel: SplitLineConfigPanel,
configPanel: V2SplitLineConfigPanel,
icon: "SeparatorVertical",
tags: ["스플릿", "분할", "분할선", "레이아웃"],
version: "1.0.0",

View File

@@ -4,7 +4,7 @@ import React from "react";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { SplitPanelLayoutWrapper } from "./SplitPanelLayoutComponent";
import { SplitPanelLayoutConfigPanel } from "./SplitPanelLayoutConfigPanel";
import { V2SplitPanelLayoutConfigPanel } from "@/components/v2/config-panels/V2SplitPanelLayoutConfigPanel";
import { SplitPanelLayoutConfig } from "./types";
/**
@@ -42,7 +42,7 @@ export const V2SplitPanelLayoutDefinition = createComponentDefinition({
syncSelection: true,
} as SplitPanelLayoutConfig,
defaultSize: { width: 800, height: 600 },
configPanel: SplitPanelLayoutConfigPanel,
configPanel: V2SplitPanelLayoutConfigPanel,
icon: "PanelLeftRight",
tags: ["분할", "마스터", "디테일", "레이아웃"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { ComponentCategory } from "@/types/component";
import { StatusCountWrapper } from "./StatusCountComponent";
import { StatusCountConfigPanel } from "./StatusCountConfigPanel";
import { V2StatusCountConfigPanel } from "@/components/v2/config-panels/V2StatusCountConfigPanel";
export const V2StatusCountDefinition = createComponentDefinition({
id: "v2-status-count",
@@ -13,7 +13,7 @@ export const V2StatusCountDefinition = createComponentDefinition({
category: ComponentCategory.DISPLAY,
webType: "text",
component: StatusCountWrapper,
configPanel: StatusCountConfigPanel,
configPanel: V2StatusCountConfigPanel,
defaultConfig: {
title: "상태 현황",
tableName: "",

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { TableGroupedComponent } from "./TableGroupedComponent";
import { TableGroupedConfigPanel } from "./TableGroupedConfigPanel";
import { V2TableGroupedConfigPanel } from "@/components/v2/config-panels/V2TableGroupedConfigPanel";
import { TableGroupedConfig } from "./types";
/**
@@ -63,7 +63,7 @@ export const V2TableGroupedDefinition = createComponentDefinition({
maxHeight: 600,
},
defaultSize: { width: 800, height: 500 },
configPanel: TableGroupedConfigPanel,
configPanel: V2TableGroupedConfigPanel,
icon: "Layers",
tags: ["테이블", "그룹화", "접기", "펼치기", "목록"],
version: "1.0.0",

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { TableListWrapper } from "./TableListComponent";
import { TableListConfigPanel } from "./TableListConfigPanel";
import { V2TableListConfigPanel } from "@/components/v2/config-panels/V2TableListConfigPanel";
import { TableListConfig } from "./types";
/**
@@ -109,7 +109,7 @@ export const V2TableListDefinition = createComponentDefinition({
autoLoad: true,
},
defaultSize: { width: 1000, height: 600 }, // 테이블 리스트 기본 크기 (너비 1000px, 높이 600px)
configPanel: TableListConfigPanel,
configPanel: V2TableListConfigPanel,
icon: "Table",
tags: ["테이블", "데이터", "목록", "그리드"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { ComponentRegistry } from "../../ComponentRegistry";
import { TableSearchWidget } from "./TableSearchWidget";
import { TableSearchWidgetRenderer } from "./TableSearchWidgetRenderer";
import { TableSearchWidgetConfigPanel } from "./TableSearchWidgetConfigPanel";
import { V2TableSearchWidgetConfigPanel } from "@/components/v2/config-panels/V2TableSearchWidgetConfigPanel";
// 검색 필터 위젯 등록 (v2)
ComponentRegistry.registerComponent({
@@ -30,7 +30,7 @@ ComponentRegistry.registerComponent({
},
},
renderer: TableSearchWidgetRenderer.render,
configPanel: TableSearchWidgetConfigPanel,
configPanel: V2TableSearchWidgetConfigPanel,
version: "1.0.0",
author: "WACE",
});

View File

@@ -5,7 +5,7 @@ import { createComponentDefinition } from "../../utils/createComponentDefinition
import { ComponentCategory } from "@/types/component";
import type { WebType } from "@/types/screen";
import { TextDisplayWrapper } from "./TextDisplayComponent";
import { TextDisplayConfigPanel } from "./TextDisplayConfigPanel";
import { V2TextDisplayConfigPanel } from "@/components/v2/config-panels/V2TextDisplayConfigPanel";
import { TextDisplayConfig } from "./types";
/**
@@ -28,7 +28,7 @@ export const V2TextDisplayDefinition = createComponentDefinition({
textAlign: "left",
},
defaultSize: { width: 150, height: 24 },
configPanel: TextDisplayConfigPanel,
configPanel: V2TextDisplayConfigPanel,
icon: "Type",
tags: ["텍스트", "표시", "라벨"],
version: "1.0.0",

View File

@@ -3,7 +3,7 @@
import { ComponentCategory } from "@/types/component";
import { createComponentDefinition } from "../../utils/createComponentDefinition";
import { TimelineSchedulerComponent } from "./TimelineSchedulerComponent";
import { TimelineSchedulerConfigPanel } from "./TimelineSchedulerConfigPanel";
import { V2TimelineSchedulerConfigPanel as TimelineSchedulerConfigPanel } from "@/components/v2/config-panels/V2TimelineSchedulerConfigPanel";
import { defaultTimelineSchedulerConfig } from "./config";
import { TimelineSchedulerConfig } from "./types";