Chore/enforce pitr for enabling branching (#19303)

* Enforce PITR on dashboard to enable branching

* Add warning if disabling pitr when branching is enabled

* Update apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx

Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>

* Update apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx

Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>

* Update apps/studio/components/interfaces/Settings/Addons/PITRSidePanel.tsx

Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>

* Update apps/studio/components/layouts/AppLayout/EnableBranchingButton/EnableBranchingModal.tsx

Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>

* Prettier

---------

Co-authored-by: Copple <10214025+kiwicopple@users.noreply.github.com>
This commit is contained in:
Joshen LimandCopple authored and GitHub committed 2023-11-29 21:14:26 +08:00
1 parent 0c5022c170
commit f56bfa58cb
3 files changed
+176 -58

No files matched your search

@@ -9,13 +9,23 @@ import { useTheme } from 'next-themes'
import { useProjectAddonRemoveMutation } from 'data/subscriptions/project-addon-remove-mutation'
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization, useSelectedProject, useStore } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import Telemetry from 'lib/telemetry'
import { useSubscriptionPageStateSnapshot } from 'state/subscription-page'
import { Alert, Button, IconExternalLink, Radio, SidePanel } from 'ui'
import {
Alert,
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
IconExternalLink,
Radio,
SidePanel,
} from 'ui'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { AlertTriangleIcon } from 'lucide-react'
const PITR_CATEGORY_OPTIONS: {
id: 'off' | 'on'
@@ -42,12 +52,15 @@ const PITRSidePanel = () => {
const router = useRouter()
const { ref: projectRef } = useParams()
const { resolvedTheme } = useTheme()
const project = useSelectedProject()
const organization = useSelectedOrganization()
const [selectedCategory, setSelectedCategory] = useState<'on' | 'off'>('off')
const [selectedOption, setSelectedOption] = useState<string>('pitr_0')
const canUpdatePitr = useCheckPermissions(PermissionAction.BILLING_WRITE, 'stripe.subscriptions')
const isBranchingEnabled =
project?.is_branch_enabled === true || project?.parent_project_ref !== undefined
const snap = useSubscriptionPageStateSnapshot()
const visible = snap.panelKey === 'pitr'
@@ -225,6 +238,19 @@ const PITRSidePanel = () => {
</div>
</div>
{selectedCategory === 'off' && subscriptionPitr !== undefined && isBranchingEnabled && (
<Alert_Shadcn_ variant="warning">
<AlertTriangleIcon strokeWidth={2} />
<AlertTitle_Shadcn_>
Are you sure you want to disable this while using Branching?
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
Without PITR, you might not be able to recover lost data if you accidentally merge a
branch that deletes a column or user data. We don't recommend this.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
)}
{selectedCategory === 'on' && (
<div className="!mt-8 pb-4">
{isFreePlan ? (
@@ -2,7 +2,16 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { useParams } from 'common'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { Button, Form_Shadcn_, IconFileText, IconGitBranch, Modal } from 'ui'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
Form_Shadcn_,
IconFileText,
IconGitBranch,
Modal,
} from 'ui'
import * as z from 'zod'
import SidePanelGitHubRepoLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker'
@@ -14,6 +23,10 @@ import { useOrgIntegrationsQuery } from 'data/integrations/integrations-query-or
import { useSelectedOrganization, useStore } from 'hooks'
import { useAppStateSnapshot } from 'state/app-state'
import GithubRepositorySelection from './GithubRepositorySelection'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { AlertCircleIcon } from 'lucide-react'
import Link from 'next/link'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
const EnableBranchingModal = () => {
const { ui } = useStore()
@@ -36,6 +49,18 @@ const EnableBranchingModal = () => {
orgSlug: selectedOrg?.slug,
})
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrg?.slug })
const isFreePlan = subscription?.plan.id === 'free'
const {
data: addons,
error: addonsError,
isLoading: isLoadingAddons,
isError: isErrorAddons,
} = useProjectAddonsQuery({ projectRef: ref })
const hasPitrEnabled =
(addons?.selected_addons ?? []).find((addon) => addon.type === 'pitr') !== undefined
const hasGithubIntegrationInstalled =
integrations?.some((integration) => integration.integration.name === 'GitHub') ?? false
const githubIntegration = integrations?.find(
@@ -124,7 +149,7 @@ const EnableBranchingModal = () => {
</div>
</Modal.Content>
{isLoadingIntegrations && (
{(isLoadingIntegrations || isLoadingAddons) && (
<>
<Modal.Separator />
<Modal.Content className="px-7 py-6">
@@ -134,73 +159,140 @@ const EnableBranchingModal = () => {
</>
)}
{isErrorIntegrations && (
{isErrorAddons && (
<>
<Modal.Separator />
<Modal.Content className="px-7 py-6">
<AlertError error={integrationsError} subject="Failed to retrieve integrations" />
<AlertError error={addonsError} subject="Failed to retrieve project addons" />
</Modal.Content>
<Modal.Separator />
</>
)}
{isSuccessIntegrations && (
<GithubRepositorySelection
form={form}
isChecking={isChecking}
isValid={canSubmit}
integration={githubIntegration}
hasGithubIntegrationInstalled={hasGithubIntegrationInstalled}
/>
)}
{hasPitrEnabled ? (
<>
{isErrorIntegrations && (
<>
<Modal.Separator />
<Modal.Content className="px-7 py-6">
<AlertError
error={integrationsError}
subject="Failed to retrieve integrations"
/>
</Modal.Content>
<Modal.Separator />
</>
)}
<Modal.Content className="px-7 py-6 flex flex-col gap-3">
<p className="text-sm text-foreground-light">Please keep in mind the following:</p>
<div className="flex flex-row gap-4">
<div>
<figure className="w-10 h-10 rounded-md bg-warning-200 border border-warning-300 flex items-center justify-center">
<IconFileText className="text-amber-900" size={20} strokeWidth={2} />
</figure>
</div>
<div>
<p className="text-sm text-foreground">
You will not be able to use the dashboard to make changes to the database
</p>
{isSuccessIntegrations && (
<GithubRepositorySelection
form={form}
isChecking={isChecking}
isValid={canSubmit}
integration={githubIntegration}
hasGithubIntegrationInstalled={hasGithubIntegrationInstalled}
/>
)}
<Modal.Content className="px-7 py-6 flex flex-col gap-3">
<p className="text-sm text-foreground-light">
Schema changes for database preview branches must be done via Git. We are
nonetheless working on allowing the dashboard to make schema changes for preview
branches.
Please keep in mind the following:
</p>
</div>
</div>
</Modal.Content>
<div className="flex flex-row gap-4">
<div>
<figure className="w-10 h-10 rounded-md bg-warning-200 border border-warning-300 flex items-center justify-center">
<IconFileText className="text-amber-900" size={20} strokeWidth={2} />
</figure>
</div>
<div>
<p className="text-sm text-foreground">
You will not be able to use the dashboard to make changes to the database
</p>
<p className="text-sm text-foreground-light">
Schema changes for database Preview Branches must be made using git.
Dashboard changes to Preview Branches are coming soon.
</p>
</div>
</div>
</Modal.Content>
<Modal.Separator />
<Modal.Separator />
<Modal.Content className="px-7">
<div className="flex items-center space-x-2 py-2 pb-4">
<Button
size="medium"
block
disabled={isCreating}
type="default"
onClick={() => snap.setShowEnableBranchingModal(false)}
>
Cancel
</Button>
<Button
block
size="medium"
form={formId}
disabled={isCreating || !canSubmit}
loading={isCreating}
type="primary"
htmlType="submit"
>
I understand, enable branching
</Button>
<Modal.Content className="px-7">
<div className="flex items-center space-x-2 py-2 pb-4">
<Button
size="medium"
block
disabled={isCreating}
type="default"
onClick={() => snap.setShowEnableBranchingModal(false)}
>
Cancel
</Button>
<Button
block
size="medium"
form={formId}
disabled={isCreating || !canSubmit}
loading={isCreating}
type="primary"
htmlType="submit"
>
I understand, enable branching
</Button>
</div>
</Modal.Content>
</>
) : (
<div className="">
<Alert_Shadcn_ className="rounded-none border-r-0 border-l-0 px-7 [&>svg]:left-6">
<AlertCircleIcon strokeWidth={2} />
<AlertTitle_Shadcn_>
Point in time recovery (PITR) is required for branching
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
This is to ensure that you can always recover data if you make a "bad
migration". For example, if you accidentally delete a column or some of your
production data.
</AlertDescription_Shadcn_>
{isFreePlan && (
<AlertDescription_Shadcn_ className="mt-2">
To enable PITR, you may first upgrade your organization's plan to at least
Pro, then purchase the PITR add on for your project via the{' '}
<Link
href={`/project/${ref}/settings/addons?panel=pitr`}
className="text-brand"
>
project settings
</Link>
.
</AlertDescription_Shadcn_>
)}
</Alert_Shadcn_>
<Modal.Content className="px-7">
<div className="flex items-center justify-end space-x-2 py-4 pb-4">
<Button
size="tiny"
type="default"
onClick={() => snap.setShowEnableBranchingModal(false)}
>
Understood
</Button>
<Button size="tiny">
<Link
href={
isFreePlan
? `/org/${selectedOrg?.slug}/billing?panel=subscriptionPlan`
: `/project/${ref}/settings/addons?panel=pitr`
}
>
{isFreePlan ? 'Upgrade to Pro' : 'Enable PITR'}
</Link>
</Button>
</div>
</Modal.Content>
</div>
</Modal.Content>
)}
</form>
</Form_Shadcn_>
</Modal>
+1 -1
View File
@@ -27,7 +27,7 @@ const AlertError = ({ ref, subject, error, className }: AlertErrorProps) => {
return (
<Alert_Shadcn_ className={className} variant="warning" title={subject}>
<IconAlertCircle className="h-4 w-4" color="bg-warning-300" strokeWidth={2} />
<IconAlertCircle className="h-4 w-4" strokeWidth={2} />
<AlertTitle_Shadcn_>{subject}</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_ className="flex flex-col gap-3">
<div>