ERP-node/frontend/components/dataflow/node-editor/dialogs/LoadFlowDialog.tsx

175 lines
6.0 KiB
TypeScript

"use client";
/**
* 플로우 불러오기 다이얼로그
*/
import { useEffect, useState } from "react";
import { Loader2, FileJson, Calendar, Trash2 } from "lucide-react";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { getNodeFlows, deleteNodeFlow } from "@/lib/api/nodeFlows";
interface Flow {
flowId: number;
flowName: string;
flowDescription: string;
createdAt: string;
updatedAt: string;
}
interface LoadFlowDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onLoad: (flowId: number) => void;
}
export function LoadFlowDialog({ open, onOpenChange, onLoad }: LoadFlowDialogProps) {
const [flows, setFlows] = useState<Flow[]>([]);
const [loading, setLoading] = useState(false);
const [selectedFlowId, setSelectedFlowId] = useState<number | null>(null);
const [deleting, setDeleting] = useState<number | null>(null);
// 플로우 목록 조회
const fetchFlows = async () => {
setLoading(true);
try {
const flows = await getNodeFlows();
setFlows(flows);
} catch (error) {
console.error("플로우 목록 조회 오류:", error);
alert(error instanceof Error ? error.message : "플로우 목록을 불러올 수 없습니다.");
} finally {
setLoading(false);
}
};
// 플로우 삭제
const handleDelete = async (flowId: number, flowName: string) => {
if (!confirm(`"${flowName}" 플로우를 삭제하시겠습니까?\n\n이 작업은 되돌릴 수 없습니다.`)) {
return;
}
setDeleting(flowId);
try {
await deleteNodeFlow(flowId);
alert("✅ 플로우가 삭제되었습니다.");
fetchFlows(); // 목록 새로고침
} catch (error) {
console.error("플로우 삭제 오류:", error);
alert(error instanceof Error ? error.message : "플로우를 삭제할 수 없습니다.");
} finally {
setDeleting(null);
}
};
// 플로우 불러오기
const handleLoad = () => {
if (selectedFlowId === null) {
alert("불러올 플로우를 선택해주세요.");
return;
}
onLoad(selectedFlowId);
onOpenChange(false);
};
// 다이얼로그 열릴 때 목록 조회
useEffect(() => {
if (open) {
fetchFlows();
setSelectedFlowId(null);
}
}, [open]);
// 날짜 포맷팅
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return new Intl.DateTimeFormat("ko-KR", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
}).format(date);
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle> </DialogTitle>
<DialogDescription> .</DialogDescription>
</DialogHeader>
{loading ? (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-gray-400" />
</div>
) : flows.length === 0 ? (
<div className="py-12 text-center">
<FileJson className="mx-auto mb-4 h-12 w-12 text-gray-300" />
<p className="text-sm text-gray-500"> .</p>
</div>
) : (
<ScrollArea className="h-[400px]">
<div className="space-y-2 pr-4">
{flows.map((flow) => (
<div
key={flow.flowId}
className={`cursor-pointer rounded-lg border-2 p-4 transition-all hover:border-blue-300 hover:bg-blue-50 ${
selectedFlowId === flow.flowId ? "border-blue-500 bg-blue-50" : "border-gray-200 bg-white"
}`}
onClick={() => setSelectedFlowId(flow.flowId)}
>
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2">
<h3 className="font-semibold text-gray-900">{flow.flowName}</h3>
<span className="text-xs text-gray-400">#{flow.flowId}</span>
</div>
{flow.flowDescription && <p className="mt-1 text-sm text-gray-600">{flow.flowDescription}</p>}
<div className="mt-2 flex items-center gap-4 text-xs text-gray-500">
<div className="flex items-center gap-1">
<Calendar className="h-3 w-3" />
<span>: {formatDate(flow.updatedAt)}</span>
</div>
</div>
</div>
<Button
size="sm"
variant="ghost"
onClick={(e) => {
e.stopPropagation();
handleDelete(flow.flowId, flow.flowName);
}}
disabled={deleting === flow.flowId}
className="text-red-600 hover:bg-red-50 hover:text-red-700"
>
{deleting === flow.flowId ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : (
<Trash2 className="h-4 w-4" />
)}
</Button>
</div>
</div>
))}
</div>
</ScrollArea>
)}
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
<Button onClick={handleLoad} disabled={selectedFlowId === null || loading}>
</Button>
</div>
</DialogContent>
</Dialog>
);
}