"use client"; import React from "react"; import { Settings } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useWebTypes } from "@/hooks/admin/useWebTypes"; import { getConfigPanelComponent } from "@/lib/utils/getConfigPanelComponent"; import { ComponentData, WidgetComponent, FileComponent, WebTypeConfig, TableInfo, LayoutComponent, } from "@/types/screen"; // 레거시 ButtonConfigPanel 제거됨 import { FileComponentConfigPanel } from "./FileComponentConfigPanel"; // 새로운 컴포넌트 설정 패널들 import import { ButtonConfigPanel as NewButtonConfigPanel } from "../config-panels/ButtonConfigPanel"; import { CardConfigPanel } from "../config-panels/CardConfigPanel"; import { DashboardConfigPanel } from "../config-panels/DashboardConfigPanel"; import { StatsCardConfigPanel } from "../config-panels/StatsCardConfigPanel"; import { ProgressBarConfigPanel } from "../config-panels/ProgressBarConfigPanel"; import { ChartConfigPanel } from "../config-panels/ChartConfigPanel"; import { AlertConfigPanel } from "../config-panels/AlertConfigPanel"; import { BadgeConfigPanel } from "../config-panels/BadgeConfigPanel"; // 동적 컴포넌트 설정 패널 import { DynamicComponentConfigPanel } from "@/lib/utils/getComponentConfigPanel"; interface DetailSettingsPanelProps { selectedComponent?: ComponentData; onUpdateProperty: (componentId: string, path: string, value: any) => void; currentTable?: TableInfo; // 현재 화면의 테이블 정보 currentTableName?: string; // 현재 화면의 테이블명 } export const DetailSettingsPanel: React.FC = ({ selectedComponent, onUpdateProperty, currentTable, currentTableName, }) => { // 데이터베이스에서 입력 가능한 웹타입들을 동적으로 가져오기 const { webTypes } = useWebTypes({ active: "Y" }); console.log(`🔍 DetailSettingsPanel props:`, { selectedComponent: selectedComponent?.id, componentType: selectedComponent?.type, currentTableName, currentTable: currentTable?.tableName, selectedComponentTableName: selectedComponent?.tableName, }); console.log(`🔍 DetailSettingsPanel webTypes 로드됨:`, webTypes?.length, "개"); console.log(`🔍 webTypes:`, webTypes); console.log(`🔍 DetailSettingsPanel selectedComponent:`, selectedComponent); console.log(`🔍 DetailSettingsPanel selectedComponent.widgetType:`, selectedComponent?.widgetType); const inputableWebTypes = webTypes.map((wt) => wt.web_type); // 레이아웃 컴포넌트 설정 렌더링 함수 const renderLayoutConfig = (layoutComponent: LayoutComponent) => { return (
{/* 헤더 */}

레이아웃 설정

타입: {layoutComponent.layoutType}
ID: {layoutComponent.id}
{/* 레이아웃 설정 영역 */}
{/* 기본 정보 */}
onUpdateProperty(layoutComponent.id, "label", e.target.value)} className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500" placeholder="레이아웃 이름을 입력하세요" />
{/* 그리드 레이아웃 설정 */} {layoutComponent.layoutType === "grid" && (

그리드 설정

{ const newRows = parseInt(e.target.value); const newCols = layoutComponent.layoutConfig?.grid?.columns || 2; // 그리드 설정 업데이트 onUpdateProperty(layoutComponent.id, "layoutConfig.grid.rows", newRows); // 존 개수 자동 업데이트 (행 × 열) const totalZones = newRows * newCols; const currentZones = layoutComponent.zones || []; if (totalZones !== currentZones.length) { const newZones = []; for (let row = 0; row < newRows; row++) { for (let col = 0; col < newCols; col++) { const zoneIndex = row * newCols + col; newZones.push({ id: `zone${zoneIndex + 1}`, name: `존 ${zoneIndex + 1}`, position: { row, column: col }, size: { width: "100%", height: "100%" }, }); } } onUpdateProperty(layoutComponent.id, "zones", newZones); } }} className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
{ const newCols = parseInt(e.target.value); const newRows = layoutComponent.layoutConfig?.grid?.rows || 2; // 그리드 설정 업데이트 onUpdateProperty(layoutComponent.id, "layoutConfig.grid.columns", newCols); // 존 개수 자동 업데이트 (행 × 열) const totalZones = newRows * newCols; const currentZones = layoutComponent.zones || []; if (totalZones !== currentZones.length) { const newZones = []; for (let row = 0; row < newRows; row++) { for (let col = 0; col < newCols; col++) { const zoneIndex = row * newCols + col; newZones.push({ id: `zone${zoneIndex + 1}`, name: `존 ${zoneIndex + 1}`, position: { row, column: col }, size: { width: "100%", height: "100%" }, }); } } onUpdateProperty(layoutComponent.id, "zones", newZones); } }} className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
onUpdateProperty(layoutComponent.id, "layoutConfig.grid.gap", parseInt(e.target.value)) } className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
)} {/* 플렉스박스 레이아웃 설정 */} {layoutComponent.layoutType === "flexbox" && (

플렉스박스 설정

{ const newZoneCount = parseInt(e.target.value); const currentZones = layoutComponent.zones || []; const direction = layoutComponent.layoutConfig?.flexbox?.direction || "row"; if (newZoneCount > currentZones.length) { // 존 추가 const newZones = [...currentZones]; for (let i = currentZones.length; i < newZoneCount; i++) { newZones.push({ id: `zone${i + 1}`, name: `존 ${i + 1}`, position: {}, size: { width: direction === "row" ? `${100 / newZoneCount}%` : "100%", height: direction === "column" ? `${100 / newZoneCount}%` : "100%", }, }); } // 기존 존들의 크기도 조정 newZones.forEach((zone, index) => { if (direction === "row") { zone.size.width = `${100 / newZoneCount}%`; } else { zone.size.height = `${100 / newZoneCount}%`; } }); onUpdateProperty(layoutComponent.id, "zones", newZones); } else if (newZoneCount < currentZones.length) { // 존 제거 const newZones = currentZones.slice(0, newZoneCount); // 남은 존들의 크기 재조정 newZones.forEach((zone, index) => { if (direction === "row") { zone.size.width = `${100 / newZoneCount}%`; } else { zone.size.height = `${100 / newZoneCount}%`; } }); onUpdateProperty(layoutComponent.id, "zones", newZones); } }} className="w-20 rounded border border-gray-300 px-2 py-1 text-sm" />
onUpdateProperty(layoutComponent.id, "layoutConfig.flexbox.gap", parseInt(e.target.value)) } className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
)} {/* 분할 레이아웃 설정 */} {layoutComponent.layoutType === "split" && (

분할 설정

)} {/* 카드 레이아웃 설정 */} {layoutComponent.layoutType === "card-layout" && (

카드 설정

{/* 테이블 컬럼 매핑 */}
테이블 컬럼 매핑
{currentTable && ( 테이블: {currentTable.table_name} )}
{/* 테이블이 선택되지 않은 경우 안내 */} {!currentTable && (

테이블을 먼저 선택해주세요

화면 설정에서 테이블을 선택하면 컬럼 목록이 표시됩니다

)} {/* 테이블이 선택된 경우 컬럼 드롭다운 */} {currentTable && ( <>
{/* 동적 표시 컬럼 추가 */}
{(layoutComponent.layoutConfig?.card?.columnMapping?.displayColumns || []).map( (column, index) => (
), )} {(!layoutComponent.layoutConfig?.card?.columnMapping?.displayColumns || layoutComponent.layoutConfig.card.columnMapping.displayColumns.length === 0) && (
"컬럼 추가" 버튼을 클릭하여 표시할 컬럼을 추가하세요
)}
)}
{/* 카드 스타일 설정 */}
카드 스타일
onUpdateProperty(layoutComponent.id, "layoutConfig.card.cardsPerRow", parseInt(e.target.value)) } className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
onUpdateProperty(layoutComponent.id, "layoutConfig.card.cardSpacing", parseInt(e.target.value)) } className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
onUpdateProperty(layoutComponent.id, "layoutConfig.card.cardStyle.showTitle", e.target.checked) } className="rounded border-gray-300" />
onUpdateProperty( layoutComponent.id, "layoutConfig.card.cardStyle.showSubtitle", e.target.checked, ) } className="rounded border-gray-300" />
onUpdateProperty( layoutComponent.id, "layoutConfig.card.cardStyle.showDescription", e.target.checked, ) } className="rounded border-gray-300" />
onUpdateProperty(layoutComponent.id, "layoutConfig.card.cardStyle.showImage", e.target.checked) } className="rounded border-gray-300" />
onUpdateProperty( layoutComponent.id, "layoutConfig.card.cardStyle.maxDescriptionLength", parseInt(e.target.value), ) } className="w-full rounded border border-gray-300 px-2 py-1 text-sm" />
)} {/* 존 목록 - 카드 레이아웃은 데이터 기반이므로 존 관리 불필요 */} {layoutComponent.layoutType !== "card-layout" && (

존 목록

{layoutComponent.zones?.map((zone, index) => (
{zone.name} ID: {zone.id}
onUpdateProperty(layoutComponent.id, `zones.${index}.size.width`, e.target.value) } className="w-full rounded border border-gray-300 px-2 py-1 text-xs" placeholder="100%" />
onUpdateProperty(layoutComponent.id, `zones.${index}.size.height`, e.target.value) } className="w-full rounded border border-gray-300 px-2 py-1 text-xs" placeholder="auto" />
))}
)}
); }; // 웹타입별 상세 설정 렌더링 함수 - useCallback 제거하여 항상 최신 widget 사용 const renderWebTypeConfig = (widget: WidgetComponent) => { const currentConfig = widget.webTypeConfig || {}; console.log("🎨 DetailSettingsPanel renderWebTypeConfig 호출:", { componentId: widget.id, widgetType: widget.widgetType, currentConfig, configExists: !!currentConfig, configKeys: Object.keys(currentConfig), configStringified: JSON.stringify(currentConfig), widgetWebTypeConfig: widget.webTypeConfig, widgetWebTypeConfigExists: !!widget.webTypeConfig, timestamp: new Date().toISOString(), }); console.log("🎨 selectedComponent 전체:", selectedComponent); const handleConfigChange = (newConfig: WebTypeConfig) => { console.log("🔧 WebTypeConfig 업데이트:", { widgetType: widget.widgetType, oldConfig: currentConfig, newConfig, componentId: widget.id, isEqual: JSON.stringify(currentConfig) === JSON.stringify(newConfig), }); // 강제 새 객체 생성으로 React 변경 감지 보장 const freshConfig = { ...newConfig }; onUpdateProperty(widget.id, "webTypeConfig", freshConfig); }; // 1순위: DB에서 지정된 설정 패널 사용 const dbWebType = webTypes.find((wt) => wt.web_type === widget.widgetType); console.log(`🎨 웹타입 "${widget.widgetType}" DB 조회 결과:`, dbWebType); if (dbWebType?.config_panel) { console.log(`🎨 웹타입 "${widget.widgetType}" → DB 지정 설정 패널 "${dbWebType.config_panel}" 사용`); const ConfigPanelComponent = getConfigPanelComponent(dbWebType.config_panel); console.log(`🎨 getConfigPanelComponent 결과:`, ConfigPanelComponent); if (ConfigPanelComponent) { console.log(`🎨 ✅ ConfigPanelComponent 렌더링 시작`); return ; } else { console.log(`🎨 ❌ ConfigPanelComponent가 null - 기본 설정 표시`); return (
⚙️ 기본 설정
웹타입 "{widget.widgetType}"은 추가 설정이 없습니다.
); } } else { console.log(`🎨 config_panel이 없음 - 기본 설정 표시`); return (
⚙️ 기본 설정
웹타입 "{widget.widgetType}"은 추가 설정이 없습니다.
); } }; if (!selectedComponent) { return (

컴포넌트를 선택하세요

위젯 컴포넌트를 선택하면 상세 설정을 편집할 수 있습니다.

); } // 컴포넌트 타입별 설정 패널 렌더링 const renderComponentConfigPanel = () => { console.log("🔍 renderComponentConfigPanel - selectedComponent:", selectedComponent); if (!selectedComponent) { console.error("❌ selectedComponent가 undefined입니다!"); return (

오류

선택된 컴포넌트가 없습니다.

); } const componentType = selectedComponent.componentConfig?.type || selectedComponent.type; const handleUpdateProperty = (path: string, value: any) => { onUpdateProperty(selectedComponent.id, path, value); }; switch (componentType) { case "button": case "button-primary": case "button-secondary": return ; case "card": return ; case "dashboard": return ; case "stats": case "stats-card": return ; case "progress": case "progress-bar": return ; case "chart": case "chart-basic": return ; case "alert": case "alert-info": return ; case "badge": case "badge-status": return ; default: return (

설정 패널 준비 중

컴포넌트 타입 "{componentType}"의 설정 패널이 준비 중입니다.

); } }; // 새로운 컴포넌트 타입들에 대한 설정 패널 확인 const componentType = selectedComponent?.componentConfig?.type || selectedComponent?.type; console.log("🔍 DetailSettingsPanel componentType 확인:", { selectedComponentType: selectedComponent?.type, componentConfigType: selectedComponent?.componentConfig?.type, finalComponentType: componentType, }); const hasNewConfigPanel = componentType && [ "button", "button-primary", "button-secondary", "card", "dashboard", "stats", "stats-card", "progress", "progress-bar", "chart", "chart-basic", "alert", "alert-info", "badge", "badge-status", ].includes(componentType); console.log("🔍 hasNewConfigPanel:", hasNewConfigPanel); if (hasNewConfigPanel) { return (
{/* 헤더 */}

컴포넌트 설정

타입: {componentType}
{/* 설정 패널 영역 */}
{renderComponentConfigPanel()}
); } // 레이아웃 컴포넌트 처리 if (selectedComponent.type === "layout") { return renderLayoutConfig(selectedComponent as LayoutComponent); } if ( selectedComponent.type !== "widget" && selectedComponent.type !== "file" && selectedComponent.type !== "button" && selectedComponent.type !== "component" ) { return (

설정할 수 없는 컴포넌트입니다

상세 설정은 위젯, 파일, 버튼, 컴포넌트, 레이아웃에서만 사용할 수 있습니다.
현재 선택된 컴포넌트: {selectedComponent.type}

); } // 파일 컴포넌트인 경우 FileComponentConfigPanel 렌더링 if (selectedComponent.type === "file") { const fileComponent = selectedComponent as FileComponent; return (
{/* 헤더 */}

파일 컴포넌트 설정

타입: 파일 업로드
문서 타입: {fileComponent.fileConfig.docTypeName}
{/* 파일 컴포넌트 설정 영역 */}
); } // 레거시 버튼을 새로운 컴포넌트 시스템으로 강제 변환 if (selectedComponent.type === "button") { console.log("🔄 레거시 버튼을 새로운 컴포넌트 시스템으로 변환:", selectedComponent); // 레거시 버튼을 새로운 시스템으로 변환 const convertedComponent = { ...selectedComponent, type: "component" as const, componentConfig: { type: "button-primary", webType: "button", ...selectedComponent.componentConfig, }, }; // 변환된 컴포넌트로 DB 업데이트 onUpdateProperty(selectedComponent.id, "type", "component"); onUpdateProperty(selectedComponent.id, "componentConfig", convertedComponent.componentConfig); // 변환된 컴포넌트로 처리 계속 selectedComponent = convertedComponent; } // 새로운 컴포넌트 시스템 처리 (type: "component") if (selectedComponent.type === "component") { const componentId = (selectedComponent as any).componentType || selectedComponent.componentConfig?.type; const webType = selectedComponent.componentConfig?.webType; console.log("🔧 새로운 컴포넌트 시스템 설정 패널:", { componentId, webType }); if (!componentId) { return (

컴포넌트 ID가 없습니다

componentConfig.type이 설정되지 않았습니다.

); } return (
{/* 헤더 */}

컴포넌트 설정

컴포넌트: {componentId}
{webType && (
웹타입: {webType}
)} {selectedComponent.columnName && (
컬럼: {selectedComponent.columnName}
)}
{/* 컴포넌트 설정 패널 */}
{ console.log("🔍 DetailSettingsPanel tableColumns 전달:", { currentTable, columns: currentTable?.columns, columnsLength: currentTable?.columns?.length, }); return currentTable?.columns || []; })()} onChange={(newConfig) => { console.log("🔧 컴포넌트 설정 변경:", newConfig); // 개별 속성별로 업데이트하여 다른 속성과의 충돌 방지 Object.entries(newConfig).forEach(([key, value]) => { onUpdateProperty(selectedComponent.id, `componentConfig.${key}`, value); }); }} />
); } // 기존 위젯 시스템 처리 (type: "widget") const widget = selectedComponent as WidgetComponent; return (
{/* 헤더 */}

상세 설정

웹타입: {widget.widgetType}
컬럼: {widget.columnName}
{/* 상세 설정 영역 */}
{renderWebTypeConfig(widget)}
); }; export default DetailSettingsPanel;