"use client"; import React, { useState, useMemo } from "react"; import { Plus, Layers, Search, Filter } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useComponents } from "@/hooks/admin/useComponents"; interface ComponentsPanelProps { onDragStart: (e: React.DragEvent, component: ComponentItem) => void; } interface ComponentItem { id: string; name: string; description: string; category: string; componentType: string; componentConfig: any; webType: string; // webType 추가 icon: React.ReactNode; defaultSize: { width: number; height: number }; } // 컴포넌트 카테고리 정의 (실제 생성된 컴포넌트에 맞게) const COMPONENT_CATEGORIES = [ { id: "액션", name: "액션", description: "사용자 동작을 처리하는 컴포넌트" }, { id: "레이아웃", name: "레이아웃", description: "화면 구조를 제공하는 컴포넌트" }, { id: "데이터", name: "데이터", description: "데이터를 표시하는 컴포넌트" }, { id: "네비게이션", name: "네비게이션", description: "화면 이동을 도와주는 컴포넌트" }, { id: "피드백", name: "피드백", description: "사용자 피드백을 제공하는 컴포넌트" }, { id: "입력", name: "입력", description: "사용자 입력을 받는 컴포넌트" }, { id: "표시", name: "표시", description: "정보를 표시하고 알리는 컴포넌트" }, { id: "컨테이너", name: "컨테이너", description: "다른 컴포넌트를 담는 컨테이너" }, { id: "위젯", name: "위젯", description: "범용 위젯 컴포넌트" }, { id: "템플릿", name: "템플릿", description: "미리 정의된 템플릿" }, { id: "차트", name: "차트", description: "데이터 시각화 컴포넌트" }, { id: "폼", name: "폼", description: "폼 관련 컴포넌트" }, { id: "미디어", name: "미디어", description: "이미지, 비디오 등 미디어 컴포넌트" }, { id: "유틸리티", name: "유틸리티", description: "보조 기능 컴포넌트" }, { id: "관리", name: "관리", description: "관리자 전용 컴포넌트" }, { id: "시스템", name: "시스템", description: "시스템 관련 컴포넌트" }, { id: "UI", name: "UI", description: "일반 UI 컴포넌트" }, { id: "컴포넌트", name: "컴포넌트", description: "일반 컴포넌트" }, { id: "기타", name: "기타", description: "기타 컴포넌트" }, ]; export const ComponentsPanel: React.FC = ({ onDragStart }) => { const [searchTerm, setSearchTerm] = useState(""); const [selectedCategory, setSelectedCategory] = useState("all"); // 데이터베이스에서 컴포넌트 가져오기 const { data: componentsData, isLoading: loading, error, } = useComponents({ active: "Y", }); // 컴포넌트를 ComponentItem으로 변환 const componentItems = useMemo(() => { if (!componentsData?.components) { console.log("🔍 ComponentsPanel: 컴포넌트 데이터 없음"); return []; } console.log("🔍 ComponentsPanel 전체 컴포넌트 데이터:", { totalComponents: componentsData.components.length, components: componentsData.components.map((c) => ({ code: c.component_code, name: c.component_name, category: c.category, config: c.component_config, })), }); return componentsData.components.map((component) => { console.log("🔍 ComponentsPanel 컴포넌트 매핑:", { component_code: component.component_code, component_name: component.component_name, component_config: component.component_config, componentType: component.component_config?.type || component.component_code, webType: component.component_config?.type || component.component_code, category: component.category, }); // 카테고리 매핑 (영어 -> 한국어) const categoryMapping: Record = { display: "표시", action: "액션", layout: "레이아웃", data: "데이터", navigation: "네비게이션", feedback: "피드백", input: "입력", container: "컨테이너", widget: "위젯", template: "템플릿", chart: "차트", form: "폼", media: "미디어", utility: "유틸리티", admin: "관리", system: "시스템", ui: "UI", component: "컴포넌트", 기타: "기타", other: "기타", // 한국어도 처리 표시: "표시", 액션: "액션", 레이아웃: "레이아웃", 데이터: "데이터", 네비게이션: "네비게이션", 피드백: "피드백", 입력: "입력", }; const mappedCategory = categoryMapping[component.category] || component.category || "other"; return { id: component.component_code, name: component.component_name, description: component.description || `${component.component_name} 컴포넌트`, category: mappedCategory, componentType: component.component_config?.type || component.component_code, componentConfig: component.component_config, webType: component.component_config?.type || component.component_code, // webType 추가 icon: getComponentIcon(component.icon_name || component.component_config?.type), defaultSize: component.default_size || getDefaultSize(component.component_config?.type), }; }); }, [componentsData]); // 필터링된 컴포넌트 const filteredComponents = useMemo(() => { return componentItems.filter((component) => { const matchesSearch = component.name.toLowerCase().includes(searchTerm.toLowerCase()) || component.description.toLowerCase().includes(searchTerm.toLowerCase()); const matchesCategory = selectedCategory === "all" || component.category === selectedCategory; return matchesSearch && matchesCategory; }); }, [componentItems, searchTerm, selectedCategory]); // 카테고리별 그룹화 const groupedComponents = useMemo(() => { const groups: Record = {}; COMPONENT_CATEGORIES.forEach((category) => { groups[category.id] = filteredComponents.filter((component) => component.category === category.id); }); console.log("🔍 카테고리별 그룹화 결과:", { 총컴포넌트: filteredComponents.length, 카테고리별개수: Object.entries(groups).map(([cat, comps]) => ({ 카테고리: cat, 개수: comps.length })), }); return groups; }, [filteredComponents]); console.log("🔍 ComponentsPanel 상태:", { loading, error: error?.message, componentsData, componentItemsLength: componentItems.length, }); if (loading) { return (

컴포넌트 로딩 중...

API: {process.env.NODE_ENV === "development" ? "http://localhost:8080" : "39.117.244.52:8080"}

); } if (error) { return (

컴포넌트 로드 실패

{error.message}

상세 오류
{JSON.stringify(error, null, 2)}
); } return (
{/* 헤더 */}

컴포넌트

{filteredComponents.length}개

드래그하여 화면에 추가하세요

{/* 검색 및 필터 */}
{/* 검색 */}
setSearchTerm(e.target.value)} className="h-8 pl-9 text-xs" />
{/* 카테고리 필터 */}
{/* 컴포넌트 목록 */}
{selectedCategory === "all" ? ( // 카테고리별 그룹 표시
{COMPONENT_CATEGORIES.map((category) => { const categoryComponents = groupedComponents[category.id]; if (categoryComponents.length === 0) return null; return (

{category.name}

{categoryComponents.length}개

{category.description}

{categoryComponents.map((component) => ( ))}
); })}
) : ( // 선택된 카테고리만 표시
{filteredComponents.map((component) => ( ))}
)} {filteredComponents.length === 0 && (

검색 결과가 없습니다

다른 검색어를 시도해보세요

)}
); }; // 컴포넌트 카드 컴포넌트 const ComponentCard: React.FC<{ component: ComponentItem; onDragStart: (e: React.DragEvent, component: ComponentItem) => void; }> = ({ component, onDragStart }) => { return (
onDragStart(e, component)} 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" >
{component.icon}

{component.name}

{component.description}

{component.webType}
); }; // 웹타입별 아이콘 매핑 function getComponentIcon(webType: string): React.ReactNode { const iconMap: Record = { text: Aa, number: 123, date: 📅, select: , checkbox: , radio: , textarea: 📝, file: 📎, button: 🔘, email: 📧, tel: 📞, password: 🔒, code: <>, entity: 🔗, }; return iconMap[webType] || ; } // 웹타입별 기본 크기 function getDefaultSize(webType: string): { width: number; height: number } { const sizeMap: Record = { text: { width: 200, height: 36 }, number: { width: 150, height: 36 }, date: { width: 180, height: 36 }, select: { width: 200, height: 36 }, checkbox: { width: 150, height: 36 }, radio: { width: 200, height: 80 }, textarea: { width: 300, height: 100 }, file: { width: 300, height: 120 }, button: { width: 120, height: 36 }, email: { width: 250, height: 36 }, tel: { width: 180, height: 36 }, password: { width: 200, height: 36 }, code: { width: 200, height: 36 }, entity: { width: 200, height: 36 }, }; return sizeMap[webType] || { width: 200, height: 36 }; } export default ComponentsPanel;