Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feature/screen-management
This commit is contained in:
@@ -272,19 +272,15 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
||||
right: undefined,
|
||||
};
|
||||
|
||||
// 디버깅: 크기 정보 로그
|
||||
if (component.id && isSelected) {
|
||||
console.log("📐 RealtimePreview baseStyle:", {
|
||||
componentId: component.id,
|
||||
componentType: (component as any).componentType || component.type,
|
||||
sizeWidth: size?.width,
|
||||
sizeHeight: size?.height,
|
||||
styleWidth: componentStyle?.width,
|
||||
styleHeight: componentStyle?.height,
|
||||
baseStyleWidth: baseStyle.width,
|
||||
baseStyleHeight: baseStyle.height,
|
||||
});
|
||||
}
|
||||
// 크기 정보는 필요시에만 디버깅 (개발 중 문제 발생 시 주석 해제)
|
||||
// if (component.id && isSelected) {
|
||||
// console.log("📐 RealtimePreview baseStyle:", {
|
||||
// componentId: component.id,
|
||||
// componentType: (component as any).componentType || component.type,
|
||||
// sizeWidth: size?.width,
|
||||
// sizeHeight: size?.height,
|
||||
// });
|
||||
// }
|
||||
|
||||
// 🔍 DOM 렌더링 후 실제 크기 측정
|
||||
const innerDivRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -883,10 +883,12 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
|
||||
|
||||
// 래퍼 컴포넌트: 새 ConfigPanel 인터페이스를 기존 패턴에 맞춤
|
||||
const ConfigPanelWrapper = () => {
|
||||
const config = currentConfig.config || definition.defaultConfig || {};
|
||||
// Section Card, Section Paper 등 신규 컴포넌트는 componentConfig 바로 아래에 설정 저장
|
||||
const config = currentConfig || definition.defaultProps?.componentConfig || {};
|
||||
|
||||
const handleConfigChange = (newConfig: any) => {
|
||||
onUpdateProperty(selectedComponent.id, "componentConfig.config", newConfig);
|
||||
// componentConfig 전체를 업데이트
|
||||
onUpdateProperty(selectedComponent.id, "componentConfig", newConfig);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -895,7 +897,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
|
||||
<Settings className="h-4 w-4 text-primary" />
|
||||
<h3 className="text-sm font-semibold">{definition.name} 설정</h3>
|
||||
</div>
|
||||
<ConfigPanelComponent config={config} onConfigChange={handleConfigChange} />
|
||||
<ConfigPanelComponent config={config} onChange={handleConfigChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { ChevronDown, Settings, Info, Database, Trash2, Copy, Palette, Monitor } from "lucide-react";
|
||||
import {
|
||||
@@ -266,7 +267,12 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
const renderComponentConfigPanel = () => {
|
||||
if (!selectedComponent) return null;
|
||||
|
||||
const componentType = selectedComponent.componentConfig?.type || selectedComponent.type;
|
||||
// 🎯 Section Card, Section Paper 등 신규 컴포넌트는 componentType에서 감지
|
||||
const componentType =
|
||||
selectedComponent.componentType || // ⭐ 1순위: ScreenDesigner가 설정한 componentType (section-card 등)
|
||||
selectedComponent.componentConfig?.type ||
|
||||
selectedComponent.componentConfig?.id ||
|
||||
selectedComponent.type;
|
||||
|
||||
const handleUpdateProperty = (path: string, value: any) => {
|
||||
onUpdateProperty(selectedComponent.id, path, value);
|
||||
@@ -276,10 +282,15 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
onUpdateProperty(selectedComponent.id, "componentConfig.config", newConfig);
|
||||
};
|
||||
|
||||
// 🆕 ComponentRegistry에서 ConfigPanel 가져오기
|
||||
const componentId = selectedComponent.componentConfig?.type || selectedComponent.componentConfig?.id;
|
||||
// 🆕 ComponentRegistry에서 ConfigPanel 가져오기 시도
|
||||
const componentId =
|
||||
selectedComponent.componentType || // ⭐ section-card 등
|
||||
selectedComponent.componentConfig?.type ||
|
||||
selectedComponent.componentConfig?.id;
|
||||
|
||||
if (componentId) {
|
||||
const definition = ComponentRegistry.getComponent(componentId);
|
||||
|
||||
if (definition?.configPanel) {
|
||||
const ConfigPanelComponent = definition.configPanel;
|
||||
const currentConfig = selectedComponent.componentConfig || {};
|
||||
@@ -293,10 +304,12 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
|
||||
// 래퍼 컴포넌트: 새 ConfigPanel 인터페이스를 기존 패턴에 맞춤
|
||||
const ConfigPanelWrapper = () => {
|
||||
const config = currentConfig.config || definition.defaultConfig || {};
|
||||
// Section Card, Section Paper 등 신규 컴포넌트는 componentConfig 바로 아래에 설정 저장
|
||||
const config = currentConfig || definition.defaultProps?.componentConfig || {};
|
||||
|
||||
const handleConfigChange = (newConfig: any) => {
|
||||
onUpdateProperty(selectedComponent.id, "componentConfig.config", newConfig);
|
||||
// componentConfig 전체를 업데이트
|
||||
onUpdateProperty(selectedComponent.id, "componentConfig", newConfig);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -305,18 +318,19 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
<Settings className="h-4 w-4 text-primary" />
|
||||
<h3 className="text-sm font-semibold">{definition.name} 설정</h3>
|
||||
</div>
|
||||
<ConfigPanelComponent config={config} onConfigChange={handleConfigChange} />
|
||||
<ConfigPanelComponent config={config} onChange={handleConfigChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return <ConfigPanelWrapper key={selectedComponent.id} />;
|
||||
} else {
|
||||
console.warn("⚠️ ConfigPanel 없음:", {
|
||||
console.warn("⚠️ ComponentRegistry에서 ConfigPanel을 찾을 수 없음 - switch case로 이동:", {
|
||||
componentId,
|
||||
definitionName: definition?.name,
|
||||
hasDefinition: !!definition,
|
||||
});
|
||||
// ConfigPanel이 없으면 아래 switch case로 넘어감
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +377,280 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
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-xs text-muted-foreground">
|
||||
제목과 테두리가 있는 명확한 그룹화 컨테이너
|
||||
</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="text-xs cursor-pointer">
|
||||
헤더 표시
|
||||
</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="text-xs resize-none"
|
||||
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 pt-2 border-t">
|
||||
<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="text-xs cursor-pointer">
|
||||
접기/펼치기 가능
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{selectedComponent.componentConfig?.collapsible && (
|
||||
<div className="flex items-center space-x-2 ml-6">
|
||||
<Checkbox
|
||||
id="defaultOpen"
|
||||
checked={selectedComponent.componentConfig?.defaultOpen !== false}
|
||||
onCheckedChange={(checked) => {
|
||||
handleUpdateProperty(selectedComponent.id, "componentConfig.defaultOpen", checked);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="defaultOpen" className="text-xs cursor-pointer">
|
||||
기본으로 펼치기
|
||||
</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-xs text-muted-foreground">
|
||||
배경색 기반의 미니멀한 그룹화 컨테이너
|
||||
</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>
|
||||
<Input
|
||||
type="color"
|
||||
value={selectedComponent.componentConfig?.customColor || "#f0f0f0"}
|
||||
onChange={(e) => {
|
||||
handleUpdateProperty(selectedComponent.id, "componentConfig.customColor", e.target.value);
|
||||
}}
|
||||
className="h-9"
|
||||
/>
|
||||
</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="text-xs cursor-pointer">
|
||||
미묘한 테두리 표시
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
// ConfigPanel이 없는 경우 경고 표시
|
||||
return (
|
||||
@@ -634,11 +922,8 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
|
||||
// 상세 설정 탭 (DetailSettingsPanel의 전체 로직 통합)
|
||||
const renderDetailTab = () => {
|
||||
console.log("🔍 [renderDetailTab] selectedComponent.type:", selectedComponent.type);
|
||||
|
||||
// 1. DataTable 컴포넌트
|
||||
if (selectedComponent.type === "datatable") {
|
||||
console.log("✅ [renderDetailTab] DataTable 컴포넌트");
|
||||
return (
|
||||
<DataTableConfigPanel
|
||||
component={selectedComponent as DataTableComponent}
|
||||
@@ -695,7 +980,6 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
|
||||
// 5. 새로운 컴포넌트 시스템 (type: "component")
|
||||
if (selectedComponent.type === "component") {
|
||||
console.log("✅ [renderDetailTab] Component 타입");
|
||||
const componentId = (selectedComponent as any).componentType || selectedComponent.componentConfig?.type;
|
||||
const webType = selectedComponent.componentConfig?.webType;
|
||||
|
||||
@@ -755,7 +1039,6 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
|
||||
tables={tables}
|
||||
menuObjid={menuObjid} // 🆕 메뉴 OBJID 전달
|
||||
onChange={(newConfig) => {
|
||||
console.log("🔄 DynamicComponentConfigPanel onChange:", newConfig);
|
||||
// 개별 속성별로 업데이트하여 다른 속성과의 충돌 방지
|
||||
Object.entries(newConfig).forEach(([key, value]) => {
|
||||
handleUpdate(`componentConfig.${key}`, value);
|
||||
|
||||
Reference in New Issue
Block a user