"use client"; import React, { useState, useMemo } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ComponentRegistry } from "@/lib/registry/ComponentRegistry"; import { ComponentDefinition, ComponentCategory } from "@/types/component"; import { Search, Package, Grid, Layers, Palette, Zap, RotateCcw } from "lucide-react"; interface ComponentsPanelProps { className?: string; } export function ComponentsPanel({ className }: ComponentsPanelProps) { const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState("all"); // 레지스트리에서 모든 컴포넌트 조회 const allComponents = useMemo(() => { const components = ComponentRegistry.getAllComponents(); console.log("🔍 ComponentsPanel - 로드된 컴포넌트:", components.map(c => ({ id: c.id, name: c.name, category: c.category }))); // 수동으로 table-list 컴포넌트 추가 (임시) const hasTableList = components.some(c => c.id === 'table-list'); if (!hasTableList) { console.log("⚠️ table-list 컴포넌트가 없어서 수동 추가"); components.push({ id: "table-list", name: "테이블 리스트", nameEng: "TableList Component", description: "데이터베이스 테이블의 데이터를 목록으로 표시하는 컴포넌트", category: "display", webType: "text", defaultConfig: {}, defaultSize: { width: 800, height: 400 }, icon: "Table", tags: ["테이블", "데이터", "목록", "그리드"], version: "1.0.0", author: "개발팀", }); } return components; }, []); // 카테고리별 분류 (input 카테고리 제외) const componentsByCategory = useMemo(() => { // input 카테고리 컴포넌트들을 제외한 컴포넌트만 필터링 const filteredComponents = allComponents.filter((component) => component.category !== "input"); const categories: Record = { all: filteredComponents, // input 카테고리 제외된 컴포넌트들만 포함 input: [], // 빈 배열로 유지 (사용되지 않음) display: [], action: [], layout: [], utility: [], }; filteredComponents.forEach((component) => { if (categories[component.category]) { categories[component.category].push(component); } }); return categories; }, [allComponents]); // 검색 및 필터링된 컴포넌트 const filteredComponents = useMemo(() => { let components = componentsByCategory[selectedCategory] || []; if (searchQuery.trim()) { const query = searchQuery.toLowerCase(); components = components.filter( (component) => component.name.toLowerCase().includes(query) || component.description.toLowerCase().includes(query) || component.tags?.some((tag) => tag.toLowerCase().includes(query)), ); } return components; }, [componentsByCategory, selectedCategory, searchQuery]); // 드래그 시작 핸들러 const handleDragStart = (e: React.DragEvent, component: ComponentDefinition) => { const dragData = { type: "component", component: component, }; console.log("🚀 컴포넌트 드래그 시작:", component.name, dragData); e.dataTransfer.setData("application/json", JSON.stringify(dragData)); e.dataTransfer.effectAllowed = "copy"; }; // 카테고리별 아이콘 const getCategoryIcon = (category: ComponentCategory | "all") => { switch (category) { case "input": return ; case "display": return ; case "action": return ; case "layout": return ; case "utility": return ; default: return ; } }; // 컴포넌트 새로고침 const handleRefresh = () => { // Hot Reload 트리거 (개발 모드에서만) if (process.env.NODE_ENV === "development") { ComponentRegistry.refreshComponents?.(); } window.location.reload(); }; return (
{/* 헤더 */}

컴포넌트

{componentsByCategory.all.length}개의 사용 가능한 컴포넌트

{/* 검색창 */}
setSearchQuery(e.target.value)} className="pl-10 border-0 bg-white/80 backdrop-blur-sm shadow-sm focus:bg-white transition-colors" />
setSelectedCategory(value as ComponentCategory | "all")} > {/* 카테고리 탭 (input 카테고리 제외) */} 전체 표시 액션 레이아웃 유틸리티 {/* 컴포넌트 목록 */}
{filteredComponents.length > 0 ? (
{filteredComponents.map((component) => (
{ handleDragStart(e, component); // 드래그 시작 시 시각적 피드백 e.currentTarget.style.opacity = '0.5'; e.currentTarget.style.transform = 'rotate(-3deg) scale(0.95)'; }} onDragEnd={(e) => { // 드래그 종료 시 원래 상태로 복원 e.currentTarget.style.opacity = '1'; e.currentTarget.style.transform = 'none'; }} className="group cursor-grab rounded-lg border border-gray-200/40 bg-white/90 backdrop-blur-sm p-5 shadow-sm transition-all duration-300 hover:bg-white hover:shadow-lg hover:shadow-purple-500/15 hover:scale-[1.02] hover:border-purple-300/60 hover:-translate-y-1 active:cursor-grabbing active:scale-[0.98] active:translate-y-0" title={component.description} >
{getCategoryIcon(component.category)}

{component.name}

신규

{component.description}

{component.defaultSize.width}×{component.defaultSize.height}
{component.category}
{/* 태그 */} {component.tags && component.tags.length > 0 && (
{component.tags.slice(0, 2).map((tag, index) => ( {tag} ))} {component.tags.length > 2 && ( +{component.tags.length - 2} )}
)}
))}
) : (

{searchQuery ? `"${searchQuery}"에 대한 컴포넌트를 찾을 수 없습니다` : "이 카테고리에 컴포넌트가 없습니다"}

검색어나 필터를 조정해보세요

)}
{/* 통계 정보 */}
{filteredComponents.length}
필터됨
{allComponents.length}
전체
{/* 개발 정보 (개발 모드에서만) */} {process.env.NODE_ENV === "development" && (
레지스트리 기반 시스템
Hot Reload 지원
완전한 타입 안전성
)}
); } export default ComponentsPanel;