import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { useFlag } from 'common' import { Loader, Shield, Wrench } from 'lucide-react' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, DialogTrigger, InfoIcon, Loading, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, WarningIcon, } from 'ui' import { Admonition } from 'ui-patterns' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { DocsButton } from '@/components/ui/DocsButton' import { useOrganizationsQuery } from '@/data/organizations/organizations-query' import { projectKeys } from '@/data/projects/keys' import { useProjectTransferMutation } from '@/data/projects/project-transfer-mutation' import { useProjectTransferPreviewQuery } from '@/data/projects/project-transfer-preview-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' export const TransferProjectButton = () => { const { data: project } = useSelectedProjectQuery() const projectRef = project?.ref const projectOrgId = project?.organization_id const [isOpen, setIsOpen] = useState(false) const { data: allOrganizations } = useOrganizationsQuery({ enabled: isOpen }) const disableProjectTransfer = useFlag('disableProjectTransfer') const organizations = (allOrganizations || []).filter((it) => it.id !== projectOrgId) const [selectedOrg, setSelectedOrg] = useState() const { mutate: transferProject, error: transferError, isPending: isTransferring, } = useProjectTransferMutation({ onSuccess: () => { toast.success(`Successfully transferred project ${project?.name}.`) setIsOpen(false) }, }) const { data: transferPreviewData, error: transferPreviewError, isPending: transferPreviewIsLoading, } = useProjectTransferPreviewQuery( { projectRef, targetOrganizationSlug: selectedOrg }, { enabled: !isTransferring && isOpen } ) const queryClient = useQueryClient() useEffect(() => { if (isOpen) { // reset state setSelectedOrg(undefined) } else { // Invalidate cache queryClient.removeQueries({ queryKey: projectKeys.projectTransferPreview(projectRef, selectedOrg), }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen]) const { can: canTransferProject } = useAsyncCheckPermissions( PermissionAction.UPDATE, 'organizations' ) async function handleTransferProject() { if (project === undefined) return if (selectedOrg === undefined) return transferProject({ projectRef, targetOrganizationSlug: selectedOrg }) } return ( setIsOpen(open)} open={isOpen}> Transfer project {`Transfer project ${project?.name}`}

To transfer projects, the owner must be a member of both the source and target organizations. Consider the following before transferring your project:

  • Possible downtime

    There might be a short downtime when transferring projects from a paid to a free organization.

  • Permissions

    Depending on your role in the target organization, your level of permissions may change after transfer.

  • Features

    Moving your project to an organization with a smaller subscription plan may result in the loss of certain features (i.e. image transformations).

{organizations && (
{organizations.length === 0 ? (
You do not have any organizations you can transfer your project to.
) : ( )}
)}
{selectedOrg !== undefined && (
{transferPreviewData && transferPreviewData.errors.length > 0 && (
{transferPreviewData.errors.map((error) => (

{error.message}

))}
{transferPreviewData.members_exceeding_free_project_limit.length > 0 && (

These members have reached their maximum limits for the number of active Free plan projects within organizations where they are an administrator or owner:

    {(transferPreviewData.members_exceeding_free_project_limit || []).map( (member, idx: number) => (
  • {member.name} (Limit: {member.limit} free projects)
  • ) )}

These members will need to either delete, pause, or upgrade one or more of their projects before you can transfer this project.

)}
)} {transferPreviewData && (transferPreviewData.warnings.length > 0 || transferPreviewData.info.length > 0) && (
{transferPreviewData.warnings.map((warning) => (

{warning.message}

))} {transferPreviewData.info.map((info) => (

{info.message}

))}
)} {transferPreviewError && !transferError && ( )} {transferError && ( )}
)}
) }