diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
index 922bd07e577..a4aa58474a6 100644
--- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
+++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
@@ -2,7 +2,7 @@ import { Copy } from 'lucide-react'
import { useCallback, useState } from 'react'
import { toast } from 'sonner'
-import { ButtonTooltip } from 'components/ui/ButtonTooltip'
+import { Button } from 'ui'
import { getDecryptedValue } from 'data/vault/vault-secret-decrypted-value-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { copyToClipboard } from 'ui'
@@ -34,29 +34,14 @@ export const CopyEnvButton = ({
).then((values) => values.join('\n'))
copyToClipboard(envFile, () => {
- toast.success('Copied to clipboard')
+ toast.success('Copied to clipboard as .env file')
setIsLoading(false)
})
}, [serverOptions, values])
return (
- }
- onClick={onCopy}
- tooltip={{
- content: {
- text: (
-
- Copies an .env file with the configuration details
- to your clipboard.
-
- ),
- },
- }}
- >
- Copy env values
-
+ } onClick={onCopy}>
+ Copy all
+
)
}
diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
index 7b9bae83c09..ad4d675eaa4 100644
--- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
+++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
@@ -4,7 +4,9 @@ import { useState } from 'react'
import { useParams } from 'common'
import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decrypted-value-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
-import { Button, Input, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
+import { Button, CardContent, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
+import { Input } from 'ui-patterns/DataInputs/Input'
+import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
export const DecryptedReadOnlyInput = ({
value,
@@ -41,52 +43,64 @@ export const DecryptedReadOnlyInput = ({
: value
return (
-
- {label}
-
-
-
-
-
-
- View parameter in Vault
-
-
- }
- value={renderedValue}
- type={secureEntry ? (isLoading ? 'text' : showHidden ? 'text' : 'password') : 'text'}
- descriptionText={descriptionText}
- layout="horizontal"
- actions={
- secureEntry ? (
- isLoading ? (
-
- } />
-
- ) : (
-
- : }
- onClick={() => setShowHidden(!showHidden)}
- />
-
- )
- ) : null
- }
- />
+
+
+ {label}
+
+
+
+
+
+
+ Open in Vault
+
+
+ }
+ description={descriptionText}
+ isReactForm={false}
+ >
+
+ }
+ />
+
+ ) : (
+
+ : }
+ onClick={() => setShowHidden(!showHidden)}
+ />
+
+ )
+ ) : null
+ }
+ />
+
+
)
}
diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
index 68085156f9f..eddf1305688 100644
--- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
+++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
@@ -1,8 +1,14 @@
-import Link from '@ui/components/Typography/Link'
-import { ScaffoldSectionDescription, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
+import {
+ ScaffoldHeader,
+ ScaffoldSection,
+ ScaffoldSectionDescription,
+ ScaffoldSectionTitle,
+} from 'components/layouts/Scaffold'
+import { InlineLink } from 'components/ui/InlineLink'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
+import { DOCS_URL } from 'lib/constants'
import { Card } from 'ui'
import { getCatalogURI, getConnectionURL } from '../StorageSettings/StorageSettings.utils'
import { DESCRIPTIONS } from './constants'
@@ -11,9 +17,9 @@ import { DecryptedReadOnlyInput } from './DecryptedReadOnlyInput'
const wrapperMeta = {
options: [
- { name: 'vault_token', label: 'Vault Token', secureEntry: false },
- { name: 'warehouse', label: 'Warehouse', secureEntry: false },
- { name: 's3.endpoint', label: 'S3 Endpoint', secureEntry: false },
+ { name: 'vault_token', label: 'Vault token', secureEntry: false },
+ { name: 'warehouse', label: 'Warehouse name', secureEntry: false },
+ { name: 's3.endpoint', label: 'S3 endpoint', secureEntry: false },
{ name: 'catalog_uri', label: 'Catalog URI', secureEntry: false },
],
}
@@ -37,24 +43,23 @@ export const SimpleConfigurationDetails = ({ bucketName }: { bucketName: string
}
return (
-
-
+
+
- Configuration Details
-
- You can use the following configuration details to connect to the bucket from your code.
+ Connection details
+
+ Connect to this bucket from an Iceberg client.{' '}
+
+ Learn more
+
-
-
-
- To get AWS credentials, you can create them using the{' '}
-
- S3 Access Keys
- {' '}
- feature.
-
+
+
+
{wrapperMeta.options.map((option) => {
return (
-
+
)
}
diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts
index c9851d3bef3..d7a2711792d 100644
--- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts
+++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts
@@ -8,17 +8,17 @@ export const OPTION_ORDER = [
]
export const LABELS: Record = {
- vault_aws_access_key_id: 'S3 Access Key ID',
- vault_aws_secret_access_key: 'S3 Secret Access Key',
- vault_token: 'Catalog Token',
- warehouse: 'Warehouse Name',
- 's3.endpoint': 'S3 Endpoint',
+ vault_aws_access_key_id: 'S3 access key ID',
+ vault_aws_secret_access_key: 'S3 secret access key',
+ vault_token: 'Catalog token',
+ warehouse: 'Warehouse name',
+ 's3.endpoint': 'S3 endpoint',
catalog_uri: 'Catalog URI',
}
export const DESCRIPTIONS: Record = {
- vault_aws_access_key_id: 'Matches the AWS access key ID from a S3 Access Key.',
- vault_aws_secret_access_key: 'Matches the AWS secret access from a S3 Access Key.',
+ vault_aws_access_key_id: 'Matches the AWS access key ID from an S3 access key.',
+ vault_aws_secret_access_key: 'Matches the AWS secret access from an S3 access key.',
vault_token: 'Corresponds to the service role key.',
warehouse: 'Matches the name of the bucket.',
's3.endpoint': '',
diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx
index 218a83eb2d8..13e365d7f2d 100644
--- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx
+++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx
@@ -1,7 +1,4 @@
-import { snakeCase, uniq } from 'lodash'
-import Link from 'next/link'
-import { useMemo } from 'react'
-
+import { useIsNewStorageUIEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { INTEGRATIONS } from 'components/interfaces/Integrations/Landing/Integrations.constants'
import { WRAPPER_HANDLERS } from 'components/interfaces/Integrations/Wrappers/Wrappers.constants'
import { WrapperMeta } from 'components/interfaces/Integrations/Wrappers/Wrappers.types'
@@ -10,14 +7,17 @@ import {
formatWrapperTables,
wrapperMetaComparator,
} from 'components/interfaces/Integrations/Wrappers/Wrappers.utils'
+import { BUCKET_TYPES } from 'components/interfaces/Storage/Storage.constants'
+import { PageLayout } from 'components/layouts/PageLayout/PageLayout'
import {
ScaffoldContainer,
ScaffoldHeader,
+ ScaffoldSection,
ScaffoldSectionDescription,
ScaffoldSectionTitle,
- ScaffoldTitle,
} from 'components/layouts/Scaffold'
import { DocsButton } from 'components/ui/DocsButton'
+import { InlineLink } from 'components/ui/InlineLink'
import {
DatabaseExtension,
useDatabaseExtensionsQuery,
@@ -29,20 +29,24 @@ import { useIcebergWrapperCreateMutation } from 'data/storage/iceberg-wrapper-cr
import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decrypted-value-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { DOCS_URL } from 'lib/constants'
+import { snakeCase, uniq } from 'lodash'
+import { Plus } from 'lucide-react'
+import Link from 'next/link'
+import { useMemo, useState } from 'react'
import {
- Alert_Shadcn_,
- AlertDescription_Shadcn_,
- AlertTitle_Shadcn_,
Button,
Card,
+ CardContent,
Table,
TableBody,
+ TableCell,
TableHead,
TableHeader,
TableRow,
- WarningIcon,
} from 'ui'
+import { Admonition } from 'ui-patterns/admonition'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
+import { DeleteBucketModal } from '../DeleteBucketModal'
import { DESCRIPTIONS, LABELS, OPTION_ORDER } from './constants'
import { CopyEnvButton } from './CopyEnvButton'
import { DecryptedReadOnlyInput } from './DecryptedReadOnlyInput'
@@ -51,6 +55,8 @@ import { SimpleConfigurationDetails } from './SimpleConfigurationDetails'
import { useIcebergWrapperExtension } from './useIcebergWrapper'
export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => {
+ const [modal, setModal] = useState<'delete' | null>(null)
+ const isStorageV2 = useIsNewStorageUIEnabled()
const { data: project } = useSelectedProjectQuery()
const { data: extensionsData } = useDatabaseExtensionsQuery({
@@ -131,6 +137,8 @@ export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => {
const wrappersExtension = extensionsData?.find((ext) => ext.name === 'wrappers')
+ const config = BUCKET_TYPES['analytics']
+
const state = isFDWsLoading
? 'loading'
: extensionState === 'installed'
@@ -140,131 +148,225 @@ export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => {
: extensionState
return (
-
-
-
-
- Analytics Bucket {bucket.name}
-
-
- Namespaces and tables connected to this bucket.
-
-
-
-
-
- {state === 'loading' && }
- {state === 'not-installed' && (
-
- )}
- {state === 'needs-upgrade' && (
-
- )}
- {state === 'added' && wrapperInstance && (
- <>
-
- {isLoadingNamespaces || isFDWsLoading ? (
-
- ) : namespaces.length === 0 ? (
-
- No namespaces in this bucket
-
- Create a namespace and add some data{' '}
-
- {' '}
- to get started
-
-
-
- ) : (
-
-
-
-
- Namespace
- Schema
- Tables
-
-
-
-
- {namespaces.map(({ namespace, schema, tables }) => (
-
- ))}
-
-
-
- )}
-
+ <>
+ ] : []}
+ >
+
+ {state === 'loading' && (
+
+
+
+ )}
+ {state === 'not-installed' && (
+
+ )}
+ {state === 'needs-upgrade' && (
+
+ )}
-
-
-
- Connection Details
-
- You can use the following parameters to connect to the bucket from an Iceberg
- client.
-
-
-
+ {state === 'added' && wrapperInstance && (
+ <>
+ {isStorageV2 ? (
+
+
+
+ Tables
+
+ Analytics tables connected to this bucket.
+
+
+ } onClick={() => {}}>
+ New table
+
+
+
+
+
+
+
+ Name
+ Schema
+ Created at
+
+
+
+
+
+ No tables yet
+
+ Create an analytics table to get started
+
+
+
+
+
+
+
+ ) : (
+
+
+ Namespaces
+
+ Connected namespaces and tables.
+
+
+
+ {isLoadingNamespaces || isFDWsLoading ? (
+
+ ) : namespaces.length === 0 ? (
+
+
+
+
+ Namespace
+ Schema
+ Tables
+
+
+
+
+
+
+
+ No namespaces in this bucket
+
+
+ Create a namespace and add some data
+
+
+
+
+
+
+ ) : (
+
+
+
+
+ Namespace
+ Schema
+ Tables
+
+
+
+
+ {namespaces.map(({ namespace, schema, tables }) => (
+
+ ))}
+
+
+
+ )}
+
+ )}
+
+
+
+
+ Configuration
+
+ Connect to this bucket from an Iceberg client.{' '}
+
+ Learn more
+
+
+
!option.hidden && wrapperValues[option.name]
)}
values={wrapperValues}
/>
-
+
+
+
+ {wrapperMeta.server.options
+ .filter((option) => !option.hidden && wrapperValues[option.name])
+ .sort((a, b) => OPTION_ORDER.indexOf(a.name) - OPTION_ORDER.indexOf(b.name))
+ .map((option) => {
+ return (
+
+ )
+ })}
+
+
+ >
+ )}
+ {state === 'missing' &&
}
+
+
+
+
+
+
+
Delete bucket
+
+ This will also delete any data in your bucket. Make sure you have a backup if
+ you want to keep your data.
+
-
-
- {wrapperMeta.server.options
- .filter((option) => !option.hidden && wrapperValues[option.name])
- .sort((a, b) => OPTION_ORDER.indexOf(a.name) - OPTION_ORDER.indexOf(b.name))
- .map((option) => {
- return (
-
- )
- })}
-
-
- >
- )}
- {state === 'missing' &&
}
-
-
+ {
+ setModal('delete')
+ }}
+ >
+ Delete bucket
+
+
+
+
+
+
+
+ setModal(null)}
+ />
+ >
)
}
@@ -284,24 +386,22 @@ const ExtensionNotInstalled = ({
return (
<>
-
-
-
- You need to install the wrappers extension to connect this Analytics bucket to the
- database.
-
-
+
+
- The {wrapperMeta.label} wrapper requires the Wrappers extension to be installed. You can
- install version {wrappersExtension?.installed_version}
+ The Wrappers extension is required in order to query analytics tables.{' '}
{databaseNeedsUpgrading &&
- ' which is below the minimum version that supports Iceberg wrapper'}
- . Please {databaseNeedsUpgrading && 'upgrade your database then '}install the{' '}
- wrappers extension to create this wrapper.
+ 'Please first upgrade your database and then install the extension.'}{' '}
+
+ Learn more
+
-
-
-
+ {}}>
- {databaseNeedsUpgrading ? 'Upgrade database' : 'Install wrappers extension'}
+ {databaseNeedsUpgrading ? 'Upgrade database' : 'Install extension'}
-
-
+
+
>
)
@@ -337,26 +437,19 @@ const ExtensionNeedsUpgrade = ({
return (
<>
-
-
-
- Your extension version is outdated for this wrapper.
-
-
+
+
The {wrapperMeta.label} wrapper requires a minimum extension version of{' '}
{wrapperMeta.minimumExtensionVersion}. You have version{' '}
{wrappersExtension?.installed_version} installed. Please{' '}
- {databaseNeedsUpgrading && 'upgrade your database then '}update the extension by
- disabling and enabling the wrappers extension to create
- this wrapper.
+ {databaseNeedsUpgrading && 'first upgrade your database, and then '}update the extension
+ by disabling and enabling the Wrappers extension.
-
- Warning: Before reinstalling the wrapper extension, you must first remove all existing
- wrappers. Afterward, you can recreate the wrappers.
+
+ Before reinstalling the wrapper extension, you must first remove all existing wrappers.
+ Afterward, you can recreate the wrappers.
-
-
- {databaseNeedsUpgrading ? 'Upgrade database' : 'View wrappers extension'}
+ {databaseNeedsUpgrading ? 'Upgrade database' : 'Extensions'}
-
-
+
+
>
)
@@ -385,20 +478,14 @@ const WrapperMissing = ({ bucketName }: { bucketName: string }) => {
return (
<>
-
-
-
- This Analytics bucket does not have a foreign data wrapper setup.
-
-
- You need to setup a wrapper to connect this bucket to the database.
-
-
+
+
+ The Iceberg Wrapper integration is required in order to query analytics tables.
- Setup a wrapper
+ Install wrapper
-
-
+
+
>
)
diff --git a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
index 994707ac6c7..6f5171d26be 100644
--- a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
+++ b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
@@ -550,59 +550,61 @@ export const CreateBucketModal = ({
) : (
<>
{icebergWrapperExtensionState === 'installed' ? (
-
-
- Supabase will setup a
-
- foreign data wrapper
- {bucketName && {`${bucketName}_fdw`} }
-
-
- {' '}
- for easier access to the data. This action will also create{' '}
+
+
+
+ Supabase will setup a
- S3 Access Keys
- {bucketName && (
- <>
- {' '}
- named {`${bucketName}_keys`}
- >
- )}
+ foreign data wrapper
+ {bucketName && {`${bucketName}_fdw`} }
- and
-
- four Vault Secrets
- {bucketName && (
- <>
- {' '}
- prefixed with{' '}
- {`${bucketName}_vault_`}
- >
- )}
-
- .
-
-
-
- As a final step, you'll need to create an{' '}
- Iceberg namespace before you
- connect the Iceberg data to your database.
-
-
+
+ {' '}
+ for easier access to the data. This action will also create{' '}
+
+ S3 Access Keys
+ {bucketName && (
+ <>
+ {' '}
+ named {`${bucketName}_keys`}
+ >
+ )}
+
+ and
+
+ four Vault Secrets
+ {bucketName && (
+ <>
+ {' '}
+ prefixed with{' '}
+ {`${bucketName}_vault_`}
+ >
+ )}
+
+ .
+
+
+
+ As a final step, you'll need to create an{' '}
+ Iceberg namespace before you
+ connect the Iceberg data to your database.
+
+
+
) : (
-
+
You need to install the Iceberg wrapper extension to connect your Analytic
diff --git a/apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts b/apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts
index 2023b7c0446..ad340ad84d2 100644
--- a/apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts
+++ b/apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts
@@ -1,11 +1,19 @@
import { useParams } from 'common'
import { useBucketsQuery } from 'data/storage/buckets-query'
+import { useStorageV2Page } from '../Storage.utils'
export const useSelectedBucket = () => {
+ const page = useStorageV2Page()
const { ref, bucketId } = useParams()
const { data: buckets = [], isSuccess, isError, error } = useBucketsQuery({ projectRef: ref })
- const bucket = buckets.find((b) => b.id === bucketId)
+ const bucketsByType =
+ page === 'files'
+ ? buckets.filter((b) => b.type === 'STANDARD')
+ : page === 'analytics'
+ ? buckets.filter((b) => b.type === 'ANALYTICS')
+ : buckets
+ const bucket = bucketsByType.find((b) => b.id === bucketId)
return { bucket, isSuccess, isError, error }
}
diff --git a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
index aebbc62f7ec..17c85bcc69a 100644
--- a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
+++ b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
@@ -26,6 +26,7 @@ const StorageLayout = ({ title, children }: StorageLayoutProps) => {
const suffix = !!featurePreviewModal ? `?featurePreviewModal=${featurePreviewModal}` : ''
if (isStorageV2) {
+ // From old UI to new UI
if (pathname.endsWith('/storage/settings')) {
router.push(`/project/${ref}/storage/files/settings${suffix}`)
} else if (pathname.endsWith('/storage/policies')) {
@@ -38,6 +39,7 @@ const StorageLayout = ({ title, children }: StorageLayoutProps) => {
}
}
} else {
+ // From new UI to old UI
if (pathname.endsWith('/files/settings')) {
router.push(`/project/${ref}/storage/settings${suffix}`)
} else if (pathname.endsWith('/files/policies')) {
@@ -47,6 +49,12 @@ const StorageLayout = ({ title, children }: StorageLayoutProps) => {
pathname.endsWith('/analytics/buckets/[bucketId]')
) {
router.push(`/project/${ref}/storage/buckets/${bucketId}${suffix}`)
+ } else if (
+ pathname.endsWith('/storage/files') ||
+ pathname.endsWith('/storage/analytics') ||
+ pathname.endsWith('/storage/vectors')
+ ) {
+ router.push(`/project/${ref}/storage/buckets`)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/apps/studio/components/ui/InlineLink.tsx b/apps/studio/components/ui/InlineLink.tsx
index 1d69d11e68c..d82ba4eefa0 100644
--- a/apps/studio/components/ui/InlineLink.tsx
+++ b/apps/studio/components/ui/InlineLink.tsx
@@ -11,7 +11,7 @@ interface InlineLinkProps {
}
export const InlineLinkClassName =
- 'underline transition underline-offset-2 decoration-foreground-lighter hover:decoration-foreground text-foreground'
+ 'underline transition underline-offset-2 decoration-foreground-lighter hover:decoration-foreground text-inherit hover:text-foreground'
export const InlineLink = ({
href,
diff --git a/apps/studio/pages/project/[ref]/storage/analytics/buckets/[bucketId].tsx b/apps/studio/pages/project/[ref]/storage/analytics/buckets/[bucketId].tsx
new file mode 100644
index 00000000000..19e8e676817
--- /dev/null
+++ b/apps/studio/pages/project/[ref]/storage/analytics/buckets/[bucketId].tsx
@@ -0,0 +1,48 @@
+import { useParams } from 'common'
+
+import { AnalyticBucketDetails } from 'components/interfaces/Storage/AnalyticBucketDetails'
+import StorageBucketsError from 'components/interfaces/Storage/StorageBucketsError'
+import { useSelectedBucket } from 'components/interfaces/Storage/StorageExplorer/useSelectedBucket'
+import DefaultLayout from 'components/layouts/DefaultLayout'
+import StorageLayout from 'components/layouts/StorageLayout/StorageLayout'
+import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
+import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
+import type { NextPageWithLayout } from 'types'
+
+const AnalyticsBucketPage: NextPageWithLayout = () => {
+ const { bucketId } = useParams()
+ const { data: project } = useSelectedProjectQuery()
+ const { projectRef } = useStorageExplorerStateSnapshot()
+ const { bucket, error, isSuccess, isError } = useSelectedBucket()
+
+ // [Joshen] Checking against projectRef from storage explorer to check if the store has initialized
+ if (!project || !projectRef) return null
+
+ return (
+
+ {isError &&
}
+
+ {isSuccess ? (
+ !bucket ? (
+
+
Bucket {bucketId} cannot be found
+
+ ) : bucket.type === 'ANALYTICS' ? (
+
+ ) : (
+
+
This bucket is not an analytics bucket
+
+ )
+ ) : null}
+
+ )
+}
+
+AnalyticsBucketPage.getLayout = (page) => (
+
+ {page}
+
+)
+
+export default AnalyticsBucketPage