mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 06:09:08 +03:00
## Problem The redesigned pipeline creation flow (mega branch being worked on in #49243) needs an incremental rollout while the existing creation sheet remains the default. ## Solution - Register **Pipeline creation wizard** in Feature Previews, with visibility controlled by the `pipelineCreationWizard` ConfigCat flag. - Require the flag and an explicit user opt-in (via Feature previews dialog) before enabling the preview. This adds the preview registration, copy, storage key, and gate. It does not change creation routing or ship the wizard. The complete gated flow can be tested on #49243. The ConfigCat flag is configured as follows: - **Local/staging:** true for everyone. - **Production:** false by default, true for the **Supabase Team Email** segment. The flag makes the preview visible; it does not opt anyone in. Users enable it once in Feature Previews. No dev toolbar override is needed in local/staging. ## Review instructions 1. In local or staging hosted Studio, open **Feature previews**. Find **Pipeline creation wizard**. With no saved preference, it should be off by default. 2. Toggle it on and reload. The opt-in should persist. Toggle it off and reload to confirm opt-out persists. **Add pipeline** still opens the sheet in this foundation PR.
202 lines
7.1 KiB
TypeScript
202 lines
7.1 KiB
TypeScript
import { LOCAL_STORAGE_KEYS, useFlag } from 'common'
|
|
import { useMemo } from 'react'
|
|
|
|
import { type BannerId } from '@/components/ui/BannerStack/BannerStackProvider'
|
|
import { IS_PLATFORM } from '@/lib/constants'
|
|
|
|
export type FeaturePreview = {
|
|
key: string
|
|
name: string
|
|
discussionsUrl?: string
|
|
isNew: boolean
|
|
/** If feature flag is only relevant for the hosted platform */
|
|
isPlatformOnly: boolean
|
|
/** If feature flag should be enabled by default for users, if not yet toggled before */
|
|
isDefaultOptIn: boolean
|
|
/** Visibility in the feature preview modal (For feature flagging a feature preview) */
|
|
enabled: boolean
|
|
/**
|
|
* Forces the preview on for this user, whatever they previously chose — for a
|
|
* preview that has become the default behavior. Overrides both `isDefaultOptIn`
|
|
* and a stored opt-out, and takes away the ability to turn the preview back off.
|
|
*/
|
|
isForced?: boolean
|
|
/** Optional category that the feature preview falls under, defaults to "Others" in the UI otherwise */
|
|
category?: 'observability' | 'database' | 'editors'
|
|
/**
|
|
* Where to send the user after enabling, to try the feature out. Omit if the
|
|
* feature has no single destination (e.g. a global layout change).
|
|
*/
|
|
getRoute?: (ref?: string) => string
|
|
bannerId?: BannerId
|
|
}
|
|
|
|
export const useFeaturePreviews = (): FeaturePreview[] => {
|
|
const isPipelineCreationWizardAvailable = useFlag('pipelineCreationWizard')
|
|
const isPlatformWebhooksEnabled = useFlag('platformWebhooks')
|
|
const jitDbAccessEnabled = useFlag('jitDbAccess')
|
|
const isMarketplaceEnabled = useFlag('marketplaceIntegrations')
|
|
const isExplorerEnabled = useFlag('explorer')
|
|
const isStorageVersioningEnabled = useFlag('storageVersioningPrivateAlpha')
|
|
|
|
const isSqlEditorManualSaveForced = useFlag('sqlEditorManualSaveForced')
|
|
|
|
return useMemo(() => {
|
|
const previews: FeaturePreview[] = [
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_PIPELINE_CREATION,
|
|
name: 'Pipeline creation wizard',
|
|
category: 'database',
|
|
enabled: isPipelineCreationWizardAvailable,
|
|
isNew: true,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_EXPLORER,
|
|
name: 'Explorer & Notebooks',
|
|
category: 'editors',
|
|
discussionsUrl: 'https://github.com/orgs/supabase/discussions/49916',
|
|
enabled: isExplorerEnabled,
|
|
isNew: true,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
getRoute: (ref?: string) => `/project/${ref}/explorer`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_UNIFIED_LOGS,
|
|
name: 'Updated Logs interface',
|
|
category: 'observability',
|
|
discussionsUrl: 'https://github.com/orgs/supabase/discussions/37234',
|
|
enabled: true,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: true,
|
|
getRoute: (ref?: string) => `/project/${ref}/logs`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_ADVISOR_RULES,
|
|
name: 'Disable Advisor rules',
|
|
discussionsUrl: undefined,
|
|
enabled: true,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
getRoute: (ref?: string) => `/project/${ref}/advisors/rules/security`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_PG_DELTA_DIFF,
|
|
name: 'PG Delta diff',
|
|
discussionsUrl: undefined,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: true,
|
|
enabled: true,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_PLATFORM_WEBHOOKS,
|
|
name: 'Platform webhooks',
|
|
discussionsUrl: undefined,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
enabled: isPlatformWebhooksEnabled,
|
|
getRoute: (ref?: string) => `/project/${ref}/settings/webhooks`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_JIT_DB_ACCESS,
|
|
name: 'Temporary database access',
|
|
category: 'database',
|
|
discussionsUrl: undefined,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
enabled: jitDbAccessEnabled,
|
|
getRoute: (ref?: string) => `/project/${ref}/database/settings`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS,
|
|
name: 'Column-level privileges',
|
|
category: 'database',
|
|
discussionsUrl: 'https://github.com/orgs/supabase/discussions/20295',
|
|
enabled: true,
|
|
isNew: false,
|
|
isPlatformOnly: false,
|
|
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: false,
|
|
isPlatformOnly: false,
|
|
isDefaultOptIn: true,
|
|
getRoute: (ref?: string) => `/project/${ref}/integrations`,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE,
|
|
category: 'editors',
|
|
name: 'Disable snippet auto-saving',
|
|
discussionsUrl: undefined,
|
|
isNew: false,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: false,
|
|
enabled: true,
|
|
// Manual saving is becoming the default for the SQL Editor. The preview
|
|
// stays listed so users who lose their local storage can opt back in
|
|
// before the rollout reaches them.
|
|
isForced: isSqlEditorManualSaveForced,
|
|
},
|
|
{
|
|
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_STORAGE_VERSIONING,
|
|
name: 'Storage versioning',
|
|
discussionsUrl: undefined,
|
|
isNew: true,
|
|
isPlatformOnly: true,
|
|
isDefaultOptIn: true,
|
|
enabled: isStorageVersioningEnabled,
|
|
getRoute: (ref?: string) => `/project/${ref}/storage/files`,
|
|
},
|
|
]
|
|
|
|
return previews.sort((a, b) => Number(b.isNew) - Number(a.isNew))
|
|
}, [
|
|
isPipelineCreationWizardAvailable,
|
|
isSqlEditorManualSaveForced,
|
|
isPlatformWebhooksEnabled,
|
|
jitDbAccessEnabled,
|
|
isMarketplaceEnabled,
|
|
isExplorerEnabled,
|
|
isStorageVersioningEnabled,
|
|
])
|
|
}
|
|
|
|
export type FeaturePreviewCategoryGroup = {
|
|
category: FeaturePreview['category']
|
|
previews: FeaturePreview[]
|
|
}
|
|
|
|
/**
|
|
* The visible feature previews (respecting platform-only/enabled gating),
|
|
* grouped by category in first-seen order, uncategorized previews last as a
|
|
* final `category: undefined` group. Shared by the feature preview modal and
|
|
* the Cmd+K "Feature previews" page so both list the exact same previews.
|
|
*/
|
|
export const useVisibleFeaturePreviewsByCategory = (): FeaturePreviewCategoryGroup[] => {
|
|
const featurePreviews = useFeaturePreviews()
|
|
|
|
return useMemo(() => {
|
|
const previews = (
|
|
IS_PLATFORM ? featurePreviews : featurePreviews.filter((preview) => !preview.isPlatformOnly)
|
|
).filter((preview) => preview.enabled)
|
|
const categories = [...new Set(previews.map((preview) => preview.category).filter(Boolean))]
|
|
|
|
return categories.concat(undefined).map((category) => ({
|
|
category,
|
|
previews: previews.filter((preview) => preview.category === category),
|
|
}))
|
|
}, [featurePreviews])
|
|
}
|