From 327b082a776276f360e8dfca58d37d6967e6b7bb Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 20 Jul 2023 16:51:40 +0800 Subject: [PATCH 1/5] Allow project subscription pages to load irregardless of project details --- .../BillingV2/Subscription/Subscription.tsx | 11 ++--- .../Addons/ComputeInstanceSidePanel.tsx | 6 +-- .../layouts/ProjectLayout/ProjectLayout.tsx | 42 +++++++++++++------ .../layouts/SettingsLayout/SettingsLayout.tsx | 20 ++++----- .../SettingsLayout/SettingsMenu.utils.ts | 2 +- .../StorageSettings/StorageSettings.tsx | 5 ++- .../ui/ProjectUpgradeFailedBanner.tsx | 3 +- studio/data/projects/project-detail-query.ts | 10 +---- .../pages/project/[ref]/settings/storage.tsx | 5 --- 9 files changed, 55 insertions(+), 49 deletions(-) diff --git a/studio/components/interfaces/BillingV2/Subscription/Subscription.tsx b/studio/components/interfaces/BillingV2/Subscription/Subscription.tsx index 655ee5d6210..b228c80fcee 100644 --- a/studio/components/interfaces/BillingV2/Subscription/Subscription.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/Subscription.tsx @@ -6,7 +6,6 @@ import SubscriptionTier from './Tier/SubscriptionTier' import { SUBSCRIPTION_PANEL_KEYS, useSubscriptionPageStateSnapshot } from 'state/subscription-page' import { useSelectedOrganization } from 'hooks' import { useEffect } from 'react' -import ProjectBackupsStore from 'stores/project/ProjectBackupsStore' export interface SubscriptionProps {} @@ -51,13 +50,15 @@ const Subscription = ({}: SubscriptionProps) => { } }, [router, organization?.slug, isOrgBilling]) + useEffect(() => { + if (panel && typeof panel === 'string' && allowedValues.includes(panel)) { + snap.setPanelKey(panel as SUBSCRIPTION_PANEL_KEYS) + } + }, [panel]) + // No need to bother rendering, we'll redirect anyway if (isOrgBilling) return null - if (panel && typeof panel === 'string' && allowedValues.includes(panel)) { - snap.setPanelKey(panel as SUBSCRIPTION_PANEL_KEYS) - } - return ( <>
diff --git a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx index c8c0a009d21..148afc6d7f9 100644 --- a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx +++ b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx @@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query' import clsx from 'clsx' import Link from 'next/link' import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useParams, useTheme } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' @@ -356,8 +356,8 @@ const ComputeInstanceSidePanel = () => { {selectedCategory === 'micro' && (

- Your database will use the standard Micro size instance of 2-core {cpuArchitecture} CPU (Shared) - with 1GB of memory. + Your database will use the standard Micro size instance of 2-core {cpuArchitecture}{' '} + CPU (Shared) with 1GB of memory.

)} diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index e2dae22e23c..34248181c50 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -17,6 +17,26 @@ import ProjectPausedState from './ProjectPausedState' import RestoringState from './RestoringState' import UpgradingState from './UpgradingState' +// [Joshen] This is temporary while we unblock users from managing their project +// if their project is not responding well for any reason. Eventually needs a bit of an overhaul +const routesToIgnoreProjectDetailsRequest = [ + '/project/[ref]/settings/general', + '/project/[ref]/settings/database', + '/project/[ref]/settings/storage', + '/project/[ref]/settings/billing/subscription', + '/project/[ref]/settings/billing/usage', + '/project/[ref]/settings/billing/invoices', +] + +const routesToIgnorePostgrestConnection = [ + '/project/[ref]/reports', + '/project/[ref]/settings/general', + '/project/[ref]/settings/database', + '/project/[ref]/settings/billing/subscription', + '/project/[ref]/settings/billing/usage', + '/project/[ref]/settings/billing/invoices', +] + export interface ProjectLayoutProps { title?: string isLoading?: boolean @@ -108,8 +128,14 @@ interface MenuBarWrapperProps { } const MenuBarWrapper = ({ isLoading, productMenu, children }: MenuBarWrapperProps) => { + const router = useRouter() const selectedProject = useSelectedProject() - return <>{!isLoading && productMenu && selectedProject !== undefined ? children : null} + const requiresProjectDetails = !routesToIgnoreProjectDetailsRequest.includes(router.pathname) + + const showMenuBar = + !requiresProjectDetails || (requiresProjectDetails && selectedProject !== undefined) + + return <>{!isLoading && productMenu && showMenuBar ? children : null} } interface ContentWrapperProps { @@ -133,17 +159,9 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { const selectedProject = useSelectedProject() const router = useRouter() - const routesToIgnorePostgrestConnection = [ - '/project/[ref]/reports', - '/project/[ref]/settings/general', - '/project/[ref]/settings/database', - '/project/[ref]/settings/billing/subscription', - '/project/[ref]/settings/billing/usage', - '/project/[ref]/settings/billing/invoices', - ] - const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' const requiresPostgrestConnection = !routesToIgnorePostgrestConnection.includes(router.pathname) + const requiresProjectDetails = !routesToIgnoreProjectDetailsRequest.includes(router.pathname) const isProjectUpgrading = selectedProject?.status === PROJECT_STATUS.UPGRADING const isProjectRestoring = selectedProject?.status === PROJECT_STATUS.RESTORING @@ -155,7 +173,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { return ( <> - {isLoading || selectedProject === undefined ? ( + {isLoading || (requiresProjectDetails && selectedProject === undefined) ? ( ) : isProjectUpgrading ? ( @@ -168,7 +186,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { ) : requiresDbConnection && isProjectBuilding ? ( ) : ( - {children} + {children} )} ) diff --git a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx index fa1edfb8c91..61c49d15af0 100644 --- a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx @@ -1,18 +1,19 @@ -import { FC, ReactNode, useEffect } from 'react' +import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' -import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' +import { PropsWithChildren, useEffect } from 'react' import { generateSettingsMenu } from './SettingsMenu.utils' -import ProjectLayout from '../' +import { useParams } from 'common' import ProductMenu from 'components/ui/ProductMenu' +import ProjectLayout from '../' -interface Props { +interface SettingsLayoutProps { title?: string - children: ReactNode } -const SettingsLayout: FC = ({ title, children }) => { +const SettingsLayout = ({ title, children }: PropsWithChildren) => { + const { ref } = useParams() const { ui, meta } = useStore() const project = useSelectedProject() const organization = useSelectedOrganization() @@ -26,12 +27,7 @@ const SettingsLayout: FC = ({ title, children }) => { : router.pathname.split('/')[4] const isVaultEnabled = useFlag('vaultExtension') - const menuRoutes = generateSettingsMenu( - project?.ref as string, - project, - isVaultEnabled, - isOrgBilling - ) + const menuRoutes = generateSettingsMenu(ref, project, isVaultEnabled, isOrgBilling) useEffect(() => { if (ui.selectedProjectRef) { diff --git a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts index b4abdacdbc8..bf9ebf7cfaf 100644 --- a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts +++ b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts @@ -3,7 +3,7 @@ import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types' export const generateSettingsMenu = ( - ref: string, + ref?: string, project?: ProjectBase, isVaultEnabled: boolean = false, isOrgBilling: boolean = false diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index d79d2746bfa..640d1a18aa6 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -9,6 +9,7 @@ import { useCheckPermissions, useStore } from 'hooks' import { IS_PLATFORM } from 'lib/constants' import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants' import { convertFromBytes, convertToBytes } from './StorageSettings.utils' +import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' export type StorageSettingsProps = { projectRef: string | undefined @@ -27,8 +28,8 @@ const StorageSettings = ({ projectRef }: StorageSettingsProps) => { if (!data) { return ( -
-

Loading...

+
+
) } diff --git a/studio/components/ui/ProjectUpgradeFailedBanner.tsx b/studio/components/ui/ProjectUpgradeFailedBanner.tsx index 3e96af454a5..6b109898b0d 100644 --- a/studio/components/ui/ProjectUpgradeFailedBanner.tsx +++ b/studio/components/ui/ProjectUpgradeFailedBanner.tsx @@ -15,7 +15,8 @@ const ProjectUpgradeFailedBanner = () => { const { target_version, status, initiated_at, error } = data?.databaseUpgradeStatus ?? {} const key = `supabase-upgrade-${ref}-${initiated_at}` - const isAcknowledged = localStorage?.getItem(key) === 'true' ?? false + const isAcknowledged = + typeof window !== 'undefined' ? localStorage?.getItem(key) === 'true' ?? false : false const [showMessage, setShowMessage] = useState(!isAcknowledged) const isFailed = status === DatabaseUpgradeStatus.Failed diff --git a/studio/data/projects/project-detail-query.ts b/studio/data/projects/project-detail-query.ts index 79dd3d9ffba..a64643541cf 100644 --- a/studio/data/projects/project-detail-query.ts +++ b/studio/data/projects/project-detail-query.ts @@ -8,16 +8,10 @@ import { projectKeys } from './keys' export type ProjectDetailVariables = { ref?: string } export async function getProjectDetail({ ref }: ProjectDetailVariables, signal?: AbortSignal) { - if (!ref) { - throw new Error('Project ref is required') - } + if (!ref) throw new Error('Project ref is required') const data = await get(`${API_URL}/projects/${ref}`, { signal }) - - if (!isResponseOk(data)) { - throw data.error - } - + if (!isResponseOk(data)) throw data.error return data } diff --git a/studio/pages/project/[ref]/settings/storage.tsx b/studio/pages/project/[ref]/settings/storage.tsx index 8086374e287..09dd18e9582 100644 --- a/studio/pages/project/[ref]/settings/storage.tsx +++ b/studio/pages/project/[ref]/settings/storage.tsx @@ -8,9 +8,6 @@ import { post } from 'lib/common/fetch' import { API_URL, PROJECT_STATUS } from 'lib/constants' import { NextPageWithLayout } from 'types' -/** - * PageLayout is used to setup layout - as usual it will requires inject global store - */ const PageLayout: NextPageWithLayout = () => { const { ref: projectRef } = useParams() const { project } = useProjectContext() @@ -21,8 +18,6 @@ const PageLayout: NextPageWithLayout = () => { } }, [project]) - if (!project) return
- return (
From 7bc11598e09660b58d38e5591eba89ab75218c8f Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 20 Jul 2023 16:52:50 +0800 Subject: [PATCH 2/5] Remove unnecessary import --- .../interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx index 148afc6d7f9..228708f7769 100644 --- a/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx +++ b/studio/components/interfaces/Settings/Addons/ComputeInstanceSidePanel.tsx @@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query' import clsx from 'clsx' import Link from 'next/link' import { useRouter } from 'next/router' -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useState } from 'react' import { useParams, useTheme } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' @@ -13,9 +13,9 @@ import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon- import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' +import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import Telemetry from 'lib/telemetry' -import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' import { useSubscriptionPageStateSnapshot } from 'state/subscription-page' import { Alert, Button, IconExternalLink, IconInfo, Modal, Radio, SidePanel } from 'ui' From e7b47ed780d7650a5bc317ed1431a5ad367d0e17 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 16:56:16 +0800 Subject: [PATCH 3/5] Unblock most settings page and only block specific UI components that are dependent on project being active --- .../ProjectUpdateDisabledTooltip.tsx | 12 +++++- .../interfaces/Settings/Addons/Addons.tsx | 38 ++++++++++++------- .../DatabaseSettings/DatabaseSettings.tsx | 9 +++-- .../DatabaseSettings/ResetDbPassword.tsx | 30 +++++++++------ .../interfaces/Settings/General/General.tsx | 1 - .../Infrastructure/PauseProjectButton.tsx | 36 ++++++++---------- .../Infrastructure/RestartServerButton.tsx | 20 +++++++--- .../layouts/ProjectLayout/ProjectLayout.tsx | 4 +- 8 files changed, 90 insertions(+), 60 deletions(-) diff --git a/studio/components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip.tsx b/studio/components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip.tsx index 873a8144f68..127f9741f06 100644 --- a/studio/components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip.tsx +++ b/studio/components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip.tsx @@ -3,16 +3,20 @@ import { PropsWithChildren } from 'react' export interface ProjectUpdateDisabledTooltipProps { projectUpdateDisabled: boolean + projectNotActive?: boolean } const ProjectUpdateDisabledTooltip = ({ projectUpdateDisabled, + projectNotActive = false, children, }: PropsWithChildren) => { + const showTooltip = projectUpdateDisabled || projectNotActive + return ( {children} - {projectUpdateDisabled ? ( + {showTooltip ? ( @@ -23,7 +27,11 @@ const ProjectUpdateDisabledTooltip = ({ ].join(' ')} > - Subscription changes are currently disabled. Our engineers are working on a fix. + {projectUpdateDisabled + ? 'Subscription changes are currently disabled. Our engineers are working on a fix.' + : projectNotActive + ? 'Unable to update subscription as project is currently not active' + : ''}
diff --git a/studio/components/interfaces/Settings/Addons/Addons.tsx b/studio/components/interfaces/Settings/Addons/Addons.tsx index 1e90ccbd0fc..bfc323c04b7 100644 --- a/studio/components/interfaces/Settings/Addons/Addons.tsx +++ b/studio/components/interfaces/Settings/Addons/Addons.tsx @@ -5,7 +5,10 @@ import { useMemo } from 'react' import { useParams, useTheme } from 'common' import { getAddons } from 'components/interfaces/BillingV2/Subscription/Subscription.utils' import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip' -import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { + useIsProjectActive, + useProjectContext, +} from 'components/layouts/ProjectLayout/ProjectContext' import { ScaffoldContainer, ScaffoldDivider, @@ -19,19 +22,19 @@ import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useFlag } from 'hooks' import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' -import { BASE_PATH } from 'lib/constants' +import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import { SUBSCRIPTION_PANEL_KEYS, useSubscriptionPageStateSnapshot } from 'state/subscription-page' import { Alert, Button, IconChevronRight, IconExternalLink } from 'ui' import { ComputeInstanceSidePanel, CustomDomainSidePanel, PITRSidePanel } from './' const Addons = () => { const { isDarkMode } = useTheme() - const { ref: projectRef } = useParams() + const { ref: projectRef, panel } = useParams() const snap = useSubscriptionPageStateSnapshot() const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate') const { project: selectedProject } = useProjectContext() - const { panel } = useParams() + const isProjectActive = useIsProjectActive() const allowedPanelValues = ['computeInstance', 'pitr', 'customDomain'] if (panel && typeof panel === 'string' && allowedPanelValues.includes(panel)) { snap.setPanelKey(panel as SUBSCRIPTION_PANEL_KEYS) @@ -150,12 +153,15 @@ const Addons = () => {

Current option:

{computeInstance?.variant.name ?? 'Micro'}

- + @@ -342,12 +348,15 @@ const Addons = () => { ? `Point in time recovery of ${pitr.variant.meta?.backup_duration_days} days is enabled` : 'Point in time recovery is not enabled'}

- + @@ -403,12 +412,15 @@ const Addons = () => { ? 'Custom domain is enabled' : 'Custom domain is not enabled'}

- + diff --git a/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx b/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx index f88e0a9704f..c3088075d1c 100644 --- a/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx +++ b/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx @@ -4,16 +4,17 @@ import { Input, Tabs } from 'ui' import { pluckObjectFields } from 'lib/helpers' import { useProjectSettingsQuery } from 'data/config/project-settings-query' -import { useTelemetryProps } from 'common' +import { useParams, useTelemetryProps } from 'common' import Telemetry from 'lib/telemetry' import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import ResetDbPassword from './ResetDbPassword' -const DatabaseSettings: FC = ({ projectRef }) => { - const { data, isLoading, isError } = useProjectSettingsQuery({ projectRef }) +const DatabaseSettings = () => { const router = useRouter() + const { ref: projectRef } = useParams() const telemetryProps = useTelemetryProps() + const { data, isLoading, isError } = useProjectSettingsQuery({ projectRef }) if (isError) { return ( @@ -50,7 +51,7 @@ const DatabaseSettings: FC = ({ projectRef }) => { - +
= ({ disabled = false }) => { - const { ui, app, meta } = useStore() +const ResetDbPassword = ({ disabled = false }) => { const { ref } = useParams() + const { ui, meta } = useStore() + const isProjectActive = useIsProjectActive() const canResetDbPassword = useCheckPermissions(PermissionAction.UPDATE, 'projects') const [showResetDbPass, setShowResetDbPass] = useState(false) @@ -109,13 +111,13 @@ const ResetDbPassword: FC = ({ disabled = false }) => { - {!canResetDbPassword && ( + {(!canResetDbPassword || !isProjectActive) && ( @@ -126,7 +128,11 @@ const ResetDbPassword: FC = ({ disabled = false }) => { ].join(' ')} > - You need additional permissions to reset the database password + {!canResetDbPassword + ? 'You need additional permissions to reset the database password' + : !isProjectActive + ? 'Unable to reset database password as project is not active' + : ''}
diff --git a/studio/components/interfaces/Settings/General/General.tsx b/studio/components/interfaces/Settings/General/General.tsx index 853edb560aa..a4f2ac6b4dd 100644 --- a/studio/components/interfaces/Settings/General/General.tsx +++ b/studio/components/interfaces/Settings/General/General.tsx @@ -17,7 +17,6 @@ import { useProjectUpdateMutation } from 'data/projects/project-update-mutation' import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks' import PauseProjectButton from './Infrastructure/PauseProjectButton' import RestartServerButton from './Infrastructure/RestartServerButton' -import { Project } from 'types' const General = () => { const { ui } = useStore() diff --git a/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx b/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx index 151e8b0492b..f8c4a5d7d98 100644 --- a/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx +++ b/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx @@ -4,7 +4,10 @@ import { useQueryClient } from '@tanstack/react-query' import { useRouter } from 'next/router' import { useState } from 'react' -import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { + useIsProjectActive, + useProjectContext, +} from 'components/layouts/ProjectLayout/ProjectContext' import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog' import { setProjectStatus } from 'data/projects/projects-query' import { useCheckPermissions, useStore } from 'hooks' @@ -19,6 +22,7 @@ const PauseProjectButton = () => { const { ui } = useStore() const { project } = useProjectContext() const router = useRouter() + const isProjectActive = useIsProjectActive() const [loading, setLoading] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) @@ -68,26 +72,12 @@ const PauseProjectButton = () => { icon={} onClick={openModal} loading={loading} - disabled={isPaused || !canPauseProject} + disabled={isPaused || !canPauseProject || !isProjectActive} > Pause Project - {isPaused ? ( - - - -
- Your project is already paused -
-
-
- ) : !canPauseProject ? ( + {isPaused || !canPauseProject || !isProjectActive ? ( @@ -98,14 +88,18 @@ const PauseProjectButton = () => { ].join(' ')} > - You need additional permissions to pause this project + {isPaused + ? 'Your project is already paused' + : !canPauseProject + ? 'You need additional permissions to pause this project' + : !isProjectActive + ? 'Unable to pause project as project is not active' + : ''}
- ) : ( - <> - )} + ) : null} { const queryClient = useQueryClient() const { project } = useProjectContext() const { ui } = useStore() const router = useRouter() + const isProjectActive = useIsProjectActive() const [loading, setLoading] = useState(false) const [serviceToRestart, setServiceToRestart] = useState<'project' | 'database'>() @@ -82,13 +86,13 @@ const RestartServerButton = () => {
- {canRestartProject && ( + {canRestartProject && isProjectActive && ( { )}
- {!canRestartProject && ( + {(!canRestartProject || !isProjectActive) && ( @@ -131,7 +135,11 @@ const RestartServerButton = () => { ].join(' ')} > - You need additional permissions to restart this project + {!canRestartProject + ? 'You need additional permissions to restart this project' + : !isProjectActive + ? 'Unable to restart project as project is not active' + : ''}
diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 34248181c50..8d20baea54e 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -159,7 +159,9 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { const selectedProject = useSelectedProject() const router = useRouter() - const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' + const requiresDbConnection: boolean = + !router.pathname.includes('/project/[ref]/settings') || + router.pathname.includes('/project/[ref]/settings/vault') const requiresPostgrestConnection = !routesToIgnorePostgrestConnection.includes(router.pathname) const requiresProjectDetails = !routesToIgnoreProjectDetailsRequest.includes(router.pathname) From 945ab1e6b3121132859e869287ce9769670e8948 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 16:57:16 +0800 Subject: [PATCH 4/5] Unblock most settings page and only block specific UI components that are dependent on project being active --- .../ProjectUpdateDisabledTooltip.tsx | 36 ------------------- .../BillingV2/Subscription/AddOns/AddOns.tsx | 35 ++++++++++++------ .../Subscription/CostControl/CostControl.tsx | 4 +-- .../Subscription/Tier/ExitSurveyModal.tsx | 3 +- .../Subscription/Tier/SubscriptionTier.tsx | 3 +- .../CostControl/CostControl.tsx | 2 +- .../Subscription/ExitSurveyModal.tsx | 1 + .../Subscription/Subscription.tsx | 1 + 8 files changed, 33 insertions(+), 52 deletions(-) delete mode 100644 studio/components/interfaces/BillingV2/ProjectUpdateDisabledTooltip.tsx diff --git a/studio/components/interfaces/BillingV2/ProjectUpdateDisabledTooltip.tsx b/studio/components/interfaces/BillingV2/ProjectUpdateDisabledTooltip.tsx deleted file mode 100644 index 873a8144f68..00000000000 --- a/studio/components/interfaces/BillingV2/ProjectUpdateDisabledTooltip.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as Tooltip from '@radix-ui/react-tooltip' -import { PropsWithChildren } from 'react' - -export interface ProjectUpdateDisabledTooltipProps { - projectUpdateDisabled: boolean -} - -const ProjectUpdateDisabledTooltip = ({ - projectUpdateDisabled, - children, -}: PropsWithChildren) => { - return ( - - {children} - {projectUpdateDisabled ? ( - - - -
- - Subscription changes are currently disabled. Our engineers are working on a fix. - -
-
-
- ) : null} -
- ) -} - -export default ProjectUpdateDisabledTooltip diff --git a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx index 8f8f19036db..3d7c6d34e0d 100644 --- a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx @@ -15,8 +15,11 @@ import { CustomDomainSidePanel, PITRSidePanel, } from 'components/interfaces/Settings/Addons' -import ProjectUpdateDisabledTooltip from '../../ProjectUpdateDisabledTooltip' -import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip' +import { + useIsProjectActive, + useProjectContext, +} from 'components/layouts/ProjectLayout/ProjectContext' import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' export interface AddOnsProps {} @@ -28,6 +31,7 @@ const AddOns = ({}: AddOnsProps) => { const { isDarkMode } = useTheme() const { project: selectedProject } = useProjectContext() + const isProjectActive = useIsProjectActive() const cpuArchitecture = getCloudProviderArchitecture(selectedProject?.cloud_provider) // [Joshen] We could possibly look into reducing the interval to be more "realtime" @@ -136,12 +140,15 @@ const AddOns = ({}: AddOnsProps) => {

Optimized compute

{computeInstance?.variant.name ?? 'Micro'}

- + @@ -303,12 +310,15 @@ const AddOns = ({}: AddOnsProps) => { ? `Point in time recovery of ${pitr.variant.meta?.backup_duration_days} days is enabled` : 'Point in time recovery is not enabled'}

- + @@ -341,12 +351,15 @@ const AddOns = ({}: AddOnsProps) => { ? 'Custom domain is enabled' : 'Custom domain is not enabled'}

- + diff --git a/studio/components/interfaces/BillingV2/Subscription/CostControl/CostControl.tsx b/studio/components/interfaces/BillingV2/Subscription/CostControl/CostControl.tsx index 34ce550a355..0d5ef46b6ff 100644 --- a/studio/components/interfaces/BillingV2/Subscription/CostControl/CostControl.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/CostControl/CostControl.tsx @@ -6,7 +6,7 @@ import { useSubscriptionPageStateSnapshot } from 'state/subscription-page' import { Alert, Button, IconExternalLink } from 'ui' import SpendCapSidePanel from './SpendCapSidePanel' import Link from 'next/link' -import ProjectUpdateDisabledTooltip from '../../ProjectUpdateDisabledTooltip' +import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip' import { useFlag } from 'hooks' export interface CostControlProps {} @@ -129,7 +129,7 @@ const CostControl = ({}: CostControlProps) => {