"use client";
import React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import {
Table,
Search,
FileText,
Grid3x3,
Info,
FormInput,
Save,
X,
Trash2,
Edit,
Plus,
RotateCcw,
Send,
ExternalLink,
MousePointer,
Settings,
Upload,
Square,
CreditCard,
Layout,
Columns,
Rows,
SidebarOpen,
Folder,
ChevronDown,
} from "lucide-react";
// 템플릿 컴포넌트 타입 정의
export interface TemplateComponent {
id: string;
name: string;
description: string;
category: "table" | "button" | "form" | "layout" | "chart" | "status" | "file" | "area";
icon: React.ReactNode;
defaultSize: { width: number; height: number };
components: Array<{
type: "widget" | "container" | "datatable" | "file" | "area";
widgetType?: string;
label: string;
placeholder?: string;
position: { x: number; y: number };
size: { width: number; height: number };
style?: any;
required?: boolean;
readonly?: boolean;
parentId?: string;
title?: string;
// 영역 컴포넌트 전용 속성
layoutType?: string;
description?: string;
layoutConfig?: any;
areaStyle?: any;
}>;
}
// 미리 정의된 템플릿 컴포넌트들
const templateComponents: TemplateComponent[] = [
// 고급 데이터 테이블 템플릿
{
id: "advanced-data-table",
name: "고급 데이터 테이블",
description: "컬럼 설정, 필터링, 페이지네이션이 포함된 완전한 데이터 테이블",
category: "table",
icon:
,
defaultSize: { width: 1000, height: 680 },
components: [
// 데이터 테이블 컴포넌트 (특별한 타입)
{
type: "datatable",
label: "데이터 테이블",
position: { x: 0, y: 0 },
size: { width: 1000, height: 680 },
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
backgroundColor: "#ffffff",
padding: "16px",
},
},
],
},
// 범용 버튼 템플릿
{
id: "universal-button",
name: "버튼",
description: "다양한 기능을 설정할 수 있는 범용 버튼. 상세설정에서 기능을 선택하세요.",
category: "button",
icon: ,
defaultSize: { width: 80, height: 36 },
components: [
{
type: "widget",
widgetType: "button",
label: "버튼",
position: { x: 0, y: 0 },
size: { width: 80, height: 36 },
style: {
backgroundColor: "#3b82f6",
color: "#ffffff",
border: "none",
borderRadius: "6px",
fontSize: "14px",
fontWeight: "500",
},
},
],
},
// 파일 첨부 템플릿
{
id: "file-upload",
name: "파일 첨부",
description: "파일 업로드, 미리보기, 다운로드가 가능한 파일 첨부 컴포넌트",
category: "file",
icon: ,
defaultSize: { width: 600, height: 300 },
components: [
{
type: "file",
label: "파일 첨부",
position: { x: 0, y: 0 },
size: { width: 600, height: 300 },
style: {
border: "1px solid #e5e7eb",
borderRadius: "8px",
backgroundColor: "#ffffff",
padding: "16px",
},
},
],
},
// === 영역 템플릿들 ===
// 기본 박스 영역
{
id: "area-box",
name: "기본 박스 영역",
description: "컴포넌트들을 그룹화할 수 있는 기본 박스 형태의 영역",
category: "area",
icon: ,
defaultSize: { width: 400, height: 300 },
components: [
{
type: "area",
label: "박스 영역",
position: { x: 0, y: 0 },
size: { width: 400, height: 300 },
layoutType: "box",
title: "박스 영역",
description: "컴포넌트들을 그룹화할 수 있는 기본 박스",
layoutConfig: {},
areaStyle: {
backgroundColor: "#f9fafb",
borderWidth: 1,
borderStyle: "solid",
borderColor: "#d1d5db",
borderRadius: 8,
padding: 16,
margin: 0,
shadow: "none",
},
style: {
border: "1px solid #d1d5db",
borderRadius: "8px",
backgroundColor: "#f9fafb",
padding: "16px",
},
},
],
},
// 카드 영역
{
id: "area-card",
name: "카드 영역",
description: "그림자와 둥근 모서리가 있는 카드 형태의 영역",
category: "area",
icon: ,
defaultSize: { width: 400, height: 300 },
components: [
{
type: "area",
label: "카드 영역",
position: { x: 0, y: 0 },
size: { width: 400, height: 300 },
layoutType: "card",
title: "카드 영역",
description: "그림자와 둥근 모서리가 있는 카드 형태",
layoutConfig: {},
areaStyle: {
backgroundColor: "#ffffff",
borderWidth: 0,
borderStyle: "none",
borderColor: "#e5e7eb",
borderRadius: 12,
padding: 20,
margin: 0,
shadow: "md",
},
style: {
backgroundColor: "#ffffff",
borderRadius: "12px",
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
padding: "20px",
},
},
],
},
// 패널 영역 (헤더 포함)
{
id: "area-panel",
name: "패널 영역",
description: "제목 헤더가 포함된 패널 형태의 영역",
category: "area",
icon: ,
defaultSize: { width: 500, height: 400 },
components: [
{
type: "area",
label: "패널 영역",
position: { x: 0, y: 0 },
size: { width: 500, height: 400 },
layoutType: "panel",
title: "패널 제목",
style: {
backgroundColor: "#ffffff",
border: "1px solid #e5e7eb",
borderRadius: "8px",
headerBackgroundColor: "#f3f4f6",
headerHeight: 48,
headerPadding: 16,
},
},
],
},
// 그리드 영역
{
id: "area-grid",
name: "그리드 영역",
description: "내부 컴포넌트들을 격자 형태로 배치하는 영역",
category: "area",
icon: ,
defaultSize: { width: 600, height: 400 },
components: [
{
type: "area",
label: "그리드 영역",
position: { x: 0, y: 0 },
size: { width: 600, height: 400 },
layoutType: "grid",
title: "그리드 영역",
description: "격자 형태로 컴포넌트 배치",
layoutConfig: {
gridColumns: 3,
gridRows: 2,
gridGap: 16,
},
areaStyle: {
backgroundColor: "#ffffff",
borderWidth: 1,
borderStyle: "solid",
borderColor: "#d1d5db",
borderRadius: 8,
padding: 16,
margin: 0,
shadow: "none",
showGridLines: true,
gridLineColor: "#e5e7eb",
},
style: {
backgroundColor: "#ffffff",
border: "1px solid #d1d5db",
borderRadius: "8px",
padding: "16px",
},
},
],
},
// 가로 플렉스 영역
{
id: "area-flex-row",
name: "가로 배치 영역",
description: "내부 컴포넌트들을 가로로 나란히 배치하는 영역",
category: "area",
icon: ,
defaultSize: { width: 600, height: 200 },
components: [
{
type: "area",
label: "가로 배치 영역",
position: { x: 0, y: 0 },
size: { width: 600, height: 200 },
layoutType: "flex-row",
layoutConfig: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
gap: 16,
},
style: {
backgroundColor: "#f8fafc",
border: "1px solid #cbd5e1",
borderRadius: "8px",
padding: "16px",
},
},
],
},
// 세로 플렉스 영역
{
id: "area-flex-column",
name: "세로 배치 영역",
description: "내부 컴포넌트들을 세로로 순차 배치하는 영역",
category: "area",
icon: ,
defaultSize: { width: 300, height: 500 },
components: [
{
type: "area",
label: "세로 배치 영역",
position: { x: 0, y: 0 },
size: { width: 300, height: 500 },
layoutType: "flex-column",
layoutConfig: {
flexDirection: "column",
justifyContent: "flex-start",
alignItems: "stretch",
gap: 12,
},
style: {
backgroundColor: "#f1f5f9",
border: "1px solid #94a3b8",
borderRadius: "8px",
padding: "16px",
},
},
],
},
// 사이드바 영역
{
id: "area-sidebar",
name: "사이드바 영역",
description: "사이드바와 메인 컨텐츠 영역으로 구분된 레이아웃",
category: "area",
icon: ,
defaultSize: { width: 700, height: 400 },
components: [
{
type: "area",
label: "사이드바 영역",
position: { x: 0, y: 0 },
size: { width: 700, height: 400 },
layoutType: "sidebar",
layoutConfig: {
sidebarPosition: "left",
sidebarWidth: 200,
collapsible: true,
},
style: {
backgroundColor: "#ffffff",
border: "1px solid #e2e8f0",
borderRadius: "8px",
},
},
],
},
// 탭 영역
{
id: "area-tabs",
name: "탭 영역",
description: "탭으로 구분된 여러 컨텐츠 영역을 제공하는 레이아웃",
category: "area",
icon: ,
defaultSize: { width: 600, height: 400 },
components: [
{
type: "area",
label: "탭 영역",
position: { x: 0, y: 0 },
size: { width: 600, height: 400 },
layoutType: "tabs",
layoutConfig: {
tabPosition: "top",
defaultActiveTab: "tab1",
},
style: {
backgroundColor: "#ffffff",
border: "1px solid #d1d5db",
borderRadius: "8px",
},
},
],
},
// 아코디언 영역
{
id: "area-accordion",
name: "아코디언 영역",
description: "접고 펼칠 수 있는 섹션들로 구성된 영역",
category: "area",
icon: ,
defaultSize: { width: 500, height: 600 },
components: [
{
type: "area",
label: "아코디언 영역",
position: { x: 0, y: 0 },
size: { width: 500, height: 600 },
layoutType: "accordion",
layoutConfig: {
allowMultiple: false,
defaultExpanded: ["section1"],
},
style: {
backgroundColor: "#ffffff",
border: "1px solid #e5e7eb",
borderRadius: "8px",
},
},
],
},
];
interface TemplatesPanelProps {
onDragStart: (e: React.DragEvent, template: TemplateComponent) => void;
}
export const TemplatesPanel: React.FC = ({ onDragStart }) => {
const [searchTerm, setSearchTerm] = React.useState("");
const [selectedCategory, setSelectedCategory] = React.useState("all");
const categories = [
{ id: "all", name: "전체", icon: },
{ id: "area", name: "영역", icon: },
{ id: "table", name: "테이블", icon: },
{ id: "button", name: "버튼", icon: },
{ id: "file", name: "파일", icon: },
];
const filteredTemplates = templateComponents.filter((template) => {
const matchesSearch =
template.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
template.description.toLowerCase().includes(searchTerm.toLowerCase());
const matchesCategory = selectedCategory === "all" || template.category === selectedCategory;
return matchesSearch && matchesCategory;
});
return (
{/* 검색 */}
setSearchTerm(e.target.value)}
className="pl-10"
/>
{/* 카테고리 필터 */}
{categories.map((category) => (
))}
{/* 템플릿 목록 */}
{filteredTemplates.length === 0 ? (
) : (
filteredTemplates.map((template) => (
onDragStart(e, template)}
className="group cursor-move rounded-lg border border-gray-200 bg-white p-3 shadow-sm transition-all hover:border-blue-300 hover:shadow-md"
>
{template.icon}
{template.name}
{template.components.length}개
{template.description}
{template.defaultSize.width}×{template.defaultSize.height}
•
{template.category}
))
)}
{/* 도움말 */}
사용 방법
템플릿을 캔버스로 드래그하여 빠르게 화면을 구성하세요.
);
};
export default TemplatesPanel;