2025-09-03 11:32:09 +09:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import React from "react";
|
|
|
|
|
import { Settings } from "lucide-react";
|
2025-09-04 14:23:35 +09:00
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
2025-09-03 11:32:09 +09:00
|
|
|
import {
|
|
|
|
|
ComponentData,
|
|
|
|
|
WidgetComponent,
|
|
|
|
|
WebTypeConfig,
|
|
|
|
|
DateTypeConfig,
|
|
|
|
|
NumberTypeConfig,
|
|
|
|
|
SelectTypeConfig,
|
|
|
|
|
TextTypeConfig,
|
|
|
|
|
TextareaTypeConfig,
|
|
|
|
|
CheckboxTypeConfig,
|
|
|
|
|
RadioTypeConfig,
|
|
|
|
|
FileTypeConfig,
|
|
|
|
|
CodeTypeConfig,
|
|
|
|
|
EntityTypeConfig,
|
2025-09-04 11:33:52 +09:00
|
|
|
ButtonTypeConfig,
|
2025-09-03 11:32:09 +09:00
|
|
|
} from "@/types/screen";
|
|
|
|
|
import { DateTypeConfigPanel } from "./webtype-configs/DateTypeConfigPanel";
|
|
|
|
|
import { NumberTypeConfigPanel } from "./webtype-configs/NumberTypeConfigPanel";
|
|
|
|
|
import { SelectTypeConfigPanel } from "./webtype-configs/SelectTypeConfigPanel";
|
|
|
|
|
import { TextTypeConfigPanel } from "./webtype-configs/TextTypeConfigPanel";
|
|
|
|
|
import { TextareaTypeConfigPanel } from "./webtype-configs/TextareaTypeConfigPanel";
|
|
|
|
|
import { CheckboxTypeConfigPanel } from "./webtype-configs/CheckboxTypeConfigPanel";
|
|
|
|
|
import { RadioTypeConfigPanel } from "./webtype-configs/RadioTypeConfigPanel";
|
|
|
|
|
import { FileTypeConfigPanel } from "./webtype-configs/FileTypeConfigPanel";
|
|
|
|
|
import { CodeTypeConfigPanel } from "./webtype-configs/CodeTypeConfigPanel";
|
|
|
|
|
import { EntityTypeConfigPanel } from "./webtype-configs/EntityTypeConfigPanel";
|
2025-09-04 11:33:52 +09:00
|
|
|
import { ButtonConfigPanel } from "./ButtonConfigPanel";
|
2025-09-03 11:32:09 +09:00
|
|
|
|
|
|
|
|
interface DetailSettingsPanelProps {
|
|
|
|
|
selectedComponent?: ComponentData;
|
|
|
|
|
onUpdateProperty: (componentId: string, path: string, value: any) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({ selectedComponent, onUpdateProperty }) => {
|
|
|
|
|
// 웹타입별 상세 설정 렌더링 함수
|
|
|
|
|
const renderWebTypeConfig = React.useCallback(
|
|
|
|
|
(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(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (widget.widgetType) {
|
|
|
|
|
case "date":
|
|
|
|
|
case "datetime":
|
|
|
|
|
return (
|
|
|
|
|
<DateTypeConfigPanel
|
|
|
|
|
key={`date-config-${widget.id}`}
|
|
|
|
|
config={currentConfig as DateTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "number":
|
|
|
|
|
case "decimal":
|
|
|
|
|
return (
|
|
|
|
|
<NumberTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-number`}
|
|
|
|
|
config={currentConfig as NumberTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "select":
|
|
|
|
|
case "dropdown":
|
|
|
|
|
return (
|
|
|
|
|
<SelectTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-select`}
|
|
|
|
|
config={currentConfig as SelectTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "text":
|
|
|
|
|
case "email":
|
|
|
|
|
case "tel":
|
|
|
|
|
return (
|
|
|
|
|
<TextTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-text`}
|
|
|
|
|
config={currentConfig as TextTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "textarea":
|
|
|
|
|
return (
|
|
|
|
|
<TextareaTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-textarea`}
|
|
|
|
|
config={currentConfig as TextareaTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "checkbox":
|
|
|
|
|
case "boolean":
|
|
|
|
|
return (
|
|
|
|
|
<CheckboxTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-checkbox`}
|
|
|
|
|
config={currentConfig as CheckboxTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "radio":
|
|
|
|
|
return (
|
|
|
|
|
<RadioTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-radio`}
|
|
|
|
|
config={currentConfig as RadioTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "file":
|
|
|
|
|
return (
|
|
|
|
|
<FileTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-file`}
|
|
|
|
|
config={currentConfig as FileTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "code":
|
|
|
|
|
return (
|
|
|
|
|
<CodeTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-code`}
|
|
|
|
|
config={currentConfig as CodeTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
case "entity":
|
|
|
|
|
return (
|
|
|
|
|
<EntityTypeConfigPanel
|
|
|
|
|
key={`${widget.id}-entity`}
|
|
|
|
|
config={currentConfig as EntityTypeConfig}
|
|
|
|
|
onConfigChange={handleConfigChange}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-04 11:33:52 +09:00
|
|
|
case "button":
|
|
|
|
|
return (
|
|
|
|
|
<ButtonConfigPanel
|
|
|
|
|
key={`${widget.id}-button`}
|
|
|
|
|
component={widget}
|
|
|
|
|
onUpdateComponent={(updates) => {
|
|
|
|
|
Object.entries(updates).forEach(([key, value]) => {
|
|
|
|
|
onUpdateProperty(widget.id, key, value);
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
|
2025-09-03 11:32:09 +09:00
|
|
|
default:
|
|
|
|
|
return <div className="text-sm text-gray-500 italic">해당 웹타입의 상세 설정이 지원되지 않습니다.</div>;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[onUpdateProperty],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedComponent.type !== "widget") {
|
|
|
|
|
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>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>
|
2025-09-04 14:23:35 +09:00
|
|
|
|
|
|
|
|
{/* 입력 타입 설정 */}
|
|
|
|
|
<div className="mt-3 space-y-2">
|
|
|
|
|
<label className="text-sm font-medium text-gray-700">입력 타입</label>
|
|
|
|
|
<Select
|
|
|
|
|
value={widget.inputType || "direct"}
|
|
|
|
|
onValueChange={(value: "direct" | "auto") => {
|
|
|
|
|
onUpdateProperty(widget.id, "inputType", value);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="w-full">
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="direct">직접입력</SelectItem>
|
|
|
|
|
<SelectItem value="auto">자동입력</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
<p className="text-xs text-gray-500">
|
|
|
|
|
{widget.inputType === "auto"
|
|
|
|
|
? "시스템에서 자동으로 값을 생성합니다 (읽기 전용)"
|
|
|
|
|
: "사용자가 직접 값을 입력할 수 있습니다"}
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
{/* 자동 값 타입 설정 (자동입력일 때만 표시) */}
|
|
|
|
|
{widget.inputType === "auto" && (
|
|
|
|
|
<div className="mt-3 space-y-2">
|
|
|
|
|
<label className="text-sm font-medium text-gray-700">자동 값 타입</label>
|
|
|
|
|
<Select
|
|
|
|
|
value={widget.autoValueType || "current_datetime"}
|
|
|
|
|
onValueChange={(
|
|
|
|
|
value:
|
|
|
|
|
| "current_datetime"
|
|
|
|
|
| "current_date"
|
|
|
|
|
| "current_time"
|
|
|
|
|
| "current_user"
|
|
|
|
|
| "uuid"
|
|
|
|
|
| "sequence"
|
|
|
|
|
| "user_defined",
|
|
|
|
|
) => {
|
|
|
|
|
onUpdateProperty(widget.id, "autoValueType", value);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="w-full">
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="current_datetime">현재 날짜시간</SelectItem>
|
|
|
|
|
<SelectItem value="current_date">현재 날짜</SelectItem>
|
|
|
|
|
<SelectItem value="current_time">현재 시간</SelectItem>
|
|
|
|
|
<SelectItem value="current_user">현재 사용자</SelectItem>
|
|
|
|
|
<SelectItem value="uuid">UUID</SelectItem>
|
|
|
|
|
<SelectItem value="sequence">시퀀스</SelectItem>
|
|
|
|
|
<SelectItem value="user_defined">사용자 정의</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
<p className="text-xs text-gray-500">
|
|
|
|
|
{(() => {
|
|
|
|
|
switch (widget.autoValueType || "current_datetime") {
|
|
|
|
|
case "current_datetime":
|
|
|
|
|
return "현재 날짜와 시간을 자동으로 입력합니다";
|
|
|
|
|
case "current_date":
|
|
|
|
|
return "현재 날짜를 자동으로 입력합니다";
|
|
|
|
|
case "current_time":
|
|
|
|
|
return "현재 시간을 자동으로 입력합니다";
|
|
|
|
|
case "current_user":
|
|
|
|
|
return "현재 로그인한 사용자 정보를 입력합니다";
|
|
|
|
|
case "uuid":
|
|
|
|
|
return "고유한 UUID를 생성합니다";
|
|
|
|
|
case "sequence":
|
|
|
|
|
return "순차적인 번호를 생성합니다";
|
|
|
|
|
case "user_defined":
|
|
|
|
|
return "사용자가 정의한 규칙에 따라 값을 생성합니다";
|
|
|
|
|
default:
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
})()}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-09-03 11:32:09 +09:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 상세 설정 영역 */}
|
|
|
|
|
<div className="flex-1 overflow-y-auto p-4">{renderWebTypeConfig(widget)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default DetailSettingsPanel;
|