Merge pull request #15923 from supabase/chore/allow-project-subscription-pages-to-load-irregardless-of-project-details

Allow project subscription pages to load irregardless of project details
This commit is contained in:
Joshen Lim authored and GitHub committed 2023-07-24 18:09:09 +08:00
commit 720f0e9dad
25 files changed
+179 -163

No files matched your search

@@ -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<ProjectUpdateDisabledTooltipProps>) => {
return (
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
{projectUpdateDisabled ? (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-scale-100 py-1 px-2 leading-none shadow',
'border border-scale-200',
].join(' ')}
>
<span className="text-xs text-scale-1200">
Subscription changes are currently disabled. Our engineers are working on a fix.
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
) : null}
</Tooltip.Root>
)
}
export default ProjectUpdateDisabledTooltip
@@ -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) => {
<div className="flex-grow">
<p className="text-sm text-scale-1000">Optimized compute</p>
<p className="">{computeInstance?.variant.name ?? 'Micro'}</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('computeInstance')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change optimized compute
</Button>
@@ -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'}
</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('pitr')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change point in time recovery
</Button>
@@ -341,12 +351,15 @@ const AddOns = ({}: AddOnsProps) => {
? 'Custom domain is enabled'
: 'Custom domain is not enabled'}
</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('customDomain')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change custom domain
</Button>
@@ -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) => {
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="default"
className="mt-4"
className="mt-4 pointer-events-auto"
disabled={!canChangeTier}
onClick={() => snap.setPanelKey('costControl')}
>
@@ -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 (
<>
<div className="border-b">
@@ -13,7 +13,7 @@ import { useFlag, useStore } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
import { Button, Input, Modal } from 'ui'
import { CANCELLATION_REASONS } from '../../Billing.constants'
import ProjectUpdateDisabledTooltip from '../../ProjectUpdateDisabledTooltip'
import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip'
export interface ExitSurveyModalProps {
visible: boolean
@@ -206,6 +206,7 @@ const ExitSurveyModal = ({ visible, onClose }: ExitSurveyModalProps) => {
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="danger"
className="pointer-events-auto"
loading={isSubmitting}
disabled={projectUpdateDisabled || isSubmitting}
onClick={onSubmit}
@@ -8,7 +8,7 @@ import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscr
import { useFlag, useSelectedOrganization } from 'hooks'
import { useSubscriptionPageStateSnapshot } from 'state/subscription-page'
import { Alert, Button, IconExternalLink } from 'ui'
import ProjectUpdateDisabledTooltip from '../../ProjectUpdateDisabledTooltip'
import ProjectUpdateDisabledTooltip from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip'
import SubscriptionPaymentMethod from './SubscriptionPaymentMethod'
import TierUpdateSidePanel from './TierUpdateSidePanel'
@@ -81,6 +81,7 @@ const SubscriptionTier = ({}: SubscriptionTierProps) => {
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="default"
className="pointer-events-auto"
disabled={!canChangeTier}
onClick={() => snap.setPanelKey('subscriptionPlan')}
>
@@ -3,16 +3,20 @@ import { PropsWithChildren } from 'react'
export interface ProjectUpdateDisabledTooltipProps {
projectUpdateDisabled: boolean
projectNotActive?: boolean
}
const ProjectUpdateDisabledTooltip = ({
projectUpdateDisabled,
projectNotActive = false,
children,
}: PropsWithChildren<ProjectUpdateDisabledTooltipProps>) => {
const showTooltip = projectUpdateDisabled || projectNotActive
return (
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger asChild>{children}</Tooltip.Trigger>
{projectUpdateDisabled ? (
{showTooltip ? (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
@@ -23,7 +27,11 @@ const ProjectUpdateDisabledTooltip = ({
].join(' ')}
>
<span className="text-xs text-scale-1200">
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'
: ''}
</span>
</div>
</Tooltip.Content>
@@ -147,7 +147,7 @@ const CostControl = ({}: CostControlProps) => {
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="default"
className="mt-4"
className="mt-4 pointer-events-auto"
disabled={!canChangeTier}
onClick={() => snap.setPanelKey('costControl')}
>
@@ -209,6 +209,7 @@ const ExitSurveyModal = ({ visible, subscription, onClose }: ExitSurveyModalProp
<ProjectUpdateDisabledTooltip projectUpdateDisabled={subscriptionUpdateDisabled}>
<Button
type="danger"
className="pointer-events-auto"
loading={isSubmitting}
disabled={subscriptionUpdateDisabled || isSubmitting}
onClick={onSubmit}
@@ -84,6 +84,7 @@ const Subscription = () => {
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<Button
type="default"
className="pointer-events-auto"
disabled={!canChangeTier}
onClick={() => snap.setPanelKey('subscriptionPlan')}
>
@@ -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 = () => {
<div className="flex-grow">
<p className="text-sm text-scale-1000">Current option:</p>
<p className="">{computeInstance?.variant.name ?? 'Micro'}</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('computeInstance')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change optimized compute
</Button>
@@ -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'}
</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('pitr')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change point in time recovery
</Button>
@@ -403,12 +412,15 @@ const Addons = () => {
? 'Custom domain is enabled'
: 'Custom domain is not enabled'}
</p>
<ProjectUpdateDisabledTooltip projectUpdateDisabled={projectUpdateDisabled}>
<ProjectUpdateDisabledTooltip
projectUpdateDisabled={projectUpdateDisabled}
projectNotActive={!isProjectActive}
>
<Button
type="default"
className="mt-2"
className="mt-2 pointer-events-auto"
onClick={() => snap.setPanelKey('customDomain')}
disabled={projectUpdateDisabled}
disabled={!isProjectActive || projectUpdateDisabled}
>
Change custom domain
</Button>
@@ -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'
@@ -356,8 +356,8 @@ const ComputeInstanceSidePanel = () => {
{selectedCategory === 'micro' && (
<p className="text-sm text-scale-1100">
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.
</p>
)}
@@ -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<any> = ({ 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<any> = ({ projectRef }) => {
</Panel>
</section>
<ResetDbPassword disabled={true} />
<ResetDbPassword disabled />
<section className="space-y-6">
<Panel
@@ -1,24 +1,26 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { FC, useState, useRef, useEffect } from 'react'
import { debounce } from 'lodash'
import generator from 'generate-password'
import { Input, Button, Modal } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import generator from 'generate-password'
import { debounce } from 'lodash'
import { useEffect, useRef, useState } from 'react'
import { Button, Input, Modal } from 'ui'
import { useCheckPermissions, useStore } from 'hooks'
import { useParams } from 'common/hooks'
import { useCheckPermissions, useStore } from 'hooks'
import { patch } from 'lib/common/fetch'
import { passwordStrength } from 'lib/helpers'
import { API_URL, DEFAULT_MINIMUM_PASSWORD_STRENGTH } from 'lib/constants'
import { passwordStrength } from 'lib/helpers'
import { useIsProjectActive } from 'components/layouts/ProjectLayout/ProjectContext'
import Panel from 'components/ui/Panel'
import PasswordStrengthBar from 'components/ui/PasswordStrengthBar'
import { getProjectDetail } from 'data/projects/project-detail-query'
const ResetDbPassword: FC<any> = ({ 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<boolean>(false)
@@ -109,13 +111,13 @@ const ResetDbPassword: FC<any> = ({ disabled = false }) => {
<Tooltip.Trigger>
<Button
type="default"
disabled={!canResetDbPassword || disabled}
disabled={!canResetDbPassword || !isProjectActive || disabled}
onClick={() => setShowResetDbPass(true)}
>
Reset Database Password
Reset database password
</Button>
</Tooltip.Trigger>
{!canResetDbPassword && (
{(!canResetDbPassword || !isProjectActive) && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
@@ -126,7 +128,11 @@ const ResetDbPassword: FC<any> = ({ disabled = false }) => {
].join(' ')}
>
<span className="text-xs text-scale-1200">
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'
: ''}
</span>
</div>
</Tooltip.Content>
@@ -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()
@@ -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={<IconPause />}
onClick={openModal}
loading={loading}
disabled={isPaused || !canPauseProject}
disabled={isPaused || !canPauseProject || !isProjectActive}
>
Pause Project
</Button>
</Tooltip.Trigger>
{isPaused ? (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-scale-100 py-1 px-2 leading-none shadow', // background
'border border-scale-200 ', //border
].join(' ')}
>
<span className="text-xs text-scale-1200">Your project is already paused</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
) : !canPauseProject ? (
{isPaused || !canPauseProject || !isProjectActive ? (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
@@ -98,14 +88,18 @@ const PauseProjectButton = () => {
].join(' ')}
>
<span className="text-xs text-scale-1200">
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'
: ''}
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
) : (
<></>
)}
) : null}
</Tooltip.Root>
<ConfirmModal
danger
@@ -11,13 +11,17 @@ import { setProjectPostgrestStatus } from 'data/projects/projects-query'
import { useCheckPermissions, useStore } from 'hooks'
import { post } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import {
useIsProjectActive,
useProjectContext,
} from 'components/layouts/ProjectLayout/ProjectContext'
const RestartServerButton = () => {
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 = () => {
<div className="flex items-center">
<Button
type="default"
className="rounded-r-none px-3"
disabled={!canRestartProject}
className={`px-3 ${canRestartProject && isProjectActive ? 'rounded-r-none' : ''}`}
disabled={!canRestartProject || !isProjectActive}
onClick={() => setServiceToRestart('project')}
>
Restart project
</Button>
{canRestartProject && (
{canRestartProject && isProjectActive && (
<Dropdown
align="end"
side="bottom"
@@ -120,7 +124,7 @@ const RestartServerButton = () => {
)}
</div>
</Tooltip.Trigger>
{!canRestartProject && (
{(!canRestartProject || !isProjectActive) && (
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
@@ -131,7 +135,11 @@ const RestartServerButton = () => {
].join(' ')}
>
<span className="text-xs text-scale-1200">
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'
: ''}
</span>
</div>
</Tooltip.Content>
@@ -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,11 @@ 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 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)
const isProjectUpgrading = selectedProject?.status === PROJECT_STATUS.UPGRADING
const isProjectRestoring = selectedProject?.status === PROJECT_STATUS.RESTORING
@@ -155,7 +175,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => {
return (
<>
{isLoading || selectedProject === undefined ? (
{isLoading || (requiresProjectDetails && selectedProject === undefined) ? (
<Connecting />
) : isProjectUpgrading ? (
<UpgradingState />
@@ -168,7 +188,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => {
) : requiresDbConnection && isProjectBuilding ? (
<BuildingState project={selectedProject} />
) : (
<Fragment key={selectedProject.ref}>{children}</Fragment>
<Fragment key={selectedProject?.ref}>{children}</Fragment>
)}
</>
)
@@ -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<Props> = ({ title, children }) => {
const SettingsLayout = ({ title, children }: PropsWithChildren<SettingsLayoutProps>) => {
const { ref } = useParams()
const { ui, meta } = useStore()
const project = useSelectedProject()
const organization = useSelectedOrganization()
@@ -26,12 +27,7 @@ const SettingsLayout: FC<Props> = ({ 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) {
@@ -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
@@ -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 (
<div className="p-6 mx-auto text-center sm:w-full md:w-3/4">
<p className="text-sm">Loading...</p>
<div className="w-full">
<GenericSkeletonLoader />
</div>
)
}
@@ -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
+2 -8
View File
@@ -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<Project>(`${API_URL}/projects/${ref}`, { signal })
if (!isResponseOk(data)) {
throw data.error
}
if (!isResponseOk(data)) throw data.error
return data
}
@@ -13,13 +13,12 @@ import SSLConfiguration from 'components/interfaces/Settings/Database/SSLConfigu
import DiskSizeConfiguration from 'components/interfaces/Settings/Database/DiskSizeConfiguration'
const ProjectSettings: NextPageWithLayout = () => {
const { ref: projectRef } = useParams()
const networkRestrictions = useFlag('networkRestrictions')
return (
<div className="1xl:px-28 mx-auto flex flex-col px-5 pt-6 pb-14 lg:px-16 xl:px-24 2xl:px-32">
<div className="content h-full w-full overflow-y-auto space-y-10">
<DatabaseSettings projectRef={projectRef} />
<DatabaseSettings />
<ConnectionPooling />
<SSLConfiguration />
<DiskSizeConfiguration />
@@ -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 <div></div>
return (
<div className="flex flex-grow p-4 storage-container">
<StorageSettings projectRef={projectRef} />