import { useRouter } from 'next/router'
import { MouseEvent, useState } from 'react'
import { components } from 'api-types'
import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { ProjectAddonVariantMeta } from 'data/subscriptions/types'
import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils'
import { INSTANCE_MICRO_SPECS } from 'lib/constants'
import { Button, HoverCard, HoverCardContent, HoverCardTrigger, Separator } from 'ui'
import { ComputeBadge } from 'ui-patterns/ComputeBadge'
import ShimmeringLoader from './ShimmeringLoader'
import { useFlag } from 'hooks/ui/useFlag'
const Row = ({ label, stat }: { label: string; stat: React.ReactNode | string }) => {
return (
{label}
{stat}
)
}
interface ComputeBadgeWrapperProps {
project: {
ref?: string
organization_slug?: string
cloud_provider?: string
infra_compute_size?: components['schemas']['DbInstanceSize']
}
}
export const ComputeBadgeWrapper = ({ project }: ComputeBadgeWrapperProps) => {
const router = useRouter()
// handles the state of the hover card
// once open it will fetch the addons
const [open, setOpenState] = useState(false)
const diskAndComputeFormEnabled = useFlag('diskAndComputeForm')
// returns hardcoded values for infra
const cpuArchitecture = getCloudProviderArchitecture(project.cloud_provider)
// fetches addons
const { data: addons, isLoading: isLoadingAddons } = useProjectAddonsQuery(
{
projectRef: project.ref,
},
{ enabled: open }
)
const selectedAddons = addons?.selected_addons ?? []
const { computeInstance } = getAddons(selectedAddons)
const computeInstanceMeta = computeInstance?.variant?.meta
const meta = (
computeInstanceMeta === undefined && project.infra_compute_size === 'micro'
? INSTANCE_MICRO_SPECS
: computeInstanceMeta
) as ProjectAddonVariantMeta
const availableCompute = addons?.available_addons.find(
(addon) => addon.name === 'Compute Instance'
)?.variants
const navigateToAddons = (e: MouseEvent) => {
// These are required as the button is inside an a tag
// side note: not the best idea to nest clickables 🥲
e.preventDefault()
e.stopPropagation()
if (diskAndComputeFormEnabled) {
router.push(`/project/${project?.ref}/settings/compute-and-disk`)
} else {
router.push(`/project/${project?.ref}/settings/addons?panel=computeInstance`)
}
}
const highestComputeAvailable = availableCompute?.[availableCompute.length - 1].identifier
const isHighestCompute =
project?.infra_compute_size === highestComputeAvailable?.replace('ci_', '')
const { data, isLoading: isLoadingSubscriptions } = useOrgSubscriptionQuery(
{ orgSlug: project?.organization_slug },
{ enabled: open }
)
const isEligibleForFreeUpgrade =
data?.plan.id !== 'free' && project?.infra_compute_size === 'nano'
const isLoading = isLoadingAddons || isLoadingSubscriptions
if (!project?.infra_compute_size) return null
return (
<>
setOpenState(!open)} openDelay={280}>
Compute size
{isLoading ? (
<>
>
) : (
<>
{meta !== undefined ? (
<>
>
) : (
<>
{/* meta is only undefined for nano sized compute */}
>
)}
>
)}
{(!isHighestCompute || isEligibleForFreeUpgrade) && (
<>
{isEligibleForFreeUpgrade
? 'Free upgrade to Micro available'
: 'Unlock more compute'}
{isEligibleForFreeUpgrade
? 'Paid plans include a free upgrade to Micro compute.'
: 'Scale your project up to 64 cores and 256 GB RAM.'}
>
)}
>
)
}