import { PermissionAction } from '@supabase/shared-types/out/constants' import { useFlag, useParams } from 'common' import { AlertCircle } from 'lucide-react' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle, cn, RadioGroupCard, RadioGroupCardItem, SidePanel, } from 'ui' import { TaxDisclaimer } from '@/components/interfaces/Billing/TaxDisclaimer' import { DocsButton } from '@/components/ui/DocsButton' import { InlineLink } from '@/components/ui/InlineLink' import { UpgradeToPro } from '@/components/ui/UpgradeToPro' 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 type { AddonVariantId } from '@/data/subscriptions/types' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { DOCS_URL } from '@/lib/constants' import { formatCurrency } from '@/lib/helpers' import { useAddonsPagePanel } from '@/state/addons-page' const CustomDomainSidePanel = () => { const { ref: projectRef } = useParams() const customDomainsDisabledDueToQuota = useFlag('customDomainsDisabledDueToQuota') const [selectedOption, setSelectedOption] = useState('cd_none') const { can: canUpdateCustomDomain } = useAsyncCheckPermissions( PermissionAction.BILLING_WRITE, 'stripe.subscriptions' ) const { panel, closePanel } = useAddonsPagePanel() const visible = panel === 'customDomain' const { data: addons, isPending: isLoading } = useProjectAddonsQuery({ projectRef }) const { mutate: updateAddon, isPending: isUpdating } = useProjectAddonUpdateMutation({ onSuccess: () => { toast.success(`Successfully enabled custom domain`) closePanel() }, onError: (error) => { toast.error(`Unable to enable custom domain: ${error.message}`) }, }) const { mutate: removeAddon, isPending: isRemoving } = useProjectAddonRemoveMutation({ onSuccess: () => { toast.success(`Successfully disabled custom domain`) closePanel() }, onError: (error) => { toast.error(`Unable to disable custom domain: ${error.message}`) }, }) const isSubmitting = isUpdating || isRemoving const subscriptionCDOption = (addons?.selected_addons ?? []).find( (addon) => addon.type === 'custom_domain' ) const availableOptions = (addons?.available_addons ?? []).find((addon) => addon.type === 'custom_domain')?.variants ?? [] const { hasAccess: hasAccessToCustomDomain, isLoading: isLoadingEntitlement } = useCheckEntitlements('custom_domain') const hasChanges = selectedOption !== (subscriptionCDOption?.variant.identifier ?? 'cd_none') const selectedCustomDomain = availableOptions.find( (option) => option.identifier === selectedOption ) useEffect(() => { if (visible) { if (subscriptionCDOption !== undefined) { setSelectedOption(subscriptionCDOption.variant.identifier) } else { setSelectedOption('cd_none') } } }, [visible, isLoading]) const onConfirm = async () => { if (!projectRef) return console.error('Project ref is required') if (selectedOption === 'cd_none' && subscriptionCDOption !== undefined) { removeAddon({ projectRef, variant: subscriptionCDOption.variant.identifier }) } else { updateAddon({ projectRef, type: 'custom_domain', variant: selectedOption as AddonVariantId }) } } return (

Custom domains

} >
{subscriptionCDOption === undefined && selectedCustomDomain !== undefined && customDomainsDisabledDueToQuota && ( Adding new custom domains temporarily disabled We are working with our upstream DNS provider before we are able to sign up new custom domains. Please check back in a few hours. )}

Custom domains allow you to present a branded experience to your users. You may set up your custom domain in the{' '} General Settings {' '} page after enabling the add-on.

setSelectedOption(value)} >

No custom domain

Use the default supabase domain for your API

$0

/ month

} showIndicator={false} /> {availableOptions.map((option) => (

{option.name}

Present a branded experience to your users

{formatCurrency(option.price)}

/ month

} showIndicator={false} /> ))} {hasChanges && selectedOption !== 'cd_none' && (

There are no immediate charges. The add-on is billed at the end of your billing cycle based on your usage and prorated to the hour.

)} {!hasAccessToCustomDomain && ( )}
) } export default CustomDomainSidePanel