mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 10:55:06 +03:00
remove integrations feature preview and old layout
This commit is contained in:
23 files changed
+35
-500
No files matched your search
@@ -134,12 +134,6 @@ export const useIsSqlEditorManualSaveEnabled = () => {
|
||||
return sqlEditorManualSaveEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE]
|
||||
}
|
||||
|
||||
export const useIsMarketplaceEnabled = () => {
|
||||
const { flags } = useFeaturePreviewContext()
|
||||
const isMarketplaceEnabled = useFlag('marketplaceIntegrations')
|
||||
return isMarketplaceEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE]
|
||||
}
|
||||
|
||||
export const useFeaturePreviewModal = () => {
|
||||
const featurePreviews = useFeaturePreviews()
|
||||
const [featurePreviewModal, setFeaturePreviewModal] = useQueryState('featurePreviewModal')
|
||||
|
||||
@@ -50,7 +50,6 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: {
|
||||
[LOCAL_STORAGE_KEYS.UI_PREVIEW_PLATFORM_WEBHOOKS]: <PlatformWebhooksPreview />,
|
||||
[LOCAL_STORAGE_KEYS.UI_PREVIEW_JIT_DB_ACCESS]: <JitDbAccessPreview />,
|
||||
[LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE]: <SqlEditorManualSavePreview />,
|
||||
[LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE]: <IntegrationsLayoutPreview />,
|
||||
}
|
||||
|
||||
export const FeaturePreviewModal = () => {
|
||||
|
||||
@@ -22,7 +22,6 @@ export type FeaturePreview = {
|
||||
export const useFeaturePreviews = (): FeaturePreview[] => {
|
||||
const platformWebhooksEnabled = useFlag('platformWebhooks')
|
||||
const jitDbAccessEnabled = useFlag('jitDbAccess')
|
||||
const isMarketplaceEnabled = useFlag('marketplaceIntegrations')
|
||||
const sqlEditorManualSaveEnabled = useFlag('sqlEditorManualSave')
|
||||
|
||||
const unifiedLogsDefaultOptIn = useFlag('unifiedLogsDefaultOptIn')
|
||||
@@ -90,16 +89,6 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
|
||||
isDefaultOptIn: false,
|
||||
getRoute: (ref?: string) => `/project/${ref}/database/column-privileges`,
|
||||
},
|
||||
{
|
||||
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE,
|
||||
name: 'One-Click Integrations',
|
||||
discussionsUrl: undefined,
|
||||
enabled: isMarketplaceEnabled,
|
||||
isNew: true,
|
||||
isPlatformOnly: false,
|
||||
isDefaultOptIn: true,
|
||||
getRoute: (ref?: string) => `/project/${ref}/integrations`,
|
||||
},
|
||||
{
|
||||
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE,
|
||||
name: 'Disable snippet auto-saving',
|
||||
@@ -114,7 +103,6 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
|
||||
unifiedLogsDefaultOptIn,
|
||||
platformWebhooksEnabled,
|
||||
jitDbAccessEnabled,
|
||||
isMarketplaceEnabled,
|
||||
sqlEditorManualSaveEnabled,
|
||||
]
|
||||
)
|
||||
|
||||
+1
-4
@@ -1,8 +1,6 @@
|
||||
import { PropsWithChildren } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
interface ConstrainedIntegrationTabScaffoldProps extends PropsWithChildren {
|
||||
className?: string
|
||||
}
|
||||
@@ -11,9 +9,8 @@ export const ConstrainedIntegrationTabScaffold = ({
|
||||
children,
|
||||
className,
|
||||
}: ConstrainedIntegrationTabScaffoldProps) => {
|
||||
const isMarketplace = useIsMarketplaceEnabled()
|
||||
return (
|
||||
<div className={cn('w-full py-6 xl:py-10', !isMarketplace && 'px-6 xl:px-10', className)}>
|
||||
<div className={cn('w-full py-6 xl:py-10', className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const CronOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
||||
return <IntegrationOverviewTab />
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { DataApiURLSettings } from './DataApiURLSettings'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const DataApiOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <DataApiURLSettings />
|
||||
|
||||
return (
|
||||
<IntegrationOverviewTab>
|
||||
<div className="px-4 md:px-10 max-w-4xl space-y-4">
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const GraphQLOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
||||
return <IntegrationOverviewTab />
|
||||
return <RequiredExtensionsSection />
|
||||
}
|
||||
+1
-7
@@ -1,11 +1,5 @@
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
import { IntegrationOverviewTab } from './IntegrationOverviewTab'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const MarketplaceIntegrationOverviewTab = (props: PropsWithChildren) => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return null
|
||||
return <IntegrationOverviewTab>{props.children}</IntegrationOverviewTab>
|
||||
return null
|
||||
}
|
||||
+1
-6
@@ -1,7 +1,5 @@
|
||||
import { Badge, Card, CardContent, cn, Separator } from 'ui'
|
||||
|
||||
import { useIsMarketplaceEnabled } from '../../App/FeaturePreview/FeaturePreviewContext'
|
||||
import { MissingExtensionAlert } from './MissingExtensionAlert'
|
||||
import { useIntegrationDetail } from '@/components/interfaces/Integrations/Landing/useIntegrationDetail'
|
||||
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
@@ -21,7 +19,6 @@ export const RequiredExtensionsSection = ({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
const requiredExtensions = integration?.requiredExtensions ?? []
|
||||
|
||||
@@ -59,10 +56,8 @@ export const RequiredExtensionsSection = ({
|
||||
{extension ? (
|
||||
isInstalled ? (
|
||||
<Badge variant="secondary">Installed</Badge>
|
||||
) : isMarketplaceEnabled ? (
|
||||
<Badge variant="warning">Required</Badge>
|
||||
) : (
|
||||
<MissingExtensionAlert extension={extension} />
|
||||
<Badge variant="warning">Required</Badge>
|
||||
)
|
||||
) : (
|
||||
<span className="text-foreground-muted">Unavailable</span>
|
||||
|
||||
@@ -13,7 +13,6 @@ import { useContext, useMemo } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { INTEGRATIONS, Loading, type IntegrationDefinition } from './Integrations.constants'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import {
|
||||
useMarketplaceIntegrationsQuery,
|
||||
type MarketplaceIntegration,
|
||||
@@ -55,16 +54,15 @@ const isPreviewEnabled = (featureFlags: FeatureFlagContextType, listingSlug: str
|
||||
|
||||
const useMarketplaceListings = () => {
|
||||
const { hasLoaded } = useContext(FeatureFlagContext)
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
const { data: marketplaceData, error } = useMarketplaceIntegrationsQuery({
|
||||
enabled: isMarketplaceEnabled,
|
||||
enabled: true,
|
||||
})
|
||||
const isPending =
|
||||
IS_PLATFORM && (!hasLoaded || (isMarketplaceEnabled && !marketplaceData && !error))
|
||||
IS_PLATFORM && (!hasLoaded || (!marketplaceData && !error))
|
||||
const isSuccess =
|
||||
!IS_PLATFORM || (hasLoaded && (!isMarketplaceEnabled || (!!marketplaceData && !error)))
|
||||
const isError = IS_PLATFORM && isMarketplaceEnabled && !!error
|
||||
!IS_PLATFORM || (hasLoaded && (!!marketplaceData && !error))
|
||||
const isError = IS_PLATFORM && !!error
|
||||
|
||||
// This flag can globally enable preview listings for all partners for a given user (i.e. for Supabase users)
|
||||
const previewAllListingsEnabled = useFlag<boolean>('previewMarketplaceListingsEnabled')
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
getInstallActionType,
|
||||
type InstallActionType,
|
||||
} from './useIntegrationDetail.utils'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
@@ -27,7 +26,6 @@ export const useIntegrationDetail = () => {
|
||||
const { ref, id, pageId, childId } = useParams()
|
||||
|
||||
const { integrationsWrappers } = useIsFeatureEnabled(['integrations:wrappers'])
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: extensions } = useDatabaseExtensionsQuery({
|
||||
@@ -65,10 +63,9 @@ export const useIntegrationDetail = () => {
|
||||
getFilteredNavItems({
|
||||
integration,
|
||||
isInstalled,
|
||||
isMarketplaceEnabled,
|
||||
areRequiredExtensionsInstalled,
|
||||
}),
|
||||
[integration, isInstalled, isMarketplaceEnabled, areRequiredExtensionsInstalled]
|
||||
[integration, isInstalled, areRequiredExtensionsInstalled]
|
||||
)
|
||||
|
||||
const activeRoute = pageId ?? 'overview'
|
||||
@@ -105,11 +102,10 @@ export const useIntegrationDetail = () => {
|
||||
() =>
|
||||
getInstallActionType({
|
||||
integration,
|
||||
isMarketplaceEnabled,
|
||||
areRequiredExtensionsInstalled,
|
||||
isInstalled,
|
||||
}),
|
||||
[integration, isMarketplaceEnabled, areRequiredExtensionsInstalled, isInstalled]
|
||||
[integration, areRequiredExtensionsInstalled, isInstalled]
|
||||
)
|
||||
|
||||
const isReady = !!router?.isReady
|
||||
|
||||
+2
-6
@@ -27,17 +27,15 @@ export function areRequiredExtensionsInstalledFor(
|
||||
export function getFilteredNavItems({
|
||||
integration,
|
||||
isInstalled,
|
||||
isMarketplaceEnabled,
|
||||
areRequiredExtensionsInstalled,
|
||||
}: {
|
||||
integration: IntegrationDefinition | undefined
|
||||
isInstalled: boolean
|
||||
isMarketplaceEnabled: boolean
|
||||
areRequiredExtensionsInstalled: boolean
|
||||
}): Navigation[] {
|
||||
if (!integration?.navigation) return []
|
||||
if (isInstalled || integration.type !== 'wrapper') return integration.navigation
|
||||
if (isMarketplaceEnabled || areRequiredExtensionsInstalled) return integration.navigation
|
||||
if (areRequiredExtensionsInstalled) return integration.navigation
|
||||
return integration.navigation.filter((nav) => nav.route !== 'wrappers')
|
||||
}
|
||||
|
||||
@@ -46,18 +44,16 @@ export function getFilteredNavItems({
|
||||
*/
|
||||
export function getInstallActionType({
|
||||
integration,
|
||||
isMarketplaceEnabled,
|
||||
areRequiredExtensionsInstalled,
|
||||
isInstalled,
|
||||
}: {
|
||||
integration: IntegrationDefinition | undefined
|
||||
isMarketplaceEnabled: boolean
|
||||
areRequiredExtensionsInstalled: boolean
|
||||
isInstalled: boolean
|
||||
}): InstallActionType {
|
||||
if (!integration) return null
|
||||
if (integration.type === 'oauth') return 'oauth'
|
||||
if (integration.type === 'wrapper' && (isMarketplaceEnabled || areRequiredExtensionsInstalled)) {
|
||||
if (integration.type === 'wrapper' && areRequiredExtensionsInstalled) {
|
||||
return 'add-wrapper'
|
||||
}
|
||||
if (isInstalled) return 'installed'
|
||||
|
||||
@@ -1,52 +1,5 @@
|
||||
import { useParams } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { Button } from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
|
||||
import { useQueuesExposePostgrestStatusQuery } from '@/data/database-queues/database-queues-expose-postgrest-status-query'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
|
||||
const QueuesContent = () => {
|
||||
const { ref } = useParams()
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
const { data: isExposed } = useQueuesExposePostgrestStatusQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const { data: extensions = [] } = useDatabaseExtensionsQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const isQueuesInstalled = !!extensions.find((x) => x.name === 'pgmq')?.installed_version
|
||||
|
||||
if (isExposed) return null
|
||||
|
||||
return (
|
||||
<Admonition
|
||||
type="default"
|
||||
title="Queues can be managed via any Supabase client library or PostgREST endpoints"
|
||||
>
|
||||
<p>
|
||||
You may choose to toggle the exposure of Queues through Data APIs via the queues settings
|
||||
</p>
|
||||
|
||||
{isQueuesInstalled && (
|
||||
<Button asChild variant="default" className="mt-2">
|
||||
<Link href={`/project/${ref}/integrations/queues/settings`}>Manage queues settings</Link>
|
||||
</Button>
|
||||
)}
|
||||
</Admonition>
|
||||
)
|
||||
}
|
||||
|
||||
export const QueuesOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
||||
return <IntegrationOverviewTab actions={<QueuesContent />} />
|
||||
return <RequiredExtensionsSection />
|
||||
}
|
||||
@@ -1,10 +1,5 @@
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const VaultOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
||||
return <IntegrationOverviewTab />
|
||||
return <RequiredExtensionsSection />
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import { toast } from 'sonner'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { IntegrationOverviewTab } from '../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../Integration/RequiredExtensionsSection'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
||||
import { NoPermission } from '@/components/ui/NoPermission'
|
||||
import { useHooksEnableMutation } from '@/data/database/hooks-enable-mutation'
|
||||
@@ -89,11 +87,5 @@ const WebhooksContent = () => {
|
||||
}
|
||||
|
||||
export const WebhooksOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) {
|
||||
return <RequiredExtensionsSection />
|
||||
}
|
||||
|
||||
return <IntegrationOverviewTab hideRequiredExtensionsSection actions={<WebhooksContent />} />
|
||||
return <RequiredExtensionsSection />
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
NewTable,
|
||||
} from './Wrappers.utils'
|
||||
import WrapperTableEditor from './WrapperTableEditor'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { getExtensionDefaultSchema } from '@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/IntegrationOverviewTabV2.utils'
|
||||
import { RequiredExtensionsSection } from '@/components/interfaces/Integrations/Integration/RequiredExtensionsSection'
|
||||
import { useIntegrationDetail } from '@/components/interfaces/Integrations/Landing/useIntegrationDetail'
|
||||
@@ -66,7 +65,6 @@ export const CreateWrapperSheet = ({
|
||||
onCloseWithConfirmation,
|
||||
}: CreateWrapperSheetProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
const { integration } = useIntegrationDetail()
|
||||
|
||||
const { data: project } = useSelectedProjectQuery()
|
||||
@@ -261,18 +259,16 @@ export const CreateWrapperSheet = ({
|
||||
<SheetTitle>Create a {wrapperMeta.label} wrapper</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="grow overflow-y-auto">
|
||||
{isMarketplaceEnabled && (
|
||||
<div className="px-5 py-5 flex flex-col gap-y-4 border-b">
|
||||
{needsExtensions && (
|
||||
<Admonition
|
||||
type="warning"
|
||||
title="Required extensions will be installed"
|
||||
description="Proceeding will also install the extensions required by this wrapper."
|
||||
/>
|
||||
)}
|
||||
<RequiredExtensionsSection hideSeparator />
|
||||
</div>
|
||||
)}
|
||||
<div className="px-5 py-5 flex flex-col gap-y-4 border-b">
|
||||
{needsExtensions && (
|
||||
<Admonition
|
||||
type="warning"
|
||||
title="Required extensions will be installed"
|
||||
description="Proceeding will also install the extensions required by this wrapper."
|
||||
/>
|
||||
)}
|
||||
<RequiredExtensionsSection hideSeparator />
|
||||
</div>
|
||||
<FormSection header={<FormSectionLabel>Wrapper Configuration</FormSectionLabel>}>
|
||||
<FormSectionContent className="flex flex-col space-y-2" loading={false}>
|
||||
<FormField
|
||||
|
||||
@@ -13,7 +13,6 @@ import { CreateIcebergWrapperSheet } from './CreateIcebergWrapperSheet'
|
||||
import { CreateWrapperSheet } from './CreateWrapperSheet'
|
||||
import { WRAPPERS } from './Wrappers.constants'
|
||||
import { WrapperTable } from './WrapperTable'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
|
||||
import { DiscardChangesConfirmationDialog } from '@/components/ui-patterns/Dialogs/DiscardChangesConfirmationDialog'
|
||||
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
||||
@@ -192,10 +191,6 @@ export const WrapperContent = () => {
|
||||
}
|
||||
|
||||
export const WrapperOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) return <RequiredExtensionsSection />
|
||||
|
||||
return (
|
||||
<IntegrationOverviewTab actions={<AddNewWrapperCTA />}>
|
||||
<div className="mx-10">
|
||||
|
||||
+4
-18
@@ -23,7 +23,6 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { IntegrationOverviewTab } from '../../Integration/IntegrationOverviewTab'
|
||||
import { RequiredExtensionsSection } from '../../Integration/RequiredExtensionsSection'
|
||||
import { InstallationError } from './InstallationError'
|
||||
import { IntegrationInstalledActions, IntegrationNotInstalledActions } from './IntegrationActions'
|
||||
@@ -38,7 +37,6 @@ import {
|
||||
isUninstalling,
|
||||
} from './stripe-sync-status'
|
||||
import { StripeSyncChangesCard } from './StripeSyncChangesCard'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { useStripeSyncStatus } from '@/components/interfaces/Integrations/templates/StripeSyncEngine/useStripeSyncStatus'
|
||||
import { useStripeSyncInstallMutation } from '@/data/database-integrations/stripe/stripe-sync-install-mutation'
|
||||
import { useStripeSyncUninstallMutation } from '@/data/database-integrations/stripe/stripe-sync-uninstall-mutation'
|
||||
@@ -392,22 +390,10 @@ const StripeSyncContent = ({ hideInstallCTA = false }: { hideInstallCTA?: boolea
|
||||
}
|
||||
|
||||
export const StripeSyncEngineOverviewTab = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
if (isMarketplaceEnabled) {
|
||||
return (
|
||||
<>
|
||||
<RequiredExtensionsSection />
|
||||
<StripeSyncContent hideInstallCTA />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<IntegrationOverviewTab>
|
||||
<div className="px-4 md:px-10 max-w-4xl space-y-4">
|
||||
<StripeSyncContent />
|
||||
</div>
|
||||
</IntegrationOverviewTab>
|
||||
<>
|
||||
<RequiredExtensionsSection />
|
||||
<StripeSyncContent hideInstallCTA />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import { PropsWithChildren } from 'react'
|
||||
import { Menu, Separator } from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import { useInstalledIntegrations } from '@/components/interfaces/Integrations/Landing/useInstalledIntegrations'
|
||||
import { ProjectLayout } from '@/components/layouts/ProjectLayout'
|
||||
import { AlertError } from '@/components/ui/AlertError'
|
||||
@@ -46,7 +45,6 @@ const IntegrationCategoriesMenu = ({ page }: { page: string }) => {
|
||||
const router = useRouter()
|
||||
const { ref } = useParams()
|
||||
const { hasLoaded: flagsLoaded } = useFeatureFlags()
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
|
||||
const urlParams = new URLSearchParams(router.asPath.split('?')[1] || '')
|
||||
const categoryParam = urlParams.get('category')
|
||||
@@ -54,10 +52,10 @@ const IntegrationCategoriesMenu = ({ page }: { page: string }) => {
|
||||
|
||||
const { integrationsWrappers: showWrappers } = useIsFeatureEnabled(['integrations:wrappers'])
|
||||
const { data: categories = [], isPending: isPendingCategories } = useMarketplaceCategoriesQuery({
|
||||
enabled: isMarketplaceEnabled,
|
||||
enabled: true,
|
||||
})
|
||||
const { data: listings = [], isPending: isPendingListings } = useMarketplaceIntegrationsQuery({
|
||||
enabled: isMarketplaceEnabled,
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
const populatedCategoryIds = new Set(
|
||||
@@ -68,7 +66,7 @@ const IntegrationCategoriesMenu = ({ page }: { page: string }) => {
|
||||
)
|
||||
|
||||
const isLoading = IS_PLATFORM
|
||||
? !flagsLoaded || (isMarketplaceEnabled && (isPendingCategories || isPendingListings))
|
||||
? !flagsLoaded || (isPendingCategories || isPendingListings)
|
||||
: false
|
||||
|
||||
const allCategories = [
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
import { ProjectIntegrationsLayout } from './ProjectIntegrationsLayout'
|
||||
import { ProjectMarketplaceLayout } from './ProjectMarketplaceLayout'
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
|
||||
export const ProjectIntegrationsLayoutDispatch = ({ children }: PropsWithChildren) => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
if (isMarketplaceEnabled) {
|
||||
return <ProjectMarketplaceLayout>{children}</ProjectMarketplaceLayout>
|
||||
}
|
||||
return <ProjectIntegrationsLayout>{children}</ProjectIntegrationsLayout>
|
||||
return <ProjectMarketplaceLayout>{children}</ProjectMarketplaceLayout>
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import LegacyIntegrationPage from '@/components/interfaces/Integrations/Integration/LegacyIntegrationPage'
|
||||
import { MarketplaceDetail } from '@/components/interfaces/Integrations/Marketplace/MarketplaceDetail'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import { ProjectIntegrationsLayoutDispatch } from '@/components/layouts/ProjectIntegrationsLayoutDispatch'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const IntegrationPage: NextPageWithLayout = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
if (isMarketplaceEnabled) return <MarketplaceDetail />
|
||||
return <LegacyIntegrationPage />
|
||||
return <MarketplaceDetail />
|
||||
}
|
||||
|
||||
IntegrationPage.getLayout = (page) => (
|
||||
|
||||
@@ -1,320 +1,10 @@
|
||||
import { IS_PLATFORM, useFeatureFlags } from 'common'
|
||||
import { Database } from 'common/marketplace.types'
|
||||
import { Search } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { parseAsString, useQueryState } from 'nuqs'
|
||||
import { useMemo, type ReactNode } from 'react'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageHeader,
|
||||
PageHeaderAside,
|
||||
PageHeaderDescription,
|
||||
PageHeaderMeta,
|
||||
PageHeaderSummary,
|
||||
PageHeaderTitle,
|
||||
} from 'ui-patterns/PageHeader'
|
||||
import { PageSection, PageSectionContent, PageSectionMeta } from 'ui-patterns/PageSection'
|
||||
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { useIsMarketplaceEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
|
||||
import {
|
||||
IntegrationCard,
|
||||
IntegrationLoadingCard,
|
||||
} from '@/components/interfaces/Integrations/Landing/IntegrationCard'
|
||||
import { IntegrationDefinition } from '@/components/interfaces/Integrations/Landing/Integrations.constants'
|
||||
import { useAvailableIntegrations } from '@/components/interfaces/Integrations/Landing/useAvailableIntegrations'
|
||||
import { useInstalledIntegrations } from '@/components/interfaces/Integrations/Landing/useInstalledIntegrations'
|
||||
import { useIntegrationFilteringAndSort } from '@/components/interfaces/Integrations/Landing/useIntegrationFilteringAndSort'
|
||||
import { MarketplaceIndex } from '@/components/interfaces/Integrations/Marketplace/MarketplaceIndex'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import { ProjectIntegrationsLayoutDispatch } from '@/components/layouts/ProjectIntegrationsLayoutDispatch'
|
||||
import { AlertError } from '@/components/ui/AlertError'
|
||||
import { DocsButton } from '@/components/ui/DocsButton'
|
||||
import { NoSearchResults } from '@/components/ui/NoSearchResults'
|
||||
import { useMarketplaceCategoriesQuery } from '@/data/marketplace/integration-categories-query'
|
||||
import { BASE_PATH, DOCS_URL } from '@/lib/constants'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const FEATURED_INTEGRATIONS = ['cron', 'queues', 'stripe_sync_engine']
|
||||
|
||||
// Featured integration images
|
||||
const FEATURED_INTEGRATION_IMAGES: Record<string, string> = {
|
||||
cron: `${BASE_PATH}/img/integrations/covers/cron-cover.webp`,
|
||||
queues: `${BASE_PATH}/img/integrations/covers/queues-cover.png`,
|
||||
stripe_wrapper: `${BASE_PATH}/img/integrations/covers/stripe-cover.png`,
|
||||
stripe_sync_engine: `${BASE_PATH}/img/integrations/covers/stripe-cover.png`,
|
||||
}
|
||||
|
||||
function getIntegrationImage(integration: IntegrationDefinition) {
|
||||
let featured_image = FEATURED_INTEGRATION_IMAGES[integration.id]
|
||||
if (featured_image) {
|
||||
return featured_image
|
||||
}
|
||||
|
||||
if (integration.files?.length) {
|
||||
const heroImage = integration?.files?.[0]
|
||||
return heroImage?.src ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
type PageContent = {
|
||||
title: string
|
||||
subtitle: string
|
||||
secondaryActions?: ReactNode
|
||||
}
|
||||
|
||||
const DEFAULT_PAGE_CONTENT: PageContent = {
|
||||
title: 'Extend your database',
|
||||
subtitle:
|
||||
'Extensions and wrappers that add functionality to your database and connect to external services.',
|
||||
}
|
||||
|
||||
const CATEGORY_PAGE_CONTENT = {
|
||||
wrapper: {
|
||||
title: 'Wrappers',
|
||||
subtitle:
|
||||
'Connect to external data sources and services by querying APIs, databases, and files as if they were Postgres tables.',
|
||||
secondaryActions: (
|
||||
<DocsButton href={`${DOCS_URL}/guides/database/extensions/wrappers/overview`} />
|
||||
),
|
||||
},
|
||||
postgres_extension: {
|
||||
title: 'Postgres Modules',
|
||||
subtitle: 'Extend your database with powerful Postgres extensions.',
|
||||
},
|
||||
} satisfies Record<string, PageContent>
|
||||
|
||||
// Converts a category string to title
|
||||
// Example: some_catory -> Some Category
|
||||
function formatCategoryTitle(category: string) {
|
||||
return category
|
||||
.split(/[-_]/)
|
||||
.filter(Boolean)
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
const PageHeaderContentSkeleton = () => (
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<ShimmeringLoader className="h-8 w-48" />
|
||||
<ShimmeringLoader className="h-4 w-full max-w-xl" />
|
||||
</div>
|
||||
)
|
||||
|
||||
// Returns selected category to filter by
|
||||
function useFilterCategory() {
|
||||
const router = useRouter()
|
||||
const [selectedCategory] = useQueryState(
|
||||
'category',
|
||||
parseAsString.withDefault('all').withOptions({ clearOnDefault: true })
|
||||
)
|
||||
const categoryFromUrl = useMemo(() => {
|
||||
const queryString = router.asPath.split('?')[1]
|
||||
if (!queryString) return null
|
||||
|
||||
return new URLSearchParams(queryString).get('category')
|
||||
}, [router.asPath])
|
||||
const resolvedSelectedCategory = router.isReady
|
||||
? (categoryFromUrl ?? selectedCategory)
|
||||
: undefined
|
||||
const filterCategory = resolvedSelectedCategory ?? 'all'
|
||||
return filterCategory
|
||||
}
|
||||
|
||||
// Dynamic page content based on selected category
|
||||
function usePageContent(
|
||||
integrationFilterCategory: string,
|
||||
categories: Database['public']['Views']['categories']['Row'][]
|
||||
) {
|
||||
const pageContent = useMemo<PageContent>(() => {
|
||||
if (integrationFilterCategory === 'all') {
|
||||
return DEFAULT_PAGE_CONTENT
|
||||
}
|
||||
|
||||
if (integrationFilterCategory in CATEGORY_PAGE_CONTENT) {
|
||||
return CATEGORY_PAGE_CONTENT[integrationFilterCategory as keyof typeof CATEGORY_PAGE_CONTENT]
|
||||
}
|
||||
|
||||
const selectedMarketplaceCategory = categories.find(
|
||||
(category) => category.slug === integrationFilterCategory
|
||||
)
|
||||
|
||||
return {
|
||||
title: selectedMarketplaceCategory?.name ?? formatCategoryTitle(integrationFilterCategory),
|
||||
subtitle: selectedMarketplaceCategory?.description ?? DEFAULT_PAGE_CONTENT.subtitle,
|
||||
}
|
||||
}, [categories, integrationFilterCategory])
|
||||
return pageContent
|
||||
}
|
||||
|
||||
const IntegrationsPage: NextPageWithLayout = () => {
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
if (isMarketplaceEnabled) return <MarketplaceIndex />
|
||||
return <LegacyIntegrationsPage />
|
||||
}
|
||||
|
||||
const LegacyIntegrationsPage = () => {
|
||||
const { hasLoaded: flagsLoaded } = useFeatureFlags()
|
||||
const isMarketplaceEnabled = useIsMarketplaceEnabled()
|
||||
const [search, setSearch] = useQueryState(
|
||||
'search',
|
||||
parseAsString.withDefault('').withOptions({ clearOnDefault: true })
|
||||
)
|
||||
|
||||
const {
|
||||
data: availableIntegrations,
|
||||
error,
|
||||
isPending: isLoadingAvailableIntegrations,
|
||||
isError,
|
||||
isSuccess: isSuccessAvailableIntegrations,
|
||||
} = useAvailableIntegrations()
|
||||
|
||||
const {
|
||||
installedIntegrations,
|
||||
isLoading: isLoadingInstalledIntegrations,
|
||||
isSuccess: isSuccessInstalledIntegrations,
|
||||
} = useInstalledIntegrations()
|
||||
|
||||
const installedIds = installedIntegrations.map((i) => i.id)
|
||||
const isLoading = isLoadingAvailableIntegrations || isLoadingInstalledIntegrations
|
||||
const isSuccess = isSuccessAvailableIntegrations && isSuccessInstalledIntegrations
|
||||
|
||||
const selectedCategory = useFilterCategory()
|
||||
|
||||
const { data: categories = [], isPending: isPendingCategories } = useMarketplaceCategoriesQuery({
|
||||
enabled: isMarketplaceEnabled,
|
||||
})
|
||||
|
||||
const isLoadingSelectedCategory =
|
||||
selectedCategory !== 'all' &&
|
||||
!(selectedCategory in CATEGORY_PAGE_CONTENT) &&
|
||||
(IS_PLATFORM ? !flagsLoaded || (isMarketplaceEnabled && isPendingCategories) : false)
|
||||
|
||||
const pageContent = usePageContent(selectedCategory, categories)
|
||||
|
||||
const filteredIntegrations = useMemo(() => {
|
||||
let filtered = availableIntegrations ?? []
|
||||
|
||||
if (selectedCategory !== 'all') {
|
||||
filtered = filtered.filter(
|
||||
(i) => i.type === selectedCategory || i.categories?.includes(selectedCategory)
|
||||
)
|
||||
}
|
||||
|
||||
if (search.length > 0) {
|
||||
filtered = filtered.filter((i) => i.name.toLowerCase().includes(search.toLowerCase()))
|
||||
}
|
||||
|
||||
return filtered
|
||||
}, [availableIntegrations, selectedCategory, search])
|
||||
|
||||
const hasActiveFilter = selectedCategory !== 'all' || search.length > 0
|
||||
|
||||
const { sorted: filteredAndSortedIntegrations, featured: featuredIntegrations } =
|
||||
useIntegrationFilteringAndSort(filteredIntegrations, availableIntegrations, installedIds, {
|
||||
featuredIds: FEATURED_INTEGRATIONS,
|
||||
hasActiveFilter,
|
||||
includeFeaturedFlag: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader size="large">
|
||||
<PageHeaderMeta>
|
||||
<PageHeaderSummary>
|
||||
{isLoadingSelectedCategory ? (
|
||||
<PageHeaderContentSkeleton />
|
||||
) : (
|
||||
<>
|
||||
<PageHeaderTitle>{pageContent.title}</PageHeaderTitle>
|
||||
<PageHeaderDescription>{pageContent.subtitle}</PageHeaderDescription>
|
||||
</>
|
||||
)}
|
||||
</PageHeaderSummary>
|
||||
{pageContent.secondaryActions && (
|
||||
<PageHeaderAside>{pageContent.secondaryActions}</PageHeaderAside>
|
||||
)}
|
||||
</PageHeaderMeta>
|
||||
</PageHeader>
|
||||
|
||||
<PageContainer size="large">
|
||||
<PageSection>
|
||||
<PageSectionMeta>
|
||||
<Input
|
||||
value={search}
|
||||
size="tiny"
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Search integrations..."
|
||||
icon={<Search size={14} />}
|
||||
className="w-52"
|
||||
/>
|
||||
</PageSectionMeta>
|
||||
|
||||
<PageSectionContent>
|
||||
{isLoading && (
|
||||
<div
|
||||
className="grid xl:grid-cols-3 2xl:grid-cols-4 gap-x-4 gap-y-3"
|
||||
style={{ gridAutoRows: 'minmax(110px, auto)' }}
|
||||
>
|
||||
{Array.from({ length: 8 }).map((_, idx) => (
|
||||
<IntegrationLoadingCard key={`integration-loading-${idx}`} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Error State */}
|
||||
{isError && (
|
||||
<AlertError subject="Failed to retrieve available integrations" error={error} />
|
||||
)}
|
||||
|
||||
{/* Success State */}
|
||||
{isSuccess && (
|
||||
<>
|
||||
{/* No Search Results */}
|
||||
{search.length > 0 && filteredAndSortedIntegrations.length === 0 && (
|
||||
<NoSearchResults searchString={search} onResetFilter={() => setSearch('')} />
|
||||
)}
|
||||
|
||||
{/* Featured Integrations */}
|
||||
{featuredIntegrations && featuredIntegrations.length > 0 && (
|
||||
<div
|
||||
className="grid grid-cols-2 @4xl:grid-cols-3 gap-4 mb-4 items-stretch pb-6 border-b"
|
||||
style={{ gridAutoRows: 'minmax(110px, auto)' }}
|
||||
>
|
||||
{featuredIntegrations.map((integration) => (
|
||||
<IntegrationCard
|
||||
key={integration.id}
|
||||
{...integration}
|
||||
isInstalled={installedIds.includes(integration.id)}
|
||||
featured={true}
|
||||
image={getIntegrationImage(integration)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* All Filtered and Sorted Integrations */}
|
||||
{filteredAndSortedIntegrations.length > 0 && (
|
||||
<div className="grid @xl:grid-cols-3 @6xl:grid-cols-4 gap-4">
|
||||
{filteredAndSortedIntegrations.map((integration) => (
|
||||
<IntegrationCard
|
||||
key={integration.id}
|
||||
{...integration}
|
||||
isInstalled={installedIds.includes(integration.id)}
|
||||
featured={false}
|
||||
image={FEATURED_INTEGRATION_IMAGES[integration.id]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</PageSectionContent>
|
||||
</PageSection>
|
||||
</PageContainer>
|
||||
</>
|
||||
)
|
||||
return <MarketplaceIndex />
|
||||
}
|
||||
|
||||
IntegrationsPage.getLayout = (page) => (
|
||||
|
||||
@@ -26,7 +26,6 @@ export const LOCAL_STORAGE_KEYS = {
|
||||
UI_PREVIEW_PLATFORM_WEBHOOKS: 'supabase-ui-platform-webhooks',
|
||||
UI_PREVIEW_JIT_DB_ACCESS: 'supabase-ui-jit-db-access',
|
||||
UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE: 'supabase-ui-sql-editor-manual-save',
|
||||
UI_PREVIEW_MARKETPLACE: 'supabase-ui-marketplace',
|
||||
|
||||
AI_ASSISTANT_MCP_OPT_IN: 'ai-assistant-mcp-opt-in',
|
||||
SIGN_IN_CHATGPT_ENABLED: 'siwc-enabled',
|
||||
@@ -155,7 +154,6 @@ const LOCAL_STORAGE_KEYS_ALLOWLIST = [
|
||||
LOCAL_STORAGE_KEYS.UI_PREVIEW_PLATFORM_WEBHOOKS,
|
||||
LOCAL_STORAGE_KEYS.UI_PREVIEW_JIT_DB_ACCESS,
|
||||
LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE,
|
||||
LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE,
|
||||
LOCAL_STORAGE_KEYS.LAST_SIGN_IN_METHOD,
|
||||
LOCAL_STORAGE_KEYS.HIDE_PROMO_TOAST,
|
||||
LOCAL_STORAGE_KEYS.BLOG_VIEW,
|
||||
|
||||
Reference in new issue
Block a user