mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Adjust disabled state while project is paused (#42096)
* Adjust disabled state while project is INACTIVE * merge master, unlock add ons page if paused
This commit is contained in:
1 parent
3fcc8b51a1
commit
164dcf3e92
9 files changed
+75
-81
No files matched your search
@@ -250,11 +250,21 @@ export const Addons = () => {
|
||||
title="Compute size has moved"
|
||||
description="Compute size is now managed alongside Disk configuration on the new Compute and Disk page."
|
||||
actions={
|
||||
<Button type="default" asChild>
|
||||
<Link href={`/project/${projectRef}/settings/compute-and-disk`}>
|
||||
Go to Compute and Disk
|
||||
</Link>
|
||||
</Button>
|
||||
<ProjectUpdateDisabledTooltip
|
||||
projectUpdateDisabled={projectUpdateDisabled}
|
||||
projectNotActive={!isProjectActive}
|
||||
>
|
||||
<Button
|
||||
asChild
|
||||
type="default"
|
||||
className="pointer-events-auto"
|
||||
disabled={projectUpdateDisabled || !isProjectActive}
|
||||
>
|
||||
<Link href={`/project/${projectRef}/settings/compute-and-disk`}>
|
||||
Go to Compute and Disk
|
||||
</Link>
|
||||
</Button>
|
||||
</ProjectUpdateDisabledTooltip>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -393,24 +403,21 @@ export const Addons = () => {
|
||||
? 'Dedicated IPv4 address is enabled'
|
||||
: 'Dedicated IPv4 address is not enabled'}
|
||||
</p>
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
className="mt-2 pointer-events-auto"
|
||||
onClick={() => setPanel('ipv4')}
|
||||
disabled={
|
||||
!isProjectActive || projectUpdateDisabled || !(canUpdateIPv4 || ipv4)
|
||||
}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !(canUpdateIPv4 || ipv4)
|
||||
? 'Temporarily disabled while we are migrating to IPv6, please check back later.'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
<ProjectUpdateDisabledTooltip
|
||||
projectUpdateDisabled={projectUpdateDisabled}
|
||||
projectNotActive={!isProjectActive}
|
||||
>
|
||||
Change dedicated IPv4 address
|
||||
</ButtonTooltip>
|
||||
<Button
|
||||
type="default"
|
||||
className="mt-2 pointer-events-auto"
|
||||
onClick={() => setPanel('ipv4')}
|
||||
disabled={
|
||||
!isProjectActive || projectUpdateDisabled || !(canUpdateIPv4 || ipv4)
|
||||
}
|
||||
>
|
||||
Change dedicated IPv4 address
|
||||
</Button>
|
||||
</ProjectUpdateDisabledTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</ScaffoldSectionContent>
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ComponentProps, ComponentPropsWithoutRef, FC, ReactNode, useEffect } from 'react'
|
||||
|
||||
import { PROJECT_STATUS } from '@/lib/constants'
|
||||
import { LOCAL_STORAGE_KEYS, useFlag, useIsMFAEnabled, useParams } from 'common'
|
||||
import {
|
||||
generateOtherRoutes,
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
Sidebar as SidebarPrimitive,
|
||||
useSidebar,
|
||||
} from 'ui'
|
||||
import { Route } from '../ui/ui.types'
|
||||
import {
|
||||
useIsAPIDocsSidePanelEnabled,
|
||||
useUnifiedLogsPreview,
|
||||
@@ -162,7 +164,7 @@ export function SideBarNavLink({
|
||||
disabled,
|
||||
...props
|
||||
}: {
|
||||
route: any
|
||||
route: Route
|
||||
active?: boolean
|
||||
disabled?: boolean
|
||||
onClick?: () => void
|
||||
@@ -228,6 +230,8 @@ const ProjectLinks = () => {
|
||||
const showReports = useIsFeatureEnabled('reports:all')
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const isProjectActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
||||
|
||||
const isNewAPIDocsEnabled = useIsAPIDocsSidePanelEnabled()
|
||||
const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview()
|
||||
|
||||
@@ -278,6 +282,7 @@ const ProjectLinks = () => {
|
||||
{toolRoutes.map((route, i) => (
|
||||
<SideBarNavLink
|
||||
key={`tools-routes-${i}`}
|
||||
disabled={!isProjectActive}
|
||||
route={route}
|
||||
active={activeRoute === route.key}
|
||||
/>
|
||||
@@ -288,6 +293,7 @@ const ProjectLinks = () => {
|
||||
{productRoutes.map((route, i) => (
|
||||
<SideBarNavLink
|
||||
key={`product-routes-${i}`}
|
||||
disabled={route.key !== 'functions' && !isProjectActive}
|
||||
route={route}
|
||||
active={activeRoute === route.key}
|
||||
/>
|
||||
@@ -326,33 +332,28 @@ const ProjectLinks = () => {
|
||||
: route
|
||||
}
|
||||
active={activeRoute === route.key}
|
||||
disabled={!isProjectActive}
|
||||
onClick={handleApiClick}
|
||||
/>
|
||||
)
|
||||
} else if (route.key === 'advisors') {
|
||||
return (
|
||||
<div className="relative" key={route.key}>
|
||||
{ActiveDot(errorLints, securityLints)}
|
||||
{isProjectActive && ActiveDot(errorLints, securityLints)}
|
||||
<SideBarNavLink
|
||||
key={`other-routes-${i}`}
|
||||
route={route}
|
||||
disabled={!isProjectActive}
|
||||
active={activeRoute === route.key}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
} else if (route.key === 'logs') {
|
||||
return (
|
||||
<SideBarNavLink
|
||||
key={`other-routes-${i}`}
|
||||
route={route}
|
||||
active={activeRoute === route.key}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<SideBarNavLink
|
||||
key={`other-routes-${i}`}
|
||||
route={route}
|
||||
disabled={!isProjectActive}
|
||||
active={activeRoute === route.key}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ProjectLayout } from 'components/layouts/ProjectLayout'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ProductMenu } from 'components/ui/ProductMenu'
|
||||
import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types'
|
||||
import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem'
|
||||
import { ProductMenuItem } from 'components/ui/ProductMenu/ProductMenuItem'
|
||||
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
|
||||
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
||||
import { withAuth } from 'hooks/misc/withAuth'
|
||||
@@ -88,14 +88,7 @@ const IntegrationsLayout = ({ children }: PropsWithChildren) => {
|
||||
{isSuccess && (
|
||||
<div>
|
||||
{installedIntegrationItems.map((item) => (
|
||||
<ProductMenuItem
|
||||
key={item.key}
|
||||
url={item.url}
|
||||
name={item.name}
|
||||
icon={item.icon}
|
||||
isActive={page === item.key}
|
||||
label={item.label}
|
||||
/>
|
||||
<ProductMenuItem key={item.key} isActive={page === item.key} item={item} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -47,6 +47,7 @@ const routesToIgnoreDBConnection = [
|
||||
'/project/[ref]/database/backups/scheduled',
|
||||
'/project/[ref]/database/backups/pitr',
|
||||
'/project/[ref]/settings/addons',
|
||||
'/project/[ref]/functions',
|
||||
]
|
||||
|
||||
const routesToIgnorePostgrestConnection = [
|
||||
@@ -103,13 +104,6 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
const organizationName = selectedOrganization?.name
|
||||
|
||||
const isPaused = selectedProject?.status === PROJECT_STATUS.INACTIVE
|
||||
const showProductMenu = selectedProject
|
||||
? selectedProject.status === PROJECT_STATUS.ACTIVE_HEALTHY ||
|
||||
(selectedProject.status === PROJECT_STATUS.COMING_UP &&
|
||||
router.pathname.includes('/project/[ref]/settings')) ||
|
||||
router.pathname.includes('/project/[ref]/branches') ||
|
||||
router.pathname.includes('/project/[ref]/functions')
|
||||
: true
|
||||
|
||||
const ignorePausedState =
|
||||
router.pathname === '/project/[ref]' ||
|
||||
@@ -140,7 +134,7 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
<div className="flex flex-row h-full w-full">
|
||||
{/* autoSaveId="project-layout" */}
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
{showProductMenu && productMenu && (
|
||||
{productMenu && (
|
||||
<ResizablePanel
|
||||
order={1}
|
||||
minSize={sidebarMinSizePercentage}
|
||||
@@ -180,7 +174,7 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
|
||||
)}
|
||||
</ResizablePanel>
|
||||
)}
|
||||
{showProductMenu && productMenu && sideBarIsOpen && (
|
||||
{productMenu && sideBarIsOpen && (
|
||||
<ResizableHandle
|
||||
withHandle
|
||||
disabled={resizableSidebar ? false : true}
|
||||
|
||||
@@ -35,6 +35,8 @@ export const generateSettingsMenu = (
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const isProjectActive = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
||||
const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP
|
||||
const buildingUrl = `/project/${ref}`
|
||||
|
||||
@@ -60,12 +62,14 @@ export const generateSettingsMenu = (
|
||||
key: 'compute-and-disk',
|
||||
url: `/project/${ref}/settings/compute-and-disk`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
{
|
||||
name: 'Infrastructure',
|
||||
key: 'infrastructure',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/infrastructure`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -73,6 +77,7 @@ export const generateSettingsMenu = (
|
||||
key: 'integrations',
|
||||
url: `/project/${ref}/settings/integrations`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -80,12 +85,14 @@ export const generateSettingsMenu = (
|
||||
key: 'api',
|
||||
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
{
|
||||
name: 'API Keys',
|
||||
key: 'api-keys',
|
||||
url: `/project/${ref}/settings/api-keys/new`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
{
|
||||
name: 'JWT Keys',
|
||||
@@ -94,6 +101,7 @@ export const generateSettingsMenu = (
|
||||
? `/project/${ref}/settings/jwt`
|
||||
: `/project/${ref}/settings/jwt/signing-keys`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -101,6 +109,7 @@ export const generateSettingsMenu = (
|
||||
key: `log-drains`,
|
||||
url: `/project/${ref}/settings/log-drains`,
|
||||
items: [],
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
{
|
||||
name: 'Add Ons',
|
||||
@@ -115,6 +124,7 @@ export const generateSettingsMenu = (
|
||||
items: [],
|
||||
rightIcon: <ArrowUpRight strokeWidth={1} className="h-4 w-4" />,
|
||||
label: 'Beta',
|
||||
disabled: !isProjectActive,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,36 +1,26 @@
|
||||
import Link from 'next/link'
|
||||
import { ReactNode } from 'react'
|
||||
import { Badge, Button, Menu } from 'ui'
|
||||
import { ProductMenuGroupItem } from './ProductMenu.types'
|
||||
|
||||
interface ProductMenuItemProps {
|
||||
name: string | ReactNode
|
||||
item: ProductMenuGroupItem
|
||||
isActive: boolean
|
||||
isExternal?: boolean
|
||||
icon?: ReactNode
|
||||
rightIcon?: ReactNode
|
||||
url?: string
|
||||
target?: '_blank' | '_self'
|
||||
onClick?: () => void
|
||||
textClassName?: string
|
||||
hoverText?: string
|
||||
label?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const ProductMenuItem = ({
|
||||
name = '',
|
||||
export const ProductMenuItem = ({
|
||||
item,
|
||||
isActive,
|
||||
isExternal,
|
||||
icon,
|
||||
rightIcon,
|
||||
url = '',
|
||||
target = '_self',
|
||||
onClick,
|
||||
textClassName = '',
|
||||
hoverText = '',
|
||||
label,
|
||||
onClick,
|
||||
}: ProductMenuItemProps) => {
|
||||
const { name = '', url = '', icon, rightIcon, isExternal, label, disabled } = item
|
||||
|
||||
const menuItem = (
|
||||
<Menu.Item icon={icon} rounded active={isActive} onClick={onClick}>
|
||||
<Menu.Item icon={icon} active={isActive} onClick={onClick}>
|
||||
<div className="flex w-full items-center justify-between gap-1">
|
||||
<div
|
||||
className="flex items-center gap-1 min-w-0 flex-1"
|
||||
@@ -39,8 +29,8 @@ const ProductMenuItem = ({
|
||||
<span className="truncate flex-1 min-w-0">{name}</span>
|
||||
{label !== undefined && (
|
||||
<Badge
|
||||
variant={label.toLowerCase() === 'new' ? 'success' : 'warning'}
|
||||
className="flex-shrink-0"
|
||||
variant={label.toLowerCase() === 'new' ? 'success' : 'warning'}
|
||||
>
|
||||
{label}
|
||||
</Badge>
|
||||
@@ -51,6 +41,10 @@ const ProductMenuItem = ({
|
||||
</Menu.Item>
|
||||
)
|
||||
|
||||
if (disabled) {
|
||||
return <div className="opacity-50 pointer-events-none">{menuItem}</div>
|
||||
}
|
||||
|
||||
if (url) {
|
||||
if (isExternal) {
|
||||
return (
|
||||
@@ -71,5 +65,3 @@ const ProductMenuItem = ({
|
||||
|
||||
return menuItem
|
||||
}
|
||||
|
||||
export default ProductMenuItem
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Badge, Menu } from 'ui'
|
||||
|
||||
import type { ProductMenuGroup } from './ProductMenu.types'
|
||||
import ProductMenuItem from './ProductMenuItem'
|
||||
import { ProductMenuItem } from './ProductMenuItem'
|
||||
|
||||
interface ProductMenuProps {
|
||||
page?: string
|
||||
@@ -35,14 +35,9 @@ export const ProductMenu = ({ page, menu }: ProductMenuProps) => {
|
||||
return (
|
||||
<ProductMenuItem
|
||||
key={item.key}
|
||||
url={item.url}
|
||||
name={item.name}
|
||||
icon={item.icon}
|
||||
rightIcon={item.rightIcon}
|
||||
item={item}
|
||||
isActive={isActive}
|
||||
isExternal={item.isExternal}
|
||||
target={item.isExternal ? '_blank' : '_self'}
|
||||
label={item.label}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -7,6 +7,5 @@ export interface Route {
|
||||
link?: string
|
||||
disabled?: boolean
|
||||
linkElement?: ReactNode
|
||||
|
||||
items?: any | Route[]
|
||||
}
|
||||
@@ -10,7 +10,10 @@ export function useSelectedProjectQuery({ enabled = true } = {}) {
|
||||
{
|
||||
enabled,
|
||||
select: (data) => {
|
||||
return { ...data, parentRef: data.parent_project_ref ?? data.ref }
|
||||
return {
|
||||
...data,
|
||||
parentRef: data.parent_project_ref ?? data.ref,
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user