| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import { MinusCircle, PauseCircle } from 'lucide-react'
- import { useMemo } from 'react'
- import { plans as subscriptionsPlans } from 'shared-data/plans'
- import { Modal } from 'ui'
- import { Admonition } from 'ui-patterns'
- import { getComputeSize, OrgProject } from '@/data/projects/org-projects-infinite-query'
- import type { OrgSubscription, ProjectAddon } from '@/data/subscriptions/types'
- export interface DowngradeModalProps {
- visible: boolean
- subscription?: OrgSubscription
- onClose: () => void
- onConfirm: () => void
- projects: OrgProject[]
- }
- 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) => {
- const computeSize = getComputeSize(project)
- return computeSize === '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) => {
- const computeSize = getComputeSize(it)
- return computeSize === '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
|