FlowCard.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. "use client";
  2. import {
  3. BadgeCheck, Bot, Braces, Database, FileCheck2, FileLock2, Flag,
  4. GitCommitHorizontal, PackageCheck, RadioTower, RefreshCcw, Rocket,
  5. Send, ShieldCheck, Workflow,
  6. } from "lucide-react";
  7. import { Handle, Position, type Node, type NodeProps } from "@xyflow/react";
  8. import type { CardData } from "@/lib/flow";
  9. import type { NodeType } from "@/lib/types";
  10. const ICONS: Record<NodeType, typeof Database> = {
  11. input: Database,
  12. contract: FileLock2,
  13. operation: RadioTower,
  14. worker: Bot,
  15. attempt: Send,
  16. artifact: PackageCheck,
  17. validator: ShieldCheck,
  18. validation: FileCheck2,
  19. decision: BadgeCheck,
  20. checkpoint: Flag,
  21. publication: Rocket,
  22. transaction: GitCommitHorizontal,
  23. readback: RefreshCcw,
  24. };
  25. export function FlowCard({ data, selected }: NodeProps<Node<CardData>>) {
  26. const item = data.item;
  27. const Icon = ICONS[item.node_type] ?? Braces;
  28. return <article
  29. className={`flowCard type-${item.node_type} status-${item.status} ${selected ? "selected" : ""} ${data.dimmed ? "dimmed" : ""}`}
  30. data-node-id={item.id}
  31. >
  32. <Handle type="target" position={Position.Left} className="nodeHandle" />
  33. <header className="cardHeader">
  34. <span className="nodeIcon"><Icon size={17} strokeWidth={1.9} /></span>
  35. <span className="cardKind">{item.node_type}</span>
  36. <span className="sequence">{String(item.sequence).padStart(2, "0")}</span>
  37. </header>
  38. <div className="cardBody">
  39. <h3>{item.title}</h3>
  40. <p>{item.subtitle}</p>
  41. <div className="recordType"><Workflow size={12} /> {item.record.model_name}</div>
  42. {item.metrics.length ? <dl className="metricGrid">
  43. {item.metrics.slice(0, 3).map((metric) => <div key={`${metric.label}:${metric.value}`}>
  44. <dt>{metric.label}</dt><dd>{metric.value}</dd>
  45. </div>)}
  46. </dl> : null}
  47. </div>
  48. <footer className="cardFooter">
  49. <span className={`statusDot status-${item.status}`} />
  50. <strong>{item.status}</strong>
  51. {item.agent_role ? <span>{item.agent_role}</span> : null}
  52. {item.task_kind ? <span className="taskKind">{item.task_kind}</span> : null}
  53. </footer>
  54. <Handle type="source" position={Position.Right} className="nodeHandle" />
  55. </article>;
  56. }