Skip to content

Commit 3be95ed

Browse files
committed
improvement(deploy): redesign workflow deployment experience
1 parent f43d6d5 commit 3be95ed

18 files changed

Lines changed: 1103 additions & 924 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx‎

Lines changed: 39 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
'use client'
22

33
import { useMemo, useState } from 'react'
4-
import {
5-
Button,
6-
ButtonGroup,
7-
ButtonGroupItem,
8-
Code,
9-
Combobox,
10-
Label,
11-
Skeleton,
12-
Tooltip,
13-
} from '@sim/emcn'
4+
import { Chip, ChipDropdown, ChipSwitch, Code, Label, Skeleton, Tooltip } from '@sim/emcn'
145
import { Check, Clipboard } from '@sim/emcn/icons'
156
import {
167
AGENT_STREAM_PROTOCOL_HEADER_LABEL,
@@ -55,6 +46,17 @@ const LANGUAGE_LABELS: Record<CodeLanguage, string> = {
5546
typescript: 'TypeScript',
5647
}
5748

49+
const LANGUAGE_OPTIONS = (Object.keys(LANGUAGE_LABELS) as CodeLanguage[]).map((value) => ({
50+
value,
51+
label: LANGUAGE_LABELS[value],
52+
}))
53+
54+
const ASYNC_EXAMPLE_OPTIONS = [
55+
{ label: 'Execute Job', value: 'execute' },
56+
{ label: 'Check Status', value: 'status' },
57+
{ label: 'Rate Limits', value: 'rate-limits' },
58+
] as const
59+
5860
const LANGUAGE_SYNTAX: Record<CodeLanguage, 'python' | 'javascript' | 'json'> = {
5961
curl: 'javascript',
6062
python: 'python',
@@ -414,19 +416,6 @@ console.log(limits);`
414416
}
415417
}
416418

417-
const getAsyncExampleTitle = () => {
418-
switch (asyncExampleType) {
419-
case 'execute':
420-
return 'Execute Job'
421-
case 'status':
422-
return 'Check Status'
423-
case 'rate-limits':
424-
return 'Rate Limits'
425-
default:
426-
return 'Execute Job'
427-
}
428-
}
429-
430419
const handleCopy = (key: keyof CopiedState, value: string) => {
431420
navigator.clipboard.writeText(value)
432421
setCopied((prev) => ({ ...prev, [key]: true }))
@@ -458,28 +447,26 @@ console.log(limits);`
458447
<div className='mb-[6.5px] flex items-center justify-between'>
459448
<Label className='block pl-0.5 text-[var(--text-primary)] text-small'>Language</Label>
460449
</div>
461-
<ButtonGroup value={language} onValueChange={(val) => setLanguage(val as CodeLanguage)}>
462-
{(Object.keys(LANGUAGE_LABELS) as CodeLanguage[]).map((lang) => (
463-
<ButtonGroupItem key={lang} value={lang}>
464-
{LANGUAGE_LABELS[lang]}
465-
</ButtonGroupItem>
466-
))}
467-
</ButtonGroup>
450+
<ChipSwitch
451+
value={language}
452+
onChange={setLanguage}
453+
options={LANGUAGE_OPTIONS}
454+
aria-label='Code language'
455+
size='compact'
456+
/>
468457
</div>
469458

470459
<div>
471460
<div className='mb-[6.5px] flex items-center justify-between'>
472461
<Label className='block pl-0.5 text-[var(--text-primary)] text-small'>Run workflow</Label>
473462
<Tooltip.Root>
474463
<Tooltip.Trigger asChild>
475-
<Button
476-
variant='ghost'
464+
<Chip
465+
leftIcon={copied.sync ? Check : Clipboard}
477466
onClick={() => handleCopy('sync', getSyncCommand())}
478-
aria-label='Copy command'
479-
className='!p-1.5 -my-1.5'
480-
>
481-
{copied.sync ? <Check className='size-3' /> : <Clipboard className='size-3' />}
482-
</Button>
467+
aria-label={copied.sync ? 'Command copied' : 'Copy command'}
468+
className='-my-1.5'
469+
/>
483470
</Tooltip.Trigger>
484471
<Tooltip.Content>
485472
<span>{copied.sync ? 'Copied' : 'Copy'}</span>
@@ -502,14 +489,12 @@ console.log(limits);`
502489
<div className='flex items-center gap-1.5'>
503490
<Tooltip.Root>
504491
<Tooltip.Trigger asChild>
505-
<Button
506-
variant='ghost'
492+
<Chip
493+
leftIcon={copied.stream ? Check : Clipboard}
507494
onClick={() => handleCopy('stream', getStreamCommand())}
508-
aria-label='Copy command'
509-
className='!p-1.5 -my-1.5'
510-
>
511-
{copied.stream ? <Check className='size-3' /> : <Clipboard className='size-3' />}
512-
</Button>
495+
aria-label={copied.stream ? 'Command copied' : 'Copy command'}
496+
className='-my-1.5'
497+
/>
513498
</Tooltip.Trigger>
514499
<Tooltip.Content>
515500
<span>{copied.stream ? 'Copied' : 'Copy'}</span>
@@ -522,6 +507,8 @@ console.log(limits);`
522507
placeholder='Select outputs'
523508
valueMode='label'
524509
align='end'
510+
size='md'
511+
className='w-[140px]'
525512
/>
526513
</div>
527514
</div>
@@ -541,31 +528,23 @@ console.log(limits);`
541528
<div className='flex items-center gap-1.5'>
542529
<Tooltip.Root>
543530
<Tooltip.Trigger asChild>
544-
<Button
545-
variant='ghost'
531+
<Chip
532+
leftIcon={copied.async ? Check : Clipboard}
546533
onClick={() => handleCopy('async', getAsyncCommand())}
547-
aria-label='Copy command'
548-
className='!p-1.5 -my-1.5'
549-
>
550-
{copied.async ? <Check className='size-3' /> : <Clipboard className='size-3' />}
551-
</Button>
534+
aria-label={copied.async ? 'Command copied' : 'Copy command'}
535+
className='-my-1.5'
536+
/>
552537
</Tooltip.Trigger>
553538
<Tooltip.Content>
554539
<span>{copied.async ? 'Copied' : 'Copy'}</span>
555540
</Tooltip.Content>
556541
</Tooltip.Root>
557-
<Combobox
558-
size='sm'
559-
className='!w-fit !py-0.5 min-w-[100px] rounded-md px-[9px]'
560-
options={[
561-
{ label: 'Execute Job', value: 'execute' },
562-
{ label: 'Check Status', value: 'status' },
563-
{ label: 'Rate Limits', value: 'rate-limits' },
564-
]}
542+
<ChipDropdown
543+
className='w-[140px]'
544+
options={ASYNC_EXAMPLE_OPTIONS}
565545
value={asyncExampleType}
566546
onChange={(value) => setAsyncExampleType(value as AsyncExampleType)}
567547
align='end'
568-
dropdownWidth={160}
569548
/>
570549
</div>
571550
</div>

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx‎

Lines changed: 52 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22

33
import { useEffect, useRef, useState } from 'react'
44
import {
5-
ButtonGroup,
6-
ButtonGroupItem,
75
ChipConfirmModal,
86
ChipEmailsInput,
97
ChipInput,
10-
cn,
11-
Input,
8+
ChipSwitch,
9+
ChipTextarea,
1210
Label,
1311
Loader,
1412
Skeleton,
15-
Switch,
16-
Textarea,
1713
Tooltip,
1814
} from '@sim/emcn'
1915
import { Check, TriangleAlert } from '@sim/emcn/icons'
@@ -47,6 +43,10 @@ import {
4743
const logger = createLogger('ChatDeploy')
4844

4945
const IDENTIFIER_PATTERN = /^[a-z0-9-]+$/
46+
const BOOLEAN_OPTIONS = [
47+
{ value: 'off', label: 'Off' },
48+
{ value: 'on', label: 'On' },
49+
] as const
5050

5151
interface ChatDeployProps {
5252
workflowId: string
@@ -402,11 +402,13 @@ export function ChatDeploy({
402402
Include thinking
403403
</Label>
404404
</div>
405-
<Switch
406-
checked={formData.includeThinking}
405+
<ChipSwitch
406+
value={formData.includeThinking ? 'on' : 'off'}
407+
options={BOOLEAN_OPTIONS}
407408
disabled={chatSubmitting}
408-
onCheckedChange={(checked) => updateField('includeThinking', checked)}
409+
onChange={(value) => updateField('includeThinking', value === 'on')}
409410
aria-label='Include thinking'
411+
size='compact'
410412
/>
411413
</div>
412414

@@ -416,11 +418,13 @@ export function ChatDeploy({
416418
Include tool calls
417419
</Label>
418420
</div>
419-
<Switch
420-
checked={formData.includeToolCalls}
421+
<ChipSwitch
422+
value={formData.includeToolCalls ? 'on' : 'off'}
423+
options={BOOLEAN_OPTIONS}
421424
disabled={chatSubmitting}
422-
onCheckedChange={(checked) => updateField('includeToolCalls', checked)}
425+
onChange={(value) => updateField('includeToolCalls', value === 'on')}
423426
aria-label='Include tool calls'
427+
size='compact'
424428
/>
425429
</div>
426430

@@ -446,7 +450,7 @@ export function ChatDeploy({
446450
>
447451
Welcome message
448452
</Label>
449-
<Textarea
453+
<ChipTextarea
450454
id='welcomeMessage'
451455
placeholder='Enter a welcome message for your chat'
452456
value={formData.welcomeMessage}
@@ -589,50 +593,35 @@ function IdentifierInput({
589593
>
590594
URL
591595
</Label>
592-
<div
593-
className={cn(
594-
'relative flex items-stretch overflow-hidden rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)]',
595-
error && 'border-[var(--text-error)]'
596-
)}
597-
>
598-
<div className='flex items-center whitespace-nowrap bg-[var(--surface-5)] pr-1.5 pl-2 text-[var(--text-secondary)] text-sm'>
599-
{getDomainPrefix()}
600-
</div>
601-
<div className='relative flex-1'>
602-
<Input
603-
id='chat-url'
604-
placeholder='my-chat'
605-
value={value}
606-
onChange={(e) => handleChange(e.target.value)}
607-
required
608-
disabled={disabled}
609-
className={cn(
610-
'rounded-none border-0 bg-transparent pl-0 shadow-none disabled:bg-transparent disabled:opacity-100',
611-
(isChecking || (isValid && value)) && 'pr-8'
612-
)}
613-
/>
614-
{isChecking ? (
615-
<div className='-translate-y-1/2 absolute top-1/2 right-2'>
616-
<Loader className='size-4 text-[var(--text-tertiary)]' animate />
617-
</div>
618-
) : (
619-
isValid &&
620-
value &&
621-
value !== originalIdentifier && (
622-
<Tooltip.Root>
623-
<Tooltip.Trigger asChild>
624-
<div className='-translate-y-1/2 absolute top-1/2 right-2'>
625-
<Check className='size-4 text-[var(--brand-accent)]' />
626-
</div>
627-
</Tooltip.Trigger>
628-
<Tooltip.Content>
629-
<span>Name is available</span>
630-
</Tooltip.Content>
631-
</Tooltip.Root>
632-
)
633-
)}
634-
</div>
635-
</div>
596+
<ChipInput
597+
id='chat-url'
598+
placeholder='my-chat'
599+
value={value}
600+
onChange={(event) => handleChange(event.target.value)}
601+
required
602+
disabled={disabled}
603+
error={Boolean(error)}
604+
aria-invalid={Boolean(error)}
605+
startAdornment={
606+
<span className='whitespace-nowrap text-[var(--text-muted)] text-sm'>
607+
{getDomainPrefix()}
608+
</span>
609+
}
610+
endAdornment={
611+
isChecking ? (
612+
<Loader className='size-[14px] shrink-0 text-[var(--text-icon)]' animate />
613+
) : isValid && value && value !== originalIdentifier ? (
614+
<Tooltip.Root>
615+
<Tooltip.Trigger asChild>
616+
<span className='shrink-0' aria-label='Name is available'>
617+
<Check className='size-[14px] text-[var(--brand-accent)]' />
618+
</span>
619+
</Tooltip.Trigger>
620+
<Tooltip.Content>Name is available</Tooltip.Content>
621+
</Tooltip.Root>
622+
) : null
623+
}
624+
/>
636625
{error && <p className='mt-[6.5px] text-[var(--text-error)] text-caption'>{error}</p>}
637626
<p className='mt-[6.5px] truncate text-[var(--text-secondary)] text-xs'>
638627
{isEditingExisting && value ? (
@@ -729,17 +718,14 @@ function AuthSelector({
729718
<Label className='mb-[6.5px] block pl-0.5 text-[var(--text-primary)] text-small'>
730719
Access control
731720
</Label>
732-
<ButtonGroup
721+
<ChipSwitch
733722
value={authType}
734-
onValueChange={(val) => onAuthTypeChange(val as AuthType)}
723+
onChange={onAuthTypeChange}
724+
options={authOptions.map((type) => ({ value: type, label: AUTH_LABELS[type] }))}
735725
disabled={disabled}
736-
>
737-
{authOptions.map((type) => (
738-
<ButtonGroupItem key={type} value={type}>
739-
{AUTH_LABELS[type]}
740-
</ButtonGroupItem>
741-
))}
742-
</ButtonGroup>
726+
aria-label='Access control'
727+
size='compact'
728+
/>
743729
</div>
744730

745731
{authType === 'password' && (

0 commit comments

Comments
 (0)