diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/webhook-settings/webhook-settings.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/webhook-settings/webhook-settings.tsx index 99b4b205a..2dd4b2be9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/webhook-settings/webhook-settings.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/webhook-settings/webhook-settings.tsx @@ -8,6 +8,7 @@ import { Eye, EyeOff, Pencil, + Play, Plus, RefreshCw, Search, @@ -75,7 +76,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti const [showSecret, setShowSecret] = useState(false) const [editingWebhookId, setEditingWebhookId] = useState(null) const [showForm, setShowForm] = useState(false) - const [copySuccess, setCopySuccess] = useState(false) + const [copySuccess, setCopySuccess] = useState>({}) const [searchTerm, setSearchTerm] = useState('') const [isGenerating, setIsGenerating] = useState(false) const [showDeleteDialog, setShowDeleteDialog] = useState(false) @@ -342,16 +343,19 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti setIsGenerating(false) } - const copyToClipboard = (text: string) => { + const copyToClipboard = (text: string, webhookId: string) => { navigator.clipboard.writeText(text) - setCopySuccess(true) + setCopySuccess((prev) => ({ ...prev, [webhookId]: true })) setTimeout(() => { - setCopySuccess(false) + setCopySuccess((prev) => ({ ...prev, [webhookId]: false })) }, 2000) } + const [originalWebhook, setOriginalWebhook] = useState(null) + const startEditWebhook = (webhook: WebhookConfig) => { setEditingWebhookId(webhook.id) + setOriginalWebhook(webhook) setNewWebhook({ url: webhook.url, secret: '', // Don't expose the existing secret @@ -368,6 +372,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti const cancelEdit = () => { setEditingWebhookId(null) + setOriginalWebhook(null) setFieldErrors({}) setOperationStatus({ type: null, message: '' }) setNewWebhook({ @@ -383,6 +388,22 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti setShowForm(false) } + const hasChanges = () => { + if (!originalWebhook) return false + return ( + newWebhook.url !== originalWebhook.url || + newWebhook.includeFinalOutput !== originalWebhook.includeFinalOutput || + newWebhook.includeTraceSpans !== originalWebhook.includeTraceSpans || + newWebhook.includeRateLimits !== (originalWebhook.includeRateLimits || false) || + newWebhook.includeUsageData !== (originalWebhook.includeUsageData || false) || + JSON.stringify([...newWebhook.levelFilter].sort()) !== + JSON.stringify([...originalWebhook.levelFilter].sort()) || + JSON.stringify([...newWebhook.triggerFilter].sort()) !== + JSON.stringify([...originalWebhook.triggerFilter].sort()) || + newWebhook.secret !== '' + ) + } + const handleCloseModal = () => { cancelEdit() setOperationStatus({ type: null, message: '' }) @@ -555,20 +576,44 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti ) : ( <> {filteredWebhooks.map((webhook, index) => ( -
+
-
- - {webhook.url.length > 40 - ? `${webhook.url.substring(0, 37)}...` - : webhook.url} +
+ + {webhook.url}
+ + + + + + Copy webhook URL + + {/* Test Status inline for this specific webhook */} {testStatus && @@ -598,13 +643,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti 'focus-visible:ring-2 focus-visible:ring-muted-foreground/20 focus-visible:ring-offset-1' )} > - + Test webhook @@ -830,7 +869,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti type='button' variant='ghost' size='sm' - onClick={() => copyToClipboard(newWebhook.secret)} + onClick={() => copyToClipboard(newWebhook.secret, 'form')} disabled={!newWebhook.secret} className={cn( 'group h-7 w-7 rounded-md p-0', @@ -841,7 +880,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti 'focus-visible:ring-2 focus-visible:ring-muted-foreground/20 focus-visible:ring-offset-1' )} > - {copySuccess ? ( + {copySuccess.form ? ( ) : ( @@ -1060,15 +1099,13 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti isCreating || !newWebhook.url || newWebhook.levelFilter.length === 0 || - newWebhook.triggerFilter.length === 0 + newWebhook.triggerFilter.length === 0 || + (!!editingWebhookId && !hasChanges()) } className='h-9 rounded-[8px] bg-[var(--brand-primary-hex)] font-[480] text-white shadow-[0_0_0_0_var(--brand-primary-hex)] transition-all duration-200 hover:bg-[var(--brand-primary-hover-hex)] hover:shadow-[0_0_0_4px_rgba(127,47,255,0.15)] disabled:opacity-50 disabled:hover:shadow-none' > {isCreating ? ( - <> - - {editingWebhookId ? 'Updating...' : 'Creating...'} - + <>{editingWebhookId ? 'Updating...' : 'Creating...'} ) : ( <>{editingWebhookId ? 'Update Webhook' : 'Create Webhook'} )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx index 8d8d485e1..9b269f083 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx @@ -54,17 +54,23 @@ export function McpToolSelector({ const selectedTool = availableTools.find((tool) => tool.id === selectedToolId) + useEffect(() => { + if (serverValue && selectedToolId && !selectedTool && availableTools.length === 0) { + refreshTools() + } + }, [serverValue, selectedToolId, selectedTool, availableTools.length, refreshTools]) + useEffect(() => { if ( storeValue && availableTools.length > 0 && !availableTools.find((tool) => tool.id === storeValue) ) { - if (!isPreview) { + if (!isPreview && !disabled) { setStoreValue('') } } - }, [serverValue, availableTools, storeValue, setStoreValue, isPreview]) + }, [serverValue, availableTools, storeValue, setStoreValue, isPreview, disabled]) const handleOpenChange = (isOpen: boolean) => { setOpen(isOpen)