Skip to content

Commit 6551355

Browse files
authored
feat(settings): show Slack workspace connection status (#2200)
1 parent 26a94a4 commit 6551355

2 files changed

Lines changed: 80 additions & 9 deletions

File tree

apps/code/src/renderer/features/integrations/stores/integrationStore.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ interface IntegrationStore {
2626
interface IntegrationSelectors {
2727
githubIntegrations: Integration[];
2828
hasGithubIntegration: boolean;
29+
slackIntegrations: Integration[];
30+
hasSlackIntegration: boolean;
2931
}
3032

3133
export const useIntegrationStore = create<IntegrationStore>((set) => ({
@@ -36,9 +38,12 @@ export const useIntegrationStore = create<IntegrationStore>((set) => ({
3638
export const useIntegrationSelectors = (): IntegrationSelectors => {
3739
const integrations = useIntegrationStore((state) => state.integrations);
3840
const githubIntegrations = integrations.filter((i) => i.kind === "github");
41+
const slackIntegrations = integrations.filter((i) => i.kind === "slack");
3942

4043
return {
4144
githubIntegrations,
4245
hasGithubIntegration: githubIntegrations.length > 0,
46+
slackIntegrations,
47+
hasSlackIntegration: slackIntegrations.length > 0,
4348
};
4449
};

apps/code/src/renderer/features/settings/components/sections/SlackSettings.tsx

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import { useAuthStateValue } from "@features/auth/hooks/authQueries";
2-
import { ArrowSquareOutIcon } from "@phosphor-icons/react";
3-
import { Button, Flex, Text, Tooltip } from "@radix-ui/themes";
2+
import {
3+
type Integration,
4+
useIntegrationSelectors,
5+
} from "@features/integrations/stores/integrationStore";
6+
import { useIntegrations } from "@hooks/useIntegrations";
7+
import { ArrowSquareOutIcon, SlackLogoIcon } from "@phosphor-icons/react";
8+
import { Box, Button, Flex, Spinner, Text, Tooltip } from "@radix-ui/themes";
9+
import { formatRelativeTimeLong } from "@renderer/utils/time";
410
import { openUrlInBrowser } from "@utils/browser";
511
import { getPostHogUrl } from "@utils/urls";
612

713
export function SlackSettings() {
814
const projectId = useAuthStateValue((s) => s.projectId);
915
const cloudRegion = useAuthStateValue((s) => s.cloudRegion);
16+
const { isLoading } = useIntegrations();
17+
const { slackIntegrations, hasSlackIntegration } = useIntegrationSelectors();
1018

1119
const slackSettingsUrl = projectId
1220
? getPostHogUrl(
@@ -15,7 +23,7 @@ export function SlackSettings() {
1523
)
1624
: null;
1725

18-
const button = (
26+
const manageButton = (
1927
<Button
2028
size="1"
2129
disabled={!slackSettingsUrl}
@@ -28,19 +36,77 @@ export function SlackSettings() {
2836
</Button>
2937
);
3038

39+
const manageButtonWithTooltip = slackSettingsUrl ? (
40+
manageButton
41+
) : (
42+
<Tooltip content="Sign in to a PostHog project to manage the Slack integration">
43+
{manageButton}
44+
</Tooltip>
45+
);
46+
3147
return (
3248
<Flex direction="column" gap="3">
3349
<Text className="text-(--gray-11) text-[13px]">
3450
Connect Slack to PostHog Code to kick off tasks like pull requests
3551
directly from Slack.
3652
</Text>
37-
<Flex>
38-
{slackSettingsUrl ? (
39-
button
53+
54+
<Flex direction="column" className="border-(--gray-5) border-t">
55+
{isLoading ? (
56+
<Flex align="center" gap="2" py="4">
57+
<Spinner size="1" />
58+
<Text className="text-(--gray-11) text-[13px]">Loading…</Text>
59+
</Flex>
60+
) : hasSlackIntegration ? (
61+
slackIntegrations.map((integration) => (
62+
<SlackIntegrationRow
63+
key={integration.id}
64+
integration={integration}
65+
/>
66+
))
4067
) : (
41-
<Tooltip content="Sign in to a PostHog project to manage the Slack integration">
42-
{button}
43-
</Tooltip>
68+
<Flex align="center" gap="3" py="4">
69+
<Box className="shrink-0 text-(--gray-11)">
70+
<SlackLogoIcon size={20} />
71+
</Box>
72+
<Text className="text-(--gray-11) text-[13px]">
73+
No Slack workspace connected yet.
74+
</Text>
75+
</Flex>
76+
)}
77+
</Flex>
78+
79+
<Flex>{manageButtonWithTooltip}</Flex>
80+
</Flex>
81+
);
82+
}
83+
84+
interface SlackIntegrationRowProps {
85+
integration: Integration;
86+
}
87+
88+
function SlackIntegrationRow({ integration }: SlackIntegrationRowProps) {
89+
const rawDisplayName = integration.display_name;
90+
const workspaceName =
91+
(typeof rawDisplayName === "string" && rawDisplayName.trim()) ||
92+
"Slack workspace";
93+
const createdAt =
94+
typeof integration.created_at === "string" ? integration.created_at : null;
95+
96+
return (
97+
<Flex align="start" gap="3" py="3" className="border-(--gray-5) border-b">
98+
<Box className="shrink-0 text-(--gray-11)">
99+
<SlackLogoIcon size={28} />
100+
</Box>
101+
<Flex direction="column" gap="1" className="min-w-0">
102+
<Text className="text-(--gray-12) text-sm">
103+
<Text className="font-medium">Connected</Text> to{" "}
104+
<Text className="font-medium">{workspaceName}</Text>
105+
</Text>
106+
{createdAt && (
107+
<Text className="text-(--gray-11) text-[13px]">
108+
Created {formatRelativeTimeLong(createdAt)}
109+
</Text>
44110
)}
45111
</Flex>
46112
</Flex>

0 commit comments

Comments
 (0)