328 lines
12 KiB
TypeScript
328 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
|
|
interface CardDisplayConfigPanelProps {
|
|
config: any;
|
|
onChange: (config: any) => void;
|
|
screenTableName?: string;
|
|
tableColumns?: any[];
|
|
}
|
|
|
|
/**
|
|
* CardDisplay 설정 패널
|
|
* 카드 레이아웃과 동일한 설정 UI 제공
|
|
*/
|
|
export const CardDisplayConfigPanel: React.FC<CardDisplayConfigPanelProps> = ({
|
|
config,
|
|
onChange,
|
|
screenTableName,
|
|
tableColumns = [],
|
|
}) => {
|
|
const handleChange = (key: string, value: any) => {
|
|
onChange({ ...config, [key]: value });
|
|
};
|
|
|
|
const handleNestedChange = (path: string, value: any) => {
|
|
const keys = path.split(".");
|
|
let newConfig = { ...config };
|
|
let current = newConfig;
|
|
|
|
// 중첩 객체 생성
|
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
if (!current[keys[i]]) {
|
|
current[keys[i]] = {};
|
|
}
|
|
current = current[keys[i]];
|
|
}
|
|
|
|
current[keys[keys.length - 1]] = value;
|
|
onChange(newConfig);
|
|
};
|
|
|
|
// 표시 컬럼 추가
|
|
const addDisplayColumn = () => {
|
|
const currentColumns = config.columnMapping?.displayColumns || [];
|
|
const newColumns = [...currentColumns, ""];
|
|
handleNestedChange("columnMapping.displayColumns", newColumns);
|
|
};
|
|
|
|
// 표시 컬럼 삭제
|
|
const removeDisplayColumn = (index: number) => {
|
|
const currentColumns = [...(config.columnMapping?.displayColumns || [])];
|
|
currentColumns.splice(index, 1);
|
|
handleNestedChange("columnMapping.displayColumns", currentColumns);
|
|
};
|
|
|
|
// 표시 컬럼 값 변경
|
|
const updateDisplayColumn = (index: number, value: string) => {
|
|
const currentColumns = [...(config.columnMapping?.displayColumns || [])];
|
|
currentColumns[index] = value;
|
|
handleNestedChange("columnMapping.displayColumns", currentColumns);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="text-sm font-medium text-gray-700">카드 디스플레이 설정</div>
|
|
|
|
{/* 테이블이 선택된 경우 컬럼 매핑 설정 */}
|
|
{tableColumns && tableColumns.length > 0 && (
|
|
<div className="space-y-3">
|
|
<h5 className="text-xs font-medium text-gray-700">컬럼 매핑</h5>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">타이틀 컬럼</label>
|
|
<select
|
|
value={config.columnMapping?.titleColumn || ""}
|
|
onChange={(e) => handleNestedChange("columnMapping.titleColumn", e.target.value)}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
>
|
|
<option value="">컬럼을 선택하세요</option>
|
|
{tableColumns.map((column) => (
|
|
<option key={column.columnName} value={column.columnName}>
|
|
{column.columnLabel || column.columnName} ({column.dataType})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">서브타이틀 컬럼</label>
|
|
<select
|
|
value={config.columnMapping?.subtitleColumn || ""}
|
|
onChange={(e) => handleNestedChange("columnMapping.subtitleColumn", e.target.value)}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
>
|
|
<option value="">컬럼을 선택하세요</option>
|
|
{tableColumns.map((column) => (
|
|
<option key={column.columnName} value={column.columnName}>
|
|
{column.columnLabel || column.columnName} ({column.dataType})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">설명 컬럼</label>
|
|
<select
|
|
value={config.columnMapping?.descriptionColumn || ""}
|
|
onChange={(e) => handleNestedChange("columnMapping.descriptionColumn", e.target.value)}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
>
|
|
<option value="">컬럼을 선택하세요</option>
|
|
{tableColumns.map((column) => (
|
|
<option key={column.columnName} value={column.columnName}>
|
|
{column.columnLabel || column.columnName} ({column.dataType})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">이미지 컬럼</label>
|
|
<select
|
|
value={config.columnMapping?.imageColumn || ""}
|
|
onChange={(e) => handleNestedChange("columnMapping.imageColumn", e.target.value)}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
>
|
|
<option value="">컬럼을 선택하세요</option>
|
|
{tableColumns.map((column) => (
|
|
<option key={column.columnName} value={column.columnName}>
|
|
{column.columnLabel || column.columnName} ({column.dataType})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* 동적 표시 컬럼 추가 */}
|
|
<div>
|
|
<div className="mb-2 flex items-center justify-between">
|
|
<label className="text-xs font-medium text-gray-600">표시 컬럼들</label>
|
|
<button
|
|
type="button"
|
|
onClick={addDisplayColumn}
|
|
className="rounded bg-blue-500 px-2 py-1 text-xs text-white hover:bg-blue-600"
|
|
>
|
|
+ 컬럼 추가
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
{(config.columnMapping?.displayColumns || []).map((column: string, index: number) => (
|
|
<div key={index} className="flex items-center space-x-2">
|
|
<select
|
|
value={column}
|
|
onChange={(e) => updateDisplayColumn(index, e.target.value)}
|
|
className="flex-1 rounded border border-gray-300 px-2 py-1 text-sm"
|
|
>
|
|
<option value="">컬럼을 선택하세요</option>
|
|
{tableColumns.map((col) => (
|
|
<option key={col.columnName} value={col.columnName}>
|
|
{col.columnLabel || col.columnName} ({col.dataType})
|
|
</option>
|
|
))}
|
|
</select>
|
|
<button
|
|
type="button"
|
|
onClick={() => removeDisplayColumn(index)}
|
|
className="rounded bg-red-500 px-2 py-1 text-xs text-white hover:bg-red-600"
|
|
>
|
|
삭제
|
|
</button>
|
|
</div>
|
|
))}
|
|
|
|
{(!config.columnMapping?.displayColumns || config.columnMapping.displayColumns.length === 0) && (
|
|
<div className="rounded border border-dashed border-gray-300 py-2 text-center text-xs text-gray-500">
|
|
"컬럼 추가" 버튼을 클릭하여 표시할 컬럼을 추가하세요
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* 카드 스타일 설정 */}
|
|
<div className="space-y-3">
|
|
<h5 className="text-xs font-medium text-gray-700">카드 스타일</h5>
|
|
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">한 행당 카드 수</label>
|
|
<input
|
|
type="number"
|
|
min="1"
|
|
max="6"
|
|
value={config.cardsPerRow || 3}
|
|
onChange={(e) => handleChange("cardsPerRow", parseInt(e.target.value))}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">카드 간격 (px)</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
max="50"
|
|
value={config.cardSpacing || 16}
|
|
onChange={(e) => handleChange("cardSpacing", parseInt(e.target.value))}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="showTitle"
|
|
checked={config.cardStyle?.showTitle ?? true}
|
|
onChange={(e) => handleNestedChange("cardStyle.showTitle", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="showTitle" className="text-xs text-gray-600">
|
|
타이틀 표시
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="showSubtitle"
|
|
checked={config.cardStyle?.showSubtitle ?? true}
|
|
onChange={(e) => handleNestedChange("cardStyle.showSubtitle", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="showSubtitle" className="text-xs text-gray-600">
|
|
서브타이틀 표시
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="showDescription"
|
|
checked={config.cardStyle?.showDescription ?? true}
|
|
onChange={(e) => handleNestedChange("cardStyle.showDescription", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="showDescription" className="text-xs text-gray-600">
|
|
설명 표시
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="showImage"
|
|
checked={config.cardStyle?.showImage ?? false}
|
|
onChange={(e) => handleNestedChange("cardStyle.showImage", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="showImage" className="text-xs text-gray-600">
|
|
이미지 표시
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="showActions"
|
|
checked={config.cardStyle?.showActions ?? true}
|
|
onChange={(e) => handleNestedChange("cardStyle.showActions", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="showActions" className="text-xs text-gray-600">
|
|
액션 버튼 표시
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-gray-600">설명 최대 길이</label>
|
|
<input
|
|
type="number"
|
|
min="10"
|
|
max="500"
|
|
value={config.cardStyle?.maxDescriptionLength || 100}
|
|
onChange={(e) => handleNestedChange("cardStyle.maxDescriptionLength", parseInt(e.target.value))}
|
|
className="w-full rounded border border-gray-300 px-2 py-1 text-sm"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 공통 설정 */}
|
|
<div className="space-y-3">
|
|
<h5 className="text-xs font-medium text-gray-700">공통 설정</h5>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="disabled"
|
|
checked={config.disabled || false}
|
|
onChange={(e) => handleChange("disabled", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="disabled" className="text-xs text-gray-600">
|
|
비활성화
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<input
|
|
type="checkbox"
|
|
id="readonly"
|
|
checked={config.readonly || false}
|
|
onChange={(e) => handleChange("readonly", e.target.checked)}
|
|
className="rounded border-gray-300"
|
|
/>
|
|
<label htmlFor="readonly" className="text-xs text-gray-600">
|
|
읽기 전용
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|