diff --git a/src/components/nodes/SlackNode.tsx b/src/components/nodes/SlackNode.tsx new file mode 100644 index 0000000..bcbdd5d --- /dev/null +++ b/src/components/nodes/SlackNode.tsx @@ -0,0 +1,214 @@ +import React from "react"; +import { NodeData } from "../../types"; +import { SlackOperation } from "../../types/slack"; +import { + MessageSquare, + CheckCircle, + XCircle, + Clock, + Hash, + User, + FileText, + Smile, + Bell, + Users, +} from "lucide-react"; + +interface SlackNodeProps { + data: NodeData; + selected: boolean; +} + +export const SlackNode: React.FC = ({ data, selected }) => { + // Get operation display info + const getOperationInfo = () => { + const operation = data.config?.operation as SlackOperation; + const operationIcons: Record> = { + message: MessageSquare, + channel: Hash, + user: User, + file: FileText, + reaction: Smile, + reminder: Bell, + }; + + const operationLabels: Record = { + message: "Message", + channel: "Channel", + user: "User", + file: "File", + reaction: "Reaction", + reminder: "Reminder", + }; + + return { + icon: operationIcons[operation] || MessageSquare, + label: operationLabels[operation] || "Slack", + }; + }; + + const operationInfo = getOperationInfo(); + const OperationIcon = operationInfo.icon; + + // Enhanced data for display + const getDisplayData = () => { + const operation = data.config?.operation; + const baseData = { + operation: operation || "Not configured", + channel: data.config?.channel || "No channel", + text: data.config?.text || "No message", + }; + + switch (operation) { + case "message": + return { + ...baseData, + channel: data.config?.channel || "No channel", + text: data.config?.text || "No message", + }; + case "channel": + return { + ...baseData, + channel: data.config?.channelName || "No channel name", + text: data.config?.channelPurpose || "No purpose", + }; + case "user": + return { + ...baseData, + channel: data.config?.userId || data.config?.userEmail || "No user", + text: "User lookup", + }; + case "file": + return { + ...baseData, + channel: data.config?.channel || "No channel", + text: data.config?.fileName || "No file name", + }; + case "reaction": + return { + ...baseData, + channel: data.config?.channel || "No channel", + text: data.config?.emoji || "No emoji", + }; + case "reminder": + return { + ...baseData, + channel: data.config?.reminderUser || "No user", + text: data.config?.reminderText || "No reminder text", + }; + default: + return baseData; + } + }; + + const displayData = getDisplayData(); + + // Custom status display for Slack operations + const getStatusDisplay = () => { + if (data.status === "success" && data.outputs && data.outputs.length > 0) { + const output = data.outputs[0]; + if (output.channelId) { + return ( +
+ Sent to #{output.channelId} +
+ ); + } + if (output.messageTs) { + return ( +
+ Message sent at{" "} + {new Date(output.messageTs * 1000).toLocaleTimeString()} +
+ ); + } + if (output.userId) { + return ( +
User: {output.userId}
+ ); + } + } + return null; + }; + + return ( +
+ {/* Top Handle */} +
+ + {/* Header */} +
+
+
+
+ +
+
+

+ {data.label || "Slack"} +

+

{operationInfo.label}

+
+
+ + {/* Status Indicator */} +
+ {data.status === "running" && ( +
+ +
+ )} + {data.status === "success" && ( +
+ +
+ )} + {data.status === "error" && ( +
+ +
+ )} +
+
+
+ + {/* Content */} +
+
+ {/* Operation Details */} +
+
+ Operation: {displayData.operation} +
+
Channel: {displayData.channel}
+
+ {displayData.text.length > 30 + ? `${displayData.text.substring(0, 30)}...` + : displayData.text} +
+
+ + {/* Status Display */} + {getStatusDisplay()} + + {/* Configuration Preview */} + {data.config?.botToken && ( +
+ Bot Token: {data.config.botToken.substring(0, 8)}... +
+ )} + + {/* Error Display */} + {data.status === "error" && data.error && ( +
+ Error: {data.error} +
+ )} +
+
+ + {/* Bottom Handle */} +
+
+ ); +};