mirror of https://github.com/buster-so/buster.git
added inegration routes
This commit is contained in:
parent
bcc95954e9
commit
c1a79deff3
|
@ -0,0 +1,2 @@
|
|||
export * from './queryRequests';
|
||||
export * from './request';
|
|
@ -0,0 +1,60 @@
|
|||
import type { CreateS3IntegrationRequest } from '@buster/server-shared/s3-integrations';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { s3IntegrationsQueryKeys } from '@/api/query_keys/s3Integrations';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { createS3Integration, deleteS3Integration, getS3Integration } from './request';
|
||||
|
||||
// GET /api/v2/s3-integrations
|
||||
export const useGetS3Integration = (enabled = true) => {
|
||||
return useQuery({
|
||||
...s3IntegrationsQueryKeys.s3IntegrationGet,
|
||||
queryFn: getS3Integration,
|
||||
enabled,
|
||||
});
|
||||
};
|
||||
|
||||
// POST /api/v2/s3-integrations
|
||||
export const useCreateS3Integration = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateS3IntegrationRequest) => createS3Integration(data),
|
||||
onSuccess: () => {
|
||||
// Invalidate the integration query to refetch the updated data
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: s3IntegrationsQueryKeys.s3IntegrationGet.queryKey,
|
||||
refetchType: 'all',
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// DELETE /api/v2/s3-integrations/:id
|
||||
export const useDeleteS3Integration = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { openConfirmModal } = useBusterNotifications();
|
||||
|
||||
const mutationFn = useMemoizedFn(async (id: string) => {
|
||||
return openConfirmModal({
|
||||
title: 'Remove Storage Integration',
|
||||
content:
|
||||
'Are you sure you want to remove the storage integration? This will disconnect your storage bucket from Buster.',
|
||||
primaryButtonProps: {
|
||||
text: 'Remove',
|
||||
},
|
||||
onOk: async () => deleteS3Integration(id),
|
||||
});
|
||||
});
|
||||
|
||||
return useMutation({
|
||||
mutationFn,
|
||||
onSuccess: () => {
|
||||
// Invalidate the integration query to refetch the updated data
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: s3IntegrationsQueryKeys.s3IntegrationGet.queryKey,
|
||||
refetchType: 'all',
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
import type {
|
||||
CreateS3IntegrationRequest,
|
||||
GetS3IntegrationResponse,
|
||||
CreateS3IntegrationResponse,
|
||||
DeleteS3IntegrationResponse
|
||||
} from '@buster/server-shared/s3-integrations';
|
||||
import { mainApiV2 } from '../instances';
|
||||
|
||||
// Using mainApiV2 for v2 endpoints
|
||||
|
||||
// GET /api/v2/s3-integrations
|
||||
export const getS3Integration = async (): Promise<GetS3IntegrationResponse> => {
|
||||
const response = await mainApiV2.get('/s3-integrations');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// POST /api/v2/s3-integrations
|
||||
export const createS3Integration = async (
|
||||
data: CreateS3IntegrationRequest
|
||||
): Promise<CreateS3IntegrationResponse> => {
|
||||
const response = await mainApiV2.post('/s3-integrations', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// DELETE /api/v2/s3-integrations/:id
|
||||
export const deleteS3Integration = async (id: string): Promise<DeleteS3IntegrationResponse> => {
|
||||
const response = await mainApiV2.delete(`/s3-integrations/${id}`);
|
||||
return response.data;
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import type { GetS3IntegrationResponse } from '@buster/server-shared/s3-integrations';
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
export const s3IntegrationGet = queryOptions<GetS3IntegrationResponse>({
|
||||
queryKey: ['s3-integrations', 'get'] as const,
|
||||
});
|
||||
|
||||
export const s3IntegrationsList = queryOptions<GetS3IntegrationResponse>({
|
||||
queryKey: ['s3-integrations', 'list'] as const,
|
||||
});
|
||||
|
||||
export const s3IntegrationsQueryKeys = {
|
||||
s3IntegrationGet,
|
||||
s3IntegrationsList,
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
export const IntegrationSkeleton = () => {
|
||||
return <div className="bg-gray-light/20 h-30 w-full animate-pulse rounded"></div>;
|
||||
};
|
|
@ -0,0 +1,306 @@
|
|||
'use client';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
type SlackSharingPermission = 'shareWithWorkspace' | 'shareWithChannel' | 'noSharing';
|
||||
|
||||
import pluralize from 'pluralize';
|
||||
import {
|
||||
useGetSlackChannels,
|
||||
useGetSlackIntegration,
|
||||
useInitiateSlackOAuth,
|
||||
useRemoveSlackIntegration,
|
||||
useUpdateSlackIntegration,
|
||||
} from '@/api/buster_rest/slack/queryRequests';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { StatusCard } from '@/components/ui/card/StatusCard';
|
||||
import {
|
||||
createDropdownItems,
|
||||
Dropdown,
|
||||
type IDropdownItem,
|
||||
type IDropdownItems,
|
||||
} from '@/components/ui/dropdown';
|
||||
import { ChevronDown } from '@/components/ui/icons';
|
||||
import { SlackIcon } from '@/components/ui/icons/customIcons/SlackIcon';
|
||||
import LinkSlash from '@/components/ui/icons/NucleoIconOutlined/link-slash';
|
||||
import Refresh2 from '@/components/ui/icons/NucleoIconOutlined/refresh-2';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { SettingsCards } from '../settings/SettingsCard';
|
||||
import { IntegrationSkeleton } from './IntegrationSkeleton';
|
||||
|
||||
export const SlackIntegrations = React.memo(() => {
|
||||
const {
|
||||
data: slackIntegration,
|
||||
isFetched: isFetchedSlackIntegration,
|
||||
error: slackIntegrationError,
|
||||
} = useGetSlackIntegration();
|
||||
|
||||
const isConnected = slackIntegration?.connected ?? false;
|
||||
|
||||
const cards = useMemo(() => {
|
||||
const sections = [
|
||||
<ConnectSlackCard key="connect-slack-card" />,
|
||||
isConnected && <ConnectedSlackChannels key="connected-slack-channels" />,
|
||||
isConnected && <SlackSharingPermissions key="slack-sharing-permissions" />,
|
||||
].filter(Boolean);
|
||||
return [{ sections }];
|
||||
}, [isConnected]);
|
||||
|
||||
if (slackIntegrationError) {
|
||||
return <StatusCard message="Error fetching Slack integration." variant={'danger'} />;
|
||||
}
|
||||
|
||||
if (!isFetchedSlackIntegration) {
|
||||
return <IntegrationSkeleton />;
|
||||
}
|
||||
|
||||
return <SettingsCards title="Slack" description="Connect Buster with Slack" cards={cards} />;
|
||||
});
|
||||
|
||||
SlackIntegrations.displayName = 'SlackIntegrations';
|
||||
|
||||
const ConnectSlackCard = React.memo(() => {
|
||||
const { data: slackIntegration } = useGetSlackIntegration();
|
||||
const { mutate: initiateSlackOAuth } = useInitiateSlackOAuth();
|
||||
|
||||
const isConnected = slackIntegration?.connected;
|
||||
const needsReinstall = slackIntegration?.status === 're_install_required';
|
||||
|
||||
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>
|
||||
|
||||
{isConnected ? (
|
||||
needsReinstall ? (
|
||||
<Button
|
||||
prefix={<SlackIcon size={16} />}
|
||||
onClick={() => initiateSlackOAuth()}
|
||||
size={'tall'}
|
||||
className="border-yellow-500 bg-yellow-50 text-yellow-700 hover:bg-yellow-100"
|
||||
>
|
||||
Re-install Required
|
||||
</Button>
|
||||
) : (
|
||||
<ConnectedDropdown />
|
||||
)
|
||||
) : (
|
||||
<Button prefix={<SlackIcon size={16} />} onClick={() => initiateSlackOAuth()} size={'tall'}>
|
||||
Connect Slack
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
ConnectSlackCard.displayName = 'ConnectSlackCard';
|
||||
|
||||
const ConnectedDropdown = React.memo(() => {
|
||||
const { mutate: removeSlackIntegration, isPending } = useRemoveSlackIntegration();
|
||||
|
||||
const dropdownItems: IDropdownItems = [
|
||||
{
|
||||
value: 'disconnect',
|
||||
label: 'Disconnect',
|
||||
icon: <LinkSlash />,
|
||||
onClick: () => {
|
||||
removeSlackIntegration();
|
||||
},
|
||||
loading: isPending,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Dropdown items={dropdownItems} align="end" side="bottom" selectType="single">
|
||||
<div className="hover:bg-item-hover flex! cursor-pointer items-center space-x-1.5 rounded p-1.5">
|
||||
<div className="bg-success-foreground h-2.5 w-2.5 rounded-full" />
|
||||
<Text className="select-none">Connected</Text>
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
});
|
||||
|
||||
ConnectedDropdown.displayName = 'ConnectedDropdown';
|
||||
|
||||
const ConnectedSlackChannels = React.memo(() => {
|
||||
const { data: slackIntegration, isLoading: isLoadingSlackIntegration } = useGetSlackIntegration();
|
||||
const {
|
||||
data: slackChannelsData,
|
||||
isLoading: isLoadingSlackChannels,
|
||||
isRefetching: isRefetchingSlackChannels,
|
||||
refetch: refetchSlackChannels,
|
||||
isFetched: isFetchedSlackChannels,
|
||||
error: slackChannelsError,
|
||||
} = useGetSlackChannels();
|
||||
|
||||
const { mutate: updateSlackIntegration } = useUpdateSlackIntegration();
|
||||
|
||||
const channels = slackChannelsData?.channels || [];
|
||||
const selectedChannelId = slackIntegration?.integration?.default_channel?.id;
|
||||
|
||||
const items = useMemo(() => {
|
||||
return channels.map((channel) => ({
|
||||
label: channel.name,
|
||||
value: channel.id,
|
||||
selected: channel.id === selectedChannelId,
|
||||
}));
|
||||
}, [channels, selectedChannelId]);
|
||||
|
||||
const numberOfSelectedChannels = useMemo(() => {
|
||||
return items.filter((item) => item.selected).length;
|
||||
}, [items]);
|
||||
|
||||
const onSelect = useMemoizedFn((channelId: string) => {
|
||||
const channel = channels.find((channel) => channel.id === channelId);
|
||||
if (!channel) return;
|
||||
updateSlackIntegration({
|
||||
default_channel: channel,
|
||||
});
|
||||
});
|
||||
|
||||
const showLoadingButton =
|
||||
isLoadingSlackChannels || isLoadingSlackIntegration || isRefetchingSlackChannels;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between space-x-4">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Text>Alerts channel</Text>
|
||||
<Text variant="secondary" size={'xs'}>
|
||||
Select which Slack channel Buster should send alerts to
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 items-center justify-end space-x-2">
|
||||
{!slackChannelsError ? (
|
||||
<>
|
||||
{isFetchedSlackChannels && (
|
||||
<Button
|
||||
size={'tall'}
|
||||
variant="ghost"
|
||||
loading={showLoadingButton}
|
||||
suffix={
|
||||
!showLoadingButton && (
|
||||
<span className="flex items-center justify-center text-base">
|
||||
<Refresh2 />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
onClick={() => refetchSlackChannels()}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Dropdown
|
||||
selectType="multiple"
|
||||
items={items}
|
||||
onSelect={onSelect}
|
||||
menuHeader="Search channels"
|
||||
className="w-fit min-w-40"
|
||||
>
|
||||
<WeirdFakeSelectButtonForBlake
|
||||
label={
|
||||
numberOfSelectedChannels > 0
|
||||
? `${numberOfSelectedChannels} ${pluralize('channel', numberOfSelectedChannels)} selected`
|
||||
: 'Select a channel'
|
||||
}
|
||||
/>
|
||||
</Dropdown>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text variant="danger" size={'xs'}>
|
||||
Error fetching channels.
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
ConnectedSlackChannels.displayName = 'ConnectedSlackChannels';
|
||||
|
||||
const SlackSharingPermissions = React.memo(() => {
|
||||
const { data: slackIntegration } = useGetSlackIntegration();
|
||||
const { mutate: updateSlackIntegration } = useUpdateSlackIntegration();
|
||||
|
||||
const selectedOption: SlackSharingPermission =
|
||||
slackIntegration?.integration?.default_sharing_permissions || 'noSharing';
|
||||
|
||||
const sharingOptions: IDropdownItem<SlackSharingPermission>[] = (
|
||||
createDropdownItems([
|
||||
{
|
||||
label: 'Workspace',
|
||||
value: 'shareWithWorkspace' satisfies SlackSharingPermission,
|
||||
secondaryLabel:
|
||||
'All workspace members will have access to any chat created from any channel.',
|
||||
},
|
||||
// {
|
||||
// label: 'Channel',
|
||||
// value: 'shareWithChannel',
|
||||
// secondaryLabel: 'All channel members will have access to any chat created from that channel.'
|
||||
// },
|
||||
{
|
||||
label: 'None',
|
||||
value: 'noSharing' satisfies SlackSharingPermission,
|
||||
secondaryLabel: 'Only the user who sent the request will have access to their chat.',
|
||||
},
|
||||
]) satisfies IDropdownItem<SlackSharingPermission>[]
|
||||
).map((option) => ({
|
||||
...option,
|
||||
selected: option.value === selectedOption,
|
||||
}));
|
||||
|
||||
const selectedLabel = sharingOptions.find((option) => option.selected)?.label || 'Select option';
|
||||
|
||||
const handleSelect = useMemoizedFn((value: string) => {
|
||||
updateSlackIntegration({
|
||||
default_sharing_permissions: value as SlackSharingPermission,
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between space-x-4">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Text>Auto-share chats with other users</Text>
|
||||
<Text variant="secondary" size={'xs'}>
|
||||
Specify how chats are auto-shared when created from Slack channels
|
||||
</Text>
|
||||
</div>
|
||||
<Dropdown
|
||||
items={sharingOptions}
|
||||
onSelect={handleSelect}
|
||||
align="end"
|
||||
side="bottom"
|
||||
selectType="single"
|
||||
>
|
||||
<WeirdFakeSelectButtonForBlake label={selectedLabel} />
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
SlackSharingPermissions.displayName = 'SlackSharingPermissions';
|
||||
|
||||
const WeirdFakeSelectButtonForBlake = ({ label }: { label: string | React.ReactNode }) => {
|
||||
return (
|
||||
<div className="bg-background hover:bg-item-hover flex min-w-32 cursor-pointer items-center justify-between space-x-2 rounded border px-3 py-1.5 transition-colors">
|
||||
<Text size="sm" className="truncate">
|
||||
{label}
|
||||
</Text>
|
||||
<span className="text-icon-color flex items-center">
|
||||
<ChevronDown />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,144 @@
|
|||
import { useNavigate } from '@tanstack/react-router';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useDeleteS3Integration, useGetS3Integration } from '@/api/buster_rest/s3-integrations';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { StatusCard } from '@/components/ui/card/StatusCard';
|
||||
import { Dropdown, type IDropdownItems } from '@/components/ui/dropdown';
|
||||
import Bucket from '@/components/ui/icons/NucleoIconOutlined/bucket';
|
||||
import LinkSlash from '@/components/ui/icons/NucleoIconOutlined/link-slash';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
import { SettingsCards } from '../settings/SettingsCard';
|
||||
import { IntegrationSkeleton } from './IntegrationSkeleton';
|
||||
|
||||
export const StorageIntegrations = React.memo(() => {
|
||||
const {
|
||||
data: s3Integration,
|
||||
isFetched: isFetchedS3Integration,
|
||||
error: s3IntegrationError,
|
||||
} = useGetS3Integration();
|
||||
|
||||
const isConnected = s3Integration !== null;
|
||||
|
||||
const cards = useMemo(() => {
|
||||
const sections = [
|
||||
<ConnectStorageCard key="connect-storage-card" />,
|
||||
isConnected && <StorageConfiguration key="storage-configuration" />,
|
||||
].filter(Boolean);
|
||||
return [{ sections }];
|
||||
}, [isConnected]);
|
||||
|
||||
if (s3IntegrationError) {
|
||||
return <StatusCard message="Error fetching storage integration." variant={'danger'} />;
|
||||
}
|
||||
|
||||
if (!isFetchedS3Integration) {
|
||||
return <IntegrationSkeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCards
|
||||
title="Object Storage"
|
||||
description="Connect an S3 compatible storage bucket to Buster"
|
||||
cards={cards}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
StorageIntegrations.displayName = 'StorageIntegrations';
|
||||
|
||||
const ConnectStorageCard = React.memo(() => {
|
||||
const { data: s3Integration } = useGetS3Integration();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isConnected = s3Integration !== null;
|
||||
|
||||
const handleConnect = () => {
|
||||
navigate({ to: '/app/settings/storage/add' });
|
||||
};
|
||||
|
||||
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">
|
||||
<Bucket strokewidth={1.5} />
|
||||
</div>
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Text>Storage account</Text>
|
||||
<Text variant="secondary" size={'xs'}>
|
||||
Link your storage bucket to use file storage with Buster
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isConnected ? (
|
||||
<ConnectedDropdown />
|
||||
) : (
|
||||
<Button prefix={<Bucket strokewidth={1.5} />} onClick={handleConnect} size={'tall'}>
|
||||
Connect Storage
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
ConnectStorageCard.displayName = 'ConnectStorageCard';
|
||||
|
||||
const ConnectedDropdown = React.memo(() => {
|
||||
const { data: s3Integration } = useGetS3Integration();
|
||||
const { mutate: deleteS3Integration, isPending } = useDeleteS3Integration();
|
||||
|
||||
const dropdownItems: IDropdownItems = [
|
||||
{
|
||||
value: 'disconnect',
|
||||
label: 'Disconnect',
|
||||
icon: <LinkSlash />,
|
||||
onClick: () => {
|
||||
if (s3Integration?.id) {
|
||||
deleteS3Integration(s3Integration.id);
|
||||
}
|
||||
},
|
||||
loading: isPending,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Dropdown items={dropdownItems} align="end" side="bottom" selectType="single">
|
||||
<div className="hover:bg-item-hover flex! cursor-pointer items-center space-x-1.5 rounded p-1.5">
|
||||
<div className="bg-success-foreground h-2.5 w-2.5 rounded-full" />
|
||||
<Text className="select-none">Connected</Text>
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
});
|
||||
|
||||
ConnectedDropdown.displayName = 'ConnectedDropdown';
|
||||
|
||||
const StorageConfiguration = React.memo(() => {
|
||||
const { data: s3Integration } = useGetS3Integration();
|
||||
|
||||
if (!s3Integration) return null;
|
||||
|
||||
const providerLabels = {
|
||||
s3: 'AWS S3',
|
||||
r2: 'Cloudflare R2',
|
||||
gcs: 'Google Cloud Storage',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between space-x-4">
|
||||
<div className="flex flex-col space-y-0.5">
|
||||
<Text>Storage provider</Text>
|
||||
<Text variant="secondary" size={'xs'}>
|
||||
Currently connected to {providerLabels[s3Integration.provider]}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Text size="sm" className="text-icon-color">
|
||||
{s3Integration.bucketName || providerLabels[s3Integration.provider]}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
StorageConfiguration.displayName = 'StorageConfiguration';
|
|
@ -54,6 +54,8 @@ import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndex
|
|||
import { Route as AppAppAssetReportsReportIdLayoutIndexRouteImport } from './routes/app/_app/_asset/reports.$reportId/_layout/index'
|
||||
import { Route as AppAppAssetMetricsMetricIdLayoutIndexRouteImport } from './routes/app/_app/_asset/metrics.$metricId/_layout/index'
|
||||
import { Route as AppAppAssetDashboardsDashboardIdLayoutIndexRouteImport } from './routes/app/_app/_asset/dashboards.$dashboardId/_layout/index'
|
||||
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.storage.storageId'
|
||||
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.storage.add'
|
||||
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.datasources.add'
|
||||
import { Route as AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRouteImport } from './routes/app/_settings/_restricted_layout/_admin_only/settings.datasources.$datasourceId'
|
||||
import { Route as AppAppAssetReportsReportIdLayoutContentRouteImport } from './routes/app/_app/_asset/reports.$reportId/_layout/content'
|
||||
|
@ -401,6 +403,20 @@ const AppAppAssetDashboardsDashboardIdLayoutIndexRoute =
|
|||
path: '/',
|
||||
getParentRoute: () => AppAppAssetDashboardsDashboardIdLayoutRoute,
|
||||
} as any)
|
||||
const AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute =
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRouteImport.update(
|
||||
{
|
||||
id: '/settings/storage/storageId',
|
||||
path: '/settings/storage/storageId',
|
||||
getParentRoute: () => AppSettingsRestricted_layoutAdmin_onlyRoute,
|
||||
} as any,
|
||||
)
|
||||
const AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute =
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRouteImport.update({
|
||||
id: '/settings/storage/add',
|
||||
path: '/settings/storage/add',
|
||||
getParentRoute: () => AppSettingsRestricted_layoutAdmin_onlyRoute,
|
||||
} as any)
|
||||
const AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute =
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRouteImport.update(
|
||||
{
|
||||
|
@ -719,6 +735,8 @@ export interface FileRoutesByFullPath {
|
|||
'/app/reports/$reportId/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
|
||||
'/app/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
|
||||
'/app/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
|
||||
'/app/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
|
||||
'/app/settings/storage/storageId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute
|
||||
'/app/dashboards/$dashboardId/': typeof AppAppAssetDashboardsDashboardIdLayoutIndexRoute
|
||||
'/app/metrics/$metricId/': typeof AppAppAssetMetricsMetricIdLayoutIndexRoute
|
||||
'/app/reports/$reportId/': typeof AppAppAssetReportsReportIdLayoutIndexRoute
|
||||
|
@ -794,6 +812,8 @@ export interface FileRoutesByTo {
|
|||
'/app/reports/$reportId/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
|
||||
'/app/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
|
||||
'/app/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
|
||||
'/app/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
|
||||
'/app/settings/storage/storageId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute
|
||||
'/app/settings/datasources': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute
|
||||
'/app/chats/$chatId/dashboards/$dashboardId': typeof AppAppAssetChatsChatIdDashboardsDashboardIdLayoutIndexRoute
|
||||
'/app/chats/$chatId/metrics/$metricId': typeof AppAppAssetChatsChatIdMetricsMetricIdLayoutIndexRoute
|
||||
|
@ -869,6 +889,8 @@ export interface FileRoutesById {
|
|||
'/app/_app/_asset/reports/$reportId/_layout/content': typeof AppAppAssetReportsReportIdLayoutContentRoute
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/$datasourceId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/add': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/storageId': typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute
|
||||
'/app/_app/_asset/dashboards/$dashboardId/_layout/': typeof AppAppAssetDashboardsDashboardIdLayoutIndexRoute
|
||||
'/app/_app/_asset/metrics/$metricId/_layout/': typeof AppAppAssetMetricsMetricIdLayoutIndexRoute
|
||||
'/app/_app/_asset/reports/$reportId/_layout/': typeof AppAppAssetReportsReportIdLayoutIndexRoute
|
||||
|
@ -954,6 +976,8 @@ export interface FileRouteTypes {
|
|||
| '/app/reports/$reportId/content'
|
||||
| '/app/settings/datasources/$datasourceId'
|
||||
| '/app/settings/datasources/add'
|
||||
| '/app/settings/storage/add'
|
||||
| '/app/settings/storage/storageId'
|
||||
| '/app/dashboards/$dashboardId/'
|
||||
| '/app/metrics/$metricId/'
|
||||
| '/app/reports/$reportId/'
|
||||
|
@ -1029,6 +1053,8 @@ export interface FileRouteTypes {
|
|||
| '/app/reports/$reportId/content'
|
||||
| '/app/settings/datasources/$datasourceId'
|
||||
| '/app/settings/datasources/add'
|
||||
| '/app/settings/storage/add'
|
||||
| '/app/settings/storage/storageId'
|
||||
| '/app/settings/datasources'
|
||||
| '/app/chats/$chatId/dashboards/$dashboardId'
|
||||
| '/app/chats/$chatId/metrics/$metricId'
|
||||
|
@ -1103,6 +1129,8 @@ export interface FileRouteTypes {
|
|||
| '/app/_app/_asset/reports/$reportId/_layout/content'
|
||||
| '/app/_settings/_restricted_layout/_admin_only/settings/datasources/$datasourceId'
|
||||
| '/app/_settings/_restricted_layout/_admin_only/settings/datasources/add'
|
||||
| '/app/_settings/_restricted_layout/_admin_only/settings/storage/add'
|
||||
| '/app/_settings/_restricted_layout/_admin_only/settings/storage/storageId'
|
||||
| '/app/_app/_asset/dashboards/$dashboardId/_layout/'
|
||||
| '/app/_app/_asset/metrics/$metricId/_layout/'
|
||||
| '/app/_app/_asset/reports/$reportId/_layout/'
|
||||
|
@ -1529,6 +1557,20 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof AppAppAssetDashboardsDashboardIdLayoutIndexRouteImport
|
||||
parentRoute: typeof AppAppAssetDashboardsDashboardIdLayoutRoute
|
||||
}
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/storageId': {
|
||||
id: '/app/_settings/_restricted_layout/_admin_only/settings/storage/storageId'
|
||||
path: '/settings/storage/storageId'
|
||||
fullPath: '/app/settings/storage/storageId'
|
||||
preLoaderRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRouteImport
|
||||
parentRoute: typeof AppSettingsRestricted_layoutAdmin_onlyRoute
|
||||
}
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/add': {
|
||||
id: '/app/_settings/_restricted_layout/_admin_only/settings/storage/add'
|
||||
path: '/settings/storage/add'
|
||||
fullPath: '/app/settings/storage/add'
|
||||
preLoaderRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRouteImport
|
||||
parentRoute: typeof AppSettingsRestricted_layoutAdmin_onlyRoute
|
||||
}
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/datasources/add': {
|
||||
id: '/app/_settings/_restricted_layout/_admin_only/settings/datasources/add'
|
||||
path: '/settings/datasources/add'
|
||||
|
@ -2286,6 +2328,8 @@ interface AppSettingsRestricted_layoutAdmin_onlyRouteChildren {
|
|||
AppSettingsRestricted_layoutAdmin_onlySettingsWorkspaceRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsWorkspaceRoute
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute: typeof AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute
|
||||
}
|
||||
|
||||
|
@ -2303,6 +2347,10 @@ const AppSettingsRestricted_layoutAdmin_onlyRouteChildren: AppSettingsRestricted
|
|||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesDatasourceIdRoute,
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute:
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesAddRoute,
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute:
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageAddRoute,
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute:
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsStorageStorageIdRoute,
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute:
|
||||
AppSettingsRestricted_layoutAdmin_onlySettingsDatasourcesIndexRoute,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { SlackIntegrations } from '@/components/features/integrations/SlackIntegrations';
|
||||
import { StorageIntegrations } from '@/components/features/integrations/StorageIntegrations';
|
||||
import { SettingsPageHeader } from '@/components/features/settings';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/integrations'
|
||||
|
@ -15,5 +18,17 @@ export const Route = createFileRoute(
|
|||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/app/settings/integrations"!</div>;
|
||||
return (
|
||||
<>
|
||||
<SettingsPageHeader
|
||||
title="Integrations"
|
||||
description="Connect Buster with other apps and services"
|
||||
/>
|
||||
|
||||
<div className="flex flex-col space-y-6">
|
||||
<SlackIntegrations />
|
||||
<StorageIntegrations />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/add',
|
||||
)({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
Hello "/app/_settings/_restricted_layout/_admin_only/storage/add"!
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/app/_settings/_restricted_layout/_admin_only/settings/storage/storageId',
|
||||
)({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
Hello "/app/_settings/_restricted_layout/_admin_only/storage/storageId"!
|
||||
</div>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue