mirror of https://github.com/buster-so/buster.git
initial slack build out (no apis)
This commit is contained in:
parent
12fa022cfe
commit
66cae400d6
|
@ -7,7 +7,7 @@ import { Button } from '@/components/ui/buttons';
|
||||||
import { Plus } from '@/components/ui/icons';
|
import { Plus } from '@/components/ui/icons';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
||||||
import { SettingsPageHeader } from '../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
import { ListDatasetGroupsComponent } from './ListDatasetGroupsComponent';
|
import { ListDatasetGroupsComponent } from './ListDatasetGroupsComponent';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Button } from '@/components/ui/buttons';
|
||||||
import { Plus } from '@/components/ui/icons';
|
import { Plus } from '@/components/ui/icons';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
||||||
import { SettingsPageHeader } from '../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
import { ListPermissionGroupsComponent } from './ListPermissionGroupsComponent';
|
import { ListPermissionGroupsComponent } from './ListPermissionGroupsComponent';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { useInviteModalStore } from '@/context/BusterAppLayout';
|
||||||
import { useUserConfigContextSelector } from '@/context/Users';
|
import { useUserConfigContextSelector } from '@/context/Users';
|
||||||
import { useMemoizedFn } from '@/hooks';
|
import { useMemoizedFn } from '@/hooks';
|
||||||
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
import { useDebounceSearch } from '@/hooks/useDebounceSearch';
|
||||||
import { SettingsPageHeader } from '../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
import { ListUsersComponent } from './ListUsersComponent';
|
import { ListUsersComponent } from './ListUsersComponent';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SettingsPageHeader } from '../../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
import { ApiKeysController } from './ApiKeysController';
|
import { ApiKeysController } from './ApiKeysController';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SettingsPageHeader } from '../../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
import { DatasourceList } from './_DatasourceList';
|
import { DatasourceList } from './_DatasourceList';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
|
|
@ -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 (
|
||||||
|
<>
|
||||||
|
<SettingsPageHeader
|
||||||
|
title="Integrations"
|
||||||
|
description="Connect Buster with other apps and services"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-6">
|
||||||
|
<SlackIntegrations />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,23 +1,21 @@
|
||||||
import { InviteLinks } from '@/components/features/security/InviteLinks';
|
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 { ApprovedEmailDomains } from '@/components/features/security/ApprovedEmailDomains';
|
||||||
import { WorkspaceRestrictions } from '@/components/features/security/WorkspaceRestrictions';
|
import { WorkspaceRestrictions } from '@/components/features/security/WorkspaceRestrictions';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-4">
|
<>
|
||||||
<div>
|
<SettingsPageHeader
|
||||||
<SettingsPageHeader
|
title="Security"
|
||||||
title="Security"
|
description="Manage security and general permission settings"
|
||||||
description="Manage security and general permission settings"
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex flex-col space-y-6">
|
<div className="flex flex-col space-y-6">
|
||||||
<InviteLinks />
|
<InviteLinks />
|
||||||
<ApprovedEmailDomains />
|
<ApprovedEmailDomains />
|
||||||
<WorkspaceRestrictions />
|
<WorkspaceRestrictions />
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Avatar } from '@/components/ui/avatar';
|
||||||
import { Text, Title } from '@/components/ui/typography';
|
import { Text, Title } from '@/components/ui/typography';
|
||||||
import { useUserConfigContextSelector } from '@/context/Users/BusterUserConfigProvider';
|
import { useUserConfigContextSelector } from '@/context/Users/BusterUserConfigProvider';
|
||||||
import { formatDate } from '@/lib/date';
|
import { formatDate } from '@/lib/date';
|
||||||
import { SettingsPageHeader } from '../../_components/SettingsPageHeader';
|
import { SettingsPageHeader } from '@/components/features/settings';
|
||||||
|
|
||||||
export default function ProfilePage() {
|
export default function ProfilePage() {
|
||||||
const user = useUserConfigContextSelector((state) => state.user);
|
const user = useUserConfigContextSelector((state) => state.user);
|
||||||
|
|
|
@ -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 (
|
||||||
|
<SettingsCards
|
||||||
|
title="Slack"
|
||||||
|
description="Connect Buster with Slack"
|
||||||
|
cards={[{ sections: [<ConnectSlackCard />] }]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
SlackIntegrations.displayName = 'SlackIntegrations';
|
||||||
|
|
||||||
|
const ConnectSlackCard = React.memo(() => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between gap-x-2">
|
||||||
|
<div className="flex space-x-2">
|
||||||
|
<div className="bg-item-select flex items-center justify-center rounded p-2">
|
||||||
|
<SlackIcon size={16} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-0.5">
|
||||||
|
<Text>Slack account</Text>
|
||||||
|
<Text variant="secondary" size={'xs'}>
|
||||||
|
Link your slack account to use Buster from Slack
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button prefix={<SlackIcon size={16} />} size={'tall'}>
|
||||||
|
Connect Slack
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { SecurityCards } from './SecurityCards';
|
import { SettingsCards } from '../settings/SettingsCard';
|
||||||
import { Input } from '@/components/ui/inputs';
|
import { Input } from '@/components/ui/inputs';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
|
@ -71,7 +71,7 @@ export const ApprovedEmailDomains = React.memo(() => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SecurityCards
|
<SettingsCards
|
||||||
title="Approved email domains"
|
title="Approved email domains"
|
||||||
description="Anyone with an email address at these domains is allowed to sign up for this workspace"
|
description="Anyone with an email address at these domains is allowed to sign up for this workspace"
|
||||||
cards={[{ sections }]}
|
cards={[{ sections }]}
|
||||||
|
@ -91,7 +91,7 @@ const AddDomainInput = React.memo(
|
||||||
const handleAddDomain = useMemoizedFn(async () => {
|
const handleAddDomain = useMemoizedFn(async () => {
|
||||||
const domain = newDomain.trim();
|
const domain = newDomain.trim();
|
||||||
if (!domain) return;
|
if (!domain) return;
|
||||||
|
|
||||||
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\.[a-zA-Z]{2,}$/;
|
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\.[a-zA-Z]{2,}$/;
|
||||||
if (!domainRegex.test(domain)) {
|
if (!domainRegex.test(domain)) {
|
||||||
openErrorMessage('Please enter a valid domain name');
|
openErrorMessage('Please enter a valid domain name');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SecurityCards } from './SecurityCards';
|
import { SettingsCards } from '../settings/SettingsCard';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Input } from '@/components/ui/inputs';
|
import { Input } from '@/components/ui/inputs';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
|
@ -34,7 +34,7 @@ export const InviteLinks = React.memo(() => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SecurityCards
|
<SettingsCards
|
||||||
title="Invite links"
|
title="Invite links"
|
||||||
description="A uniquely generated invite link allows anyone with the link to join your workspace"
|
description="A uniquely generated invite link allows anyone with the link to join your workspace"
|
||||||
cards={[
|
cards={[
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useMemo, type ReactNode } from 'react';
|
import React, { useMemo, type ReactNode } from 'react';
|
||||||
import { SecurityCards } from './SecurityCards';
|
import { SettingsCards } from '../settings/SettingsCard';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
import { Button } from '@/components/ui/buttons';
|
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import {
|
import {
|
||||||
useGetWorkspaceSettings,
|
useGetWorkspaceSettings,
|
||||||
|
@ -45,7 +44,7 @@ export const WorkspaceRestrictions = React.memo(() => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SecurityCards
|
<SettingsCards
|
||||||
title="Workspace restrictions"
|
title="Workspace restrictions"
|
||||||
description="Restrict the workspace to only allow users with an email address at these domains"
|
description="Restrict the workspace to only allow users with an email address at these domains"
|
||||||
cards={[{ sections }]}
|
cards={[{ sections }]}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { SecurityCards } from './SecurityCards';
|
import { SettingsCards } from './SettingsCard';
|
||||||
import { Button } from '@/components/ui/buttons';
|
import { Button } from '@/components/ui/buttons';
|
||||||
import { Pill } from '@/components/ui/pills/Pill';
|
import { Pill } from '@/components/ui/pills/Pill';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { Text } from '@/components/ui/typography';
|
||||||
|
|
||||||
const meta: Meta<typeof SecurityCards> = {
|
const meta: Meta<typeof SettingsCards> = {
|
||||||
title: 'Features/SecurityCards',
|
title: 'Features/SettingsCards',
|
||||||
component: SecurityCards,
|
component: SettingsCards,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'padded',
|
layout: 'padded',
|
||||||
docs: {
|
docs: {
|
||||||
|
@ -20,7 +20,7 @@ const meta: Meta<typeof SecurityCards> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof SecurityCards>;
|
type Story = StoryObj<typeof SettingsCards>;
|
||||||
|
|
||||||
// Mock data for different use cases
|
// Mock data for different use cases
|
||||||
const basicSections = [
|
const basicSections = [
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { Title, Paragraph } from '@/components/ui/typography';
|
import { Title, Paragraph } from '@/components/ui/typography';
|
||||||
import { cn } from '@/lib/classMerge';
|
import { cn } from '@/lib/classMerge';
|
||||||
|
|
||||||
interface SecurityCardsProps {
|
interface SettingsCardsProps {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
cards: {
|
cards: {
|
||||||
|
@ -10,7 +10,7 @@ interface SecurityCardsProps {
|
||||||
}[];
|
}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SecurityCards: React.FC<SecurityCardsProps> = ({ title, description, cards }) => {
|
export const SettingsCards: React.FC<SettingsCardsProps> = ({ title, description, cards }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-3.5">
|
<div className="flex flex-col space-y-3.5">
|
||||||
<div className="flex flex-col space-y-1.5">
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
@ -20,17 +20,17 @@ export const SecurityCards: React.FC<SecurityCardsProps> = ({ title, description
|
||||||
<Paragraph variant="secondary">{description}</Paragraph>
|
<Paragraph variant="secondary">{description}</Paragraph>
|
||||||
</div>
|
</div>
|
||||||
{cards.map((card, index) => (
|
{cards.map((card, index) => (
|
||||||
<SecurityCard key={index} sections={card.sections} />
|
<SettingsCard key={index} sections={card.sections} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SecurityCard = ({ sections }: { sections: React.ReactNode[] }) => {
|
const SettingsCard = ({ sections }: { sections: React.ReactNode[] }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col rounded border">
|
<div className="flex flex-col rounded border">
|
||||||
{sections.map((section, index) => (
|
{sections.map((section, index) => (
|
||||||
<div key={index} className={cn(index !== sections.length - 1 && 'border-b', 'px-4 py-2.5')}>
|
<div key={index} className={cn(index !== sections.length - 1 && 'border-b', 'px-4 py-3.5')}>
|
||||||
{section}
|
{section}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './SettingsEmptyState';
|
export * from './SettingsEmptyState';
|
||||||
export * from './SettingsPageHeader';
|
export * from './SettingsPageHeader';
|
||||||
|
export * from './SettingsCard';
|
|
@ -39,6 +39,11 @@ const workspaceItems = (currentParentRoute: BusterRoutes): ISidebarGroup => ({
|
||||||
label: 'Data Sources',
|
label: 'Data Sources',
|
||||||
route: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES }),
|
route: createBusterRoute({ route: BusterRoutes.SETTINGS_DATASOURCES }),
|
||||||
id: BusterRoutes.SETTINGS_DATASOURCES
|
id: BusterRoutes.SETTINGS_DATASOURCES
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Integrations',
|
||||||
|
route: createBusterRoute({ route: BusterRoutes.SETTINGS_INTEGRATIONS }),
|
||||||
|
id: createBusterRoute({ route: BusterRoutes.SETTINGS_INTEGRATIONS })
|
||||||
}
|
}
|
||||||
].map((item) => ({
|
].map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const SlackIcon: React.FC<
|
||||||
|
React.SVGProps<SVGSVGElement> & {
|
||||||
|
size?: number;
|
||||||
|
}
|
||||||
|
> = ({ className, size = 24, ...props }) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox="0 0 127 127"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className={className}
|
||||||
|
{...props}>
|
||||||
|
<path
|
||||||
|
d="M27.2 80c0 7.3-5.9 13.2-13.2 13.2C6.7 93.2.8 87.3.8 80c0-7.3 5.9-13.2 13.2-13.2h13.2V80zm6.6 0c0-7.3 5.9-13.2 13.2-13.2 7.3 0 13.2 5.9 13.2 13.2v33c0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V80z"
|
||||||
|
fill="#E01E5A"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M47 27c-7.3 0-13.2-5.9-13.2-13.2C33.8 6.5 39.7.6 47 .6c7.3 0 13.2 5.9 13.2 13.2V27H47zm0 6.7c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H13.9C6.6 60.1.7 54.2.7 46.9c0-7.3 5.9-13.2 13.2-13.2H47z"
|
||||||
|
fill="#36C5F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M99.9 46.9c0-7.3 5.9-13.2 13.2-13.2 7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H99.9V46.9zm-6.6 0c0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V13.8C66.9 6.5 72.8.6 80.1.6c7.3 0 13.2 5.9 13.2 13.2v33.1z"
|
||||||
|
fill="#2EB67D"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M80.1 99.8c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2-7.3 0-13.2-5.9-13.2-13.2V99.8h13.2zm0-6.6c-7.3 0-13.2-5.9-13.2-13.2 0-7.3 5.9-13.2 13.2-13.2h33.1c7.3 0 13.2 5.9 13.2 13.2 0 7.3-5.9 13.2-13.2 13.2H80.1z"
|
||||||
|
fill="#ECB22E"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue