338 lines
13 KiB
TypeScript
338 lines
13 KiB
TypeScript
"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 } from "@/types/screen";
|
||
import { ButtonConfigPanel } from "./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";
|
||
|
||
interface DetailSettingsPanelProps {
|
||
selectedComponent?: ComponentData;
|
||
onUpdateProperty: (componentId: string, path: string, value: any) => void;
|
||
currentTable?: TableInfo; // 현재 화면의 테이블 정보
|
||
currentTableName?: string; // 현재 화면의 테이블명
|
||
}
|
||
|
||
export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
|
||
selectedComponent,
|
||
onUpdateProperty,
|
||
currentTable,
|
||
currentTableName,
|
||
}) => {
|
||
// 데이터베이스에서 입력 가능한 웹타입들을 동적으로 가져오기
|
||
const { webTypes } = useWebTypes({ active: "Y" });
|
||
|
||
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);
|
||
|
||
// 웹타입별 상세 설정 렌더링 함수 - 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 <ConfigPanelComponent config={currentConfig} onConfigChange={handleConfigChange} />;
|
||
} else {
|
||
console.log(`🎨 ❌ ConfigPanelComponent가 null - 기본 설정 표시`);
|
||
return (
|
||
<div className="py-8 text-center text-gray-500">
|
||
⚙️ 기본 설정
|
||
<br />
|
||
웹타입 "{widget.widgetType}"은 추가 설정이 없습니다.
|
||
</div>
|
||
);
|
||
}
|
||
} else {
|
||
console.log(`🎨 config_panel이 없음 - 기본 설정 표시`);
|
||
return (
|
||
<div className="py-8 text-center text-gray-500">
|
||
⚙️ 기본 설정
|
||
<br />
|
||
웹타입 "{widget.widgetType}"은 추가 설정이 없습니다.
|
||
</div>
|
||
);
|
||
}
|
||
};
|
||
|
||
if (!selectedComponent) {
|
||
return (
|
||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||
<Settings className="mb-4 h-12 w-12 text-gray-400" />
|
||
<h3 className="mb-2 text-lg font-medium text-gray-900">컴포넌트를 선택하세요</h3>
|
||
<p className="text-sm text-gray-500">위젯 컴포넌트를 선택하면 상세 설정을 편집할 수 있습니다.</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 컴포넌트 타입별 설정 패널 렌더링
|
||
const renderComponentConfigPanel = () => {
|
||
console.log("🔍 renderComponentConfigPanel - selectedComponent:", selectedComponent);
|
||
|
||
if (!selectedComponent) {
|
||
console.error("❌ selectedComponent가 undefined입니다!");
|
||
return (
|
||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||
<Settings className="mb-4 h-12 w-12 text-red-400" />
|
||
<h3 className="mb-2 text-lg font-medium text-red-900">오류</h3>
|
||
<p className="text-sm text-red-500">선택된 컴포넌트가 없습니다.</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
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 <NewButtonConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "card":
|
||
return <CardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "dashboard":
|
||
return <DashboardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "stats":
|
||
case "stats-card":
|
||
return <StatsCardConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "progress":
|
||
case "progress-bar":
|
||
return <ProgressBarConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "chart":
|
||
case "chart-basic":
|
||
return <ChartConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "alert":
|
||
case "alert-info":
|
||
return <AlertConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
case "badge":
|
||
case "badge-status":
|
||
return <BadgeConfigPanel component={selectedComponent} onUpdateProperty={handleUpdateProperty} />;
|
||
|
||
default:
|
||
return (
|
||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||
<Settings className="mb-4 h-12 w-12 text-gray-400" />
|
||
<h3 className="mb-2 text-lg font-medium text-gray-900">설정 패널 준비 중</h3>
|
||
<p className="text-sm text-gray-500">컴포넌트 타입 "{componentType}"의 설정 패널이 준비 중입니다.</p>
|
||
</div>
|
||
);
|
||
}
|
||
};
|
||
|
||
// 새로운 컴포넌트 타입들에 대한 설정 패널 확인
|
||
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 (
|
||
<div className="flex h-full flex-col">
|
||
{/* 헤더 */}
|
||
<div className="border-b border-gray-200 p-4">
|
||
<div className="flex items-center space-x-2">
|
||
<Settings className="h-4 w-4 text-gray-600" />
|
||
<h3 className="font-medium text-gray-900">컴포넌트 설정</h3>
|
||
</div>
|
||
<div className="mt-2 flex items-center space-x-2">
|
||
<span className="text-sm text-gray-600">타입:</span>
|
||
<span className="rounded bg-green-100 px-2 py-1 text-xs font-medium text-green-800">{componentType}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 설정 패널 영역 */}
|
||
<div className="flex-1 overflow-y-auto p-4">{renderComponentConfigPanel()}</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
if (selectedComponent.type !== "widget" && selectedComponent.type !== "file" && selectedComponent.type !== "button") {
|
||
return (
|
||
<div className="flex h-full flex-col items-center justify-center p-6 text-center">
|
||
<Settings className="mb-4 h-12 w-12 text-gray-400" />
|
||
<h3 className="mb-2 text-lg font-medium text-gray-900">설정할 수 없는 컴포넌트입니다</h3>
|
||
<p className="text-sm text-gray-500">
|
||
상세 설정은 위젯, 파일, 버튼 컴포넌트에서만 사용할 수 있습니다.
|
||
<br />
|
||
현재 선택된 컴포넌트: {selectedComponent.type}
|
||
</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 파일 컴포넌트인 경우 FileComponentConfigPanel 렌더링
|
||
if (selectedComponent.type === "file") {
|
||
const fileComponent = selectedComponent as FileComponent;
|
||
|
||
return (
|
||
<div className="flex h-full flex-col">
|
||
{/* 헤더 */}
|
||
<div className="border-b border-gray-200 p-4">
|
||
<div className="flex items-center space-x-2">
|
||
<Settings className="h-4 w-4 text-gray-600" />
|
||
<h3 className="font-medium text-gray-900">파일 컴포넌트 설정</h3>
|
||
</div>
|
||
<div className="mt-2 flex items-center space-x-2">
|
||
<span className="text-sm text-gray-600">타입:</span>
|
||
<span className="rounded bg-purple-100 px-2 py-1 text-xs font-medium text-purple-800">파일 업로드</span>
|
||
</div>
|
||
<div className="mt-1 text-xs text-gray-500">문서 타입: {fileComponent.fileConfig.docTypeName}</div>
|
||
</div>
|
||
|
||
{/* 파일 컴포넌트 설정 영역 */}
|
||
<div className="flex-1 overflow-y-auto p-4">
|
||
<FileComponentConfigPanel
|
||
component={fileComponent}
|
||
onUpdateProperty={onUpdateProperty}
|
||
currentTable={currentTable}
|
||
currentTableName={currentTableName}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// 버튼 컴포넌트인 경우 ButtonConfigPanel 렌더링
|
||
if (selectedComponent.type === "button") {
|
||
const buttonWidget = selectedComponent as WidgetComponent;
|
||
|
||
return (
|
||
<div className="flex h-full flex-col">
|
||
{/* 헤더 */}
|
||
<div className="border-b border-gray-200 p-4">
|
||
<div className="flex items-center space-x-2">
|
||
<Settings className="h-4 w-4 text-gray-600" />
|
||
<h3 className="font-medium text-gray-900">버튼 상세 설정</h3>
|
||
</div>
|
||
<div className="mt-2 flex items-center space-x-2">
|
||
<span className="text-sm text-gray-600">타입:</span>
|
||
<span className="rounded bg-green-100 px-2 py-1 text-xs font-medium text-green-800">버튼</span>
|
||
</div>
|
||
<div className="mt-1 text-xs text-gray-500">라벨: {buttonWidget.label || "버튼"}</div>
|
||
</div>
|
||
|
||
{/* 버튼 설정 영역 */}
|
||
<div className="flex-1 overflow-y-auto p-4">
|
||
<ButtonConfigPanel
|
||
component={buttonWidget}
|
||
onUpdateComponent={(updates) => {
|
||
Object.entries(updates).forEach(([key, value]) => {
|
||
onUpdateProperty(buttonWidget.id, key, value);
|
||
});
|
||
}}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const widget = selectedComponent as WidgetComponent;
|
||
|
||
return (
|
||
<div className="flex h-full flex-col">
|
||
{/* 헤더 */}
|
||
<div className="border-b border-gray-200 p-4">
|
||
<div className="flex items-center space-x-2">
|
||
<Settings className="h-4 w-4 text-gray-600" />
|
||
<h3 className="font-medium text-gray-900">상세 설정</h3>
|
||
</div>
|
||
<div className="mt-2 flex items-center space-x-2">
|
||
<span className="text-sm text-gray-600">웹타입:</span>
|
||
<span className="rounded bg-blue-100 px-2 py-1 text-xs font-medium text-blue-800">{widget.widgetType}</span>
|
||
</div>
|
||
<div className="mt-1 text-xs text-gray-500">컬럼: {widget.columnName}</div>
|
||
</div>
|
||
|
||
{/* 상세 설정 영역 */}
|
||
<div className="flex-1 overflow-y-auto p-4">{renderWebTypeConfig(widget)}</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default DetailSettingsPanel;
|