{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "n8n-workflow-block-shadcnui",
  "type": "registry:block",
  "title": "N8N Workflow Block",
  "description": "Visual workflow automation builder with animated nodes, connections, and real-time execution monitoring",
  "registryDependencies": [
    "button"
  ],
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/sections/n8n-workflow-block.tsx",
      "content": "\"use client\";\n\nimport { motion, type PanInfo } from \"framer-motion\";\nimport type React from \"react\";\nimport { useRef, useState } from \"react\";\nimport { flushSync } from \"react-dom\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\nimport {\n  ArrowRight,\n  Database,\n  Mail,\n  Plus,\n  Settings,\n  Webhook,\n  Zap,\n} from \"lucide-react\";\n\n// Interfaces\ninterface WorkflowNode {\n  id: string;\n  type: \"trigger\" | \"action\" | \"condition\";\n  title: string;\n  description: string;\n  icon: React.ComponentType<{ className?: string }>;\n  color: string;\n  position: { x: number; y: number };\n}\n\ninterface WorkflowConnection {\n  from: string;\n  to: string;\n}\n\n// Constants\nconst NODE_WIDTH = 200;\nconst NODE_HEIGHT = 100;\n\nconst nodeTemplates: Omit<WorkflowNode, \"id\" | \"position\">[] = [\n  {\n    type: \"trigger\",\n    title: \"Webhook\",\n    description: \"Receive data from external service\",\n    icon: Webhook,\n    color: \"emerald\",\n  },\n  {\n    type: \"action\",\n    title: \"Database Query\",\n    description: \"Fetch user records\",\n    icon: Database,\n    color: \"blue\",\n  },\n  {\n    type: \"condition\",\n    title: \"Condition\",\n    description: \"Check user status\",\n    icon: Settings,\n    color: \"amber\",\n  },\n  {\n    type: \"action\",\n    title: \"Send Email\",\n    description: \"Notify user\",\n    icon: Mail,\n    color: \"purple\",\n  },\n  {\n    type: \"action\",\n    title: \"Log Event\",\n    description: \"Record activity\",\n    icon: Zap,\n    color: \"indigo\",\n  },\n];\n\nconst initialNodes: WorkflowNode[] = [\n  {\n    id: \"node-1\",\n    type: \"trigger\",\n    title: \"Webhook\",\n    description: \"Receive data from external service\",\n    icon: Webhook,\n    color: \"emerald\",\n    position: { x: 50, y: 100 },\n  },\n  {\n    id: \"node-2\",\n    type: \"action\",\n    title: \"Database Query\",\n    description: \"Fetch user records\",\n    icon: Database,\n    color: \"blue\",\n    position: { x: 300, y: 100 },\n  },\n  {\n    id: \"node-3\",\n    type: \"condition\",\n    title: \"Condition\",\n    description: \"Check user status\",\n    icon: Settings,\n    color: \"amber\",\n    position: { x: 550, y: 100 },\n  },\n];\n\nconst initialConnections: WorkflowConnection[] = [\n  { from: \"node-1\", to: \"node-2\" },\n  { from: \"node-2\", to: \"node-3\" },\n];\n\nconst colorClasses: Record<string, string> = {\n  emerald: \"border-emerald-400/40 bg-emerald-400/10 text-emerald-400\",\n  blue: \"border-blue-400/40 bg-blue-400/10 text-blue-400\",\n  amber: \"border-amber-400/40 bg-amber-400/10 text-amber-400\",\n  purple: \"border-purple-400/40 bg-purple-400/10 text-purple-400\",\n  indigo: \"border-indigo-400/40 bg-indigo-400/10 text-indigo-400\",\n};\n\n// Connection Line Component\nfunction WorkflowConnectionLine({\n  from,\n  to,\n  nodes,\n}: {\n  from: string;\n  to: string;\n  nodes: WorkflowNode[];\n}) {\n  const fromNode = nodes.find((n) => n.id === from);\n  const toNode = nodes.find((n) => n.id === to);\n  if (!fromNode || !toNode) return null;\n\n  const startX = fromNode.position.x + NODE_WIDTH;\n  const startY = fromNode.position.y + NODE_HEIGHT / 2;\n  const endX = toNode.position.x;\n  const endY = toNode.position.y + NODE_HEIGHT / 2;\n\n  const cp1X = startX + (endX - startX) * 0.5;\n  const cp2X = endX - (endX - startX) * 0.5;\n\n  const path = `M${startX},${startY} C${cp1X},${startY} ${cp2X},${endY} ${endX},${endY}`;\n\n  return (\n    <path\n      d={path}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeDasharray=\"8,6\"\n      strokeLinecap=\"round\"\n      opacity={0.35}\n      className=\"text-foreground\"\n    />\n  );\n}\n\n// Main Component\nexport function N8nWorkflowBlock() {\n  const [nodes, setNodes] = useState<WorkflowNode[]>(initialNodes);\n  const [connections, setConnections] =\n    useState<WorkflowConnection[]>(initialConnections);\n  const canvasRef = useRef<HTMLDivElement>(null);\n  const dragStartPosition = useRef<{ x: number; y: number } | null>(null);\n  const [draggingNodeId, setDraggingNodeId] = useState<string | null>(null);\n  const [contentSize, setContentSize] = useState(() => {\n    const maxX = Math.max(\n      ...initialNodes.map((n) => n.position.x + NODE_WIDTH)\n    );\n    const maxY = Math.max(\n      ...initialNodes.map((n) => n.position.y + NODE_HEIGHT)\n    );\n    return { width: maxX + 50, height: maxY + 50 };\n  });\n\n  // Drag Handlers\n  const handleDragStart = (nodeId: string) => {\n    setDraggingNodeId(nodeId);\n    const node = nodes.find((n) => n.id === nodeId);\n    if (node) {\n      dragStartPosition.current = { x: node.position.x, y: node.position.y };\n    }\n  };\n\n  const handleDrag = (nodeId: string, { offset }: PanInfo) => {\n    if (draggingNodeId !== nodeId || !dragStartPosition.current) return;\n\n    const newX = dragStartPosition.current.x + offset.x;\n    const newY = dragStartPosition.current.y + offset.y;\n\n    const constrainedX = Math.max(0, newX);\n    const constrainedY = Math.max(0, newY);\n\n    flushSync(() => {\n      setNodes((prev) =>\n        prev.map((node) =>\n          node.id === nodeId\n            ? { ...node, position: { x: constrainedX, y: constrainedY } }\n            : node\n        )\n      );\n    });\n\n    setContentSize((prev) => ({\n      width: Math.max(prev.width, constrainedX + NODE_WIDTH + 50),\n      height: Math.max(prev.height, constrainedY + NODE_HEIGHT + 50),\n    }));\n  };\n\n  const handleDragEnd = () => {\n    setDraggingNodeId(null);\n    dragStartPosition.current = null;\n  };\n\n  // Add Node Handler\n  const addNode = () => {\n    const template =\n      nodeTemplates[Math.floor(Math.random() * nodeTemplates.length)];\n    const lastNode = nodes[nodes.length - 1];\n    const newPosition = lastNode\n      ? { x: lastNode.position.x + 250, y: lastNode.position.y }\n      : { x: 50, y: 100 };\n\n    const newNode: WorkflowNode = {\n      id: `node-${Date.now()}`,\n      ...template,\n      position: newPosition,\n    };\n\n    flushSync(() => {\n      setNodes((prev) => [...prev, newNode]);\n      if (lastNode) {\n        setConnections((prev) => [\n          ...prev,\n          { from: lastNode.id, to: newNode.id },\n        ]);\n      }\n    });\n\n    setContentSize((prev) => ({\n      width: Math.max(prev.width, newPosition.x + NODE_WIDTH + 50),\n      height: Math.max(prev.height, newPosition.y + NODE_HEIGHT + 50),\n    }));\n\n    // Scroll to new node\n    const canvas = canvasRef.current;\n    if (canvas) {\n      canvas.scrollTo({\n        left: newPosition.x + NODE_WIDTH - canvas.clientWidth + 100,\n        behavior: \"smooth\",\n      });\n    }\n  };\n\n  return (\n    <div className=\"relative w-full overflow-hidden rounded-2xl border border-border/40 bg-background/60 backdrop-blur p-4 sm:p-6\">\n      {/* Header */}\n      <div className=\"mb-4 flex flex-wrap items-center justify-between gap-3\">\n        <div className=\"flex items-center gap-3\">\n          <Badge\n            variant=\"outline\"\n            className=\"rounded-full border-emerald-400/40 bg-emerald-400/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.25em] text-emerald-400\"\n          >\n            Active\n          </Badge>\n          <span className=\"text-xs sm:text-sm uppercase tracking-[0.25em] text-foreground/50\">\n            Workflow Builder\n          </span>\n        </div>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          onClick={addNode}\n          className=\"h-8 gap-2 rounded-lg text-xs uppercase tracking-[0.2em] text-foreground/70 hover:text-foreground\"\n          aria-label=\"Add new node\"\n        >\n          <Plus className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n          <span className=\"hidden sm:inline\">Add Node</span>\n        </Button>\n      </div>\n\n      {/* Canvas */}\n      <div\n        ref={canvasRef}\n        className=\"relative h-[400px] w-full overflow-auto rounded-xl border border-border/30 bg-background/40 sm:h-[500px] md:h-[600px]\"\n        style={{ minHeight: \"400px\" }}\n        role=\"region\"\n        aria-label=\"Workflow canvas\"\n        tabIndex={0}\n      >\n        {/* Content Wrapper */}\n        <div\n          className=\"relative\"\n          style={{\n            minWidth: contentSize.width,\n            minHeight: contentSize.height,\n          }}\n        >\n          {/* SVG Connections */}\n          <svg\n            className=\"absolute top-0 left-0 pointer-events-none\"\n            width={contentSize.width}\n            height={contentSize.height}\n            style={{ overflow: \"visible\" }}\n            aria-hidden=\"true\"\n          >\n            {connections.map((c) => (\n              <WorkflowConnectionLine\n                key={`${c.from}-${c.to}`}\n                from={c.from}\n                to={c.to}\n                nodes={nodes}\n              />\n            ))}\n          </svg>\n\n          {/* Nodes */}\n          {nodes.map((node) => {\n            const Icon = node.icon;\n            const isDragging = draggingNodeId === node.id;\n\n            return (\n              <motion.div\n                key={node.id}\n                drag\n                dragMomentum={false}\n                dragConstraints={{\n                  left: 0,\n                  top: 0,\n                  right: 100000,\n                  bottom: 100000,\n                }}\n                onDragStart={() => handleDragStart(node.id)}\n                onDrag={(_, info) => handleDrag(node.id, info)}\n                onDragEnd={handleDragEnd}\n                style={{\n                  x: node.position.x,\n                  y: node.position.y,\n                  width: NODE_WIDTH,\n                  transformOrigin: \"0 0\",\n                }}\n                className=\"absolute cursor-grab\"\n                initial={{ scale: 0.8, opacity: 0 }}\n                animate={{ scale: 1, opacity: 1 }}\n                transition={{ duration: 0.2 }}\n                whileHover={{ scale: 1.02 }}\n                whileDrag={{ scale: 1.05, zIndex: 50, cursor: \"grabbing\" }}\n                aria-grabbed={isDragging}\n              >\n                <Card\n                  className={`group/node relative w-full overflow-hidden rounded-xl border ${colorClasses[node.color]} bg-background/70 p-3 backdrop-blur transition-all hover:shadow-lg ${isDragging ? \"shadow-xl ring-2 ring-primary/50\" : \"\"}`}\n                  role=\"article\"\n                  aria-label={`${node.type} node: ${node.title}`}\n                >\n                  <div className=\"absolute inset-0 bg-gradient-to-br from-foreground/[0.04] via-transparent to-transparent opacity-0 transition-opacity duration-300 group-hover/node:opacity-100\" />\n\n                  <div className=\"relative space-y-2\">\n                    <div className=\"flex items-center gap-2\">\n                      <div\n                        className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border ${colorClasses[node.color]} bg-background/80 backdrop-blur`}\n                        aria-hidden=\"true\"\n                      >\n                        <Icon className=\"h-4 w-4\" />\n                      </div>\n                      <div className=\"min-w-0 flex-1\">\n                        <Badge\n                          variant=\"outline\"\n                          className=\"mb-0.5 rounded-full border-border/40 bg-background/80 px-1.5 py-0 text-[9px] uppercase tracking-[0.15em] text-foreground/60\"\n                        >\n                          {node.type}\n                        </Badge>\n                        <h3 className=\"truncate text-xs font-semibold tracking-tight text-foreground\">\n                          {node.title}\n                        </h3>\n                      </div>\n                    </div>\n                    <p className=\"line-clamp-2 text-[10px] leading-relaxed text-foreground/70\">\n                      {node.description}\n                    </p>\n                    <div className=\"flex items-center gap-1.5 text-[10px] text-foreground/50\">\n                      <ArrowRight className=\"h-2.5 w-2.5\" aria-hidden=\"true\" />\n                      <span className=\"uppercase tracking-[0.1em]\">\n                        Connected\n                      </span>\n                    </div>\n                  </div>\n                </Card>\n              </motion.div>\n            );\n          })}\n        </div>\n      </div>\n\n      {/* Footer Stats */}\n      <div\n        className=\"mt-4 flex flex-wrap items-center justify-between gap-3 rounded-lg border border-border/30 bg-background/40 px-4 py-2.5 backdrop-blur-sm\"\n        role=\"status\"\n        aria-live=\"polite\"\n      >\n        <div className=\"flex flex-wrap items-center gap-4 text-xs text-foreground/60\">\n          <div className=\"flex items-center gap-2\">\n            <div\n              className=\"h-1.5 w-1.5 rounded-full bg-emerald-500\"\n              aria-hidden=\"true\"\n            />\n            <span className=\"uppercase tracking-[0.15em]\">\n              {nodes.length} {nodes.length === 1 ? \"Node\" : \"Nodes\"}\n            </span>\n          </div>\n          <div className=\"flex items-center gap-2\">\n            <div\n              className=\"h-1.5 w-1.5 rounded-full bg-primary\"\n              aria-hidden=\"true\"\n            />\n            <span className=\"uppercase tracking-[0.15em]\">\n              {connections.length}{\" \"}\n              {connections.length === 1 ? \"Connection\" : \"Connections\"}\n            </span>\n          </div>\n        </div>\n        <p className=\"text-[10px] uppercase tracking-[0.2em] text-foreground/40\">\n          Drag nodes to reposition\n        </p>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:block",
      "target": "components/uitripled/n8n-workflow-block-shadcnui.tsx"
    }
  ]
}