ERP-node/frontend/components/pop/designer/panels/ComponentEditorPanel.tsx

216 lines
6.8 KiB
TypeScript
Raw Normal View History

"use client";
import React from "react";
import { cn } from "@/lib/utils";
import { PopComponentDefinition, PopComponentConfig } from "../types/pop-layout";
import { Settings, Database, Link2 } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
// ========================================
// Props 정의
// ========================================
interface ComponentEditorPanelProps {
/** 선택된 컴포넌트 (없으면 null) */
component: PopComponentDefinition | null;
/** 컴포넌트 설정 변경 시 호출 */
onConfigChange?: (config: Partial<PopComponentConfig>) => void;
/** 컴포넌트 라벨 변경 시 호출 */
onLabelChange?: (label: string) => void;
/** 추가 className */
className?: string;
}
// ========================================
// 컴포넌트 편집 패널
//
// 역할:
// - 선택된 컴포넌트의 설정을 편집
// - 3개 탭: 기본 설정 / 데이터 바인딩 / 데이터 연결
//
// TODO:
// - 타입별 상세 설정 UI 구현
// - 데이터 바인딩 UI 구현
// - 데이터 플로우 UI 구현
// ========================================
export function ComponentEditorPanel({
component,
onConfigChange,
onLabelChange,
className,
}: ComponentEditorPanelProps) {
// 컴포넌트가 선택되지 않은 경우
if (!component) {
return (
<div className={cn("flex h-full flex-col", className)}>
<div className="border-b px-4 py-3">
<h3 className="text-sm font-medium"> </h3>
</div>
<div className="flex flex-1 items-center justify-center p-4 text-sm text-muted-foreground">
</div>
</div>
);
}
return (
<div className={cn("flex h-full flex-col", className)}>
{/* 헤더 */}
<div className="border-b px-4 py-3">
<h3 className="text-sm font-medium">
{component.label || getComponentTypeLabel(component.type)}
</h3>
<p className="text-xs text-muted-foreground">{component.type}</p>
</div>
{/* 탭 컨텐츠 */}
<Tabs defaultValue="settings" className="flex-1">
<TabsList className="w-full justify-start rounded-none border-b bg-transparent px-2">
<TabsTrigger value="settings" className="gap-1 text-xs">
<Settings className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="data" className="gap-1 text-xs">
<Database className="h-3 w-3" />
</TabsTrigger>
<TabsTrigger value="flow" className="gap-1 text-xs">
<Link2 className="h-3 w-3" />
</TabsTrigger>
</TabsList>
{/* 기본 설정 탭 */}
<TabsContent value="settings" className="flex-1 overflow-auto p-4">
<ComponentSettingsForm
component={component}
onConfigChange={onConfigChange}
onLabelChange={onLabelChange}
/>
</TabsContent>
{/* 데이터 바인딩 탭 (뼈대) */}
<TabsContent value="data" className="flex-1 overflow-auto p-4">
<DataBindingPlaceholder />
</TabsContent>
{/* 데이터 연결 탭 (뼈대) */}
<TabsContent value="flow" className="flex-1 overflow-auto p-4">
<DataFlowPlaceholder />
</TabsContent>
</Tabs>
</div>
);
}
// ========================================
// 컴포넌트 설정 폼
// ========================================
interface ComponentSettingsFormProps {
component: PopComponentDefinition;
onConfigChange?: (config: Partial<PopComponentConfig>) => void;
onLabelChange?: (label: string) => void;
}
function ComponentSettingsForm({
component,
onConfigChange,
onLabelChange,
}: ComponentSettingsFormProps) {
return (
<div className="space-y-4">
{/* 라벨 입력 */}
<div className="space-y-1.5">
<label className="text-xs font-medium"></label>
<input
type="text"
className="h-8 w-full rounded border border-input bg-background px-2 text-sm"
value={component.label || ""}
onChange={(e) => onLabelChange?.(e.target.value)}
placeholder="컴포넌트 라벨"
/>
</div>
{/* 타입별 설정 (TODO: 상세 구현) */}
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
<p className="text-center text-xs text-muted-foreground">
{getComponentTypeLabel(component.type)}
</p>
<p className="mt-1 text-center text-xs text-muted-foreground">
( )
</p>
</div>
</div>
);
}
// ========================================
// 데이터 바인딩 플레이스홀더 (뼈대)
// ========================================
function DataBindingPlaceholder() {
return (
<div className="space-y-4">
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
<div className="flex flex-col items-center gap-2">
<Database className="h-8 w-8 text-gray-400" />
<p className="text-center text-xs text-muted-foreground">
</p>
<p className="text-center text-xs text-muted-foreground">
</p>
<p className="mt-2 text-center text-xs text-gray-400">
( )
</p>
</div>
</div>
</div>
);
}
// ========================================
// 데이터 플로우 플레이스홀더 (뼈대)
// ========================================
function DataFlowPlaceholder() {
return (
<div className="space-y-4">
<div className="rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4">
<div className="flex flex-col items-center gap-2">
<Link2 className="h-8 w-8 text-gray-400" />
<p className="text-center text-xs text-muted-foreground">
</p>
<p className="text-center text-xs text-muted-foreground">
/ /
</p>
<p className="mt-2 text-center text-xs text-gray-400">
( )
</p>
</div>
</div>
</div>
);
}
// ========================================
// 헬퍼 함수
// ========================================
function getComponentTypeLabel(type: string): string {
const labels: Record<string, string> = {
"pop-field": "필드",
"pop-button": "버튼",
"pop-list": "리스트",
"pop-indicator": "인디케이터",
"pop-scanner": "스캐너",
"pop-numpad": "넘패드",
};
return labels[type] || type;
}
export default ComponentEditorPanel;