Skip to content

Commit cff0a87

Browse files
authored
fix(webhooks): made spacing more clear, added copy button for webhook URL & fixed race condition for mcp tools/server fetching in the mcp block (#1309)
* update infra and remove railway * fix(webooks-ui): made spacing more clear, added copy button for webhook URL & fixed race condition for mcp tools/server fetching in the mcp block * Revert "update infra and remove railway" This reverts commit 5a88762. * remove extraneous comments * ack PR comments
1 parent abca731 commit cff0a87

File tree

2 files changed

+69
-26
lines changed
  • apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components

2 files changed

+69
-26
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/webhook-settings/webhook-settings.tsx

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Eye,
99
EyeOff,
1010
Pencil,
11+
Play,
1112
Plus,
1213
RefreshCw,
1314
Search,
@@ -75,7 +76,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
7576
const [showSecret, setShowSecret] = useState(false)
7677
const [editingWebhookId, setEditingWebhookId] = useState<string | null>(null)
7778
const [showForm, setShowForm] = useState(false)
78-
const [copySuccess, setCopySuccess] = useState(false)
79+
const [copySuccess, setCopySuccess] = useState<Record<string, boolean>>({})
7980
const [searchTerm, setSearchTerm] = useState('')
8081
const [isGenerating, setIsGenerating] = useState(false)
8182
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
@@ -342,16 +343,19 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
342343
setIsGenerating(false)
343344
}
344345

345-
const copyToClipboard = (text: string) => {
346+
const copyToClipboard = (text: string, webhookId: string) => {
346347
navigator.clipboard.writeText(text)
347-
setCopySuccess(true)
348+
setCopySuccess((prev) => ({ ...prev, [webhookId]: true }))
348349
setTimeout(() => {
349-
setCopySuccess(false)
350+
setCopySuccess((prev) => ({ ...prev, [webhookId]: false }))
350351
}, 2000)
351352
}
352353

354+
const [originalWebhook, setOriginalWebhook] = useState<WebhookConfig | null>(null)
355+
353356
const startEditWebhook = (webhook: WebhookConfig) => {
354357
setEditingWebhookId(webhook.id)
358+
setOriginalWebhook(webhook)
355359
setNewWebhook({
356360
url: webhook.url,
357361
secret: '', // Don't expose the existing secret
@@ -368,6 +372,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
368372

369373
const cancelEdit = () => {
370374
setEditingWebhookId(null)
375+
setOriginalWebhook(null)
371376
setFieldErrors({})
372377
setOperationStatus({ type: null, message: '' })
373378
setNewWebhook({
@@ -383,6 +388,22 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
383388
setShowForm(false)
384389
}
385390

391+
const hasChanges = () => {
392+
if (!originalWebhook) return false
393+
return (
394+
newWebhook.url !== originalWebhook.url ||
395+
newWebhook.includeFinalOutput !== originalWebhook.includeFinalOutput ||
396+
newWebhook.includeTraceSpans !== originalWebhook.includeTraceSpans ||
397+
newWebhook.includeRateLimits !== (originalWebhook.includeRateLimits || false) ||
398+
newWebhook.includeUsageData !== (originalWebhook.includeUsageData || false) ||
399+
JSON.stringify([...newWebhook.levelFilter].sort()) !==
400+
JSON.stringify([...originalWebhook.levelFilter].sort()) ||
401+
JSON.stringify([...newWebhook.triggerFilter].sort()) !==
402+
JSON.stringify([...originalWebhook.triggerFilter].sort()) ||
403+
newWebhook.secret !== ''
404+
)
405+
}
406+
386407
const handleCloseModal = () => {
387408
cancelEdit()
388409
setOperationStatus({ type: null, message: '' })
@@ -555,20 +576,44 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
555576
) : (
556577
<>
557578
{filteredWebhooks.map((webhook, index) => (
558-
<div key={webhook.id} className='relative flex flex-col gap-2'>
579+
<div key={webhook.id} className='relative mb-4 flex flex-col gap-2'>
559580
<Label className='font-normal text-muted-foreground text-xs uppercase'>
560581
Webhook {index + 1}
561582
</Label>
562583
<div className='flex flex-col gap-2'>
563584
<div className='flex items-center justify-between gap-4'>
564585
<div className='flex flex-1 items-center gap-3'>
565-
<div className='flex h-8 items-center rounded-[8px] bg-muted px-3'>
566-
<code className='font-mono text-foreground text-xs'>
567-
{webhook.url.length > 40
568-
? `${webhook.url.substring(0, 37)}...`
569-
: webhook.url}
586+
<div className='flex h-8 max-w-[400px] items-center overflow-hidden rounded-[8px] bg-muted px-3'>
587+
<code className='scrollbar-hide overflow-x-auto whitespace-nowrap font-mono text-foreground text-xs'>
588+
{webhook.url}
570589
</code>
571590
</div>
591+
<Tooltip>
592+
<TooltipTrigger asChild>
593+
<Button
594+
variant='ghost'
595+
size='icon'
596+
onClick={() => copyToClipboard(webhook.url, webhook.id)}
597+
className={cn(
598+
'group relative h-8 w-8 rounded-md border border-border/40 bg-background/80 backdrop-blur-sm',
599+
'text-muted-foreground/70 shadow-sm transition-all duration-200',
600+
'hover:border-border hover:bg-muted/50 hover:text-foreground hover:shadow-md',
601+
'active:scale-95 active:shadow-sm',
602+
'focus-visible:ring-2 focus-visible:ring-muted-foreground/20 focus-visible:ring-offset-1'
603+
)}
604+
>
605+
{copySuccess[webhook.id] ? (
606+
<Check className='h-3.5 w-3.5 text-foreground' />
607+
) : (
608+
<Copy className='h-3.5 w-3.5 transition-transform duration-200 group-hover:scale-110' />
609+
)}
610+
<span className='sr-only'>Copy webhook URL</span>
611+
</Button>
612+
</TooltipTrigger>
613+
<TooltipContent side='top' align='center'>
614+
Copy webhook URL
615+
</TooltipContent>
616+
</Tooltip>
572617

573618
{/* Test Status inline for this specific webhook */}
574619
{testStatus &&
@@ -598,13 +643,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
598643
'focus-visible:ring-2 focus-visible:ring-muted-foreground/20 focus-visible:ring-offset-1'
599644
)}
600645
>
601-
<RefreshCw
602-
className={cn(
603-
'h-3.5 w-3.5 transition-transform duration-200',
604-
'group-hover:scale-110',
605-
isTesting === webhook.id && 'animate-spin'
606-
)}
607-
/>
646+
<Play className='h-3.5 w-3.5 transition-transform duration-200 group-hover:scale-110' />
608647
<span className='sr-only'>Test webhook</span>
609648
</Button>
610649
</TooltipTrigger>
@@ -830,7 +869,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
830869
type='button'
831870
variant='ghost'
832871
size='sm'
833-
onClick={() => copyToClipboard(newWebhook.secret)}
872+
onClick={() => copyToClipboard(newWebhook.secret, 'form')}
834873
disabled={!newWebhook.secret}
835874
className={cn(
836875
'group h-7 w-7 rounded-md p-0',
@@ -841,7 +880,7 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
841880
'focus-visible:ring-2 focus-visible:ring-muted-foreground/20 focus-visible:ring-offset-1'
842881
)}
843882
>
844-
{copySuccess ? (
883+
{copySuccess.form ? (
845884
<Check className='h-3.5 w-3.5 text-foreground' />
846885
) : (
847886
<Copy className='h-3.5 w-3.5 transition-transform duration-200 group-hover:scale-110' />
@@ -1060,15 +1099,13 @@ export function WebhookSettings({ workflowId, open, onOpenChange }: WebhookSetti
10601099
isCreating ||
10611100
!newWebhook.url ||
10621101
newWebhook.levelFilter.length === 0 ||
1063-
newWebhook.triggerFilter.length === 0
1102+
newWebhook.triggerFilter.length === 0 ||
1103+
(!!editingWebhookId && !hasChanges())
10641104
}
10651105
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'
10661106
>
10671107
{isCreating ? (
1068-
<>
1069-
<RefreshCw className='mr-2 h-4 w-4 animate-spin' />
1070-
{editingWebhookId ? 'Updating...' : 'Creating...'}
1071-
</>
1108+
<>{editingWebhookId ? 'Updating...' : 'Creating...'}</>
10721109
) : (
10731110
<>{editingWebhookId ? 'Update Webhook' : 'Create Webhook'}</>
10741111
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,23 @@ export function McpToolSelector({
5454

5555
const selectedTool = availableTools.find((tool) => tool.id === selectedToolId)
5656

57+
useEffect(() => {
58+
if (serverValue && selectedToolId && !selectedTool && availableTools.length === 0) {
59+
refreshTools()
60+
}
61+
}, [serverValue, selectedToolId, selectedTool, availableTools.length, refreshTools])
62+
5763
useEffect(() => {
5864
if (
5965
storeValue &&
6066
availableTools.length > 0 &&
6167
!availableTools.find((tool) => tool.id === storeValue)
6268
) {
63-
if (!isPreview) {
69+
if (!isPreview && !disabled) {
6470
setStoreValue('')
6571
}
6672
}
67-
}, [serverValue, availableTools, storeValue, setStoreValue, isPreview])
73+
}, [serverValue, availableTools, storeValue, setStoreValue, isPreview, disabled])
6874

6975
const handleOpenChange = (isOpen: boolean) => {
7076
setOpen(isOpen)

0 commit comments

Comments
 (0)