344 lines
13 KiB
TypeScript
344 lines
13 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Plus, X } from "lucide-react";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
|
|
interface TableSearchWidgetConfigPanelProps {
|
|
component?: any; // 레거시 지원
|
|
config?: any; // 새 인터페이스
|
|
onUpdateProperty?: (property: string, value: any) => void; // 레거시 지원
|
|
onChange?: (newConfig: any) => void; // 새 인터페이스
|
|
tables?: any[]; // 화면의 테이블 정보
|
|
}
|
|
|
|
interface PresetFilter {
|
|
id: string;
|
|
columnName: string;
|
|
columnLabel: string;
|
|
filterType: "text" | "number" | "date" | "select";
|
|
width?: number;
|
|
}
|
|
|
|
export function TableSearchWidgetConfigPanel({
|
|
component,
|
|
config,
|
|
onUpdateProperty,
|
|
onChange,
|
|
tables = [],
|
|
}: TableSearchWidgetConfigPanelProps) {
|
|
// 레거시와 새 인터페이스 모두 지원
|
|
const currentConfig = config || component?.componentConfig || {};
|
|
const updateConfig = onChange || ((key: string, value: any) => {
|
|
if (onUpdateProperty) {
|
|
onUpdateProperty(`componentConfig.${key}`, value);
|
|
}
|
|
});
|
|
|
|
// 첫 번째 테이블의 컬럼 목록 가져오기
|
|
const availableColumns = tables.length > 0 && tables[0].columns ? tables[0].columns : [];
|
|
|
|
// inputType에서 filterType 추출 헬퍼 함수
|
|
const getFilterTypeFromInputType = (inputType: string): "text" | "number" | "date" | "select" => {
|
|
if (inputType.includes("number") || inputType.includes("decimal") || inputType.includes("integer")) {
|
|
return "number";
|
|
}
|
|
if (inputType.includes("date") || inputType.includes("time")) {
|
|
return "date";
|
|
}
|
|
if (inputType.includes("select") || inputType.includes("dropdown") || inputType.includes("code") || inputType.includes("category")) {
|
|
return "select";
|
|
}
|
|
return "text";
|
|
};
|
|
|
|
const [localAutoSelect, setLocalAutoSelect] = useState(
|
|
currentConfig.autoSelectFirstTable ?? true
|
|
);
|
|
const [localShowSelector, setLocalShowSelector] = useState(
|
|
currentConfig.showTableSelector ?? true
|
|
);
|
|
const [localFilterMode, setLocalFilterMode] = useState<"dynamic" | "preset">(
|
|
currentConfig.filterMode ?? "dynamic"
|
|
);
|
|
const [localPresetFilters, setLocalPresetFilters] = useState<PresetFilter[]>(
|
|
currentConfig.presetFilters ?? []
|
|
);
|
|
|
|
useEffect(() => {
|
|
setLocalAutoSelect(currentConfig.autoSelectFirstTable ?? true);
|
|
setLocalShowSelector(currentConfig.showTableSelector ?? true);
|
|
setLocalFilterMode(currentConfig.filterMode ?? "dynamic");
|
|
setLocalPresetFilters(currentConfig.presetFilters ?? []);
|
|
}, [currentConfig]);
|
|
|
|
// 설정 업데이트 헬퍼
|
|
const handleUpdate = (key: string, value: any) => {
|
|
if (onChange) {
|
|
// 새 인터페이스: 전체 config 업데이트
|
|
onChange({ ...currentConfig, [key]: value });
|
|
} else if (onUpdateProperty) {
|
|
// 레거시: 개별 속성 업데이트
|
|
onUpdateProperty(`componentConfig.${key}`, value);
|
|
}
|
|
};
|
|
|
|
// 필터 추가
|
|
const addFilter = () => {
|
|
const newFilter: PresetFilter = {
|
|
id: `filter_${Date.now()}`,
|
|
columnName: "",
|
|
columnLabel: "",
|
|
filterType: "text",
|
|
width: 200,
|
|
};
|
|
const updatedFilters = [...localPresetFilters, newFilter];
|
|
setLocalPresetFilters(updatedFilters);
|
|
handleUpdate("presetFilters", updatedFilters);
|
|
};
|
|
|
|
// 필터 삭제
|
|
const removeFilter = (id: string) => {
|
|
const updatedFilters = localPresetFilters.filter((f) => f.id !== id);
|
|
setLocalPresetFilters(updatedFilters);
|
|
handleUpdate("presetFilters", updatedFilters);
|
|
};
|
|
|
|
// 필터 업데이트
|
|
const updateFilter = (id: string, field: keyof PresetFilter, value: any) => {
|
|
const updatedFilters = localPresetFilters.map((f) =>
|
|
f.id === id ? { ...f, [field]: value } : f
|
|
);
|
|
setLocalPresetFilters(updatedFilters);
|
|
handleUpdate("presetFilters", updatedFilters);
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-semibold">검색 필터 위젯 설정</h3>
|
|
<p className="text-xs text-muted-foreground">
|
|
이 위젯은 화면 내의 테이블들을 자동으로 감지하여 검색, 필터, 그룹 기능을 제공합니다.
|
|
</p>
|
|
</div>
|
|
|
|
{/* 첫 번째 테이블 자동 선택 */}
|
|
<div className="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="autoSelectFirstTable"
|
|
checked={localAutoSelect}
|
|
onCheckedChange={(checked) => {
|
|
setLocalAutoSelect(checked as boolean);
|
|
handleUpdate("autoSelectFirstTable", checked);
|
|
}}
|
|
/>
|
|
<Label htmlFor="autoSelectFirstTable" className="text-xs sm:text-sm cursor-pointer">
|
|
첫 번째 테이블 자동 선택
|
|
</Label>
|
|
</div>
|
|
|
|
{/* 테이블 선택 드롭다운 표시 */}
|
|
<div className="flex items-center space-x-2">
|
|
<Checkbox
|
|
id="showTableSelector"
|
|
checked={localShowSelector}
|
|
onCheckedChange={(checked) => {
|
|
setLocalShowSelector(checked as boolean);
|
|
handleUpdate("showTableSelector", checked);
|
|
}}
|
|
/>
|
|
<Label htmlFor="showTableSelector" className="text-xs sm:text-sm cursor-pointer">
|
|
테이블 선택 드롭다운 표시 (여러 테이블이 있을 때)
|
|
</Label>
|
|
</div>
|
|
|
|
{/* 필터 모드 선택 */}
|
|
<div className="space-y-2 border-t pt-4">
|
|
<Label className="text-xs sm:text-sm font-medium">필터 모드</Label>
|
|
<RadioGroup
|
|
value={localFilterMode}
|
|
onValueChange={(value: "dynamic" | "preset") => {
|
|
setLocalFilterMode(value);
|
|
handleUpdate("filterMode", value);
|
|
}}
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="dynamic" id="mode-dynamic" />
|
|
<Label htmlFor="mode-dynamic" className="text-xs sm:text-sm cursor-pointer font-normal">
|
|
동적 모드 (사용자가 필터 설정 버튼으로 선택)
|
|
</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="preset" id="mode-preset" />
|
|
<Label htmlFor="mode-preset" className="text-xs sm:text-sm cursor-pointer font-normal">
|
|
고정 모드 (디자이너가 미리 필터 지정)
|
|
</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</div>
|
|
|
|
{/* 고정 모드일 때만 필터 설정 UI 표시 */}
|
|
{localFilterMode === "preset" && (
|
|
<div className="space-y-3 border-t pt-4">
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-xs sm:text-sm font-medium">고정 필터 목록</Label>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={addFilter}
|
|
className="h-7 text-xs"
|
|
>
|
|
<Plus className="mr-1 h-3 w-3" />
|
|
필터 추가
|
|
</Button>
|
|
</div>
|
|
|
|
{localPresetFilters.length === 0 ? (
|
|
<div className="rounded-md bg-muted p-3 text-center text-xs text-muted-foreground">
|
|
필터가 없습니다. 필터 추가 버튼을 클릭하세요.
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{localPresetFilters.map((filter) => (
|
|
<div
|
|
key={filter.id}
|
|
className="rounded-md border bg-card p-3 space-y-2"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-xs font-medium">필터 설정</Label>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => removeFilter(filter.id)}
|
|
className="h-6 w-6 p-0"
|
|
>
|
|
<X className="h-3 w-3" />
|
|
</Button>
|
|
</div>
|
|
|
|
{/* 컬럼 선택 */}
|
|
<div>
|
|
<Label className="text-[10px] sm:text-xs mb-1">컬럼 선택</Label>
|
|
{availableColumns.length > 0 ? (
|
|
<Select
|
|
value={filter.columnName}
|
|
onValueChange={(value) => {
|
|
// 선택된 컬럼 정보 가져오기
|
|
const selectedColumn = availableColumns.find(
|
|
(col: any) => col.columnName === value
|
|
);
|
|
// 컬럼명과 라벨 동시 업데이트
|
|
const updatedFilters = localPresetFilters.map((f) =>
|
|
f.id === filter.id
|
|
? {
|
|
...f,
|
|
columnName: value,
|
|
columnLabel: selectedColumn?.columnLabel || value,
|
|
filterType: getFilterTypeFromInputType(selectedColumn?.inputType || "text"),
|
|
}
|
|
: f
|
|
);
|
|
setLocalPresetFilters(updatedFilters);
|
|
handleUpdate("presetFilters", updatedFilters);
|
|
}}
|
|
>
|
|
<SelectTrigger className="h-7 text-xs">
|
|
<SelectValue placeholder="컬럼 선택" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{availableColumns.map((col: any) => (
|
|
<SelectItem key={col.columnName} value={col.columnName}>
|
|
<div className="flex items-center gap-2">
|
|
<span className="font-medium">{col.columnLabel}</span>
|
|
<span className="text-muted-foreground text-[10px]">
|
|
({col.columnName})
|
|
</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
) : (
|
|
<Input
|
|
value={filter.columnName}
|
|
onChange={(e) => updateFilter(filter.id, "columnName", e.target.value)}
|
|
placeholder="예: customer_name"
|
|
className="h-7 text-xs"
|
|
/>
|
|
)}
|
|
{filter.columnLabel && (
|
|
<p className="text-muted-foreground mt-1 text-[10px]">
|
|
표시명: {filter.columnLabel}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* 필터 타입 */}
|
|
<div>
|
|
<Label className="text-[10px] sm:text-xs mb-1">필터 타입</Label>
|
|
<Select
|
|
value={filter.filterType}
|
|
onValueChange={(value: "text" | "number" | "date" | "select") =>
|
|
updateFilter(filter.id, "filterType", value)
|
|
}
|
|
>
|
|
<SelectTrigger className="h-7 text-xs">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="text">텍스트</SelectItem>
|
|
<SelectItem value="number">숫자</SelectItem>
|
|
<SelectItem value="date">날짜</SelectItem>
|
|
<SelectItem value="select">선택</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 너비 */}
|
|
<div>
|
|
<Label className="text-[10px] sm:text-xs mb-1">너비 (px)</Label>
|
|
<Input
|
|
type="number"
|
|
value={filter.width || 200}
|
|
onChange={(e) => updateFilter(filter.id, "width", parseInt(e.target.value))}
|
|
placeholder="200"
|
|
className="h-7 text-xs"
|
|
min={100}
|
|
max={500}
|
|
/>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="rounded-md bg-muted p-3 text-xs">
|
|
<p className="font-medium mb-1">참고사항:</p>
|
|
<ul className="list-disc list-inside space-y-1 text-muted-foreground">
|
|
<li>테이블 리스트, 분할 패널, 플로우 위젯이 자동 감지됩니다</li>
|
|
<li>여러 테이블이 있으면 드롭다운에서 선택할 수 있습니다</li>
|
|
{localFilterMode === "dynamic" ? (
|
|
<li>사용자가 필터 설정 버튼을 클릭하여 원하는 필터를 선택합니다</li>
|
|
) : (
|
|
<li>고정 모드에서는 설정 버튼이 숨겨지고 지정된 필터만 표시됩니다</li>
|
|
)}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|