Files
Joshen Lim 2d1a2ff9a2 Set up feature preview for explorer (#49602)
## Context

Sets up the Explorer behind the feature preview modal + removes the
temporary entry point from the SQL Editor
Changes are still not live on production, so will only affect local +
staging.

Enabling the feature preview will replace the sidebar nav for SQL Editor
to new Explorer (Icon remains unchanged, just the label)

<img width="918" height="647" alt="image"
src="https://github.com/user-attachments/assets/b088eb47-1176-4618-b345-d1ec0521b092"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added an “Explorer & Notebooks” feature preview with an overview image
and direct access to Explorer or SQL Editor.
  - Added Explorer navigation when the preview is enabled.

- **Improvements**
- Updated desktop and mobile navigation to consistently display the
available editor destination.
- Improved the Explorer shortcut tooltip to clearly say “Go to
Explorer.”
  - Organized SQL Editor previews under the Editors category.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-26 22:45:11 +08:00

280 lines
8.9 KiB
TypeScript

import { useParams } from 'common'
import { Auth, Database, EdgeFunctions, Realtime, SqlEditor, Storage, TableEditor } from 'icons'
import { Blocks, Box, Lightbulb, List, Settings, Telescope } from 'lucide-react'
import {
useIsExplorerEnabled,
useUnifiedLogsPreview,
} from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { ICON_SIZE, ICON_STROKE_WIDTH } from '@/components/interfaces/Sidebar'
import type { Route } from '@/components/ui/ui.types'
import { EditorIndexPageLink } from '@/data/prefetchers/project.$ref.editor'
import type { Project } from '@/data/projects/project-detail-query'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { IS_PLATFORM, PROJECT_STATUS } from '@/lib/constants'
import { PRODUCT_NAME } from '@/lib/constants/workers'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
interface RouteContext {
ref?: string
isProjectActive: boolean
isProjectBuilding: boolean
buildingUrl: string
}
interface ProductFeatures {
auth?: boolean
edgeFunctions?: boolean
storage?: boolean
realtime?: boolean
authOverviewPage?: boolean
workers?: boolean
}
interface OtherFeatures {
isPlatform?: boolean
unifiedLogs?: boolean
showReports?: boolean
showLogs?: boolean
}
function getRouteContext(ref?: string, project?: Project): RouteContext {
return {
ref,
isProjectActive: project?.status === PROJECT_STATUS.ACTIVE_HEALTHY,
isProjectBuilding: project?.status === PROJECT_STATUS.COMING_UP,
buildingUrl: `/project/${ref}`,
}
}
export const useGenerateToolRoutes = (): Route[] => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
const isExplorerEnabled = useIsExplorerEnabled()
return [
{
key: 'editor',
label: 'Table Editor',
disabled: !isProjectActive,
icon: <TableEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/editor`),
linkElement: <EditorIndexPageLink projectRef={ref} />,
shortcutId: SHORTCUT_IDS.NAV_TABLE_EDITOR,
},
...(isExplorerEnabled
? [
{
key: 'explorer',
label: 'Explorer',
disabled: !isProjectActive,
icon: <SqlEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/explorer`),
shortcutId: SHORTCUT_IDS.NAV_SQL_EDITOR,
},
]
: [
{
key: 'sql',
label: 'SQL Editor',
disabled: !isProjectActive,
icon: <SqlEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/sql`),
shortcutId: SHORTCUT_IDS.NAV_SQL_EDITOR,
},
]),
]
}
export const generateProductRoutes = (
ref?: string,
project?: Project,
features?: ProductFeatures
): Route[] => {
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
const authEnabled = features?.auth ?? true
const edgeFunctionsEnabled = features?.edgeFunctions ?? true
const storageEnabled = features?.storage ?? true
const realtimeEnabled = features?.realtime ?? true
const authOverviewPageEnabled = features?.authOverviewPage ?? false
const workersEnabled = features?.workers ?? false
return [
{
key: 'database',
label: 'Database',
disabled: !isProjectActive,
icon: <Database size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding
? buildingUrl
: isProjectActive
? `/project/${ref}/database/schemas`
: `/project/${ref}/database/backups/scheduled`),
shortcutId: SHORTCUT_IDS.NAV_DATABASE,
},
...(authEnabled
? [
{
key: 'auth',
label: 'Authentication',
disabled: !isProjectActive,
icon: <Auth size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding
? buildingUrl
: authOverviewPageEnabled
? `/project/${ref}/auth/overview`
: `/project/${ref}/auth/users`),
shortcutId: SHORTCUT_IDS.NAV_AUTH,
},
]
: []),
...(storageEnabled
? [
{
key: 'storage',
label: 'Storage',
disabled: !isProjectActive,
icon: <Storage size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/storage/files`),
shortcutId: SHORTCUT_IDS.NAV_STORAGE,
},
]
: []),
...(edgeFunctionsEnabled
? [
{
key: 'functions',
label: 'Edge Functions',
disabled: false,
icon: <EdgeFunctions size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && `/project/${ref}/functions`,
shortcutId: SHORTCUT_IDS.NAV_FUNCTIONS,
},
]
: []),
...(workersEnabled
? [
{
key: 'workers',
label: PRODUCT_NAME,
disabled: !isProjectActive,
icon: <Box size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/workers`),
isNew: true,
},
]
: []),
...(realtimeEnabled
? [
{
key: 'realtime',
label: 'Realtime',
disabled: !isProjectActive,
icon: <Realtime size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/realtime/inspector`),
shortcutId: SHORTCUT_IDS.NAV_REALTIME,
},
]
: []),
]
}
export const generateOtherRoutes = (
ref?: string,
project?: Project,
features?: OtherFeatures
): Route[] => {
const { isProjectActive, isProjectBuilding, buildingUrl } = getRouteContext(ref, project)
const unifiedLogsEnabled = features?.unifiedLogs ?? false
const reportsEnabled = features?.showReports ?? true
const logsEnabled = features?.showLogs ?? true
return [
{
key: 'advisors',
label: 'Advisors',
disabled: !isProjectActive,
icon: <Lightbulb size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/advisors/security`),
shortcutId: SHORTCUT_IDS.NAV_ADVISORS,
},
...(reportsEnabled
? [
{
key: 'observability',
label: 'Observability',
disabled: !isProjectActive,
icon: <Telescope size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding
? buildingUrl
: IS_PLATFORM
? `/project/${ref}/observability`
: `/project/${ref}/query-performance`),
shortcutId: SHORTCUT_IDS.NAV_OBSERVABILITY,
},
]
: []),
...(logsEnabled
? [
{
key: 'logs',
label: 'Logs',
disabled: false,
icon: <List size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(unifiedLogsEnabled ? `/project/${ref}/logs` : `/project/${ref}/logs/explorer`),
shortcutId: SHORTCUT_IDS.NAV_LOGS,
},
]
: []),
{
key: 'integrations',
label: 'Integrations',
disabled: !isProjectActive,
icon: <Blocks size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/integrations`),
shortcutId: SHORTCUT_IDS.NAV_INTEGRATIONS,
},
]
}
// [Joshen] Main hook to consume as it standardizes the generation of the menu items
export const useGenerateOtherRoutes = (): Route[] => {
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const { isEnabled: unifiedLogsEnabled } = useUnifiedLogsPreview()
const reportsEnabled = useIsFeatureEnabled('reports:all')
const logsEnabled = useIsFeatureEnabled('logs:all')
return generateOtherRoutes(ref, project, {
unifiedLogs: unifiedLogsEnabled,
showReports: reportsEnabled,
showLogs: logsEnabled,
})
}
export const generateSettingsRoutes = (ref?: string): Route[] => {
return [
{
key: 'settings',
label: 'Project Settings',
icon: <Settings size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && `/project/${ref}/settings/general`,
disabled: false,
shortcutId: SHORTCUT_IDS.NAV_SETTINGS,
},
]
}