diff --git a/apps/studio/components/interfaces/Database/Backups/BackupItem.test.tsx b/apps/studio/components/interfaces/Database/Backups/BackupItem.test.tsx
new file mode 100644
index 00000000000..9c7d2d89c52
--- /dev/null
+++ b/apps/studio/components/interfaces/Database/Backups/BackupItem.test.tsx
@@ -0,0 +1,69 @@
+import { fireEvent, screen } from '@testing-library/react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { BackupItem } from './BackupItem'
+import type { DatabaseBackup } from '@/data/database/backups-query'
+import { customRender } from '@/tests/lib/custom-render'
+
+const { mockUseAsyncCheckPermissions } = vi.hoisted(() => ({
+ mockUseAsyncCheckPermissions: vi.fn(),
+}))
+
+vi.mock('@/hooks/misc/useCheckPermissions', () => ({
+ useAsyncCheckPermissions: mockUseAsyncCheckPermissions,
+}))
+
+const backup: DatabaseBackup = {
+ id: 1,
+ inserted_at: '2024-01-01T00:00:00Z',
+ isPhysicalBackup: false,
+ project_id: 1,
+ status: 'COMPLETED',
+}
+
+describe('BackupItem', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseAsyncCheckPermissions.mockReturnValue({ can: true })
+ })
+
+ it('enables restoring for a healthy project that is not High Availability', () => {
+ customRender(
+
+ )
+
+ expect(screen.getByRole('button', { name: 'Restore' })).toBeEnabled()
+ })
+
+ it('disables restoring with a tooltip on High Availability projects', async () => {
+ customRender(
+
+ )
+
+ const button = screen.getByRole('button', { name: 'Restore' })
+ expect(button).toBeDisabled()
+
+ // Radix opens the tooltip on pointermove; userEvent does not synthesize
+ // pointer events on disabled buttons
+ fireEvent.pointerMove(button)
+ expect(
+ await screen.findAllByText(
+ 'Restoring from a backup is unavailable on High Availability projects',
+ {},
+ { timeout: 2000 }
+ )
+ ).not.toHaveLength(0)
+ })
+})
diff --git a/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx b/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
index 85ca7e62f9f..b5a7c913c16 100644
--- a/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
+++ b/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
@@ -13,11 +13,18 @@ import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
interface BackupItemProps {
index: number
isHealthy: boolean
+ isHighAvailability: boolean
backup: DatabaseBackup
onSelectBackup: () => void
}
-export const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupItemProps) => {
+export const BackupItem = ({
+ index,
+ isHealthy,
+ isHighAvailability,
+ backup,
+ onSelectBackup,
+}: BackupItemProps) => {
const { ref: projectRef } = useParams()
const { can: canTriggerScheduledBackups } = useAsyncCheckPermissions(
PermissionAction.INFRA_EXECUTE,
@@ -37,22 +44,30 @@ export const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupI
},
})
+ function getTooltipText() {
+ if (isHighAvailability) {
+ return 'Restoring from a backup is unavailable on High Availability projects'
+ } else if (!isHealthy) {
+ return 'Cannot be restored as project is not active'
+ } else if (!canTriggerScheduledBackups) {
+ return 'You need additional permissions to trigger a restore'
+ } else {
+ return undefined
+ }
+ }
+
const generateSideButtons = (backup: DatabaseBackup) => {
if (backup.status === 'COMPLETED')
return (
diff --git a/apps/studio/components/interfaces/Database/Backups/BackupsList.tsx b/apps/studio/components/interfaces/Database/Backups/BackupsList.tsx
index dadc2ee0820..46075e4e997 100644
--- a/apps/studio/components/interfaces/Database/Backups/BackupsList.tsx
+++ b/apps/studio/components/interfaces/Database/Backups/BackupsList.tsx
@@ -17,7 +17,7 @@ import { useBackupRestoreMutation } from '@/data/database/backup-restore-mutatio
import { DatabaseBackup, useBackupsQuery } from '@/data/database/backups-query'
import { useSetProjectStatus } from '@/data/projects/project-detail-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
-import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
+import { useIsHighAvailability, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from '@/lib/constants'
export const BackupsList = () => {
@@ -29,6 +29,7 @@ export const BackupsList = () => {
const { setProjectStatus } = useSetProjectStatus()
const { data: selectedProject } = useSelectedProjectQuery()
const isHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY
+ const isHighAvailability = useIsHighAvailability()
const { data: backups } = useBackupsQuery({ projectRef })
const {
@@ -88,6 +89,7 @@ export const BackupsList = () => {
backup={x}
index={i}
isHealthy={isHealthy}
+ isHighAvailability={isHighAvailability}
onSelectBackup={() => setSelectedBackup(x)}
/>
)
diff --git a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
index 30319bc6fd3..8b2b38a8b7a 100644
--- a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
+++ b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx
@@ -28,6 +28,7 @@ import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import {
useIsAwsK8sCloudProvider,
+ useIsHighAvailability,
useIsOrioleDb,
useSelectedProjectQuery,
} from '@/hooks/misc/useSelectedProject'
@@ -41,6 +42,7 @@ export const RestoreToNewProject = () => {
useCheckEntitlements('backup.restore_to_new_project')
const isOrioleDb = useIsOrioleDb()
const isAwsK8s = useIsAwsK8sCloudProvider()
+ const isHighAvailability = useIsHighAvailability()
const [refetchInterval, setRefetchInterval] = useState(false)
const [selectedBackupId, setSelectedBackupId] = useState(null)
@@ -133,6 +135,16 @@ export const RestoreToNewProject = () => {
)
}
+ if (isHighAvailability) {
+ return (
+
+ )
+ }
+
if (isAwsK8s) {
return (
{
const isAws = useIsAwsCloudProvider()
const isProjectActive = useIsProjectActive()
const isOrioleDbInAws = useIsOrioleDbInAws() === true
+ const isHighAvailability = useIsHighAvailability()
const { projectSettingsCustomDomains, projectAddonsDedicatedIpv4Address } = useIsFeatureEnabled([
'project_settings:custom_domains',
@@ -100,14 +102,19 @@ export const Addons = () => {
const customDomainEnabled = customDomain !== undefined
const canOpenIPv4 =
- isAws && isProjectActive && !projectUpdateDisabled && (canUpdateIPv4 || ipv4Enabled)
+ isAws &&
+ isProjectActive &&
+ !projectUpdateDisabled &&
+ (canUpdateIPv4 || ipv4Enabled) &&
+ !isHighAvailability
const canOpenPITR =
isProjectActive &&
!projectUpdateDisabled &&
sufficientPgVersion &&
!hasHipaaAddon &&
- !isOrioleDbInAws
- const canOpenCustomDomain = isProjectActive && !projectUpdateDisabled
+ !isOrioleDbInAws &&
+ !isHighAvailability
+ const canOpenCustomDomain = isProjectActive && !projectUpdateDisabled && !isHighAvailability
const ipv4DisabledReason = getIPv4DisabledReason({
isAws,
@@ -115,6 +122,7 @@ export const Addons = () => {
projectUpdateDisabled,
canUpdateIPv4,
ipv4Enabled,
+ isHighAvailability,
})
const pitrDisabledReason = getPitrDisabledReason({
@@ -123,11 +131,13 @@ export const Addons = () => {
hasHipaaAddon,
sufficientPgVersion,
isOrioleDbInAws,
+ isHighAvailability,
})
const customDomainDisabledReason = getCustomDomainDisabledReason({
isProjectActive,
projectUpdateDisabled,
+ isHighAvailability,
})
const pitrAlertState = getPitrAlertState({
hasHipaaAddon,
@@ -190,6 +200,11 @@ export const Addons = () => {
return (
+
{isBranch && (
{
}
meta={
-
- {ipv4Enabled ? (
- Enabled
- ) : (
- Disabled
- )}
-
+ {ipv4Enabled ? (
+
Enabled
+ ) : (
+
Disabled
+ )}
+ {!canOpenIPv4 && ipv4DisabledReason && (
+
+
+
+
+ {ipv4DisabledReason}
+
+ )}
}
>
@@ -384,9 +401,13 @@ export const Addons = () => {
)}
-
-
-
+ {!isHighAvailability && (
+ <>
+
+
+
+ >
+ )}
)
diff --git a/apps/studio/components/interfaces/Settings/Addons/Addons.utils.test.ts b/apps/studio/components/interfaces/Settings/Addons/Addons.utils.test.ts
index 525d3a5f6d4..06bfd537838 100644
--- a/apps/studio/components/interfaces/Settings/Addons/Addons.utils.test.ts
+++ b/apps/studio/components/interfaces/Settings/Addons/Addons.utils.test.ts
@@ -8,6 +8,19 @@ import {
} from './Addons.utils'
describe('getIPv4DisabledReason', () => {
+ it('returns the High Availability message before other checks', () => {
+ expect(
+ getIPv4DisabledReason({
+ isAws: false,
+ isProjectActive: false,
+ projectUpdateDisabled: true,
+ canUpdateIPv4: false,
+ ipv4Enabled: false,
+ isHighAvailability: true,
+ })
+ ).toBe('Dedicated IPv4 address is unavailable on High Availability projects')
+ })
+
it('returns the AWS-only message for non-AWS projects', () => {
expect(
getIPv4DisabledReason({
@@ -16,6 +29,7 @@ describe('getIPv4DisabledReason', () => {
projectUpdateDisabled: false,
canUpdateIPv4: true,
ipv4Enabled: false,
+ isHighAvailability: false,
})
).toBe('Dedicated IPv4 address is only available for AWS projects')
})
@@ -28,6 +42,7 @@ describe('getIPv4DisabledReason', () => {
projectUpdateDisabled: true,
canUpdateIPv4: false,
ipv4Enabled: false,
+ isHighAvailability: false,
})
).toBe('Project must be active to update IPv4')
})
@@ -40,6 +55,7 @@ describe('getIPv4DisabledReason', () => {
projectUpdateDisabled: true,
canUpdateIPv4: true,
ipv4Enabled: false,
+ isHighAvailability: false,
})
).toBe('Project updates are currently disabled')
})
@@ -52,6 +68,7 @@ describe('getIPv4DisabledReason', () => {
projectUpdateDisabled: false,
canUpdateIPv4: false,
ipv4Enabled: false,
+ isHighAvailability: false,
})
).toBe('You can only add IPv4 when your project network configuration is set to IPv6')
})
@@ -64,12 +81,26 @@ describe('getIPv4DisabledReason', () => {
projectUpdateDisabled: false,
canUpdateIPv4: false,
ipv4Enabled: true,
+ isHighAvailability: false,
})
).toBeUndefined()
})
})
describe('getPitrDisabledReason', () => {
+ it('returns the High Availability message before other checks', () => {
+ expect(
+ getPitrDisabledReason({
+ isProjectActive: false,
+ projectUpdateDisabled: true,
+ hasHipaaAddon: true,
+ sufficientPgVersion: false,
+ isOrioleDbInAws: true,
+ isHighAvailability: true,
+ })
+ ).toBe('Point in time recovery is unavailable on High Availability projects')
+ })
+
it('returns the inactive-project message first', () => {
expect(
getPitrDisabledReason({
@@ -78,6 +109,7 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: true,
sufficientPgVersion: false,
isOrioleDbInAws: true,
+ isHighAvailability: false,
})
).toBe('Project must be active to update PITR')
})
@@ -90,6 +122,7 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: true,
sufficientPgVersion: false,
isOrioleDbInAws: true,
+ isHighAvailability: false,
})
).toBe('Project updates are currently disabled')
})
@@ -102,6 +135,7 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: true,
sufficientPgVersion: true,
isOrioleDbInAws: false,
+ isHighAvailability: false,
})
).toBe('PITR cannot be changed with HIPAA enabled')
})
@@ -114,6 +148,7 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: false,
sufficientPgVersion: false,
isOrioleDbInAws: false,
+ isHighAvailability: false,
})
).toBe('Your project is too old to enable PITR')
})
@@ -126,6 +161,7 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: false,
sufficientPgVersion: true,
isOrioleDbInAws: true,
+ isHighAvailability: false,
})
).toBe('Point in time recovery is not supported with OrioleDB')
})
@@ -138,17 +174,29 @@ describe('getPitrDisabledReason', () => {
hasHipaaAddon: false,
sufficientPgVersion: true,
isOrioleDbInAws: false,
+ isHighAvailability: false,
})
).toBeUndefined()
})
})
describe('getCustomDomainDisabledReason', () => {
+ it('returns the High Availability message before other checks', () => {
+ expect(
+ getCustomDomainDisabledReason({
+ isProjectActive: false,
+ projectUpdateDisabled: true,
+ isHighAvailability: true,
+ })
+ ).toBe('Custom domain is unavailable on High Availability projects')
+ })
+
it('returns the inactive-project message', () => {
expect(
getCustomDomainDisabledReason({
isProjectActive: false,
projectUpdateDisabled: true,
+ isHighAvailability: false,
})
).toBe('Project must be active to update custom domain')
})
@@ -158,6 +206,7 @@ describe('getCustomDomainDisabledReason', () => {
getCustomDomainDisabledReason({
isProjectActive: true,
projectUpdateDisabled: true,
+ isHighAvailability: false,
})
).toBe('Project updates are currently disabled')
})
@@ -167,6 +216,7 @@ describe('getCustomDomainDisabledReason', () => {
getCustomDomainDisabledReason({
isProjectActive: true,
projectUpdateDisabled: false,
+ isHighAvailability: false,
})
).toBeUndefined()
})
diff --git a/apps/studio/components/interfaces/Settings/Addons/Addons.utils.ts b/apps/studio/components/interfaces/Settings/Addons/Addons.utils.ts
index 95729611108..072d32a7494 100644
--- a/apps/studio/components/interfaces/Settings/Addons/Addons.utils.ts
+++ b/apps/studio/components/interfaces/Settings/Addons/Addons.utils.ts
@@ -4,6 +4,7 @@ export interface IPv4DisabledReasonOptions {
projectUpdateDisabled: boolean
canUpdateIPv4: boolean
ipv4Enabled: boolean
+ isHighAvailability: boolean
}
export const getIPv4DisabledReason = ({
@@ -12,7 +13,12 @@ export const getIPv4DisabledReason = ({
projectUpdateDisabled,
canUpdateIPv4,
ipv4Enabled,
+ isHighAvailability,
}: IPv4DisabledReasonOptions) => {
+ if (isHighAvailability) {
+ return 'Dedicated IPv4 address is unavailable on High Availability projects'
+ }
+
if (!isAws) {
return 'Dedicated IPv4 address is only available for AWS projects'
}
@@ -38,6 +44,7 @@ export interface PitrDisabledReasonOptions {
hasHipaaAddon: boolean
sufficientPgVersion: boolean
isOrioleDbInAws: boolean
+ isHighAvailability: boolean
}
export const getPitrDisabledReason = ({
@@ -46,7 +53,12 @@ export const getPitrDisabledReason = ({
hasHipaaAddon,
sufficientPgVersion,
isOrioleDbInAws,
+ isHighAvailability,
}: PitrDisabledReasonOptions) => {
+ if (isHighAvailability) {
+ return 'Point in time recovery is unavailable on High Availability projects'
+ }
+
if (!isProjectActive) {
return 'Project must be active to update PITR'
}
@@ -73,12 +85,18 @@ export const getPitrDisabledReason = ({
export interface CustomDomainDisabledReasonOptions {
isProjectActive: boolean
projectUpdateDisabled: boolean
+ isHighAvailability: boolean
}
export const getCustomDomainDisabledReason = ({
isProjectActive,
projectUpdateDisabled,
+ isHighAvailability,
}: CustomDomainDisabledReasonOptions) => {
+ if (isHighAvailability) {
+ return 'Custom domain is unavailable on High Availability projects'
+ }
+
if (!isProjectActive) {
return 'Project must be active to update custom domain'
}
diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.test.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.test.tsx
new file mode 100644
index 00000000000..0be7102c2fb
--- /dev/null
+++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.test.tsx
@@ -0,0 +1,75 @@
+import { fireEvent, screen } from '@testing-library/react'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+import { PauseProjectButton } from './PauseProjectButton'
+import { customRender } from '@/tests/lib/custom-render'
+
+const {
+ mockUseAsyncCheckPermissions,
+ mockUseCheckEntitlements,
+ mockUseIsHighAvailability,
+ mockUseSelectedOrganizationQuery,
+ mockUseSelectedProjectQuery,
+} = vi.hoisted(() => ({
+ mockUseAsyncCheckPermissions: vi.fn(),
+ mockUseCheckEntitlements: vi.fn(),
+ mockUseIsHighAvailability: vi.fn(),
+ mockUseSelectedOrganizationQuery: vi.fn(),
+ mockUseSelectedProjectQuery: vi.fn(),
+}))
+
+vi.mock('@/hooks/misc/useCheckEntitlements', () => ({
+ useCheckEntitlements: mockUseCheckEntitlements,
+}))
+
+vi.mock('@/hooks/misc/useCheckPermissions', () => ({
+ useAsyncCheckPermissions: mockUseAsyncCheckPermissions,
+}))
+
+vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
+ useSelectedOrganizationQuery: mockUseSelectedOrganizationQuery,
+}))
+
+vi.mock('@/hooks/misc/useSelectedProject', () => ({
+ useIsHighAvailability: mockUseIsHighAvailability,
+ useIsProjectActive: () => true,
+ useSelectedProjectQuery: mockUseSelectedProjectQuery,
+}))
+
+describe('PauseProjectButton', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseAsyncCheckPermissions.mockReturnValue({ can: true })
+ mockUseCheckEntitlements.mockReturnValue({ hasAccess: true })
+ mockUseSelectedOrganizationQuery.mockReturnValue({ data: { plan: { id: 'free' } } })
+ mockUseSelectedProjectQuery.mockReturnValue({
+ data: { ref: 'default', status: 'ACTIVE_HEALTHY' },
+ })
+ mockUseIsHighAvailability.mockReturnValue(false)
+ })
+
+ it('enables pausing for an active project that is not High Availability', () => {
+ customRender()
+
+ expect(screen.getByRole('button', { name: 'Pause project' })).toBeEnabled()
+ })
+
+ it('disables pausing with a tooltip on High Availability projects', async () => {
+ mockUseIsHighAvailability.mockReturnValue(true)
+ customRender()
+
+ const button = screen.getByRole('button', { name: 'Pause project' })
+ expect(button).toBeDisabled()
+
+ // Radix opens the tooltip on pointermove; userEvent does not synthesize
+ // pointer events on disabled buttons
+ fireEvent.pointerMove(button)
+ expect(
+ await screen.findAllByText(
+ 'Pausing is unavailable on High Availability projects',
+ {},
+ { timeout: 2000 }
+ )
+ ).not.toHaveLength(0)
+ })
+})
diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx
index 923ccf3c7d0..c449a2ae13b 100644
--- a/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx
+++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx
@@ -20,7 +20,11 @@ import { useProjectPauseMutation } from '@/data/projects/project-pause-mutation'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
-import { useIsProjectActive, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
+import {
+ useIsHighAvailability,
+ useIsProjectActive,
+ useSelectedProjectQuery,
+} from '@/hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from '@/lib/constants'
export const PauseProjectButton = () => {
@@ -30,6 +34,7 @@ export const PauseProjectButton = () => {
const { setProjectStatus } = useSetProjectStatus()
const isProjectActive = useIsProjectActive()
+ const isHighAvailability = useIsHighAvailability()
const isProjectUnhealthy = project?.status === PROJECT_STATUS.ACTIVE_UNHEALTHY
const [isModalOpen, setIsModalOpen] = useState(false)
@@ -68,11 +73,14 @@ export const PauseProjectButton = () => {
project === undefined ||
isPaused ||
!canPauseProject ||
- !isProjectActive
+ !isProjectActive ||
+ isHighAvailability
function getTooltipText() {
if (isPaused) {
return `Your ${entityLabel} is already paused`
+ } else if (isHighAvailability) {
+ return 'Pausing is unavailable on High Availability projects'
} else if (!canPauseProject) {
return `You need additional permissions to pause this ${entityLabel}`
} else if (isProjectUnhealthy) {
diff --git a/apps/studio/components/ui/HighAvailability/HighAvailabilityDisabledSectionNotice.tsx b/apps/studio/components/ui/HighAvailability/HighAvailabilityDisabledSectionNotice.tsx
index 28a928bcd9c..09e7ec47e44 100644
--- a/apps/studio/components/ui/HighAvailability/HighAvailabilityDisabledSectionNotice.tsx
+++ b/apps/studio/components/ui/HighAvailability/HighAvailabilityDisabledSectionNotice.tsx
@@ -10,18 +10,20 @@ const DEFAULT_DESCRIPTION =
interface HighAvailabilityDisabledSectionNoticeProps {
title?: string
description?: ReactNode
+ className?: string
}
export function HighAvailabilityDisabledSectionNotice({
title = DEFAULT_TITLE,
description = DEFAULT_DESCRIPTION,
+ className,
}: HighAvailabilityDisabledSectionNoticeProps) {
const isHighAvailability = useIsHighAvailability()
if (!isHighAvailability) return null
return (
-
+
{description}
)
diff --git a/apps/studio/pages/project/[ref]/database/backups/scheduled.tsx b/apps/studio/pages/project/[ref]/database/backups/scheduled.tsx
index 547e0018607..1f3fa15292e 100644
--- a/apps/studio/pages/project/[ref]/database/backups/scheduled.tsx
+++ b/apps/studio/pages/project/[ref]/database/backups/scheduled.tsx
@@ -1,6 +1,6 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
-import { Info } from 'lucide-react'
+import { DatabaseBackup, Info } from 'lucide-react'
import { Admonition } from 'ui-patterns/Admonition'
import { PageContainer } from 'ui-patterns/PageContainer'
import {
@@ -19,33 +19,16 @@ import DatabaseLayout from '@/components/layouts/DatabaseLayout/DatabaseLayout'
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
import { AlertError } from '@/components/ui/AlertError'
import { DocsButton } from '@/components/ui/DocsButton'
+import { HighAvailabilityDisabledEmptyState } from '@/components/ui/HighAvailability/HighAvailabilityDisabledEmptyState'
import InformationBox from '@/components/ui/InformationBox'
import { NoPermission } from '@/components/ui/NoPermission'
import { useBackupsQuery } from '@/data/database/backups-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
-import { useIsOrioleDbInAws } from '@/hooks/misc/useSelectedProject'
+import { useIsHighAvailability, useIsOrioleDbInAws } from '@/hooks/misc/useSelectedProject'
import { DOCS_URL } from '@/lib/constants'
import type { NextPageWithLayout } from '@/types'
const DatabaseScheduledBackups: NextPageWithLayout = () => {
- const { ref: projectRef } = useParams()
-
- const {
- data: backups,
- error,
- isPending: isLoading,
- isError,
- isSuccess,
- } = useBackupsQuery({ projectRef })
-
- const isOrioleDbInAws = useIsOrioleDbInAws()
- const isPitrEnabled = backups?.pitr_enabled
-
- const { can: canReadScheduledBackups, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
- PermissionAction.READ,
- 'back_ups'
- )
-
return (
<>
@@ -61,62 +44,7 @@ const DatabaseScheduledBackups: NextPageWithLayout = () => {
- {isOrioleDbInAws ? (
-
-
-
- ) : (
-
- {isLoading &&
}
-
- {isError && (
-
- )}
-
- {isSuccess && (
- <>
- {!isPitrEnabled && (
-
- Projects are backed up daily around midnight of your project’s region and
- can be restored at any time.
-
- )}
-
- {isPitrEnabled && (
-
}
- title="Point-In-Time-Recovery (PITR) enabled"
- description={
-
- Your project uses PITR and full daily backups are no longer taken. PITR
- lets you restore to a specific time (down to the second) within your
- selected PITR retention period.{' '}
-
- Learn more
-
-
- }
- />
- )}
-
- {isPermissionsLoaded && !canReadScheduledBackups ? (
-
- ) : (
-
- )}
- >
- )}
-
- )}
+
@@ -124,6 +52,97 @@ const DatabaseScheduledBackups: NextPageWithLayout = () => {
)
}
+const ScheduledBackups = () => {
+ const { ref: projectRef } = useParams()
+
+ const {
+ data: backups,
+ error,
+ isPending: isLoading,
+ isError,
+ isSuccess,
+ } = useBackupsQuery({ projectRef })
+
+ const isOrioleDbInAws = useIsOrioleDbInAws()
+ const isHighAvailability = useIsHighAvailability()
+ const isPitrEnabled = backups?.pitr_enabled
+
+ const { can: canReadScheduledBackups, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions(
+ PermissionAction.READ,
+ 'back_ups'
+ )
+
+ if (isOrioleDbInAws) {
+ return (
+
+
+
+ )
+ }
+
+ if (isHighAvailability) {
+ return (
+
+ )
+ }
+
+ return (
+
+ {isLoading &&
}
+
+ {isError &&
}
+
+ {isSuccess && (
+ <>
+ {!isPitrEnabled && (
+
+ Projects are backed up daily around midnight of your project’s region and can be
+ restored at any time.
+
+ )}
+
+ {isPitrEnabled && (
+
}
+ title="Point-In-Time-Recovery (PITR) enabled"
+ description={
+
+ Your project uses PITR and full daily backups are no longer taken. PITR lets you
+ restore to a specific time (down to the second) within your selected PITR
+ retention period.{' '}
+
+ Learn more
+
+
+ }
+ />
+ )}
+
+ {isPermissionsLoaded && !canReadScheduledBackups ? (
+
+ ) : (
+
+ )}
+ >
+ )}
+
+ )
+}
+
DatabaseScheduledBackups.getLayout = (page) => (
{page}