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:
Joshen Lim authored and GitHub committed 2026-01-27 15:28:23 +08:00
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>
+11 -10
View File
@@ -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}
/>
)
})}
-1
View File
@@ -7,6 +7,5 @@ export interface Route {
link?: string
disabled?: boolean
linkElement?: ReactNode
items?: any | Route[]
}
+4 -1
View File
@@ -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,
}
},
}
)