Skip to content

Commit 615ddfa

Browse files
bchapuisclaude
andcommitted
Improve Slack bot create dialog with multi-step webhook setup
Split the 2-step dialog into 3 steps aligned with the Discord bot creation UX: credentials → event subscriptions (webhook URL + setup instructions) → invite bot (OAuth scopes, channel invite, workflow trigger). Use CopyableValue component for webhook URL display. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 777ef9a commit 615ddfa

1 file changed

Lines changed: 74 additions & 44 deletions

File tree

apps/app/src/components/workflow/widgets/input/slack-bot-create-dialog.tsx

Lines changed: 74 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import Copy from "lucide-react/icons/copy";
21
import ExternalLink from "lucide-react/icons/external-link";
32
import { useState } from "react";
4-
import { toast } from "sonner";
53

64
import { useAuth } from "@/components/auth-context";
75
import { Button } from "@/components/ui/button";
@@ -17,18 +15,23 @@ import { Spinner } from "@/components/ui/spinner";
1715
import { getApiBaseUrl } from "@/config/api";
1816
import { createSlackBot } from "@/services/bot-service";
1917

20-
type Step = "credentials" | "setup";
18+
import { CopyableValue } from "./copyable-value";
19+
20+
type Step = "credentials" | "webhook" | "setup";
2121

2222
const STEP_TITLES: Record<Step, string> = {
2323
credentials: "Create a Slack Bot",
24-
setup: "Bot Created",
24+
webhook: "Event Subscriptions",
25+
setup: "Invite Bot",
2526
};
2627

2728
const STEP_DESCRIPTIONS: Record<Step, string> = {
2829
credentials:
2930
"Create a Slack app at api.slack.com/apps, then copy the Bot User OAuth Token and Signing Secret.",
31+
webhook:
32+
"Copy the webhook URL below and paste it as the Request URL in the Event Subscriptions page of your Slack app.",
3033
setup:
31-
"Your bot is ready. Configure the Event Subscriptions URL in your Slack app settings.",
34+
"Verify permissions, invite the bot to a channel, and create a workflow.",
3235
};
3336

3437
interface SlackBotCreateDialogProps {
@@ -83,7 +86,7 @@ export function SlackBotCreateDialog({
8386
(response.metadata as Record<string, string | undefined> | null)
8487
?.teamName ?? ""
8588
);
86-
setStep("setup");
89+
setStep("webhook");
8790
onCreated(response.id);
8891
} catch (err) {
8992
setError(err instanceof Error ? err.message : "Failed to create bot");
@@ -93,7 +96,7 @@ export function SlackBotCreateDialog({
9396
};
9497

9598
const webhookUrl = createdBotId
96-
? `${getApiBaseUrl()}/slack/webhook/${createdBotId}`
99+
? `${getApiBaseUrl().replace(/\/$/, "")}/slack/webhook/${createdBotId}`
97100
: "";
98101

99102
return (
@@ -206,7 +209,7 @@ export function SlackBotCreateDialog({
206209
</div>
207210
)}
208211

209-
{step === "setup" && (
212+
{step === "webhook" && (
210213
<div className="space-y-4">
211214
<div className="flex items-center gap-2 text-sm">
212215
<span className="text-xs px-2 py-0.5 bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400 rounded-md font-medium">
@@ -223,29 +226,12 @@ export function SlackBotCreateDialog({
223226
</span>
224227
</div>
225228

226-
<div className="space-y-2">
227-
<Label>Webhook URL</Label>
228-
<div className="flex items-center gap-2">
229-
<Input
230-
value={webhookUrl}
231-
readOnly
232-
className="font-mono text-xs"
233-
/>
234-
<Button
235-
variant="outline"
236-
size="icon"
237-
className="shrink-0"
238-
onClick={() => {
239-
navigator.clipboard.writeText(webhookUrl);
240-
toast.success("Webhook URL copied");
241-
}}
242-
>
243-
<Copy className="h-4 w-4" />
244-
</Button>
245-
</div>
246-
<ol className="text-xs text-muted-foreground list-decimal list-inside space-y-1.5 mt-2">
247-
<li>
248-
Go to{" "}
229+
<div className="space-y-2 text-sm">
230+
<div className="space-y-1">
231+
<p className="font-medium text-foreground">Request URL</p>
232+
<CopyableValue value={webhookUrl} />
233+
<p className="text-muted-foreground text-xs">
234+
Paste this as the Request URL in the{" "}
249235
<a
250236
href="https://api.slack.com/apps"
251237
target="_blank"
@@ -255,18 +241,24 @@ export function SlackBotCreateDialog({
255241
Event Subscriptions
256242
<ExternalLink className="w-2.5 h-2.5" />
257243
</a>{" "}
258-
in your Slack app and toggle{" "}
244+
page of your Slack app. Slack will verify it automatically.
245+
</p>
246+
</div>
247+
248+
<ol className="text-xs text-muted-foreground list-decimal list-inside space-y-1.5 mt-3">
249+
<li>
250+
Go to{" "}
251+
<span className="font-medium text-foreground">
252+
Event Subscriptions
253+
</span>{" "}
254+
and toggle{" "}
259255
<span className="font-medium text-foreground">
260256
Enable Events
261257
</span>{" "}
262258
to On.
263259
</li>
264260
<li>
265-
Paste the Webhook URL above as the{" "}
266-
<span className="font-medium text-foreground">
267-
Request URL
268-
</span>
269-
. Slack will verify it automatically.
261+
Paste the Request URL above and wait for Slack to verify it.
270262
</li>
271263
<li>
272264
Under{" "}
@@ -276,17 +268,55 @@ export function SlackBotCreateDialog({
276268
, add <span className="font-mono">message.channels</span> and{" "}
277269
<span className="font-mono">message.groups</span>, then save.
278270
</li>
279-
<li>
280-
Invite the bot to a channel and create a workflow with a{" "}
281-
<span className="font-medium text-foreground">
282-
Receive Slack Message
283-
</span>{" "}
284-
trigger.
285-
</li>
286271
</ol>
287272
</div>
288273

289274
<div className="flex justify-end">
275+
<Button onClick={() => setStep("setup")}>Next</Button>
276+
</div>
277+
</div>
278+
)}
279+
280+
{step === "setup" && (
281+
<div className="space-y-4">
282+
<ol className="text-xs text-muted-foreground list-decimal list-inside space-y-1.5">
283+
<li>
284+
Under{" "}
285+
<a
286+
href="https://api.slack.com/apps"
287+
target="_blank"
288+
rel="noopener noreferrer"
289+
className="text-primary hover:underline inline-flex items-center gap-0.5"
290+
>
291+
OAuth &amp; Permissions
292+
<ExternalLink className="w-2.5 h-2.5" />
293+
</a>
294+
, verify the bot has{" "}
295+
<span className="font-mono">channels:history</span>,{" "}
296+
<span className="font-mono">groups:history</span>, and{" "}
297+
<span className="font-mono">chat:write</span> scopes.
298+
</li>
299+
<li>
300+
Invite the bot to a channel with{" "}
301+
<span className="font-mono">/invite @{name || "botname"}</span>.
302+
</li>
303+
<li>
304+
Create a workflow with a{" "}
305+
<span className="font-medium text-foreground">
306+
Receive Slack Message
307+
</span>{" "}
308+
trigger.
309+
</li>
310+
</ol>
311+
312+
<div className="flex justify-end gap-2 pt-1">
313+
<Button
314+
type="button"
315+
variant="outline"
316+
onClick={() => setStep("webhook")}
317+
>
318+
Back
319+
</Button>
290320
<Button onClick={handleClose}>Done</Button>
291321
</div>
292322
</div>

0 commit comments

Comments
 (0)