From c3c611b8b57652f84c4829456a4eef2fda8b8a5a Mon Sep 17 00:00:00 2001 From: Flavien David Date: Tue, 13 Aug 2024 12:24:15 +0200 Subject: [PATCH 1/2] Add system vault navigation --- front/components/vaults/VaultSideBarMenu.tsx | 112 ++++++++++++++++-- .../[category]/data_sources/index.tsx | 78 ++++++++++++ types/src/front/api_handlers/public/vaults.ts | 8 ++ 3 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 front/pages/w/[wId]/data-sources/vaults/[vaultId]/categories/[category]/data_sources/index.tsx diff --git a/front/components/vaults/VaultSideBarMenu.tsx b/front/components/vaults/VaultSideBarMenu.tsx index 332545b22228..8d49ce9a42d8 100644 --- a/front/components/vaults/VaultSideBarMenu.tsx +++ b/front/components/vaults/VaultSideBarMenu.tsx @@ -73,7 +73,7 @@ const renderVaultItems = (vaults: VaultType[], owner: LightWorkspaceType) => vaults.map((vault) => ( {vault.kind === "system" ? ( - + ) : ( )} @@ -110,9 +110,102 @@ const SubItemIconItemWrapper = ( // System vault. -const SystemVaultMenu = () => { - // TODO(Groups UI) Implement system vault menu. - return <>; +const SYSTEM_VAULTS_ITEMS = [ + { + label: "Connection Management", + visual: RootItemIconWrapper(CloudArrowLeftRightIcon), + category: "managed", + }, + { + label: "Files", + visual: RootItemIconWrapper(FolderIcon), + category: "files", + }, + // TODO(GROUPS_UI) Add support for Dust apps. +]; + +const SystemVaultMenu = ({ + owner, + vault, +}: { + owner: LightWorkspaceType; + vault: VaultType; +}) => { + return ( + <> + {SYSTEM_VAULTS_ITEMS.map((item) => ( + + ))} + + ); +}; + +const SystemVaultItem = ({ + category, + label, + owner, + vault, + visual, +}: { + category: string; + label: string; + owner: LightWorkspaceType; + vault: VaultType; + visual: ReactElement; +}) => { + const router = useRouter(); + + const itemPath = `/w/${owner.sId}/data-sources/vaults/${vault.sId}/categories/${category}/data_sources`; + const isAncestorToCurrentPage = router.asPath.includes(itemPath); + + // Unfold the item if it's an ancestor of the current page. + const [isExpanded, setIsExpanded] = useState(isAncestorToCurrentPage); + + const { isVaultDataSourceOrViewsLoading, vaultDataSourceOrViews } = + useVaultDataSourceOrViews({ + workspaceId: owner.sId, + vaultId: vault.sId, + category, + // System vault only has data sources. + type: "data_sources", + disabled: !isExpanded, + }); + + return ( + router.push(itemPath)} + isSelected={router.asPath === itemPath} + onChevronClick={() => setIsExpanded(!isExpanded)} + visual={visual} + size="md" + areActionsFading={false} + > + {isExpanded && ( + + {vaultDataSourceOrViews && + vaultDataSourceOrViews.map((ds) => ( + + ))} + + )} + + ); }; // Global + regular vaults. @@ -205,13 +298,15 @@ const DATA_SOURCE_OR_VIEW_SUB_ITEMS: { }; const VaultDataSourceOrViewItem = ({ + item, owner, vault, - item, + viewType, }: { + item: DataSourceOrViewInfo; owner: LightWorkspaceType; vault: VaultType; - item: DataSourceOrViewInfo; + viewType: "data_sources" | "data_source_views"; }): ReactElement => { const router = useRouter(); const configuration = item.connectorProvider @@ -220,8 +315,6 @@ const VaultDataSourceOrViewItem = ({ const LogoComponent = configuration?.logoComponent ?? FolderIcon; const label = configuration?.name ?? item.name; - const viewType = - DATA_SOURCE_OR_VIEW_SUB_ITEMS[item.category].dataSourceOrView; const dataSourceOrViewPath = `/w/${owner.sId}/data-sources/vaults/${vault.sId}/categories/${item.category}/${viewType}/${item.sId}`; return ( @@ -278,10 +371,11 @@ const VaultCategoryItem = ({ {vaultDataSourceOrViews && vaultDataSourceOrViews.map((ds) => ( ))} diff --git a/front/pages/w/[wId]/data-sources/vaults/[vaultId]/categories/[category]/data_sources/index.tsx b/front/pages/w/[wId]/data-sources/vaults/[vaultId]/categories/[category]/data_sources/index.tsx new file mode 100644 index 000000000000..30d591051668 --- /dev/null +++ b/front/pages/w/[wId]/data-sources/vaults/[vaultId]/categories/[category]/data_sources/index.tsx @@ -0,0 +1,78 @@ +import type { + DataSourceOrViewCategory, + UserType, + VaultType, +} from "@dust-tt/types"; +import { isDataSourceOrViewCategory } from "@dust-tt/types"; +import type { InferGetServerSidePropsType } from "next"; +import type { ReactElement } from "react"; + +import type { VaultLayoutProps } from "@app/components/vaults/VaultLayout"; +import { VaultLayout } from "@app/components/vaults/VaultLayout"; +import config from "@app/lib/api/config"; +import { withDefaultUserAuthRequirements } from "@app/lib/iam/session"; +import { VaultResource } from "@app/lib/resources/vault_resource"; + +export const getServerSideProps = withDefaultUserAuthRequirements< + VaultLayoutProps & { + category: DataSourceOrViewCategory; + user: UserType; + vault: VaultType; + } +>(async (context, auth) => { + const owner = auth.getNonNullableWorkspace(); + const user = auth.getNonNullableUser(); + const subscription = auth.subscription(); + + if (!subscription) { + return { + notFound: true, + }; + } + + const vault = await VaultResource.fetchById( + auth, + context.query.vaultId as string + ); + if (!vault) { + return { + notFound: true, + }; + } + + const dataSourceOrViewCategory = context.query.category; + if ( + typeof dataSourceOrViewCategory !== "string" || + !isDataSourceOrViewCategory(dataSourceOrViewCategory) + ) { + return { + notFound: true, + }; + } + + return { + props: { + category: dataSourceOrViewCategory, + gaTrackingId: config.getGaTrackingId(), + owner, + subscription, + user, + vault: vault.toJSON(), + }, + }; +}); + +export default function Vault({ + category, + vault, +}: InferGetServerSidePropsType) { + return ( + <> + Manage {category} connections in vault {vault.name} + + ); +} + +Vault.getLayout = (page: ReactElement, pageProps: any) => { + return {page}; +}; diff --git a/types/src/front/api_handlers/public/vaults.ts b/types/src/front/api_handlers/public/vaults.ts index ebad6bdf3457..e7f1e88464bd 100644 --- a/types/src/front/api_handlers/public/vaults.ts +++ b/types/src/front/api_handlers/public/vaults.ts @@ -67,6 +67,14 @@ export const DATA_SOURCE_OR_VIEW_CATEGORIES = [ export type DataSourceOrViewCategory = (typeof DATA_SOURCE_OR_VIEW_CATEGORIES)[number]; +export function isDataSourceOrViewCategory( + category: string +): category is DataSourceOrViewCategory { + return DATA_SOURCE_OR_VIEW_CATEGORIES.includes( + category as DataSourceOrViewCategory + ); +} + export type DataSourceOrViewInfo = { createdAt: number; sId: string; From 80e4ba728028d2e7d9a50af9e6c693c11d7fa487 Mon Sep 17 00:00:00 2001 From: Flavien David Date: Tue, 13 Aug 2024 13:38:05 +0200 Subject: [PATCH 2/2] Move DataSourceOrView to type. --- front/components/vaults/VaultSideBarMenu.tsx | 3 ++- front/lib/swr.ts | 3 +-- types/src/front/data_source_view.ts | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/front/components/vaults/VaultSideBarMenu.tsx b/front/components/vaults/VaultSideBarMenu.tsx index 8d49ce9a42d8..186eea16308c 100644 --- a/front/components/vaults/VaultSideBarMenu.tsx +++ b/front/components/vaults/VaultSideBarMenu.tsx @@ -9,6 +9,7 @@ import { Tree, } from "@dust-tt/sparkle"; import type { + DataSourceOrView, DataSourceOrViewInfo, LightWorkspaceType, VaultType, @@ -272,7 +273,7 @@ const DATA_SOURCE_OR_VIEW_SUB_ITEMS: { icon: ReactElement<{ className?: string; }>; - dataSourceOrView: "data_sources" | "data_source_views"; + dataSourceOrView: DataSourceOrView; }; } = { managed: { diff --git a/front/lib/swr.ts b/front/lib/swr.ts index 659c9f83bccb..35cbd044dfa0 100644 --- a/front/lib/swr.ts +++ b/front/lib/swr.ts @@ -7,6 +7,7 @@ import type { ContentNodesViewType, ConversationMessageReactions, ConversationType, + DataSourceOrView, DataSourceType, GetDataSourceOrViewContentResponseBody, RunRunType, @@ -1341,8 +1342,6 @@ export function useVaultDataSourceOrViews({ }; } -type DataSourceOrView = "data_sources" | "data_source_views"; - export function useVaultDataSourceOrViewContent({ workspaceId, vaultId, diff --git a/types/src/front/data_source_view.ts b/types/src/front/data_source_view.ts index 8848a1a97bd4..34035e9e188c 100644 --- a/types/src/front/data_source_view.ts +++ b/types/src/front/data_source_view.ts @@ -4,3 +4,5 @@ export interface DataSourceViewType { sId: string; updatedAt: number; } + +export type DataSourceOrView = "data_sources" | "data_source_views";