Files
supabase/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx
T
Joshen Lim 0b97e0ff86 Joshen/fe 2642 project settings allow to select a replica to power (#43626)
## Context

Related to dashboard scalability
Previous PR: https://github.com/supabase/supabase/pull/42856

Note: Changes are all feature flagged still and I'm still not entirely
convinced with the current UX
Will iterate as as go along, and only make this publicly available when
we're satisfied with the behaviour

Adds a "Dashboard preference" section to the project settings
<img width="265" height="740" alt="image"
src="https://github.com/user-attachments/assets/6ce1aa19-26c2-47c6-a9c4-595137266631"
/>

In which users can then select which database they'd like to use for
read queries run from the dashboard
Note: Everything is local storage for now, but we'd need middleware
support if we want to make this setting persist for all users on the
project
<img width="791" height="434" alt="image"
src="https://github.com/user-attachments/assets/e651d6d9-fed4-4da4-b552-c9f93f8d46d3"
/>

Added a dialog as well to further explain what this implies
<img width="610" height="312" alt="image"
src="https://github.com/user-attachments/assets/0aa957af-cb51-476f-aa79-8948a7cbe5ae"
/>

## To test

- Choosing a replica in dashboard preferences will only affect the table
editor as thats the only place that's set up so far to use a replica for
read queries (I'll need to follow up for other parts of the dashboard in
subsequent PRs)
2026-03-12 11:18:28 +07:00

198 lines
6.5 KiB
TypeScript

import { ICON_SIZE, ICON_STROKE_WIDTH } from 'components/interfaces/Sidebar'
import { useGenerateSettingsMenu } from 'components/layouts/ProjectSettingsLayout/SettingsMenu.utils'
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 { Auth, Database, EdgeFunctions, Realtime, SqlEditor, Storage, TableEditor } from 'icons'
import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { Blocks, FileText, Lightbulb, List, Settings, Telescope } from 'lucide-react'
export const generateToolRoutes = (ref?: string, project?: Project, features?: {}): Route[] => {
const isProjectActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
const buildingUrl = `/project/${ref}`
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} />,
},
{
key: 'sql',
label: 'SQL Editor',
disabled: !isProjectActive,
icon: <SqlEditor size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/sql`),
},
]
}
export const generateProductRoutes = (
ref?: string,
project?: Project,
features?: {
auth?: boolean
edgeFunctions?: boolean
storage?: boolean
realtime?: boolean
authOverviewPage?: boolean
}
): Route[] => {
const isProjectActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
const buildingUrl = `/project/${ref}`
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
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`),
},
...(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`),
},
]
: []),
...(storageEnabled
? [
{
key: 'storage',
label: 'Storage',
disabled: !isProjectActive,
icon: <Storage size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/storage/files`),
},
]
: []),
...(edgeFunctionsEnabled
? [
{
key: 'functions',
label: 'Edge Functions',
disabled: false,
icon: <EdgeFunctions size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && `/project/${ref}/functions`,
},
]
: []),
...(realtimeEnabled
? [
{
key: 'realtime',
label: 'Realtime',
disabled: !isProjectActive,
icon: <Realtime size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/realtime/inspector`),
},
]
: []),
]
}
export const generateOtherRoutes = (
ref?: string,
project?: Project,
features?: { unifiedLogs?: boolean; showReports?: boolean; apiDocsSidePanel?: boolean }
): Route[] => {
const isProjectActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
const buildingUrl = `/project/${ref}`
const { unifiedLogs, showReports, apiDocsSidePanel } = features ?? {}
const unifiedLogsEnabled = unifiedLogs ?? false
const reportsEnabled = showReports ?? true
const apiDocsSidePanelEnabled = apiDocsSidePanel ?? false
return [
{
key: 'advisors',
label: 'Advisors',
disabled: !isProjectActive,
icon: <Lightbulb size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/advisors/security`),
},
...(IS_PLATFORM && reportsEnabled
? [
{
key: 'observability',
label: 'Observability',
disabled: !isProjectActive,
icon: <Telescope size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/observability`),
},
]
: []),
{
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`),
},
...(apiDocsSidePanelEnabled
? [
{
key: 'api',
label: 'API Docs',
disabled: !isProjectActive,
icon: <FileText size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(isProjectBuilding ? buildingUrl : `/project/${ref}/integrations/data_api/docs`),
},
]
: []),
{
key: 'integrations',
label: 'Integrations',
disabled: !isProjectActive,
icon: <Blocks size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link: ref && (isProjectBuilding ? buildingUrl : `/project/${ref}/integrations`),
},
]
}
export const generateSettingsRoutes = (ref?: string): Route[] => {
return [
{
key: 'settings',
label: 'Project Settings',
icon: <Settings size={ICON_SIZE} strokeWidth={ICON_STROKE_WIDTH} />,
link:
ref &&
(IS_PLATFORM ? `/project/${ref}/settings/general` : `/project/${ref}/settings/log-drains`),
disabled: false,
},
]
}