178 lines
6.9 KiB
TypeScript
178 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
|
import { getFlowDefinitions } from "@/lib/api/flow";
|
|
import type { FlowDefinition } from "@/types/flow";
|
|
import { Loader2, Check, ChevronsUpDown } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
import { DataFilterConfigPanel } from "@/components/screen/config-panels/DataFilterConfigPanel";
|
|
|
|
interface FlowWidgetConfigPanelProps {
|
|
config: Record<string, any>;
|
|
onChange: (config: Record<string, any>) => void;
|
|
}
|
|
|
|
export function FlowWidgetConfigPanel({ config = {}, onChange }: FlowWidgetConfigPanelProps) {
|
|
const [flowList, setFlowList] = useState<FlowDefinition[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [openCombobox, setOpenCombobox] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const loadFlows = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await getFlowDefinitions({ isActive: true });
|
|
if (response.success && response.data) {
|
|
setFlowList(response.data);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to load flows:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
loadFlows();
|
|
}, []);
|
|
|
|
const selectedFlow = flowList.find((flow) => flow.id === config.flowId);
|
|
|
|
return (
|
|
<div className="space-y-4 p-4">
|
|
<div>
|
|
<div className="mb-2">
|
|
<h3 className="text-sm font-medium">플로우 선택</h3>
|
|
</div>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label>플로우</Label>
|
|
{loading ? (
|
|
<div className="flex items-center gap-2 rounded-md border px-3 py-2">
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
<span className="text-muted-foreground text-xs">로딩 중...</span>
|
|
</div>
|
|
) : (
|
|
<>
|
|
<Popover open={openCombobox} onOpenChange={setOpenCombobox}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
aria-expanded={openCombobox}
|
|
className="w-full justify-between"
|
|
>
|
|
{selectedFlow ? selectedFlow.name : "플로우 선택"}
|
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-[400px] p-0">
|
|
<Command>
|
|
<CommandInput placeholder="플로우 검색..." />
|
|
<CommandList>
|
|
<CommandEmpty>플로우를 찾을 수 없습니다.</CommandEmpty>
|
|
<CommandGroup>
|
|
{flowList.map((flow) => (
|
|
<CommandItem
|
|
key={flow.id}
|
|
value={flow.name}
|
|
onSelect={() => {
|
|
onChange({
|
|
...config,
|
|
flowId: flow.id,
|
|
flowName: flow.name,
|
|
});
|
|
setOpenCombobox(false);
|
|
}}
|
|
>
|
|
<Check
|
|
className={cn("mr-2 h-4 w-4", config.flowId === flow.id ? "opacity-100" : "opacity-0")}
|
|
/>
|
|
<span className="font-medium">{flow.name}</span>
|
|
</CommandItem>
|
|
))}
|
|
</CommandGroup>
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
{selectedFlow && (
|
|
<p className="text-muted-foreground mt-1 text-xs">테이블: {selectedFlow.tableName || "없음"}</p>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="mb-2">
|
|
<h3 className="text-sm font-medium">표시 옵션</h3>
|
|
</div>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label>표시 방향</Label>
|
|
<Select
|
|
value={config.displayMode || "horizontal"}
|
|
onValueChange={(value: "horizontal" | "vertical") => onChange({ ...config, displayMode: value })}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="horizontal">가로 (→)</SelectItem>
|
|
<SelectItem value="vertical">세로 (↓)</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<Label>데이터 건수 표시</Label>
|
|
<p className="text-muted-foreground text-xs">각 스텝의 데이터 건수를 표시합니다</p>
|
|
</div>
|
|
<Switch
|
|
checked={config.showStepCount !== false}
|
|
onCheckedChange={(checked) => onChange({ ...config, showStepCount: checked })}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<Label>데이터 이동 허용</Label>
|
|
<p className="text-muted-foreground text-xs">사용자가 데이터를 다음 스텝으로 이동할 수 있습니다</p>
|
|
</div>
|
|
<Switch
|
|
checked={config.allowDataMove || false}
|
|
onCheckedChange={(checked) => onChange({ ...config, allowDataMove: checked })}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 🆕 데이터 필터링 설정 */}
|
|
{config.flowId && (
|
|
<div>
|
|
<div className="mb-2">
|
|
<h3 className="text-sm font-medium">데이터 필터링</h3>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
특정 컬럼 값으로 플로우 데이터를 필터링합니다
|
|
</p>
|
|
</div>
|
|
<DataFilterConfigPanel
|
|
tableName={selectedFlow?.name}
|
|
columns={[]} // 플로우의 첫 번째 스텝 테이블 컬럼 정보 필요 (TODO: API 연동)
|
|
config={config.dataFilter}
|
|
onConfigChange={(dataFilter) => onChange({ ...config, dataFilter })}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|