플로우 구현
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
"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";
|
||||
|
||||
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-sm">로딩 중...</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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user