플로우 분기처리 구현
This commit is contained in:
@@ -9,13 +9,14 @@ import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { updateFlowStep, deleteFlowStep } from "@/lib/api/flow";
|
||||
import { FlowStep } from "@/types/flow";
|
||||
import { FlowConditionBuilder } from "./FlowConditionBuilder";
|
||||
import { tableManagementApi } from "@/lib/api/tableManagement";
|
||||
import { tableManagementApi, getTableColumns } from "@/lib/api/tableManagement";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface FlowStepPanelProps {
|
||||
@@ -32,12 +33,23 @@ export function FlowStepPanel({ step, flowId, onClose, onUpdate }: FlowStepPanel
|
||||
stepName: step.stepName,
|
||||
tableName: step.tableName || "",
|
||||
conditionJson: step.conditionJson,
|
||||
// 하이브리드 모드 필드
|
||||
moveType: step.moveType || "status",
|
||||
statusColumn: step.statusColumn || "",
|
||||
statusValue: step.statusValue || "",
|
||||
targetTable: step.targetTable || "",
|
||||
fieldMappings: step.fieldMappings || {},
|
||||
});
|
||||
|
||||
const [tableList, setTableList] = useState<any[]>([]);
|
||||
const [loadingTables, setLoadingTables] = useState(true);
|
||||
const [openTableCombobox, setOpenTableCombobox] = useState(false);
|
||||
|
||||
// 컬럼 목록 (상태 컬럼 선택용)
|
||||
const [columns, setColumns] = useState<any[]>([]);
|
||||
const [loadingColumns, setLoadingColumns] = useState(false);
|
||||
const [openStatusColumnCombobox, setOpenStatusColumnCombobox] = useState(false);
|
||||
|
||||
// 테이블 목록 조회
|
||||
useEffect(() => {
|
||||
const loadTables = async () => {
|
||||
@@ -61,9 +73,47 @@ export function FlowStepPanel({ step, flowId, onClose, onUpdate }: FlowStepPanel
|
||||
stepName: step.stepName,
|
||||
tableName: step.tableName || "",
|
||||
conditionJson: step.conditionJson,
|
||||
// 하이브리드 모드 필드
|
||||
moveType: step.moveType || "status",
|
||||
statusColumn: step.statusColumn || "",
|
||||
statusValue: step.statusValue || "",
|
||||
targetTable: step.targetTable || "",
|
||||
fieldMappings: step.fieldMappings || {},
|
||||
});
|
||||
}, [step]);
|
||||
|
||||
// 테이블 선택 시 컬럼 로드
|
||||
useEffect(() => {
|
||||
const loadColumns = async () => {
|
||||
if (!formData.tableName) {
|
||||
setColumns([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoadingColumns(true);
|
||||
console.log("🔍 Loading columns for status column selector:", formData.tableName);
|
||||
const response = await getTableColumns(formData.tableName);
|
||||
console.log("📦 Columns response:", response);
|
||||
|
||||
if (response.success && response.data && response.data.columns) {
|
||||
console.log("✅ Setting columns:", response.data.columns);
|
||||
setColumns(response.data.columns);
|
||||
} else {
|
||||
console.log("❌ No columns in response");
|
||||
setColumns([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load columns:", error);
|
||||
setColumns([]);
|
||||
} finally {
|
||||
setLoadingColumns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadColumns();
|
||||
}, [formData.tableName]);
|
||||
|
||||
// 저장
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
@@ -230,6 +280,149 @@ export function FlowStepPanel({ step, flowId, onClose, onUpdate }: FlowStepPanel
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 데이터 이동 설정 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>데이터 이동 설정</CardTitle>
|
||||
<CardDescription>다음 단계로 데이터를 이동할 때의 동작을 설정합니다</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{/* 이동 방식 선택 */}
|
||||
<div>
|
||||
<Label>이동 방식</Label>
|
||||
<Select
|
||||
value={formData.moveType}
|
||||
onValueChange={(value: "status" | "table" | "both") => setFormData({ ...formData, moveType: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="status">
|
||||
<div>
|
||||
<div className="font-medium">상태 변경</div>
|
||||
<div className="text-xs text-gray-500">같은 테이블 내에서 상태 컬럼만 업데이트</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="table">
|
||||
<div>
|
||||
<div className="font-medium">테이블 이동</div>
|
||||
<div className="text-xs text-gray-500">다른 테이블로 데이터 복사</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="both">
|
||||
<div>
|
||||
<div className="font-medium">하이브리드</div>
|
||||
<div className="text-xs text-gray-500">상태 변경 + 테이블 이동</div>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 상태 변경 설정 (status 또는 both일 때) */}
|
||||
{(formData.moveType === "status" || formData.moveType === "both") && (
|
||||
<>
|
||||
<div>
|
||||
<Label>상태 컬럼명</Label>
|
||||
<Popover open={openStatusColumnCombobox} onOpenChange={setOpenStatusColumnCombobox}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={openStatusColumnCombobox}
|
||||
className="w-full justify-between"
|
||||
disabled={!formData.tableName || loadingColumns}
|
||||
>
|
||||
{loadingColumns
|
||||
? "컬럼 로딩 중..."
|
||||
: formData.statusColumn
|
||||
? columns.find((col) => col.columnName === formData.statusColumn)?.columnName ||
|
||||
formData.statusColumn
|
||||
: "상태 컬럼 선택"}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="컬럼 검색..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>컬럼을 찾을 수 없습니다.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{columns.map((column) => (
|
||||
<CommandItem
|
||||
key={column.columnName}
|
||||
value={column.columnName}
|
||||
onSelect={() => {
|
||||
setFormData({ ...formData, statusColumn: column.columnName });
|
||||
setOpenStatusColumnCombobox(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
formData.statusColumn === column.columnName ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<div>{column.columnName}</div>
|
||||
<div className="text-xs text-gray-500">({column.dataType})</div>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<p className="mt-1 text-xs text-gray-500">업데이트할 컬럼의 이름</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>이 단계의 상태값</Label>
|
||||
<Input
|
||||
value={formData.statusValue}
|
||||
onChange={(e) => setFormData({ ...formData, statusValue: e.target.value })}
|
||||
placeholder="예: approved"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500">이 단계에 있을 때의 상태값</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 테이블 이동 설정 (table 또는 both일 때) */}
|
||||
{(formData.moveType === "table" || formData.moveType === "both") && (
|
||||
<>
|
||||
<div>
|
||||
<Label>타겟 테이블</Label>
|
||||
<Select
|
||||
value={formData.targetTable}
|
||||
onValueChange={(value) => setFormData({ ...formData, targetTable: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="테이블 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{tableList.map((table) => (
|
||||
<SelectItem key={table.tableName} value={table.tableName}>
|
||||
{table.displayName || table.tableName}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="mt-1 text-xs text-gray-500">다음 단계로 이동 시 데이터가 저장될 테이블</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md bg-blue-50 p-3">
|
||||
<p className="text-sm text-blue-900">
|
||||
💡 필드 매핑은 향후 구현 예정입니다. 현재는 같은 이름의 컬럼만 자동 매핑됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 액션 버튼 */}
|
||||
<div className="flex gap-2">
|
||||
<Button className="flex-1" onClick={handleSave}>
|
||||
|
||||
Reference in New Issue
Block a user