mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
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:
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')}
|
||||
>
|
||||
|
||||
+10
-2
@@ -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>
|
||||
|
||||
+1
-1
@@ -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')}
|
||||
>
|
||||
|
||||
+1
@@ -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}
|
||||
|
||||
+1
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
+5
-4
@@ -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
|
||||
|
||||
+18
-12
@@ -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()
|
||||
|
||||
+15
-21
@@ -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
|
||||
|
||||
+14
-6
@@ -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
|
||||
|
||||
@@ -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} />
|
||||
|
||||
Reference in new issue
Block a user