UNPKG

@webdevarif/reactflow

Version:

Reusable ReactFlow components for social media bot automation

1,140 lines (1,128 loc) 58.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/facebook/index.ts var facebook_exports = {}; __export(facebook_exports, { AIReplyNode: () => AIReplyNode, AudioMessageNode: () => AudioMessageNode, CarouselNode: () => CarouselNode, ConditionNode: () => ConditionNode, DelayNode: () => DelayNode, EcommerceNode: () => EcommerceNode, FacebookMediaNode: () => FacebookMediaNode, FacebookWorkflow: () => FacebookWorkflow_default, FacebookWorkflowCore: () => FacebookWorkflow, FileMessageNode: () => FileMessageNode, ImageMessageNode: () => ImageMessageNode, TextMessageNode: () => TextMessageNode, TriggerNode: () => TriggerNode, VideoMessageNode: () => VideoMessageNode, facebookEdgeTypes: () => facebookEdgeTypes, facebookNodeTemplates: () => facebookNodeTemplates, facebookNodeTypes: () => facebookNodeTypes, facebookSupportedMessageTypes: () => facebookSupportedMessageTypes }); module.exports = __toCommonJS(facebook_exports); // src/facebook/FacebookWorkflow.tsx var import_react = __toESM(require("react")); var import_reactflow2 = __toESM(require("reactflow")); var import_style = require("reactflow/dist/style.css"); var import_lucide_react2 = require("lucide-react"); // src/facebook/components/MessageNodes.tsx var import_reactflow = require("reactflow"); var import_jsx_runtime = require("react/jsx-runtime"); var BaseNode = ({ data, selected }) => { const getIcon = (type) => { switch (type) { case "text": return "\u{1F4AC}"; case "image": return "\u{1F5BC}\uFE0F"; case "audio": return "\u{1F3B5}"; case "video": return "\u{1F3A5}"; case "file": return "\u{1F4C4}"; case "fb_media": return "\u{1F4F1}"; case "carousel": return "\u{1F3A0}"; case "ecommerce": return "\u{1F6D2}"; case "ai_reply": return "\u{1F916}"; default: return "\u2699\uFE0F"; } }; const getColor = (type) => { switch (type) { case "text": return "bg-blue-50 border-blue-200 text-blue-800"; case "image": return "bg-green-50 border-green-200 text-green-800"; case "audio": return "bg-purple-50 border-purple-200 text-purple-800"; case "video": return "bg-red-50 border-red-200 text-red-800"; case "file": return "bg-gray-50 border-gray-200 text-gray-800"; case "fb_media": return "bg-indigo-50 border-indigo-200 text-indigo-800"; case "carousel": return "bg-yellow-50 border-yellow-200 text-yellow-800"; case "ecommerce": return "bg-pink-50 border-pink-200 text-pink-800"; case "ai_reply": return "bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200 text-purple-800"; default: return "bg-gray-50 border-gray-200 text-gray-800"; } }; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `px-4 py-3 shadow-sm rounded-lg border-2 min-w-[200px] max-w-[250px] ${getColor(data.type)} ${selected ? "ring-2 ring-blue-500 shadow-lg" : "hover:shadow-md"} transition-all duration-200`, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xl", children: getIcon(data.type) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "font-semibold text-sm truncate", children: data.label }), data.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-gray-600 mt-1 line-clamp-2", children: data.content }) ] }) ] }), data.mediaUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-2 text-xs text-gray-500 flex items-center gap-1", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u{1F4CE}" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: data.mediaType || "media" }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "target", position: import_reactflow.Position.Top, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-blue-500", style: { top: -6 } } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "source", position: import_reactflow.Position.Bottom, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-blue-500", style: { bottom: -6 } } ) ] }); }; var TextMessageNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var ImageMessageNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var AudioMessageNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var VideoMessageNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var FileMessageNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var FacebookMediaNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var CarouselNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var EcommerceNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var AIReplyNode = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseNode, { ...props }); var ConditionNode = ({ data, selected }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `px-4 py-3 shadow-sm rounded-lg border-2 min-w-[200px] max-w-[250px] bg-orange-50 border-orange-200 text-orange-800 ${selected ? "ring-2 ring-orange-500 shadow-lg" : "hover:shadow-md"} transition-all duration-200`, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xl", children: "\u{1F500}" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "font-semibold text-sm truncate", children: data.label }), data.metadata?.condition && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs text-gray-600 mt-1", children: data.metadata.condition }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "target", position: import_reactflow.Position.Top, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-orange-500", style: { top: -6 } } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "source", position: import_reactflow.Position.Bottom, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-orange-500", style: { bottom: -6 } } ) ] }); var DelayNode = ({ data, selected }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `px-4 py-3 shadow-sm rounded-lg border-2 min-w-[200px] max-w-[250px] bg-cyan-50 border-cyan-200 text-cyan-800 ${selected ? "ring-2 ring-cyan-500 shadow-lg" : "hover:shadow-md"} transition-all duration-200`, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xl", children: "\u23F1\uFE0F" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "font-semibold text-sm truncate", children: data.label }), data.metadata?.delay && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-xs text-gray-600 mt-1", children: [ (data.metadata.delay / 1e3).toFixed(1), "s" ] }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "target", position: import_reactflow.Position.Top, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-cyan-500", style: { top: -6 } } ), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "source", position: import_reactflow.Position.Bottom, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-cyan-500", style: { bottom: -6 } } ) ] }); var TriggerNode = ({ data, selected }) => { const getIcon = (type) => { switch (type) { case "receive_message": return "\u{1F4AC}"; case "receive_comment": return "\u{1F4AC}"; case "receive_post_reaction": return "\u{1F44D}"; case "receive_page_like": return "\u2764\uFE0F"; default: return "\u26A1"; } }; return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `px-4 py-3 shadow-sm rounded-lg border-2 min-w-[200px] max-w-[250px] bg-green-50 border-green-200 text-green-800 ${selected ? "ring-2 ring-green-500 shadow-lg" : "hover:shadow-md"} transition-all duration-200`, children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-3", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xl", children: getIcon(data.type) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "font-semibold text-sm truncate", children: data.label }), data.metadata?.keywords && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-xs text-green-600 mt-1 font-medium", children: [ "Keywords: ", data.metadata.keywords ] }) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-2 text-xs text-green-600 font-medium", children: "TRIGGER" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)( import_reactflow.Handle, { type: "source", position: import_reactflow.Position.Bottom, className: "w-3 h-3 bg-white border-2 border-gray-400 hover:border-green-500", style: { bottom: -6 } } ) ] }); }; // src/facebook/config.ts var facebookNodeTypes = { // Trigger nodes receive_message: TriggerNode, receive_comment: TriggerNode, receive_post_reaction: TriggerNode, receive_page_like: TriggerNode, // Action nodes text: TextMessageNode, image: ImageMessageNode, audio: AudioMessageNode, video: VideoMessageNode, file: FileMessageNode, fb_media: FacebookMediaNode, carousel: CarouselNode, ecommerce: EcommerceNode, ai_reply: AIReplyNode, condition: ConditionNode, delay: DelayNode }; var facebookEdgeTypes = { default: "smoothstep", conditional: "smoothstep" }; var facebookSupportedMessageTypes = [ "text", "image", "audio", "video", "file", "fb_media", "carousel", "ecommerce", "ai_reply" ]; var facebookNodeTemplates = { // Trigger nodes receive_message: { type: "receive_message", data: { label: "Receive Message", type: "trigger", metadata: { triggerType: "message", event: "message_received", keywords: "{{ body }}", matchType: "contains", exactMatch: false } }, position: { x: 0, y: 0 } }, receive_comment: { type: "receive_comment", data: { label: "Receive Comment", type: "trigger", metadata: { triggerType: "comment", event: "comment_received", keywords: "{{ body }}", matchType: "contains", exactMatch: false } }, position: { x: 0, y: 0 } }, receive_post_reaction: { type: "receive_post_reaction", data: { label: "Receive Post Reaction", type: "trigger", metadata: { triggerType: "reaction", event: "post_reaction" } }, position: { x: 0, y: 0 } }, receive_page_like: { type: "receive_page_like", data: { label: "Receive Page Like", type: "trigger", metadata: { triggerType: "page_like", event: "page_liked" } }, position: { x: 0, y: 0 } }, // Action nodes text: { type: "text", data: { label: "Text Message", type: "text", content: "Hello! How can I help you today?" }, position: { x: 0, y: 0 } }, image: { type: "image", data: { label: "Image Message", type: "image", content: "Check out this image!", mediaType: "image" }, position: { x: 0, y: 0 } }, audio: { type: "audio", data: { label: "Audio Message", type: "audio", content: "Listen to this audio", mediaType: "audio" }, position: { x: 0, y: 0 } }, video: { type: "video", data: { label: "Video Message", type: "video", content: "Watch this video", mediaType: "video" }, position: { x: 0, y: 0 } }, file: { type: "file", data: { label: "File Message", type: "file", content: "Here is your file", mediaType: "file" }, position: { x: 0, y: 0 } }, fb_media: { type: "fb_media", data: { label: "Facebook Media", type: "fb_media", content: "Facebook media content", mediaType: "image" }, position: { x: 0, y: 0 } }, carousel: { type: "carousel", data: { label: "Carousel", type: "carousel", content: "Browse our products", metadata: { items: [] } }, position: { x: 0, y: 0 } }, ecommerce: { type: "ecommerce", data: { label: "E-commerce", type: "ecommerce", content: "Shop now", metadata: { products: [] } }, position: { x: 0, y: 0 } }, ai_reply: { type: "ai_reply", data: { label: "AI Reply", type: "ai_reply", content: "AI-powered response", metadata: { prompt: "", model: "gpt-3.5-turbo" } }, position: { x: 0, y: 0 } }, condition: { type: "condition", data: { label: "Condition", type: "condition", metadata: { condition: "user_input", operator: "contains", value: "help" } }, position: { x: 0, y: 0 } }, delay: { type: "delay", data: { label: "Delay", type: "delay", metadata: { delay: 300 } }, position: { x: 0, y: 0 } } }; // src/components/ui/button.tsx var React = __toESM(require("react")); var import_react_slot = require("@radix-ui/react-slot"); var import_class_variance_authority = require("class-variance-authority"); // src/lib/utils.ts var import_clsx = require("clsx"); var import_tailwind_merge = require("tailwind-merge"); function cn(...inputs) { return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs)); } // src/components/ui/button.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var buttonVariants = (0, import_class_variance_authority.cva)( "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline" }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", icon: "h-10 w-10" } }, defaultVariants: { variant: "default", size: "default" } } ); var Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? import_react_slot.Slot : "button"; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Comp, { className: cn(buttonVariants({ variant, size, className })), ref, ...props } ); } ); Button.displayName = "Button"; // src/components/ui/input.tsx var React2 = __toESM(require("react")); var import_jsx_runtime3 = require("react/jsx-runtime"); var Input = React2.forwardRef( ({ className, type, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "input", { type, className: cn( "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className ), ref, ...props } ); } ); Input.displayName = "Input"; // src/components/ui/select.tsx var React3 = __toESM(require("react")); var SelectPrimitive = __toESM(require("@radix-ui/react-select")); var import_lucide_react = require("lucide-react"); var import_jsx_runtime4 = require("react/jsx-runtime"); var Select = SelectPrimitive.Root; var SelectValue = SelectPrimitive.Value; var SelectTrigger = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( SelectPrimitive.Trigger, { ref, className: cn( "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className ), ...props, children: [ children, /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.ChevronDown, { className: "h-4 w-4 opacity-50" }) }) ] } )); SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; var SelectScrollUpButton = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( SelectPrimitive.ScrollUpButton, { ref, className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.ChevronUp, { className: "h-4 w-4" }) } )); SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; var SelectScrollDownButton = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( SelectPrimitive.ScrollDownButton, { ref, className: cn( "flex cursor-default items-center justify-center py-1", className ), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.ChevronDown, { className: "h-4 w-4" }) } )); SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; var SelectContent = React3.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectPrimitive.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( SelectPrimitive.Content, { ref, className: cn( "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className ), position, ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectScrollUpButton, {}), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( SelectPrimitive.Viewport, { className: cn( "p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" ), children } ), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectScrollDownButton, {}) ] } ) })); SelectContent.displayName = SelectPrimitive.Content.displayName; var SelectLabel = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( SelectPrimitive.Label, { ref, className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className), ...props } )); SelectLabel.displayName = SelectPrimitive.Label.displayName; var SelectItem = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)( SelectPrimitive.Item, { ref, className: cn( "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className ), ...props, children: [ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react.Check, { className: "h-4 w-4" }) }) }), /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SelectPrimitive.ItemText, { children }) ] } )); SelectItem.displayName = SelectPrimitive.Item.displayName; var SelectSeparator = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)( SelectPrimitive.Separator, { ref, className: cn("-mx-1 my-1 h-px bg-muted", className), ...props } )); SelectSeparator.displayName = SelectPrimitive.Separator.displayName; // src/components/ui/switch.tsx var React4 = __toESM(require("react")); var SwitchPrimitives = __toESM(require("@radix-ui/react-switch")); var import_jsx_runtime5 = require("react/jsx-runtime"); var Switch = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( SwitchPrimitives.Root, { className: cn( "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input", className ), ...props, ref, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( SwitchPrimitives.Thumb, { className: cn( "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0" ) } ) } )); Switch.displayName = SwitchPrimitives.Root.displayName; // src/components/ui/textarea.tsx var React5 = __toESM(require("react")); var import_jsx_runtime6 = require("react/jsx-runtime"); var Textarea = React5.forwardRef( ({ className, ...props }, ref) => { return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)( "textarea", { className: cn( "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className ), ref, ...props } ); } ); Textarea.displayName = "Textarea"; // src/facebook/FacebookWorkflow.tsx var import_jsx_runtime7 = require("react/jsx-runtime"); var FacebookWorkflow = ({ initialNodes = [], initialEdges = [], initialTitle = "Facebook Bot Workflow", onSave, onNodeClick, onEdgeClick, onTitleChange, className = "", height = "600px" }) => { const [nodes, setNodes, onNodesChange] = (0, import_reactflow2.useNodesState)(initialNodes); const [edges, setEdges, onEdgesChange] = (0, import_reactflow2.useEdgesState)(initialEdges); const [selectedNode, setSelectedNode] = (0, import_react.useState)(null); const [showTriggerPopup, setShowTriggerPopup] = (0, import_react.useState)(false); const [showSidebar, setShowSidebar] = (0, import_react.useState)(true); const [sidebarTab, setSidebarTab] = (0, import_react.useState)("nodes"); const [isDragOver, setIsDragOver] = (0, import_react.useState)(false); const [isClient, setIsClient] = (0, import_react.useState)(false); const [forceUpdate, setForceUpdate] = (0, import_react.useState)(0); const [workflowTitle, setWorkflowTitle] = (0, import_react.useState)(initialTitle); const [isEditingTitle, setIsEditingTitle] = (0, import_react.useState)(false); const reactFlowInstance = (0, import_react.useRef)(null); import_react.default.useEffect(() => { setIsClient(true); }, []); const memoizedNodeTypes = (0, import_react.useMemo)(() => facebookNodeTypes, []); const memoizedEdgeTypes = (0, import_react.useMemo)(() => facebookEdgeTypes, []); const forceRerender = (0, import_react.useCallback)(() => { setForceUpdate((prev) => prev + 1); }, []); const handleTitleChange = (0, import_react.useCallback)((newTitle) => { setWorkflowTitle(newTitle); onTitleChange?.(newTitle); }, [onTitleChange]); const handleTitleSubmit = (0, import_react.useCallback)(() => { setIsEditingTitle(false); onTitleChange?.(workflowTitle); }, [workflowTitle, onTitleChange]); const handleTitleKeyDown = (0, import_react.useCallback)((e) => { if (e.key === "Enter") { handleTitleSubmit(); } else if (e.key === "Escape") { setWorkflowTitle(initialTitle); setIsEditingTitle(false); } }, [handleTitleSubmit, initialTitle]); const onConnect = (0, import_react.useCallback)( (params) => setEdges((eds) => (0, import_reactflow2.addEdge)(params, eds)), [setEdges] ); const handleNodeClick = (0, import_react.useCallback)((event, node) => { setSelectedNode(node); setSidebarTab("properties"); onNodeClick?.(node); }, [onNodeClick]); const handleEdgeClick = (0, import_react.useCallback)((event, edge) => { onEdgeClick?.(edge); }, [onEdgeClick]); const addNode = (0, import_react.useCallback)((nodeType, position) => { const template = facebookNodeTemplates[nodeType]; if (template) { const newNode = { ...template, id: `${nodeType}_${Date.now()}`, position: position || { x: Math.random() * 400 + 200, y: Math.random() * 400 + 200 } }; setNodes((nds) => [...nds, newNode]); } setShowTriggerPopup(false); }, [setNodes]); const onInit = (0, import_react.useCallback)((instance) => { reactFlowInstance.current = instance; }, []); const onDragOver = (0, import_react.useCallback)((event) => { event.preventDefault(); event.dataTransfer.dropEffect = "move"; setIsDragOver(true); }, []); const onDrop = (0, import_react.useCallback)((event) => { event.preventDefault(); setIsDragOver(false); const nodeType = event.dataTransfer.getData("application/reactflow"); if (!nodeType || !reactFlowInstance.current) { return; } const position = reactFlowInstance.current.screenToFlowPosition({ x: event.clientX, y: event.clientY }); addNode(nodeType, position); }, [addNode]); const onDragLeave = (0, import_react.useCallback)(() => { setIsDragOver(false); }, []); const onDragStart = (0, import_react.useCallback)((event, nodeType) => { event.dataTransfer.setData("application/reactflow", nodeType); event.dataTransfer.effectAllowed = "move"; }, []); const deleteNode = (0, import_react.useCallback)((nodeId) => { setNodes((nds) => nds.filter((node) => node.id !== nodeId)); setEdges((eds) => eds.filter((edge) => edge.source !== nodeId && edge.target !== nodeId)); setSelectedNode(null); }, [setNodes, setEdges]); const saveWorkflow = (0, import_react.useCallback)(() => { const config = { name: workflowTitle, description: "Automated Facebook Messenger bot workflow", platform: "facebook", nodes, edges }; onSave?.(config); }, [nodes, edges, workflowTitle, onSave]); const nodeGroups = { "Triggers": [ { type: "receive_message", label: "Receive Message", icon: "\u{1F4E8}", description: "Triggered when user sends a message" }, { type: "receive_comment", label: "Receive Comment", icon: "\u{1F4AC}", description: "Triggered when user comments on post" }, { type: "receive_post_reaction", label: "Receive Post Reaction", icon: "\u{1F44D}", description: "Triggered when user reacts to post" }, { type: "receive_page_like", label: "Receive Page Like", icon: "\u2764\uFE0F", description: "Triggered when user likes the page" } ], "Messages": [ { type: "text", label: "Text Message", icon: "\u{1F4AC}", description: "Send a text message" }, { type: "image", label: "Image Message", icon: "\u{1F5BC}\uFE0F", description: "Send an image" }, { type: "audio", label: "Audio Message", icon: "\u{1F3B5}", description: "Send audio" }, { type: "video", label: "Video Message", icon: "\u{1F3A5}", description: "Send video" } ], "Media": [ { type: "file", label: "File Message", icon: "\u{1F4C4}", description: "Send a file" }, { type: "fb_media", label: "Facebook Media", icon: "\u{1F4F1}", description: "Facebook media content" }, { type: "carousel", label: "Carousel", icon: "\u{1F3A0}", description: "Carousel of items" } ], "Commerce": [ { type: "ecommerce", label: "E-commerce", icon: "\u{1F6D2}", description: "E-commerce products" } ], "AI & Logic": [ { type: "ai_reply", label: "AI Reply", icon: "\u{1F916}", description: "AI-powered response" }, { type: "condition", label: "Condition", icon: "\u{1F500}", description: "Conditional logic" }, { type: "delay", label: "Delay", icon: "\u23F1\uFE0F", description: "Add delay" } ] }; if (!isClient) { return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `w-full h-full bg-gray-50 flex items-center justify-center ${className}`, style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-center", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-4" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-gray-600", children: "Loading workflow builder..." }) ] }) }); } return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `w-full h-full bg-gray-50 flex flex-col ${className}`, style: { height }, children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "h-14 bg-white border-b border-gray-200 flex items-center justify-between px-4", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-4", children: [ isEditingTitle ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Input, { value: workflowTitle, onChange: (e) => setWorkflowTitle(e.target.value), onBlur: handleTitleSubmit, onKeyDown: handleTitleKeyDown, className: "text-lg font-semibold text-gray-900 bg-transparent border-none p-0 focus:ring-0 focus:border-none", autoFocus: true } ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "h1", { className: "text-lg font-semibold text-gray-900 cursor-pointer hover:bg-gray-100 px-2 py-1 rounded transition-colors", onClick: () => setIsEditingTitle(true), title: "Click to edit workflow title", children: workflowTitle } ), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { onClick: () => setShowSidebar(!showSidebar), className: "p-2 hover:bg-gray-100 rounded-md transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Settings, { className: "w-4 h-4" }) } ) }) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Button, { variant: "outline", size: "sm", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Upload, { className: "w-4 h-4 mr-2" }), "Import" ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Button, { variant: "outline", size: "sm", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Download, { className: "w-4 h-4 mr-2" }), "Export" ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Button, { variant: "default", size: "sm", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Play, { className: "w-4 h-4 mr-2" }), "Test" ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( Button, { onClick: saveWorkflow, variant: "default", size: "sm", className: "bg-green-600 hover:bg-green-700", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Save, { className: "w-4 h-4 mr-2" }), "Save" ] } ) ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-[calc(100%-3.5rem)] flex-1", children: [ showSidebar && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "w-80 bg-white border-r border-gray-200 flex flex-col h-full", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex border-b border-gray-200", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { onClick: () => setSidebarTab("nodes"), className: `flex-1 px-4 py-3 text-sm font-medium transition-colors ${sidebarTab === "nodes" ? "text-blue-600 border-b-2 border-blue-600 bg-blue-50" : "text-gray-600 hover:text-gray-900"}`, children: "Nodes" } ), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( "button", { onClick: () => setSidebarTab("properties"), className: `flex-1 px-4 py-3 text-sm font-medium transition-colors ${sidebarTab === "properties" ? "text-blue-600 border-b-2 border-blue-600 bg-blue-50" : "text-gray-600 hover:text-gray-900"}`, children: "Properties" } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-1 overflow-y-auto", children: sidebarTab === "nodes" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-4", children: nodes.length === 0 ? ( // Show only triggers when canvas is empty /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mb-6", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-sm font-semibold text-gray-900 mb-3 uppercase tracking-wide", children: "Triggers" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-2", children: nodeGroups["Triggers"].map((node) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( "div", { draggable: true, onDragStart: (event) => onDragStart(event, node.type), onClick: () => addNode(node.type), className: "w-full flex items-center gap-3 p-3 text-left hover:bg-gray-50 rounded-lg border border-gray-200 transition-colors group cursor-grab active:cursor-grabbing", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-2xl", children: node.icon }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm font-medium text-gray-900 group-hover:text-blue-600", children: node.label }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-gray-500 mt-0.5", children: node.description }) ] }) ] }, node.type )) }) ] }) ) : ( // Show action nodes when canvas has nodes (hide triggers) Object.entries(nodeGroups).filter(([groupName]) => groupName !== "Triggers").map(([groupName, groupNodes]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mb-6", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-sm font-semibold text-gray-900 mb-3 uppercase tracking-wide", children: groupName }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-2", children: groupNodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( "div", { draggable: true, onDragStart: (event) => onDragStart(event, node.type), onClick: () => addNode(node.type), className: "w-full flex items-center gap-3 p-3 text-left hover:bg-gray-50 rounded-lg border border-gray-200 transition-colors group cursor-grab active:cursor-grabbing", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-2xl", children: node.icon }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1 min-w-0", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm font-medium text-gray-900 group-hover:text-blue-600", children: node.label }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-gray-500 mt-0.5", children: node.description }) ] }) ] }, node.type )) }) ] }, groupName)) ) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "p-4", children: selectedNode ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-4", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Node Type" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-sm text-gray-900 bg-gray-50 px-3 py-2 rounded-md", children: selectedNode.type }) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Label" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Input, { type: "text", value: selectedNode.data.label, onChange: (e) => { setNodes((nds) => nds.map( (n) => n.id === selectedNode.id ? { ...n, data: { ...n.data, label: e.target.value } } : n )); setSelectedNode((prev) => prev ? { ...prev, data: { ...prev.data, label: e.target.value } } : null); forceRerender(); } } ) ] }), selectedNode.data.type !== "trigger" && selectedNode.data.content !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Content" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Textarea, { value: selectedNode.data.content || "", onChange: (e) => { setNodes((nds) => nds.map( (n) => n.id === selectedNode.id ? { ...n, data: { ...n.data, content: e.target.value } } : n )); setSelectedNode((prev) => prev ? { ...prev, data: { ...prev.data, content: e.target.value } } : null); forceRerender(); }, rows: 3 } ) ] }), selectedNode.data.type === "trigger" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Keywords" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Textarea, { placeholder: "hello, help, {{ body }}", value: selectedNode.data.metadata?.keywords || "", onChange: (e) => { setNodes((nds) => nds.map( (n) => n.id === selectedNode.id ? { ...n, data: { ...n.data, metadata: { ...n.data.metadata, keywords: e.target.value } } } : n )); setSelectedNode((prev) => prev ? { ...prev, data: { ...prev.data, metadata: { ...prev.data.metadata, keywords: e.target.value } } } : null); forceRerender(); }, rows: 3 } ), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-xs text-gray-500 mt-1", children: [ "Comma separated keywords or ", `{{ body }}`, " for full content" ] }) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Match Type" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)( Select, { value: selectedNode.data.metadata?.matchType || "contains", onValueChange: (value) => { setNodes((nds) => nds.map( (n) => n.id === selectedNode.id ? { ...n, data: { ...n.data, metadata: { ...n.data.metadata, matchType: value } } } : n )); setSelectedNode((prev) => prev ? { ...prev, data: { ...prev.data, metadata: { ...prev.data.metadata, matchType: value } } } : null); forceRerender(); }, children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectValue, { placeholder: "Select match type" }) }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(SelectContent, { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectItem, { value: "contains", children: "Contains" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectItem, { value: "exact", children: "Exact Match" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectItem, { value: "starts_with", children: "Starts With" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SelectItem, { value: "ends_with", children: "Ends With" }) ] }) ] } ) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between", children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { htmlFor: "exactMatch", className: "text-sm font-medium text-gray-700", children: "Exact Match (case sensitive)" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-xs text-gray-500 mt-1", children: "Enable case-sensitive matching" }) ] }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Switch, { checked: selectedNode.data.metadata?.exactMatch || false, onCheckedChange: (checked) => { console.log("Switch toggled:", { checked, nodeId: selectedNode.id }); setNodes((nds) => nds.map( (n) => n.id === selectedNode.id ? { ...n, data: { ...n.data, metadata: { ...n.data.metadata, exactMatch: checked } } } : n )); setSelectedNode((prev) => prev ? { ...prev, data: { ...prev.data, metadata: { ...prev.data.metadata, exactMatch: checked } } } : null); } } ) }) ] }) ] }), selectedNode.data.type === "delay" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Delay Duration (milliseconds)" }), /* @__PURE__ */ (0, import_jsx_runtime7.jsx)( Input, { type: "number", min: "0", step: "100", value: selectedNode.data.metadata?.