mirror of
https://github.com/supabase/supabase.git
synced 2026-10-15 06:09:08 +03:00
This PR implements the new flow to confirm subscription upgrades using Orb pending changes. This is backwards compatible and based on a flag exposed by the backend (`subscriptionPreview.pending_subscription_flow`). Just like the organization creation, the entire flow is slightly different - instead of creating a payment method separately, the payment method is added inline while doing the upgrade and then attached to the customer. If payment fails, the upgrade will not go through. If payment requires additional action, the user needs to confirm the payment before allowing the upgrade. For testing the new flow locally, toggle the flag in `flags.ts` on the backend. Changes include - No longer rely on the `changeType` from the plans endpoint as this is regularly out-of-sync and displays wrong up/downgrade info due to race conditions - `readOnly` mode for Stripe elements if anything is loading/submitting - Reduced prop drilling for some components - Hide payment method and address selection on downgrade
157 lines
5.5 KiB
TypeScript
157 lines
5.5 KiB
TypeScript
import { MinusCircle, PauseCircle } from 'lucide-react'
|
|
|
|
import type { ProjectInfo } from 'data/projects/projects-query'
|
|
import type { OrgSubscription, ProjectAddon } from 'data/subscriptions/types'
|
|
import { PricingInformation } from 'shared-data'
|
|
import { Modal } from 'ui'
|
|
import { Admonition } from 'ui-patterns'
|
|
import { plans as subscriptionsPlans } from 'shared-data/plans'
|
|
import { useMemo } from 'react'
|
|
|
|
export interface DowngradeModalProps {
|
|
visible: boolean
|
|
subscription?: OrgSubscription
|
|
onClose: () => void
|
|
onConfirm: () => void
|
|
projects: ProjectInfo[]
|
|
}
|
|
|
|
const ProjectDowngradeListItem = ({ projectAddon }: { projectAddon: ProjectAddon }) => {
|
|
const needsRestart = projectAddon.addons.find((addon) => addon.type === 'compute_instance')
|
|
|
|
/**
|
|
* We do not include Log Drains and Advanced MFA Phone for the following reasons:
|
|
* 1. These addons are not removed automatically. Instead, users have to remove the respective configuration themselves
|
|
* 2. It's not obvious to users that Log Drains and MFA Phone are addons
|
|
*/
|
|
const relevantAddonsToList = projectAddon.addons.filter(
|
|
(addon) => !['log_drain', 'auth_mfa_phone'].includes(addon.type)
|
|
)
|
|
|
|
const addonNames = relevantAddonsToList.map((addon) => {
|
|
if (addon.type === 'compute_instance') return `${addon.variant.name} Compute Instance`
|
|
return addon.variant.name
|
|
})
|
|
|
|
return (
|
|
<li className="list-disc ml-6">
|
|
{projectAddon.name}: {addonNames.join(', ')} will be removed.
|
|
{needsRestart ? (
|
|
<>
|
|
{' '}
|
|
Project will also <span className="font-bold">need to be restarted</span> due to change in
|
|
compute instance
|
|
</>
|
|
) : (
|
|
''
|
|
)}
|
|
</li>
|
|
)
|
|
}
|
|
|
|
const DowngradeModal = ({
|
|
visible,
|
|
subscription,
|
|
onClose,
|
|
onConfirm,
|
|
projects,
|
|
}: DowngradeModalProps) => {
|
|
const selectedPlan = useMemo(() => subscriptionsPlans.find((tier) => tier.id === 'tier_free'), [])
|
|
|
|
// Filter out the micro addon as we're dealing with that separately
|
|
const previousProjectAddons =
|
|
subscription?.project_addons.flatMap((projectAddons) => {
|
|
const addons = projectAddons.addons.filter((it) => it.variant.identifier !== 'ci_micro')
|
|
if (!addons.length) {
|
|
return []
|
|
} else {
|
|
return {
|
|
...projectAddons,
|
|
// Overwrite addons, filtered out the micro addon
|
|
addons,
|
|
}
|
|
}
|
|
}) || []
|
|
|
|
const hasInstancesOnMicro = projects.some((project) => project.infra_compute_size === 'micro')
|
|
|
|
return (
|
|
<Modal
|
|
size="large"
|
|
alignFooter="right"
|
|
variant="warning"
|
|
visible={visible}
|
|
onCancel={onClose}
|
|
onConfirm={onConfirm}
|
|
header={`Confirm to downgrade to ${selectedPlan?.name} plan`}
|
|
>
|
|
<Modal.Content>
|
|
<div className="space-y-2">
|
|
<Admonition
|
|
type="warning"
|
|
title="Downgrading to the Free Plan will lead to reductions in your organization's quota"
|
|
description="If you're already past the limits of the Free Plan, your projects could become
|
|
unresponsive or enter read only mode."
|
|
/>
|
|
|
|
{((previousProjectAddons.length ?? 0) > 0 || hasInstancesOnMicro) && (
|
|
<Admonition type="warning" title="Projects affected by the downgrade">
|
|
<ul className="space-y-1 max-h-[100px] overflow-y-auto">
|
|
{previousProjectAddons.map((project) => (
|
|
<ProjectDowngradeListItem key={project.ref} projectAddon={project} />
|
|
))}
|
|
|
|
{projects
|
|
.filter((it) => it.infra_compute_size === 'micro')
|
|
.map((project) => (
|
|
<li className="list-disc ml-6" key={project.ref}>
|
|
{project.name}: Compute will be downgraded. Project will also{' '}
|
|
<span className="font-bold">need to be restarted</span>.
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</Admonition>
|
|
)}
|
|
</div>
|
|
|
|
<ul className="mt-4 space-y-5 text-sm">
|
|
<li className="flex items-center gap-3">
|
|
<PauseCircle size={18} />
|
|
<span>Projects will be paused after a week of inactivity</span>
|
|
</li>
|
|
|
|
<li className="flex items-center gap-3 mb-2">
|
|
<MinusCircle size={18} />
|
|
<span>Add ons from all projects under this organization will be removed.</span>
|
|
</li>
|
|
|
|
<li className="flex gap-3">
|
|
<div>
|
|
<strong>Before you downgrade to the {selectedPlan?.name} plan, consider:</strong>
|
|
<ul className="space-y-2 mt-2">
|
|
<li className="list-disc ml-6 text-foreground-light">
|
|
Your projects no longer require their respective add ons.
|
|
</li>
|
|
<li className="list-disc ml-6 text-foreground-light">
|
|
Your resource consumption are well within the {selectedPlan?.name} plan's quota.
|
|
</li>
|
|
<li className="list-disc ml-6 text-foreground-light">
|
|
Alternatively, you may also transfer projects across organizations.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
{subscription?.billing_via_partner === true && subscription.billing_partner === 'fly' && (
|
|
<p className="mt-4 text-sm">
|
|
Your organization will be downgraded at the end of your current billing cycle.
|
|
</p>
|
|
)}
|
|
</Modal.Content>
|
|
</Modal>
|
|
)
|
|
}
|
|
|
|
export default DowngradeModal
|