From 7b6d686e85c66e39f310dfb65cb3e651a04c3811 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Wed, 22 Jul 2026 12:35:47 -0400 Subject: [PATCH 1/4] fix: support Slack workspaces in loop notifications Generated-By: PostHog Code Task-Id: e2b452a2-5abd-4732-82bd-2e380f30a465 --- .../components/LoopNotificationsFields.tsx | 66 +++++++++++------ .../components/SlackChannelCombobox.test.tsx | 73 +++++++++++++++++++ .../components/SlackChannelCombobox.tsx | 28 +++++-- .../components/SlackWorkspaceSelect.tsx | 39 ++++++++++ .../SlackInboxNotificationsSettings.tsx | 45 ++++-------- 5 files changed, 189 insertions(+), 62 deletions(-) create mode 100644 packages/ui/src/features/settings/components/SlackChannelCombobox.test.tsx create mode 100644 packages/ui/src/features/settings/components/SlackWorkspaceSelect.tsx diff --git a/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx b/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx index b880ab234b..54404f6c14 100644 --- a/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx +++ b/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx @@ -8,6 +8,7 @@ import { Switch } from "@posthog/quill"; import { useIntegrationSelectors } from "@posthog/ui/features/integrations/store"; import { useSlackConnect } from "@posthog/ui/features/integrations/useSlackConnect"; import { SlackChannelCombobox } from "@posthog/ui/features/settings/components/SlackChannelCombobox"; +import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; import { Button } from "@posthog/ui/primitives/Button"; import { Checkbox, Flex, Text } from "@radix-ui/themes"; @@ -211,29 +212,48 @@ function SlackNotificationRow({ : "Connect Slack workspace"} ) : ( - { - if (!target || !integrationId) { - const next: SlackChannelParams = { ...params }; - delete next.channel_id; - delete next.channel_name; - onChange({ params: next }); - return; - } - onChange({ - params: { - integration_id: integrationId, - channel_id: parseChannelIdFromTargetValue(target), - channel_name: parseChannelNameFromTargetValue(target), - }, - }); - }} - /> + + {slackIntegrations.length > 1 ? ( + { + const next: SlackChannelParams = { + ...params, + integration_id: nextIntegrationId, + }; + delete next.channel_id; + delete next.channel_name; + onChange({ params: next }); + }} + /> + ) : null} + { + if (!target || !integrationId) { + const next: SlackChannelParams = { ...params }; + delete next.channel_id; + delete next.channel_name; + onChange({ params: next }); + return; + } + onChange({ + params: { + integration_id: integrationId, + channel_id: parseChannelIdFromTargetValue(target), + channel_name: parseChannelNameFromTargetValue(target), + }, + }); + }} + /> + )} ); diff --git a/packages/ui/src/features/settings/components/SlackChannelCombobox.test.tsx b/packages/ui/src/features/settings/components/SlackChannelCombobox.test.tsx new file mode 100644 index 0000000000..1be1419173 --- /dev/null +++ b/packages/ui/src/features/settings/components/SlackChannelCombobox.test.tsx @@ -0,0 +1,73 @@ +import type { SlackChannelOption } from "@posthog/shared/domain-types"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { useState } from "react"; +import { describe, expect, it, vi } from "vitest"; +import { SlackChannelCombobox } from "./SlackChannelCombobox"; + +const generalChannel: SlackChannelOption = { + id: "C_GENERAL", + name: "general", + is_private: false, + is_member: true, + is_ext_shared: false, + is_private_without_access: false, +}; + +const analyticsChannel: SlackChannelOption = { + ...generalChannel, + id: "C_ANALYTICS", + name: "analytics-platform", +}; + +vi.mock("@posthog/ui/features/inbox/hooks/useSlackChannels", () => ({ + useSlackChannels: ( + _integrationId: number, + options?: { search?: string }, + ) => ({ + data: { + channels: options?.search ? [analyticsChannel] : [generalChannel], + }, + isFetching: false, + }), +})); + +vi.mock("@posthog/ui/primitives/hooks/useDebouncedValue", () => ({ + useDebouncedValue: (value: T) => ({ + debounced: value, + isPending: false, + }), +})); + +describe("SlackChannelCombobox", () => { + it("updates server search results without replacing the focused input", async () => { + const user = userEvent.setup(); + + function TestPicker() { + const [value, setValue] = useState(null); + return ( + + ); + } + + render(); + + const trigger = screen.getByRole("combobox", { name: "Slack channel" }); + trigger.focus(); + await user.keyboard("{ArrowDown}"); + expect(screen.getByText("general")).toBeInTheDocument(); + expect(screen.queryByText("No channel selected")).not.toBeInTheDocument(); + + const input = screen.getByPlaceholderText("Search channels…"); + await user.type(input, "analytics"); + + expect(input).toHaveFocus(); + expect(screen.getByText("analytics-platform")).toBeInTheDocument(); + expect(screen.queryByText("general")).not.toBeInTheDocument(); + }); +}); diff --git a/packages/ui/src/features/settings/components/SlackChannelCombobox.tsx b/packages/ui/src/features/settings/components/SlackChannelCombobox.tsx index 957d54a55c..32660e61ab 100644 --- a/packages/ui/src/features/settings/components/SlackChannelCombobox.tsx +++ b/packages/ui/src/features/settings/components/SlackChannelCombobox.tsx @@ -31,8 +31,8 @@ interface SlackChannelComboboxProps { value: string | null; /** Fires with a new target, or null when "off" is chosen. */ onChange: (channelTarget: string | null) => void; - /** Label for the "off" item, e.g. "Off — don't notify me" or "No default channel". */ - offLabel: string; + /** When set, includes an option that clears the selected channel. */ + offLabel?: string; ariaLabel: string; modal?: boolean; disabled?: boolean; @@ -59,8 +59,8 @@ export function SlackChannelCombobox({ const { data: channelsData, isFetching } = useSlackChannels(integrationId, { search: debouncedSearch || undefined, - enabled: open, }); + const initialLoading = !!integrationId && !channelsData && isFetching; const searchPending = open && (isFetching || searchDebouncing); const visibleChannels = useMemo( @@ -74,17 +74,27 @@ export function SlackChannelCombobox({ ); const comboboxItems = useMemo( - () => [OFF_VALUE, ...visibleChannels.map((c) => c.id)], - [visibleChannels], + () => [ + ...(offLabel ? [OFF_VALUE] : []), + ...visibleChannels.map((c) => c.id), + ], + [offLabel, visibleChannels], ); - const triggerLabel = (() => { - if (searchPending && !hasChannel) return "Loading channels…"; + if ((initialLoading || searchPending) && !hasChannel) { + return "Loading channels…"; + } if (selectedChannelName) return selectedChannelName; if (selectedChannelId) return selectedChannelId; return "Pick a channel"; })(); + const comboboxValue = (() => { + if (hasChannel && selectedChannelId) return selectedChannelId; + if (offLabel) return OFF_VALUE; + return null; + })(); + const onComboboxChange = (rawValue: string | null) => { setOpen(false); setSearchQuery(""); @@ -110,6 +120,7 @@ export function SlackChannelCombobox({ {(itemValue: string) => { if (itemValue === OFF_VALUE) { + if (!offLabel) return null; return ( {offLabel} @@ -151,7 +162,7 @@ export function SlackChannelCombobox({ onComboboxChange(v as string | null)} open={open} onOpenChange={(next) => { @@ -170,6 +181,7 @@ export function SlackChannelCombobox({ size="sm" disabled={disabled || !integrationId} aria-label={ariaLabel} + aria-busy={initialLoading || searchPending} className={`${CONTROL_CLASS} justify-between`} > diff --git a/packages/ui/src/features/settings/components/SlackWorkspaceSelect.tsx b/packages/ui/src/features/settings/components/SlackWorkspaceSelect.tsx new file mode 100644 index 0000000000..d4e98a068f --- /dev/null +++ b/packages/ui/src/features/settings/components/SlackWorkspaceSelect.tsx @@ -0,0 +1,39 @@ +import type { Integration } from "@posthog/core/integrations/selectors"; +import { getSlackIntegrationLabel } from "@posthog/core/settings/slackNotificationTarget"; +import { SettingsOptionSelect } from "@posthog/ui/features/settings/SettingsOptionSelect"; + +interface SlackWorkspaceSelectProps { + integrations: Integration[]; + value: number | null; + onValueChange: (integrationId: number) => void; + disabled?: boolean; + className?: string; +} + +export function SlackWorkspaceSelect({ + integrations, + value, + onValueChange, + disabled, + className, +}: SlackWorkspaceSelectProps) { + return ( + ({ + value: String(integration.id), + label: getSlackIntegrationLabel(integration), + }))} + ariaLabel="Slack workspace" + placeholder="Select workspace" + disabled={disabled} + className={className} + onValueChange={(nextValue) => { + const integrationId = Number(nextValue); + if (Number.isFinite(integrationId)) { + onValueChange(integrationId); + } + }} + /> + ); +} diff --git a/packages/ui/src/features/settings/sections/SlackInboxNotificationsSettings.tsx b/packages/ui/src/features/settings/sections/SlackInboxNotificationsSettings.tsx index d7e51fab51..42530da7bd 100644 --- a/packages/ui/src/features/settings/sections/SlackInboxNotificationsSettings.tsx +++ b/packages/ui/src/features/settings/sections/SlackInboxNotificationsSettings.tsx @@ -5,7 +5,7 @@ import { } from "@posthog/core/settings/slackNotificationTarget"; import { useSignalSourceManager } from "@posthog/ui/features/inbox/hooks/useSignalSourceManager"; import { useIntegrationSelectors } from "@posthog/ui/features/integrations/store"; -import { SettingsOptionSelect } from "@posthog/ui/features/settings/SettingsOptionSelect"; +import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; import { SignalDefaultChannelSettings } from "@posthog/ui/features/settings/sections/SignalDefaultChannelSettings"; import { SignalSlackNotificationsSettings } from "@posthog/ui/features/settings/sections/SignalSlackNotificationsSettings"; import { @@ -13,7 +13,6 @@ import { SlackWorkspaceConnectionCallouts, } from "@posthog/ui/features/settings/sections/SlackWorkspaceConnection"; import { Box, Flex, Text } from "@radix-ui/themes"; -import { useMemo } from "react"; const WORKSPACE_CONTROL_CLASS = "min-w-[160px] max-w-[240px]"; @@ -36,9 +35,8 @@ export function SlackInboxNotificationsSettings({ const { userAutonomyConfig, handleUpdateSlackNotifications } = useSignalSourceManager(); - // Workspace is shared by both the team default and the per-user channel. We - // default to the only workspace when there's a single one; otherwise the user - // picks (which also persists their personal notification integration). + // Personal notifications default to the only workspace when there's a single + // one; otherwise the user picks and persists their notification integration. const selectedIntegrationId = userAutonomyConfig?.slack_notification_integration_id ?? null; const effectiveIntegrationId = deriveEffectiveIntegrationId( @@ -46,18 +44,7 @@ export function SlackInboxNotificationsSettings({ slackIntegrations, ); - const integrationOptions = useMemo( - () => - slackIntegrations.map((integration) => ({ - value: String(integration.id), - label: getSlackIntegrationLabel(integration), - })), - [slackIntegrations], - ); - - const onIntegrationChange = (value: string) => { - const integrationId = Number(value); - if (!Number.isFinite(integrationId)) return; + const onIntegrationChange = (integrationId: number) => { // Switching workspaces clears the personal channel — the previously picked // channel won't exist in the new workspace. void handleUpdateSlackNotifications({ integrationId, channel: null }); @@ -90,19 +77,21 @@ export function SlackInboxNotificationsSettings({ + + {!isLoading && hasSlackIntegration ? ( - Workspace + Personal workspace {slackIntegrations.length > 1 ? ( - @@ -113,12 +102,6 @@ export function SlackInboxNotificationsSettings({ ) : null} ) : null} - - Date: Wed, 22 Jul 2026 12:42:55 -0400 Subject: [PATCH 2/4] fix: colocate personal Slack workspace setting Generated-By: PostHog Code Task-Id: e2b452a2-5abd-4732-82bd-2e380f30a465 --- .../SignalSlackNotificationsSettings.tsx | 28 +++++++++++++- .../SlackInboxNotificationsSettings.tsx | 38 ++----------------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx b/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx index 36d1af2bfe..db47f30eb5 100644 --- a/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx +++ b/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx @@ -1,9 +1,11 @@ +import { getSlackIntegrationLabel } from "@posthog/core/settings/slackNotificationTarget"; import { Button } from "@posthog/quill"; import type { SignalReportPriority } from "@posthog/shared/domain-types"; import { useSignalSourceManager } from "@posthog/ui/features/inbox/hooks/useSignalSourceManager"; import { useIntegrationSelectors } from "@posthog/ui/features/integrations/store"; import { useSlackConnect } from "@posthog/ui/features/integrations/useSlackConnect"; import { SlackChannelCombobox } from "@posthog/ui/features/settings/components/SlackChannelCombobox"; +import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; import { SettingsOptionSelect } from "@posthog/ui/features/settings/SettingsOptionSelect"; import { Box, Callout, Flex, Text } from "@radix-ui/themes"; @@ -24,7 +26,7 @@ const MIN_PRIORITY_OPTIONS: { const SETTINGS_CONTROL_CLASS = "min-w-[200px] max-w-[240px]"; interface SignalSlackNotificationsSettingsProps { - /** Workspace whose channels are listed — shared with the team default. */ + /** Workspace used for the current user's notifications. */ integrationId: number | null; channelComboboxModal?: boolean; isLoading?: boolean; @@ -44,7 +46,7 @@ export function SignalSlackNotificationsSettings({ const topBorderClass = showTopBorder ? "border-(--gray-5) border-t border-dashed pt-4" : ""; - const { hasSlackIntegration } = useIntegrationSelectors(); + const { hasSlackIntegration, slackIntegrations } = useIntegrationSelectors(); const { userAutonomyConfig, handleUpdateSlackNotifications } = useSignalSourceManager(); const slackConnect = useSlackConnect(); @@ -124,6 +126,13 @@ export function SignalSlackNotificationsSettings({ void handleUpdateSlackNotifications({ integrationId, channel }); }; + const onIntegrationChange = (nextIntegrationId: number) => { + void handleUpdateSlackNotifications({ + integrationId: nextIntegrationId, + channel: null, + }); + }; + const onMinPriorityChange = (value: string) => { void handleUpdateSlackNotifications({ minPriority: value === NOTIFY_ALL_VALUE ? null : value, @@ -143,6 +152,21 @@ export function SignalSlackNotificationsSettings({ + + Workspace + {slackIntegrations.length > 1 ? ( + + ) : slackIntegrations[0] ? ( + + {getSlackIntegrationLabel(slackIntegrations[0])} + + ) : null} + Channel { - // Switching workspaces clears the personal channel — the previously picked - // channel won't exist in the new workspace. - void handleUpdateSlackNotifications({ integrationId, channel: null }); - }; - const topBorderClass = showTopBorder ? "border-(--gray-5) border-t border-dashed pt-3" : ""; @@ -83,25 +70,6 @@ export function SlackInboxNotificationsSettings({ isLoading={isLoading} /> - {!isLoading && hasSlackIntegration ? ( - - - Personal workspace - - {slackIntegrations.length > 1 ? ( - - ) : slackIntegrations[0] ? ( - - {getSlackIntegrationLabel(slackIntegrations[0])} - - ) : null} - - ) : null} Date: Wed, 22 Jul 2026 12:46:16 -0400 Subject: [PATCH 3/4] refactor: extract Slack workspace channel picker Generated-By: PostHog Code Task-Id: e2b452a2-5abd-4732-82bd-2e380f30a465 --- .../components/LoopNotificationsFields.tsx | 77 ++++++++----------- .../SlackWorkspaceChannelPicker.tsx | 52 +++++++++++++ 2 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 packages/ui/src/features/settings/components/SlackWorkspaceChannelPicker.tsx diff --git a/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx b/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx index 54404f6c14..13e09c0088 100644 --- a/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx +++ b/packages/ui/src/features/loops/components/LoopNotificationsFields.tsx @@ -7,8 +7,7 @@ import { import { Switch } from "@posthog/quill"; import { useIntegrationSelectors } from "@posthog/ui/features/integrations/store"; import { useSlackConnect } from "@posthog/ui/features/integrations/useSlackConnect"; -import { SlackChannelCombobox } from "@posthog/ui/features/settings/components/SlackChannelCombobox"; -import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; +import { SlackWorkspaceChannelPicker } from "@posthog/ui/features/settings/components/SlackWorkspaceChannelPicker"; import { Button } from "@posthog/ui/primitives/Button"; import { Checkbox, Flex, Text } from "@radix-ui/themes"; @@ -212,48 +211,38 @@ function SlackNotificationRow({ : "Connect Slack workspace"} ) : ( - - {slackIntegrations.length > 1 ? ( - { - const next: SlackChannelParams = { - ...params, - integration_id: nextIntegrationId, - }; - delete next.channel_id; - delete next.channel_name; - onChange({ params: next }); - }} - /> - ) : null} - { - if (!target || !integrationId) { - const next: SlackChannelParams = { ...params }; - delete next.channel_id; - delete next.channel_name; - onChange({ params: next }); - return; - } - onChange({ - params: { - integration_id: integrationId, - channel_id: parseChannelIdFromTargetValue(target), - channel_name: parseChannelNameFromTargetValue(target), - }, - }); - }} - /> - + { + const next: SlackChannelParams = { + ...params, + integration_id: nextIntegrationId, + }; + delete next.channel_id; + delete next.channel_name; + onChange({ params: next }); + }} + onChannelChange={(target) => { + if (!target || !integrationId) { + const next: SlackChannelParams = { ...params }; + delete next.channel_id; + delete next.channel_name; + onChange({ params: next }); + return; + } + onChange({ + params: { + integration_id: integrationId, + channel_id: parseChannelIdFromTargetValue(target), + channel_name: parseChannelNameFromTargetValue(target), + }, + }); + }} + /> )} ); diff --git a/packages/ui/src/features/settings/components/SlackWorkspaceChannelPicker.tsx b/packages/ui/src/features/settings/components/SlackWorkspaceChannelPicker.tsx new file mode 100644 index 0000000000..960e6accff --- /dev/null +++ b/packages/ui/src/features/settings/components/SlackWorkspaceChannelPicker.tsx @@ -0,0 +1,52 @@ +import type { Integration } from "@posthog/core/integrations/selectors"; +import { SlackChannelCombobox } from "@posthog/ui/features/settings/components/SlackChannelCombobox"; +import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; +import { Flex } from "@radix-ui/themes"; + +interface SlackWorkspaceChannelPickerProps { + integrations: Integration[]; + integrationId: number | null; + channelValue: string | null; + onIntegrationChange: (integrationId: number) => void; + onChannelChange: (channelTarget: string | null) => void; + channelAriaLabel: string; + offLabel?: string; + disabled?: boolean; + modal?: boolean; +} + +export function SlackWorkspaceChannelPicker({ + integrations, + integrationId, + channelValue, + onIntegrationChange, + onChannelChange, + channelAriaLabel, + offLabel, + disabled, + modal, +}: SlackWorkspaceChannelPickerProps) { + return ( + + {integrations.length > 1 ? ( + + ) : null} + + + ); +} From 84328e8df13e7579f3ab81f989b571b207318747 Mon Sep 17 00:00:00 2001 From: Matt Pua Date: Wed, 22 Jul 2026 12:54:46 -0400 Subject: [PATCH 4/4] fix: scope Slack workspace selection to loops Generated-By: PostHog Code Task-Id: e2b452a2-5abd-4732-82bd-2e380f30a465 --- .../SignalSlackNotificationsSettings.tsx | 28 +-------- .../SlackInboxNotificationsSettings.tsx | 63 ++++++++++++++++--- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx b/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx index db47f30eb5..36d1af2bfe 100644 --- a/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx +++ b/packages/ui/src/features/settings/sections/SignalSlackNotificationsSettings.tsx @@ -1,11 +1,9 @@ -import { getSlackIntegrationLabel } from "@posthog/core/settings/slackNotificationTarget"; import { Button } from "@posthog/quill"; import type { SignalReportPriority } from "@posthog/shared/domain-types"; import { useSignalSourceManager } from "@posthog/ui/features/inbox/hooks/useSignalSourceManager"; import { useIntegrationSelectors } from "@posthog/ui/features/integrations/store"; import { useSlackConnect } from "@posthog/ui/features/integrations/useSlackConnect"; import { SlackChannelCombobox } from "@posthog/ui/features/settings/components/SlackChannelCombobox"; -import { SlackWorkspaceSelect } from "@posthog/ui/features/settings/components/SlackWorkspaceSelect"; import { SettingsOptionSelect } from "@posthog/ui/features/settings/SettingsOptionSelect"; import { Box, Callout, Flex, Text } from "@radix-ui/themes"; @@ -26,7 +24,7 @@ const MIN_PRIORITY_OPTIONS: { const SETTINGS_CONTROL_CLASS = "min-w-[200px] max-w-[240px]"; interface SignalSlackNotificationsSettingsProps { - /** Workspace used for the current user's notifications. */ + /** Workspace whose channels are listed — shared with the team default. */ integrationId: number | null; channelComboboxModal?: boolean; isLoading?: boolean; @@ -46,7 +44,7 @@ export function SignalSlackNotificationsSettings({ const topBorderClass = showTopBorder ? "border-(--gray-5) border-t border-dashed pt-4" : ""; - const { hasSlackIntegration, slackIntegrations } = useIntegrationSelectors(); + const { hasSlackIntegration } = useIntegrationSelectors(); const { userAutonomyConfig, handleUpdateSlackNotifications } = useSignalSourceManager(); const slackConnect = useSlackConnect(); @@ -126,13 +124,6 @@ export function SignalSlackNotificationsSettings({ void handleUpdateSlackNotifications({ integrationId, channel }); }; - const onIntegrationChange = (nextIntegrationId: number) => { - void handleUpdateSlackNotifications({ - integrationId: nextIntegrationId, - channel: null, - }); - }; - const onMinPriorityChange = (value: string) => { void handleUpdateSlackNotifications({ minPriority: value === NOTIFY_ALL_VALUE ? null : value, @@ -152,21 +143,6 @@ export function SignalSlackNotificationsSettings({ - - Workspace - {slackIntegrations.length > 1 ? ( - - ) : slackIntegrations[0] ? ( - - {getSlackIntegrationLabel(slackIntegrations[0])} - - ) : null} - Channel + slackIntegrations.map((integration) => ({ + value: String(integration.id), + label: getSlackIntegrationLabel(integration), + })), + [slackIntegrations], + ); + + const onIntegrationChange = (value: string) => { + const integrationId = Number(value); + if (!Number.isFinite(integrationId)) return; + // Switching workspaces clears the personal channel — the previously picked + // channel won't exist in the new workspace. + void handleUpdateSlackNotifications({ integrationId, channel: null }); + }; + const topBorderClass = showTopBorder ? "border-(--gray-5) border-t border-dashed pt-3" : ""; @@ -64,12 +90,35 @@ export function SlackInboxNotificationsSettings({ + {!isLoading && hasSlackIntegration ? ( + + + Workspace + + {slackIntegrations.length > 1 ? ( + + ) : slackIntegrations[0] ? ( + + {getSlackIntegrationLabel(slackIntegrations[0])} + + ) : null} + + ) : null} + -