import { Handle, Position } from "@xyflow/react"; import { CheckCircle2, Circle, Clock, Loader2, Pause, XCircle, } from "lucide-react"; import { memo } from "react"; import type { FlowNode as FlowNodeType } from "@/core/types"; import { cn } from "@/lib/utils"; export interface FlowNodeData extends Record { flowNode: FlowNodeType; onClick?: (flowNode: FlowNodeType) => void; } interface FlowNodeProps { data: FlowNodeData; } const statusConfig: Record< string, { icon: typeof CheckCircle2; color: string; bg: string; border: string; animate?: boolean; } > = { completed: { icon: CheckCircle2, color: "text-emerald-600 dark:text-emerald-400", bg: "bg-emerald-50 dark:bg-emerald-500/10", border: "border-emerald-200 dark:border-emerald-500/20", }, active: { icon: Loader2, color: "text-blue-600 dark:text-blue-400", bg: "bg-blue-50 dark:bg-blue-500/10", border: "border-blue-200 dark:border-blue-500/20", animate: true, }, waiting: { icon: Circle, color: "text-muted-foreground", bg: "bg-muted/30", border: "border-border", }, delayed: { icon: Clock, color: "text-amber-600 dark:text-amber-400", bg: "bg-amber-50 dark:bg-amber-500/10", border: "border-amber-200 dark:border-amber-500/20", }, failed: { icon: XCircle, color: "text-destructive", bg: "bg-red-50 dark:bg-red-500/10", border: "border-red-200 dark:border-red-500/20", }, paused: { icon: Pause, color: "text-muted-foreground", bg: "bg-muted/30", border: "border-border", }, unknown: { icon: Circle, color: "text-muted-foreground", bg: "bg-muted/30", border: "border-border", }, }; function formatDuration(ms: number): string { if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60000).toFixed(1)}m`; } function FlowNodeComponent({ data }: FlowNodeProps) { const { flowNode, onClick } = data; const { job, queueName } = flowNode; const config = statusConfig[job.status] || statusConfig.unknown; const Icon = config.icon; return (
onClick?.(flowNode)} > {/* Input handle (top) */} {/* Content */}
{job.name}
{queueName}
{job.duration !== undefined && (
{formatDuration(job.duration)}
)}
{/* Status badge */}
{job.status}
{/* Output handle (bottom) */}
); } export const FlowNodeMemo = memo(FlowNodeComponent);