[agent-pipeline] pipe-20260311052455-y968 round-1
This commit is contained in:
parent
afd936ff67
commit
b329b52036
|
|
@ -8,7 +8,6 @@ import { Badge } from "@/components/ui/badge";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||||
import { ChevronDown, Settings, Info, Database, Trash2, Copy, Palette } from "lucide-react";
|
import { ChevronDown, Settings, Info, Database, Trash2, Copy, Palette } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
|
|
@ -44,20 +43,11 @@ import {
|
||||||
DetailTypeOption,
|
DetailTypeOption,
|
||||||
} from "@/types/input-type-mapping";
|
} from "@/types/input-type-mapping";
|
||||||
|
|
||||||
// 새로운 컴포넌트 설정 패널들
|
|
||||||
import { ButtonConfigPanel } from "../config-panels/ButtonConfigPanel";
|
|
||||||
import { CardConfigPanel } from "../config-panels/CardConfigPanel";
|
|
||||||
import { DashboardConfigPanel } from "../config-panels/DashboardConfigPanel";
|
|
||||||
import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent";
|
import { ColorPickerWithTransparent } from "../common/ColorPickerWithTransparent";
|
||||||
import { StatsCardConfigPanel } from "../config-panels/StatsCardConfigPanel";
|
|
||||||
|
|
||||||
// ComponentRegistry import (동적 ConfigPanel 가져오기용)
|
// ComponentRegistry import (동적 ConfigPanel 가져오기용)
|
||||||
import { ComponentRegistry } from "@/lib/registry/ComponentRegistry";
|
import { ComponentRegistry } from "@/lib/registry/ComponentRegistry";
|
||||||
import { ProgressBarConfigPanel } from "../config-panels/ProgressBarConfigPanel";
|
import { DynamicComponentConfigPanel, hasComponentConfigPanel } from "@/lib/utils/getComponentConfigPanel";
|
||||||
import { ChartConfigPanel } from "../config-panels/ChartConfigPanel";
|
|
||||||
import { AlertConfigPanel } from "../config-panels/AlertConfigPanel";
|
|
||||||
import { BadgeConfigPanel } from "../config-panels/BadgeConfigPanel";
|
|
||||||
import { DynamicComponentConfigPanel } from "@/lib/utils/getComponentConfigPanel";
|
|
||||||
import StyleEditor from "../StyleEditor";
|
import StyleEditor from "../StyleEditor";
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { Zap } from "lucide-react";
|
import { Zap } from "lucide-react";
|
||||||
|
|
@ -186,17 +176,6 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||||
selectedComponent.componentConfig?.id ||
|
selectedComponent.componentConfig?.id ||
|
||||||
selectedComponent.type;
|
selectedComponent.type;
|
||||||
|
|
||||||
const handleUpdateProperty = (path: string, value: any) => {
|
|
||||||
onUpdateProperty(selectedComponent.id, path, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConfigChange = (newConfig: any) => {
|
|
||||||
// 기존 config와 병합하여 다른 속성 유지
|
|
||||||
const currentConfig = selectedComponent.componentConfig?.config || {};
|
|
||||||
const mergedConfig = { ...currentConfig, ...newConfig };
|
|
||||||
onUpdateProperty(selectedComponent.id, "componentConfig.config", mergedConfig);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 🆕 ComponentRegistry에서 ConfigPanel 가져오기 시도
|
// 🆕 ComponentRegistry에서 ConfigPanel 가져오기 시도
|
||||||
const componentId =
|
const componentId =
|
||||||
selectedComponent.componentType || // ⭐ section-card 등
|
selectedComponent.componentType || // ⭐ section-card 등
|
||||||
|
|
@ -204,58 +183,6 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||||
selectedComponent.componentConfig?.id ||
|
selectedComponent.componentConfig?.id ||
|
||||||
(selectedComponent.type === "component" ? selectedComponent.id : null); // 🆕 독립 컴포넌트 (table-search-widget 등)
|
(selectedComponent.type === "component" ? selectedComponent.id : null); // 🆕 독립 컴포넌트 (table-search-widget 등)
|
||||||
|
|
||||||
// 🆕 V2 컴포넌트 직접 감지 및 설정 패널 렌더링
|
|
||||||
if (componentId?.startsWith("v2-")) {
|
|
||||||
const v2ConfigPanels: Record<string, React.FC<{ config: any; onChange: (config: any) => void }>> = {
|
|
||||||
"v2-input": require("@/components/v2/config-panels/V2InputConfigPanel").V2InputConfigPanel,
|
|
||||||
"v2-select": require("@/components/v2/config-panels/V2SelectConfigPanel").V2SelectConfigPanel,
|
|
||||||
"v2-date": require("@/components/v2/config-panels/V2DateConfigPanel").V2DateConfigPanel,
|
|
||||||
"v2-list": require("@/components/v2/config-panels/V2ListConfigPanel").V2ListConfigPanel,
|
|
||||||
"v2-layout": require("@/components/v2/config-panels/V2LayoutConfigPanel").V2LayoutConfigPanel,
|
|
||||||
"v2-group": require("@/components/v2/config-panels/V2GroupConfigPanel").V2GroupConfigPanel,
|
|
||||||
"v2-media": require("@/components/v2/config-panels/V2MediaConfigPanel").V2MediaConfigPanel,
|
|
||||||
"v2-biz": require("@/components/v2/config-panels/V2BizConfigPanel").V2BizConfigPanel,
|
|
||||||
"v2-hierarchy": require("@/components/v2/config-panels/V2HierarchyConfigPanel").V2HierarchyConfigPanel,
|
|
||||||
"v2-bom-item-editor": require("@/components/v2/config-panels/V2BomItemEditorConfigPanel").V2BomItemEditorConfigPanel,
|
|
||||||
"v2-bom-tree": require("@/components/v2/config-panels/V2BomTreeConfigPanel").V2BomTreeConfigPanel,
|
|
||||||
};
|
|
||||||
|
|
||||||
const V2ConfigPanel = v2ConfigPanels[componentId];
|
|
||||||
if (V2ConfigPanel) {
|
|
||||||
const currentConfig = selectedComponent.componentConfig || {};
|
|
||||||
const handleV2ConfigChange = (newConfig: any) => {
|
|
||||||
onUpdateProperty(selectedComponent.id, "componentConfig", { ...currentConfig, ...newConfig });
|
|
||||||
};
|
|
||||||
|
|
||||||
// 컬럼의 inputType 가져오기 (entity 타입인지 확인용)
|
|
||||||
const inputType = currentConfig.inputType || currentConfig.webType || (selectedComponent as any).inputType;
|
|
||||||
|
|
||||||
// 현재 화면의 테이블명 가져오기
|
|
||||||
const currentTableName = tables?.[0]?.tableName;
|
|
||||||
|
|
||||||
// 컴포넌트별 추가 props
|
|
||||||
const extraProps: Record<string, any> = {};
|
|
||||||
if (componentId === "v2-select") {
|
|
||||||
extraProps.inputType = inputType;
|
|
||||||
extraProps.tableName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
|
|
||||||
extraProps.columnName = selectedComponent.columnName || currentConfig.fieldKey || currentConfig.columnName;
|
|
||||||
}
|
|
||||||
if (componentId === "v2-list") {
|
|
||||||
extraProps.currentTableName = currentTableName;
|
|
||||||
}
|
|
||||||
if (componentId === "v2-bom-item-editor" || componentId === "v2-bom-tree") {
|
|
||||||
extraProps.currentTableName = currentTableName;
|
|
||||||
extraProps.screenTableName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={selectedComponent.id} className="space-y-4">
|
|
||||||
<V2ConfigPanel config={currentConfig} onChange={handleV2ConfigChange} {...extraProps} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (componentId) {
|
if (componentId) {
|
||||||
const definition = ComponentRegistry.getComponent(componentId);
|
const definition = ComponentRegistry.getComponent(componentId);
|
||||||
|
|
||||||
|
|
@ -322,335 +249,42 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// ConfigPanel이 없으면 아래 switch case로 넘어감
|
// ConfigPanel이 없으면 DynamicComponentConfigPanel fallback으로 처리
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 하드코딩된 설정 패널들 (레거시)
|
// DynamicComponentConfigPanel을 통한 동적 로드 (CONFIG_PANEL_MAP 기반)
|
||||||
switch (componentType) {
|
const fallbackId = componentId || componentType;
|
||||||
case "button":
|
if (fallbackId && hasComponentConfigPanel(fallbackId)) {
|
||||||
case "button-primary":
|
const handleDynamicConfigChange = (newConfig: any) => {
|
||||||
case "button-secondary":
|
const currentConfig = selectedComponent.componentConfig || {};
|
||||||
case "v2-button-primary":
|
const mergedConfig = { ...currentConfig, ...newConfig };
|
||||||
// 🔧 component.id만 key로 사용 (unmount 방지)
|
onUpdateProperty(selectedComponent.id, "componentConfig", mergedConfig);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonConfigPanel
|
<DynamicComponentConfigPanel
|
||||||
key={selectedComponent.id}
|
componentId={fallbackId}
|
||||||
component={selectedComponent}
|
config={selectedComponent.componentConfig || {}}
|
||||||
onUpdateProperty={handleUpdateProperty}
|
onChange={handleDynamicConfigChange}
|
||||||
|
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
|
||||||
|
tableColumns={currentTable?.columns || []}
|
||||||
|
tables={tables}
|
||||||
|
menuObjid={menuObjid}
|
||||||
allComponents={allComponents}
|
allComponents={allComponents}
|
||||||
currentTableName={currentTableName}
|
currentComponent={selectedComponent}
|
||||||
currentScreenCompanyCode={currentScreenCompanyCode}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
case "card":
|
|
||||||
return <CardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "dashboard":
|
|
||||||
return <DashboardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "stats":
|
|
||||||
case "stats-card":
|
|
||||||
return <StatsCardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "progress":
|
|
||||||
case "progress-bar":
|
|
||||||
return <ProgressBarConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "chart":
|
|
||||||
case "chart-basic":
|
|
||||||
return <ChartConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "alert":
|
|
||||||
case "alert-info":
|
|
||||||
return <AlertConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "badge":
|
|
||||||
case "badge-status":
|
|
||||||
return <BadgeConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
|
||||||
|
|
||||||
case "section-card":
|
|
||||||
return (
|
|
||||||
<div className="space-y-4 p-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<h3 className="text-sm font-semibold">Section Card 설정</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">제목과 테두리가 있는 명확한 그룹화 컨테이너</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 헤더 표시 */}
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="showHeader"
|
|
||||||
checked={selectedComponent.componentConfig?.showHeader !== false}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.showHeader", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="showHeader" className="cursor-pointer text-xs">
|
|
||||||
헤더 표시
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 제목 */}
|
|
||||||
{selectedComponent.componentConfig?.showHeader !== false && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">제목</Label>
|
|
||||||
<Input
|
|
||||||
value={selectedComponent.componentConfig?.title || ""}
|
|
||||||
onChange={(e) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.title", e.target.value);
|
|
||||||
}}
|
|
||||||
placeholder="섹션 제목 입력"
|
|
||||||
className="h-9 text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 설명 */}
|
|
||||||
{selectedComponent.componentConfig?.showHeader !== false && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">설명 (선택)</Label>
|
|
||||||
<Textarea
|
|
||||||
value={selectedComponent.componentConfig?.description || ""}
|
|
||||||
onChange={(e) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.description", e.target.value);
|
|
||||||
}}
|
|
||||||
placeholder="섹션 설명 입력"
|
|
||||||
className="resize-none text-xs"
|
|
||||||
rows={2}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 패딩 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">내부 여백</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.padding || "md"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.padding", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="none">없음</SelectItem>
|
|
||||||
<SelectItem value="sm">작게 (12px)</SelectItem>
|
|
||||||
<SelectItem value="md">중간 (24px)</SelectItem>
|
|
||||||
<SelectItem value="lg">크게 (32px)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 배경색 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">배경색</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.backgroundColor || "default"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.backgroundColor", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="default">기본 (카드)</SelectItem>
|
|
||||||
<SelectItem value="muted">회색</SelectItem>
|
|
||||||
<SelectItem value="transparent">투명</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 테두리 스타일 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">테두리 스타일</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.borderStyle || "solid"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.borderStyle", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="solid">실선</SelectItem>
|
|
||||||
<SelectItem value="dashed">점선</SelectItem>
|
|
||||||
<SelectItem value="none">없음</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 접기/펼치기 기능 */}
|
|
||||||
<div className="space-y-2 border-t pt-2">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="collapsible"
|
|
||||||
checked={selectedComponent.componentConfig?.collapsible || false}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.collapsible", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="collapsible" className="cursor-pointer text-xs">
|
|
||||||
접기/펼치기 가능
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{selectedComponent.componentConfig?.collapsible && (
|
|
||||||
<div className="ml-6 flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="defaultOpen"
|
|
||||||
checked={selectedComponent.componentConfig?.defaultOpen !== false}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.defaultOpen", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="defaultOpen" className="cursor-pointer text-xs">
|
|
||||||
기본으로 펼치기
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
case "section-paper":
|
|
||||||
return (
|
|
||||||
<div className="space-y-4 p-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<h3 className="text-sm font-semibold">Section Paper 설정</h3>
|
|
||||||
<p className="text-muted-foreground text-xs">배경색 기반의 미니멀한 그룹화 컨테이너</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 배경색 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">배경색</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.backgroundColor || "default"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.backgroundColor", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="default">기본 (연한 회색)</SelectItem>
|
|
||||||
<SelectItem value="muted">회색</SelectItem>
|
|
||||||
<SelectItem value="accent">강조 (연한 파랑)</SelectItem>
|
|
||||||
<SelectItem value="primary">브랜드 컬러</SelectItem>
|
|
||||||
<SelectItem value="custom">커스텀</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 커스텀 색상 */}
|
|
||||||
{selectedComponent.componentConfig?.backgroundColor === "custom" && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">커스텀 색상</Label>
|
|
||||||
<ColorPickerWithTransparent
|
|
||||||
value={selectedComponent.componentConfig?.customColor}
|
|
||||||
onChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.customColor", value);
|
|
||||||
}}
|
|
||||||
defaultColor="#f0f0f0"
|
|
||||||
placeholder="#f0f0f0"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 패딩 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">내부 여백</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.padding || "md"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.padding", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="none">없음</SelectItem>
|
|
||||||
<SelectItem value="sm">작게 (12px)</SelectItem>
|
|
||||||
<SelectItem value="md">중간 (16px)</SelectItem>
|
|
||||||
<SelectItem value="lg">크게 (24px)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 둥근 모서리 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">둥근 모서리</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.roundedCorners || "md"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.roundedCorners", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="none">없음</SelectItem>
|
|
||||||
<SelectItem value="sm">작게 (2px)</SelectItem>
|
|
||||||
<SelectItem value="md">중간 (6px)</SelectItem>
|
|
||||||
<SelectItem value="lg">크게 (8px)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 그림자 */}
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label className="text-xs">그림자</Label>
|
|
||||||
<Select
|
|
||||||
value={selectedComponent.componentConfig?.shadow || "none"}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.shadow", value);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="h-9 text-xs">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="none">없음</SelectItem>
|
|
||||||
<SelectItem value="sm">작게</SelectItem>
|
|
||||||
<SelectItem value="md">중간</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 테두리 표시 */}
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Checkbox
|
|
||||||
id="showBorder"
|
|
||||||
checked={selectedComponent.componentConfig?.showBorder || false}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
handleUpdateProperty(selectedComponent.id, "componentConfig.showBorder", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Label htmlFor="showBorder" className="cursor-pointer text-xs">
|
|
||||||
미묘한 테두리 표시
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
// ConfigPanel이 없는 경우 경고 표시
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||||||
<Settings className="text-muted-foreground mb-4 h-12 w-12" />
|
<Settings className="text-muted-foreground mb-4 h-12 w-12" />
|
||||||
<h3 className="mb-2 text-base font-medium">⚠️ 설정 패널 없음</h3>
|
<h3 className="mb-2 text-base font-medium">설정 패널 없음</h3>
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
컴포넌트 "{componentId || componentType}"에 대한 설정 패널이 없습니다.
|
컴포넌트 "{fallbackId || componentType}"에 대한 설정 패널이 없습니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 기본 정보 탭
|
// 기본 정보 탭
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,6 @@ const CONFIG_PANEL_MAP: Record<string, () => Promise<any>> = {
|
||||||
"v2-repeat-container": () => import("@/lib/registry/components/v2-repeat-container/RepeatContainerConfigPanel"),
|
"v2-repeat-container": () => import("@/lib/registry/components/v2-repeat-container/RepeatContainerConfigPanel"),
|
||||||
"repeater-field-group": () => import("@/components/webtypes/config/RepeaterConfigPanel"),
|
"repeater-field-group": () => import("@/components/webtypes/config/RepeaterConfigPanel"),
|
||||||
"v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"),
|
"v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"),
|
||||||
"v2-repeater": () => import("@/components/v2/config-panels/V2RepeaterConfigPanel"),
|
|
||||||
"simple-repeater-table": () => import("@/lib/registry/components/simple-repeater-table/SimpleRepeaterTableConfigPanel"),
|
"simple-repeater-table": () => import("@/lib/registry/components/simple-repeater-table/SimpleRepeaterTableConfigPanel"),
|
||||||
"modal-repeater-table": () => import("@/lib/registry/components/modal-repeater-table/ModalRepeaterTableConfigPanel"),
|
"modal-repeater-table": () => import("@/lib/registry/components/modal-repeater-table/ModalRepeaterTableConfigPanel"),
|
||||||
"repeat-screen-modal": () => import("@/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel"),
|
"repeat-screen-modal": () => import("@/lib/registry/components/repeat-screen-modal/RepeatScreenModalConfigPanel"),
|
||||||
|
|
@ -104,6 +103,24 @@ const CONFIG_PANEL_MAP: Record<string, () => Promise<any>> = {
|
||||||
"category-manager": () => import("@/lib/registry/components/category-manager/CategoryManagerConfigPanel"),
|
"category-manager": () => import("@/lib/registry/components/category-manager/CategoryManagerConfigPanel"),
|
||||||
"universal-form-modal": () => import("@/lib/registry/components/universal-form-modal/UniversalFormModalConfigPanel"),
|
"universal-form-modal": () => import("@/lib/registry/components/universal-form-modal/UniversalFormModalConfigPanel"),
|
||||||
"v2-process-work-standard": () => import("@/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel"),
|
"v2-process-work-standard": () => import("@/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel"),
|
||||||
|
|
||||||
|
// ========== V2 BOM 컴포넌트 ==========
|
||||||
|
"v2-bom-item-editor": () => import("@/components/v2/config-panels/V2BomItemEditorConfigPanel"),
|
||||||
|
"v2-bom-tree": () => import("@/components/v2/config-panels/V2BomTreeConfigPanel"),
|
||||||
|
|
||||||
|
// ========== 레거시 위젯 (component/onUpdateProperty props 사용) ==========
|
||||||
|
"card": () => import("@/components/screen/config-panels/CardConfigPanel"),
|
||||||
|
"dashboard": () => import("@/components/screen/config-panels/DashboardConfigPanel"),
|
||||||
|
"stats": () => import("@/components/screen/config-panels/StatsCardConfigPanel"),
|
||||||
|
"stats-card": () => import("@/components/screen/config-panels/StatsCardConfigPanel"),
|
||||||
|
"progress": () => import("@/components/screen/config-panels/ProgressBarConfigPanel"),
|
||||||
|
"progress-bar": () => import("@/components/screen/config-panels/ProgressBarConfigPanel"),
|
||||||
|
"chart": () => import("@/components/screen/config-panels/ChartConfigPanel"),
|
||||||
|
"chart-basic": () => import("@/components/screen/config-panels/ChartConfigPanel"),
|
||||||
|
"alert": () => import("@/components/screen/config-panels/AlertConfigPanel"),
|
||||||
|
"alert-info": () => import("@/components/screen/config-panels/AlertConfigPanel"),
|
||||||
|
"badge": () => import("@/components/screen/config-panels/BadgeConfigPanel"),
|
||||||
|
"badge-status": () => import("@/components/screen/config-panels/BadgeConfigPanel"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ConfigPanel 컴포넌트 캐시
|
// ConfigPanel 컴포넌트 캐시
|
||||||
|
|
@ -128,60 +145,30 @@ export async function getComponentConfigPanel(componentId: string): Promise<Reac
|
||||||
try {
|
try {
|
||||||
const module = await importFn();
|
const module = await importFn();
|
||||||
|
|
||||||
// 모듈에서 ConfigPanel 컴포넌트 추출
|
// 모듈에서 ConfigPanel 컴포넌트 추출 (우선순위):
|
||||||
// 1차: PascalCase 변환된 이름으로 찾기 (예: text-input -> TextInputConfigPanel)
|
// 1차: PascalCase 변환된 이름 (예: text-input -> TextInputConfigPanel)
|
||||||
// 2차: v2- 접두사 제거 후 PascalCase 이름으로 찾기 (예: v2-table-list -> TableListConfigPanel)
|
// 2차: v2- 접두사 제거 후 PascalCase (예: v2-table-list -> TableListConfigPanel)
|
||||||
// 3차: 특수 export명들 fallback
|
// 3차: *ConfigPanel로 끝나는 첫 번째 named export
|
||||||
// 4차: default export
|
// 4차: default export
|
||||||
const pascalCaseName = `${toPascalCase(componentId)}ConfigPanel`;
|
const pascalCaseName = `${toPascalCase(componentId)}ConfigPanel`;
|
||||||
// v2- 접두사가 있는 경우 접두사를 제거한 이름도 시도
|
// v2- 접두사가 있는 경우 접두사를 제거한 이름도 시도
|
||||||
const baseComponentId = componentId.startsWith("v2-") ? componentId.slice(3) : componentId;
|
const baseComponentId = componentId.startsWith("v2-") ? componentId.slice(3) : componentId;
|
||||||
const basePascalCaseName = `${toPascalCase(baseComponentId)}ConfigPanel`;
|
const basePascalCaseName = `${toPascalCase(baseComponentId)}ConfigPanel`;
|
||||||
|
|
||||||
|
// 모듈에서 ConfigPanel로 끝나는 첫 번째 named export를 찾는 fallback
|
||||||
|
const findConfigPanelExport = () => {
|
||||||
|
for (const key of Object.keys(module)) {
|
||||||
|
if (key.endsWith("ConfigPanel") && typeof module[key] === "function") {
|
||||||
|
return module[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const ConfigPanelComponent =
|
const ConfigPanelComponent =
|
||||||
module[pascalCaseName] ||
|
module[pascalCaseName] ||
|
||||||
module[basePascalCaseName] ||
|
module[basePascalCaseName] ||
|
||||||
// 특수 export명들
|
findConfigPanelExport() ||
|
||||||
module.RepeaterConfigPanel ||
|
|
||||||
module.FlowWidgetConfigPanel ||
|
|
||||||
module.CustomerItemMappingConfigPanel ||
|
|
||||||
module.SelectedItemsDetailInputConfigPanel ||
|
|
||||||
module.ButtonConfigPanel ||
|
|
||||||
module.TableListConfigPanel ||
|
|
||||||
module.SectionCardConfigPanel ||
|
|
||||||
module.SectionPaperConfigPanel ||
|
|
||||||
module.TabsConfigPanel ||
|
|
||||||
module.V2RepeaterConfigPanel ||
|
|
||||||
module.V2InputConfigPanel ||
|
|
||||||
module.V2SelectConfigPanel ||
|
|
||||||
module.V2DateConfigPanel ||
|
|
||||||
module.V2ListConfigPanel ||
|
|
||||||
module.V2MediaConfigPanel ||
|
|
||||||
module.V2BizConfigPanel ||
|
|
||||||
module.V2GroupConfigPanel ||
|
|
||||||
module.V2HierarchyConfigPanel ||
|
|
||||||
module.V2LayoutConfigPanel ||
|
|
||||||
module.RepeatContainerConfigPanel ||
|
|
||||||
module.ScreenSplitPanelConfigPanel ||
|
|
||||||
module.SimpleRepeaterTableConfigPanel ||
|
|
||||||
module.ModalRepeaterTableConfigPanel ||
|
|
||||||
module.RepeatScreenModalConfigPanel ||
|
|
||||||
module.RelatedDataButtonsConfigPanel ||
|
|
||||||
module.AutocompleteSearchInputConfigPanel ||
|
|
||||||
module.EntitySearchInputConfigPanel ||
|
|
||||||
module.MailRecipientSelectorConfigPanel ||
|
|
||||||
module.LocationSwapSelectorConfigPanel ||
|
|
||||||
module.MapConfigPanel ||
|
|
||||||
module.RackStructureConfigPanel ||
|
|
||||||
module.AggregationWidgetConfigPanel ||
|
|
||||||
module.NumberingRuleConfigPanel ||
|
|
||||||
module.CategoryManagerConfigPanel ||
|
|
||||||
module.UniversalFormModalConfigPanel ||
|
|
||||||
module.PivotGridConfigPanel ||
|
|
||||||
module.TableSearchWidgetConfigPanel ||
|
|
||||||
module.TaxInvoiceListConfigPanel ||
|
|
||||||
module.ImageWidgetConfigPanel ||
|
|
||||||
module.TestInputConfigPanel ||
|
|
||||||
module.default;
|
module.default;
|
||||||
|
|
||||||
if (!ConfigPanelComponent) {
|
if (!ConfigPanelComponent) {
|
||||||
|
|
@ -522,6 +509,33 @@ export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> =
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 레거시 위젯 패널 (component/onUpdateProperty props 사용)
|
||||||
|
const LEGACY_PANELS = new Set([
|
||||||
|
"card", "dashboard", "stats", "stats-card",
|
||||||
|
"progress", "progress-bar", "chart", "chart-basic",
|
||||||
|
"alert", "alert-info", "badge", "badge-status",
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (LEGACY_PANELS.has(componentId)) {
|
||||||
|
const pseudoComponent = {
|
||||||
|
id: currentComponent?.id || "temp",
|
||||||
|
type: "component",
|
||||||
|
componentConfig: config,
|
||||||
|
...currentComponent,
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<ConfigPanelComponent
|
||||||
|
component={pseudoComponent}
|
||||||
|
onUpdateProperty={(path: string, value: any) => {
|
||||||
|
if (path.startsWith("componentConfig.")) {
|
||||||
|
const key = path.replace("componentConfig.", "");
|
||||||
|
onChange({ ...config, [key]: value });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 🆕 수주 등록 관련 컴포넌트들은 간단한 인터페이스 사용
|
// 🆕 수주 등록 관련 컴포넌트들은 간단한 인터페이스 사용
|
||||||
const isSimpleConfigPanel = [
|
const isSimpleConfigPanel = [
|
||||||
"autocomplete-search-input",
|
"autocomplete-search-input",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue