Skip to content

Commit 1563cbc

Browse files
clean up modal
1 parent c1d3db2 commit 1563cbc

2 files changed

Lines changed: 146 additions & 105 deletions

File tree

  • apps/sim
    • app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard
    • triggers/slack

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard/slack-setup-wizard.tsx

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

3-
import { useCallback, useMemo, useState } from 'react'
4-
import { Check, Clipboard, Info } from 'lucide-react'
3+
import { type ReactNode, useCallback, useMemo, useState } from 'react'
4+
import { Check, ChevronRight, Clipboard, Info } from 'lucide-react'
55
import { useShallow } from 'zustand/react/shallow'
66
import {
77
Button,
@@ -38,7 +38,6 @@ const GROUP_ORDER: readonly SlackCapabilityGroup[] = ['trigger', 'action'] as co
3838

3939
const STEP_TITLES = [
4040
'Configure your bot',
41-
'Copy your manifest',
4241
'Create the app in Slack',
4342
'Paste your Signing Secret',
4443
'Install and paste your Bot Token',
@@ -47,6 +46,8 @@ const STEP_TITLES = [
4746

4847
const STEP_COUNT = STEP_TITLES.length
4948

49+
const MODAL_HEIGHT_CLASS = 'h-[580px]'
50+
5051
interface SlackSetupWizardProps {
5152
blockId: string
5253
isPreview?: boolean
@@ -58,31 +59,35 @@ interface SlackSetupWizardProps {
5859
*
5960
* @remarks
6061
* The panel renders a single launcher button. The wizard lives in a modal
61-
* that walks the user through: configuring the bot, copying the manifest,
62-
* creating the app in Slack, pasting the Signing Secret, and pasting the
63-
* Bot Token. Credentials are written directly into the sibling
64-
* `signingSecret` and `botToken` sub-blocks via the shared sub-block store,
65-
* so those fields in the panel are populated by the time the user clicks
66-
* Done.
62+
* with a fixed-height body so navigating between steps doesn't resize the
63+
* dialog. Credentials are written directly into the sibling `signingSecret`
64+
* and `botToken` sub-blocks via the shared sub-block store, so those fields
65+
* in the panel are populated by the time the user clicks Done.
6766
*/
6867
export function SlackSetupWizard({
6968
blockId,
7069
isPreview = false,
7170
disabled = false,
7271
}: SlackSetupWizardProps) {
7372
const [open, setOpen] = useState<boolean>(false)
73+
const launcherDisabled = isPreview || disabled
7474

7575
return (
7676
<>
77-
<Button
77+
<button
7878
type='button'
79-
variant='primary'
8079
onClick={() => setOpen(true)}
81-
disabled={isPreview || disabled}
82-
className='w-full'
80+
disabled={launcherDisabled}
81+
className={cn(
82+
'flex w-full items-center justify-between rounded-md border border-[var(--border-muted)] bg-[var(--surface-1)] px-3 py-2 text-left transition-colors',
83+
launcherDisabled
84+
? 'cursor-not-allowed opacity-70'
85+
: 'cursor-pointer hover-hover:bg-[var(--surface-hover)]'
86+
)}
8387
>
84-
Set up Slack app
85-
</Button>
88+
<span className='font-medium text-[var(--text-primary)] text-sm'>Setup Slack App</span>
89+
<ChevronRight className='h-4 w-4 text-[var(--text-muted)]' />
90+
</button>
8691

8792
<WizardModal
8893
blockId={blockId}
@@ -153,7 +158,7 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
153158

154159
return (
155160
<Modal open={open} onOpenChange={handleOpenChange}>
156-
<ModalContent size='lg'>
161+
<ModalContent size='lg' className={MODAL_HEIGHT_CLASS}>
157162
<ModalHeader>
158163
<div className='flex items-baseline justify-between gap-3'>
159164
<span>{STEP_TITLES[step]}</span>
@@ -163,9 +168,9 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
163168
</div>
164169
</ModalHeader>
165170

166-
<StepProgress current={step} total={STEP_COUNT} />
171+
<StepProgress current={step} />
167172

168-
<ModalBody className='min-h-[280px]'>
173+
<ModalBody>
169174
{step === 0 && (
170175
<StepConfigure
171176
blockId={blockId}
@@ -177,9 +182,8 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
177182
disabled={controlsDisabled}
178183
/>
179184
)}
180-
{step === 1 && <StepCopy manifestJson={manifestJson} canCopy={canCopy} />}
181-
{step === 2 && <StepCreate />}
182-
{step === 3 && (
185+
{step === 1 && <StepCreate manifestJson={manifestJson} canCopy={canCopy} />}
186+
{step === 2 && (
183187
<StepSecret
184188
blockId={blockId}
185189
value={signingSecret ?? ''}
@@ -189,7 +193,7 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
189193
disabled={controlsDisabled}
190194
/>
191195
)}
192-
{step === 4 && (
196+
{step === 3 && (
193197
<StepToken
194198
blockId={blockId}
195199
value={botToken ?? ''}
@@ -199,7 +203,7 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
199203
disabled={controlsDisabled}
200204
/>
201205
)}
202-
{step === 5 && (
206+
{step === 4 && (
203207
<StepDone hasSigningSecret={Boolean(signingSecret)} hasBotToken={Boolean(botToken)} />
204208
)}
205209
</ModalBody>
@@ -225,10 +229,9 @@ function WizardModal({ blockId, open, onOpenChange, isPreview, disabled }: Wizar
225229

226230
interface StepProgressProps {
227231
current: number
228-
total: number
229232
}
230233

231-
function StepProgress({ current, total: _total }: StepProgressProps) {
234+
function StepProgress({ current }: StepProgressProps) {
232235
return (
233236
<div className='flex gap-1.5 px-6 pb-4'>
234237
{STEP_TITLES.map((title, i) => (
@@ -244,6 +247,30 @@ function StepProgress({ current, total: _total }: StepProgressProps) {
244247
)
245248
}
246249

250+
interface SubStepListProps {
251+
children: ReactNode
252+
}
253+
254+
function SubStepList({ children }: SubStepListProps) {
255+
return <ol className='space-y-2.5'>{children}</ol>
256+
}
257+
258+
interface SubStepProps {
259+
n: number
260+
children: ReactNode
261+
}
262+
263+
function SubStep({ n, children }: SubStepProps) {
264+
return (
265+
<li className='flex gap-2.5'>
266+
<span className='mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[var(--surface-5)] font-medium text-[var(--text-secondary)] text-xs tabular-nums'>
267+
{n}
268+
</span>
269+
<div className='flex-1 text-[var(--text-secondary)] text-sm leading-relaxed'>{children}</div>
270+
</li>
271+
)
272+
}
273+
247274
interface StepConfigureProps {
248275
blockId: string
249276
appName: string
@@ -281,7 +308,7 @@ function StepConfigure({
281308
className='h-9 text-sm'
282309
/>
283310
</div>
284-
<div className='space-y-3'>
311+
<div className='grid grid-cols-2 gap-x-4 gap-y-4'>
285312
{GROUP_ORDER.map((group) => {
286313
const items = SLACK_CAPABILITIES.filter((c) => c.group === group)
287314
if (items.length === 0) return null
@@ -301,12 +328,12 @@ function StepConfigure({
301328
)
302329
}
303330

304-
interface StepCopyProps {
331+
interface StepCreateProps {
305332
manifestJson: string
306333
canCopy: boolean
307334
}
308335

309-
function StepCopy({ manifestJson, canCopy }: StepCopyProps) {
336+
function StepCreate({ manifestJson, canCopy }: StepCreateProps) {
310337
const [copied, setCopied] = useState<boolean>(false)
311338

312339
const handleCopy = useCallback(() => {
@@ -317,52 +344,52 @@ function StepCopy({ manifestJson, canCopy }: StepCopyProps) {
317344
}, [canCopy, manifestJson])
318345

319346
return (
320-
<div className='space-y-3'>
321-
<p className='text-[var(--text-secondary)] text-sm leading-relaxed'>
322-
Copy the generated manifest. You'll paste it into Slack in the next step.
323-
</p>
324-
<button
325-
type='button'
326-
onClick={handleCopy}
327-
disabled={!canCopy}
328-
className={cn(
329-
'flex w-full items-center justify-between rounded-md border border-[var(--border-muted)] bg-[var(--surface-1)] px-3 py-2 text-left transition-colors',
330-
canCopy
331-
? 'cursor-pointer hover-hover:bg-[var(--surface-hover)]'
332-
: 'cursor-not-allowed opacity-70'
333-
)}
334-
>
335-
<span className='font-medium text-[var(--text-secondary)] text-sm'>
336-
{canCopy ? 'Click to copy manifest' : 'Deploy once to lock in the webhook URL'}
337-
</span>
338-
{canCopy &&
339-
(copied ? (
340-
<Check className='h-3 w-3 text-[var(--text-success)]' />
341-
) : (
342-
<Clipboard className='h-3 w-3 text-[var(--text-muted)]' />
343-
))}
344-
</button>
345-
</div>
346-
)
347-
}
348-
349-
function StepCreate() {
350-
return (
351-
<div className='space-y-3 text-[var(--text-secondary)] text-sm leading-relaxed'>
352-
<p>
353-
Open the{' '}
354-
<a
355-
href='https://api.slack.com/apps'
356-
target='_blank'
357-
rel='noopener noreferrer'
358-
className='text-[var(--brand-secondary)] underline underline-offset-2'
359-
>
360-
Slack Apps page
361-
</a>
362-
, click <strong>Create New App</strong><strong>From a manifest</strong>, pick your
363-
workspace, paste the manifest, and click <strong>Create</strong>.
364-
</p>
365-
<p>Leave that Slack tab open — you'll pull a couple of values out of it in the next steps.</p>
347+
<div className='space-y-4'>
348+
<SubStepList>
349+
<SubStep n={1}>
350+
<div>Copy your manifest:</div>
351+
<button
352+
type='button'
353+
onClick={handleCopy}
354+
disabled={!canCopy}
355+
className={cn(
356+
'mt-2 inline-flex items-center gap-2 rounded-md border border-[var(--border-muted)] bg-[var(--surface-1)] px-3 py-1.5 text-left transition-colors',
357+
canCopy
358+
? 'cursor-pointer hover-hover:bg-[var(--surface-hover)]'
359+
: 'cursor-not-allowed opacity-70'
360+
)}
361+
>
362+
<span className='font-medium text-[var(--text-secondary)] text-sm'>
363+
{canCopy ? 'Click to copy manifest' : 'Deploy once to lock in the webhook URL'}
364+
</span>
365+
{canCopy &&
366+
(copied ? (
367+
<Check className='h-3 w-3 text-[var(--text-success)]' />
368+
) : (
369+
<Clipboard className='h-3 w-3 text-[var(--text-muted)]' />
370+
))}
371+
</button>
372+
</SubStep>
373+
<SubStep n={2}>
374+
Open the{' '}
375+
<a
376+
href='https://api.slack.com/apps'
377+
target='_blank'
378+
rel='noopener noreferrer'
379+
className='text-[var(--brand-secondary)] underline underline-offset-2'
380+
>
381+
Slack Apps page
382+
</a>
383+
.
384+
</SubStep>
385+
<SubStep n={3}>
386+
Click <strong>Create New App</strong><strong>From a manifest</strong> and pick your
387+
workspace.
388+
</SubStep>
389+
<SubStep n={4}>
390+
Paste your manifest, then click <strong>Next</strong> <strong>Create</strong>.
391+
</SubStep>
392+
</SubStepList>
366393
</div>
367394
)
368395
}
@@ -376,11 +403,16 @@ interface StepSecretProps {
376403

377404
function StepSecret({ blockId, value, onChange, disabled }: StepSecretProps) {
378405
return (
379-
<div className='space-y-3'>
380-
<p className='text-[var(--text-secondary)] text-sm leading-relaxed'>
381-
In your new Slack app, open <strong>Basic Information</strong>, find the{' '}
382-
<strong>Signing Secret</strong>, and paste it here.
383-
</p>
406+
<div className='space-y-4'>
407+
<SubStepList>
408+
<SubStep n={1}>
409+
In your new Slack app, open <strong>Basic Information</strong>.
410+
</SubStep>
411+
<SubStep n={2}>
412+
Find <strong>Signing Secret</strong> and click <strong>Show</strong>, then copy it.
413+
</SubStep>
414+
<SubStep n={3}>Paste it into the field below.</SubStep>
415+
</SubStepList>
384416
<div className='space-y-1.5'>
385417
<Label
386418
htmlFor={`${blockId}-wizard-signing-secret`}
@@ -411,12 +443,17 @@ interface StepTokenProps {
411443

412444
function StepToken({ blockId, value, onChange, disabled }: StepTokenProps) {
413445
return (
414-
<div className='space-y-3'>
415-
<p className='text-[var(--text-secondary)] text-sm leading-relaxed'>
416-
In Slack, open <strong>Install App</strong><strong>Install to Workspace</strong> and
417-
authorize. Then copy the <strong>Bot User OAuth Token</strong> (starts with{' '}
418-
<code>xoxb-</code>) and paste it here.
419-
</p>
446+
<div className='space-y-4'>
447+
<SubStepList>
448+
<SubStep n={1}>
449+
In Slack, open <strong>Install App</strong><strong>Install to Workspace</strong> and
450+
authorize.
451+
</SubStep>
452+
<SubStep n={2}>
453+
Copy the <strong>Bot User OAuth Token</strong> (starts with <code>xoxb-</code>).
454+
</SubStep>
455+
<SubStep n={3}>Paste it into the field below.</SubStep>
456+
</SubStepList>
420457
<div className='space-y-1.5'>
421458
<Label
422459
htmlFor={`${blockId}-wizard-bot-token`}
@@ -445,16 +482,20 @@ interface StepDoneProps {
445482

446483
function StepDone({ hasSigningSecret, hasBotToken }: StepDoneProps) {
447484
return (
448-
<div className='space-y-3 text-[var(--text-secondary)] text-sm leading-relaxed'>
449-
<p>
450-
Your Slack app is set up. The Signing Secret and Bot Token have been saved to the trigger —
451-
you can edit them anytime from the panel.
485+
<div className='space-y-4'>
486+
<p className='text-[var(--text-secondary)] text-sm leading-relaxed'>
487+
Your Slack app is set up. Save the workflow and Slack will verify the webhook URL
488+
automatically.
452489
</p>
453-
<ul className='space-y-1'>
454-
<StatusRow label='Signing Secret' ok={hasSigningSecret} />
455-
<StatusRow label='Bot Token' ok={hasBotToken} />
456-
</ul>
457-
<p>Save the workflow and Slack will verify the webhook URL automatically.</p>
490+
<SubStepList>
491+
<SubStep n={1}>
492+
<StatusRow label='Signing Secret' ok={hasSigningSecret} />
493+
</SubStep>
494+
<SubStep n={2}>
495+
<StatusRow label='Bot Token' ok={hasBotToken} />
496+
</SubStep>
497+
<SubStep n={3}>Click Done and save this workflow.</SubStep>
498+
</SubStepList>
458499
</div>
459500
)
460501
}
@@ -466,7 +507,7 @@ interface StatusRowProps {
466507

467508
function StatusRow({ label, ok }: StatusRowProps) {
468509
return (
469-
<li className='flex items-center gap-2'>
510+
<span className='flex items-center gap-2'>
470511
<Check
471512
className={cn(
472513
'h-[14px] w-[14px]',
@@ -475,9 +516,9 @@ function StatusRow({ label, ok }: StatusRowProps) {
475516
/>
476517
<span>
477518
{label}
478-
{!ok && <span className='ml-1 text-[var(--text-muted)]'>missing</span>}
519+
{!ok && <span className='ml-1 text-[var(--text-muted)]'>not saved yet</span>}
479520
</span>
480-
</li>
521+
</span>
481522
)
482523
}
483524

0 commit comments

Comments
 (0)