From 66cae400d673e7344a6ac0f85f17b2e97e93bb77 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 9 Jul 2025 11:46:27 -0600 Subject: [PATCH] initial slack build out (no apis) --- .../(permissions)/dataset-groups/page.tsx | 2 +- .../(permissions)/permission-groups/page.tsx | 2 +- .../settings/(permissions)/users/page.tsx | 2 +- .../(admin-only)/api-keys/page.tsx | 2 +- .../(admin-only)/datasources/page.tsx | 2 +- .../(admin-only)/integrations/page.tsx | 18 +++++++++ .../(admin-only)/security/page.tsx | 24 ++++++------ .../(restricted-width)/profile/page.tsx | 2 +- .../integrations/SlackIntegrations.tsx | 38 +++++++++++++++++++ .../security/ApprovedEmailDomains.tsx | 6 +-- .../features/security/InviteLinks.tsx | 4 +- .../security/WorkspaceRestrictions.tsx | 5 +-- .../SettingsCard.stories.tsx} | 10 ++--- .../SettingsCard.tsx} | 10 ++--- .../features/settings}/SettingsEmptyState.tsx | 0 .../features/settings}/SettingsPageHeader.tsx | 0 .../features/settings}/index.ts | 1 + .../features/sidebars/SidebarSettings.tsx | 5 +++ .../ui/icons/customIcons/SlackIcon.tsx | 34 +++++++++++++++++ 19 files changed, 130 insertions(+), 37 deletions(-) create mode 100644 apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/integrations/page.tsx create mode 100644 apps/web/src/components/features/integrations/SlackIntegrations.tsx rename apps/web/src/components/features/{security/SecurityCards.stories.tsx => settings/SettingsCard.stories.tsx} (97%) rename apps/web/src/components/features/{security/SecurityCards.tsx => settings/SettingsCard.tsx} (76%) rename apps/web/src/{app/app/(settings_layout)/settings/_components => components/features/settings}/SettingsEmptyState.tsx (100%) rename apps/web/src/{app/app/(settings_layout)/settings/_components => components/features/settings}/SettingsPageHeader.tsx (100%) rename apps/web/src/{app/app/(settings_layout)/settings/_components => components/features/settings}/index.ts (70%) create mode 100644 apps/web/src/components/ui/icons/customIcons/SlackIcon.tsx diff --git a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/dataset-groups/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/dataset-groups/page.tsx index 14e692697..3df86edd7 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/dataset-groups/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/dataset-groups/page.tsx @@ -7,7 +7,7 @@ import { Button } from '@/components/ui/buttons'; import { Plus } from '@/components/ui/icons'; import { useMemoizedFn } from '@/hooks'; import { useDebounceSearch } from '@/hooks/useDebounceSearch'; -import { SettingsPageHeader } from '../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { ListDatasetGroupsComponent } from './ListDatasetGroupsComponent'; export default function Page() { diff --git a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/permission-groups/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/permission-groups/page.tsx index 70b68a243..ebd5417c5 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/permission-groups/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/permission-groups/page.tsx @@ -10,7 +10,7 @@ import { Button } from '@/components/ui/buttons'; import { Plus } from '@/components/ui/icons'; import { useMemoizedFn } from '@/hooks'; import { useDebounceSearch } from '@/hooks/useDebounceSearch'; -import { SettingsPageHeader } from '../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { ListPermissionGroupsComponent } from './ListPermissionGroupsComponent'; export default function Page() { diff --git a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/users/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/users/page.tsx index 30f64fd92..69975334f 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(permissions)/users/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(permissions)/users/page.tsx @@ -9,7 +9,7 @@ import { useInviteModalStore } from '@/context/BusterAppLayout'; import { useUserConfigContextSelector } from '@/context/Users'; import { useMemoizedFn } from '@/hooks'; import { useDebounceSearch } from '@/hooks/useDebounceSearch'; -import { SettingsPageHeader } from '../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { ListUsersComponent } from './ListUsersComponent'; export default function Page() { diff --git a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/api-keys/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/api-keys/page.tsx index 7a629d396..3df8628aa 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/api-keys/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/api-keys/page.tsx @@ -1,4 +1,4 @@ -import { SettingsPageHeader } from '../../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { ApiKeysController } from './ApiKeysController'; import type { Metadata } from 'next'; diff --git a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/datasources/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/datasources/page.tsx index 1bfb421f5..38b27e93c 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/datasources/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/datasources/page.tsx @@ -1,4 +1,4 @@ -import { SettingsPageHeader } from '../../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { DatasourceList } from './_DatasourceList'; export default function Page() { diff --git a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/integrations/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/integrations/page.tsx new file mode 100644 index 000000000..4dc129421 --- /dev/null +++ b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/integrations/page.tsx @@ -0,0 +1,18 @@ +import { SlackIntegrations } from '@/components/features/integrations/SlackIntegrations'; +import { SettingsPageHeader } from '@/components/features/settings'; +import { SettingsCards } from '@/components/features/settings'; + +export default function IntegrationsPage() { + return ( + <> + + +
+ +
+ + ); +} diff --git a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/security/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/security/page.tsx index c88316992..4383e5491 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/security/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/(admin-only)/security/page.tsx @@ -1,23 +1,21 @@ import { InviteLinks } from '@/components/features/security/InviteLinks'; -import { SettingsPageHeader } from '../../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; import { ApprovedEmailDomains } from '@/components/features/security/ApprovedEmailDomains'; import { WorkspaceRestrictions } from '@/components/features/security/WorkspaceRestrictions'; export default function Page() { return ( -
-
- + <> + -
- - - -
+
+ + +
-
+ ); } diff --git a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/profile/page.tsx b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/profile/page.tsx index 7326a1d92..c3b9a8e0c 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/profile/page.tsx +++ b/apps/web/src/app/app/(settings_layout)/settings/(restricted-width)/profile/page.tsx @@ -4,7 +4,7 @@ import { Avatar } from '@/components/ui/avatar'; import { Text, Title } from '@/components/ui/typography'; import { useUserConfigContextSelector } from '@/context/Users/BusterUserConfigProvider'; import { formatDate } from '@/lib/date'; -import { SettingsPageHeader } from '../../_components/SettingsPageHeader'; +import { SettingsPageHeader } from '@/components/features/settings'; export default function ProfilePage() { const user = useUserConfigContextSelector((state) => state.user); diff --git a/apps/web/src/components/features/integrations/SlackIntegrations.tsx b/apps/web/src/components/features/integrations/SlackIntegrations.tsx new file mode 100644 index 000000000..d546b2395 --- /dev/null +++ b/apps/web/src/components/features/integrations/SlackIntegrations.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { SettingsCards } from '../settings/SettingsCard'; +import { SlackIcon } from '@/components/ui/icons/customIcons/SlackIcon'; +import { Text } from '@/components/ui/typography'; +import { Button } from '@/components/ui/buttons'; + +export const SlackIntegrations = React.memo(() => { + return ( + ] }]} + /> + ); +}); + +SlackIntegrations.displayName = 'SlackIntegrations'; + +const ConnectSlackCard = React.memo(() => { + return ( +
+
+
+ +
+
+ Slack account + + Link your slack account to use Buster from Slack + +
+
+ +
+ ); +}); diff --git a/apps/web/src/components/features/security/ApprovedEmailDomains.tsx b/apps/web/src/components/features/security/ApprovedEmailDomains.tsx index ee46b0ed3..932a872a2 100644 --- a/apps/web/src/components/features/security/ApprovedEmailDomains.tsx +++ b/apps/web/src/components/features/security/ApprovedEmailDomains.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { useMemo, useState } from 'react'; -import { SecurityCards } from './SecurityCards'; +import { SettingsCards } from '../settings/SettingsCard'; import { Input } from '@/components/ui/inputs'; import { Button } from '@/components/ui/buttons'; import { Text } from '@/components/ui/typography'; @@ -71,7 +71,7 @@ export const ApprovedEmailDomains = React.memo(() => { ); return ( - { const domain = newDomain.trim(); if (!domain) return; - + const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\.[a-zA-Z]{2,}$/; if (!domainRegex.test(domain)) { openErrorMessage('Please enter a valid domain name'); diff --git a/apps/web/src/components/features/security/InviteLinks.tsx b/apps/web/src/components/features/security/InviteLinks.tsx index 7b3db6d42..829247e6b 100644 --- a/apps/web/src/components/features/security/InviteLinks.tsx +++ b/apps/web/src/components/features/security/InviteLinks.tsx @@ -1,7 +1,7 @@ 'use client'; import React from 'react'; -import { SecurityCards } from './SecurityCards'; +import { SettingsCards } from '../settings/SettingsCard'; import { Switch } from '@/components/ui/switch'; import { Input } from '@/components/ui/inputs'; import { Button } from '@/components/ui/buttons'; @@ -34,7 +34,7 @@ export const InviteLinks = React.memo(() => { }; return ( - { ); return ( - = { - title: 'Features/SecurityCards', - component: SecurityCards, +const meta: Meta = { + title: 'Features/SettingsCards', + component: SettingsCards, parameters: { layout: 'padded', docs: { @@ -20,7 +20,7 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; // Mock data for different use cases const basicSections = [ diff --git a/apps/web/src/components/features/security/SecurityCards.tsx b/apps/web/src/components/features/settings/SettingsCard.tsx similarity index 76% rename from apps/web/src/components/features/security/SecurityCards.tsx rename to apps/web/src/components/features/settings/SettingsCard.tsx index 49676a7b5..f79b33826 100644 --- a/apps/web/src/components/features/security/SecurityCards.tsx +++ b/apps/web/src/components/features/settings/SettingsCard.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Title, Paragraph } from '@/components/ui/typography'; import { cn } from '@/lib/classMerge'; -interface SecurityCardsProps { +interface SettingsCardsProps { title: string; description: string; cards: { @@ -10,7 +10,7 @@ interface SecurityCardsProps { }[]; } -export const SecurityCards: React.FC = ({ title, description, cards }) => { +export const SettingsCards: React.FC = ({ title, description, cards }) => { return (
@@ -20,17 +20,17 @@ export const SecurityCards: React.FC = ({ title, description {description}
{cards.map((card, index) => ( - + ))}
); }; -const SecurityCard = ({ sections }: { sections: React.ReactNode[] }) => { +const SettingsCard = ({ sections }: { sections: React.ReactNode[] }) => { return (
{sections.map((section, index) => ( -
+
{section}
))} diff --git a/apps/web/src/app/app/(settings_layout)/settings/_components/SettingsEmptyState.tsx b/apps/web/src/components/features/settings/SettingsEmptyState.tsx similarity index 100% rename from apps/web/src/app/app/(settings_layout)/settings/_components/SettingsEmptyState.tsx rename to apps/web/src/components/features/settings/SettingsEmptyState.tsx diff --git a/apps/web/src/app/app/(settings_layout)/settings/_components/SettingsPageHeader.tsx b/apps/web/src/components/features/settings/SettingsPageHeader.tsx similarity index 100% rename from apps/web/src/app/app/(settings_layout)/settings/_components/SettingsPageHeader.tsx rename to apps/web/src/components/features/settings/SettingsPageHeader.tsx diff --git a/apps/web/src/app/app/(settings_layout)/settings/_components/index.ts b/apps/web/src/components/features/settings/index.ts similarity index 70% rename from apps/web/src/app/app/(settings_layout)/settings/_components/index.ts rename to apps/web/src/components/features/settings/index.ts index f9d9e2929..fd238e53a 100644 --- a/apps/web/src/app/app/(settings_layout)/settings/_components/index.ts +++ b/apps/web/src/components/features/settings/index.ts @@ -1,2 +1,3 @@ export * from './SettingsEmptyState'; export * from './SettingsPageHeader'; +export * from './SettingsCard'; diff --git a/apps/web/src/components/features/sidebars/SidebarSettings.tsx b/apps/web/src/components/features/sidebars/SidebarSettings.tsx index 4e6392b28..30a1d815e 100644 --- a/apps/web/src/components/features/sidebars/SidebarSettings.tsx +++ b/apps/web/src/components/features/sidebars/SidebarSettings.tsx @@ -39,6 +39,11 @@ const workspaceItems = (currentParentRoute: BusterRoutes): ISidebarGroup => ({ label: 'Data Sources', route: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES }), id: BusterRoutes.SETTINGS_DATASOURCES + }, + { + label: 'Integrations', + route: createBusterRoute({ route: BusterRoutes.SETTINGS_INTEGRATIONS }), + id: createBusterRoute({ route: BusterRoutes.SETTINGS_INTEGRATIONS }) } ].map((item) => ({ ...item, diff --git a/apps/web/src/components/ui/icons/customIcons/SlackIcon.tsx b/apps/web/src/components/ui/icons/customIcons/SlackIcon.tsx new file mode 100644 index 000000000..b68b47e3c --- /dev/null +++ b/apps/web/src/components/ui/icons/customIcons/SlackIcon.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +export const SlackIcon: React.FC< + React.SVGProps & { + size?: number; + } +> = ({ className, size = 24, ...props }) => { + return ( + + + + + + + ); +};