import { zodResolver } from '@hookform/resolvers/zod' import { useParams } from 'common' import { last } from 'lodash' import Link from 'next/link' import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' import * as z from 'zod' import { PermissionAction } from '@supabase/shared-types/out/constants' import SidePanelGitHubRepoLinker from 'components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker' import AlertError from 'components/ui/AlertError' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useBranchCreateMutation } from 'data/branches/branch-create-mutation' import { useProjectUpgradeEligibilityQuery } from 'data/config/project-upgrade-eligibility-query' import { useCheckGithubBranchValidity } from 'data/integrations/github-branch-check-query' import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { DollarSign, ExternalLink, FileText, GitBranch } from 'lucide-react' import { useAppStateSnapshot } from 'state/app-state' import { Button, Form_Shadcn_, Modal } from 'ui' import BranchingPITRNotice from './BranchingPITRNotice' import BranchingPlanNotice from './BranchingPlanNotice' import BranchingPostgresVersionNotice from './BranchingPostgresVersionNotice' import GithubRepositorySelection from './GithubRepositorySelection' const EnableBranchingModal = () => { const { ref } = useParams() const snap = useAppStateSnapshot() const selectedOrg = useSelectedOrganization() // [Joshen] There's something weird with RHF that I can't figure out atm // but calling form.formState.isValid somehow removes the onBlur check, // and makes the validation run onChange instead. This is a workaround const [isValid, setIsValid] = useState(false) const canCreateBranches = useCheckPermissions(PermissionAction.CREATE, 'preview_branches') const { data: connections, error: connectionsError, isLoading: isLoadingConnections, isSuccess: isSuccessConnections, isError: isErrorConnections, } = useGitHubConnectionsQuery({ organizationId: selectedOrg?.id }) const { data, error: upgradeEligibilityError, isLoading: isLoadingUpgradeEligibility, isError: isErrorUpgradeEligibility, isSuccess: isSuccessUpgradeEligibility, } = useProjectUpgradeEligibilityQuery({ projectRef: ref, }) const hasMinimumPgVersion = Number(last(data?.current_app_version.split('-') ?? [])?.split('.')[0] ?? 0) >= 15 const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrg?.slug }) const isFreePlan = subscription?.plan.id === 'free' const { data: addons } = useProjectAddonsQuery({ projectRef: ref }) const hasPitrEnabled = (addons?.selected_addons ?? []).find((addon) => addon.type === 'pitr') !== undefined const githubConnection = connections?.find((connection) => connection.project.ref === ref) const [repoOwner, repoName] = githubConnection?.repository.name.split('/') ?? [] const { mutateAsync: checkGithubBranchValidity, isLoading: isChecking } = useCheckGithubBranchValidity({ onError: () => {} }) const { mutate: createBranch, isLoading: isCreating } = useBranchCreateMutation({ onSuccess: () => { toast.success(`Successfully created new branch`) snap.setShowEnableBranchingModal(false) }, }) const formId = 'enable-branching-form' const FormSchema = z.object({ branchName: z .string() .refine((val) => val.length > 1, `Please enter a branch name from ${repoOwner}/${repoName}`) .refine(async (val) => { try { if (val.length > 0) { if (!githubConnection?.id) { throw new Error('No GitHub connection found') } await checkGithubBranchValidity({ connectionId: githubConnection.id, branchName: val, }) setIsValid(true) } return true } catch (error) { setIsValid(false) return false } }, `Unable to find branch from ${repoOwner}/${repoName}`), }) const form = useForm>({ mode: 'onBlur', reValidateMode: 'onChange', resolver: zodResolver(FormSchema), defaultValues: { branchName: '' }, }) const isLoading = isLoadingConnections || isLoadingUpgradeEligibility const isError = isErrorConnections || isErrorUpgradeEligibility const isSuccess = isSuccessConnections && isSuccessUpgradeEligibility const canSubmit = form.getValues('branchName').length > 0 && !isChecking && isValid const onSubmit = (data: z.infer) => { if (!ref) return console.error('Project ref is required') createBranch({ projectRef: ref, branchName: data.branchName, gitBranch: data.branchName }) } useEffect(() => { if (form && snap.showEnableBranchingModal) { setIsValid(false) form.reset() } }, [form, snap.showEnableBranchingModal]) return ( <> snap.setShowEnableBranchingModal(false)} className="block" size="medium" hideClose >
setIsValid(false)} >

Enable database branching

Manage environments in Supabase

{isLoading && ( <> )} {isError && ( <> {isErrorConnections ? ( ) : isErrorUpgradeEligibility ? ( ) : null} )} {isSuccess && ( <> {isFreePlan ? ( ) : !hasMinimumPgVersion ? ( ) : ( <> {!hasPitrEnabled && } )}

Please keep in mind the following:

Preview branches are billed $0.32 per day

This cost will continue for as long as the branch has not been removed.

Migrations are applied from your GitHub repository

Migration files in your ./supabase{' '} directory will run on both Preview Branches and Production when pushing and merging branches.

)}
) } export default EnableBranchingModal