import Link from 'next/link' import { useState } from 'react' import { Button, cn, HoverCard, HoverCardContent, HoverCardTrigger, Separator } from 'ui' import { ComputeBadge } from 'ui-patterns/ComputeBadge' import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' import { getAvailableComputeOptions } from '@/components/interfaces/DiskManagement/DiskManagement.utils' import { ProjectDetail } from '@/data/projects/project-detail-query' import { useOrgSubscriptionQuery } from '@/data/subscriptions/org-subscription-query' import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query' import { ResourceWarning } from '@/data/usage/resource-warnings-query' import { getCloudProviderArchitecture } from '@/lib/cloudprovider-utils' import { useTrack } from '@/lib/telemetry/track' export const ChevronsUpAnimated = () => ( ) const Row = ({ label, stat }: { label: string; stat: React.ReactNode | string }) => { return (
{label} {stat}
) } interface ComputeBadgeWrapperProps { slug?: string projectRef?: string cloudProvider?: string computeSize?: ProjectDetail['infra_compute_size'] resourceWarnings?: ResourceWarning badgeClassName?: string } export const ComputeBadgeWrapper = ({ slug, projectRef, cloudProvider, computeSize, resourceWarnings, badgeClassName, }: ComputeBadgeWrapperProps) => { // handles the state of the hover card // once open it will fetch the addons const [open, setOpenState] = useState(false) // returns hardcoded values for infra const cpuArchitecture = getCloudProviderArchitecture(cloudProvider) // fetches addons const { data: addons, isPending: isLoadingAddons } = useProjectAddonsQuery( { projectRef }, { enabled: open } ) // Derive cores/memory from the same source as the badge (infra_compute_size) by looking up // the matching variant in available_addons. Sourcing from selected_addons can drift out of // sync with infra_compute_size and produce a card that contradicts its own badge. const computeOptions = getAvailableComputeOptions(addons?.available_addons ?? [], cloudProvider) const meta = computeOptions.find((variant) => variant.identifier === `ci_${computeSize}`)?.meta const highestComputeAvailable = computeOptions[computeOptions.length - 1]?.identifier const isHighestCompute = computeSize === highestComputeAvailable?.replace('ci_', '') const { data, isPending: isLoadingSubscriptions } = useOrgSubscriptionQuery( { orgSlug: slug }, { enabled: open } ) const isEligibleForFreeUpgrade = data?.plan.id !== 'free' && computeSize === 'nano' const isComputeNearExhaustion = !!resourceWarnings?.cpu_exhaustion || !!resourceWarnings?.memory_and_swap_exhaustion || !!resourceWarnings?.disk_space_exhaustion || !!resourceWarnings?.disk_io_exhaustion const showUpgradeGlow = isEligibleForFreeUpgrade && isComputeNearExhaustion const track = useTrack() const isLoading = isLoadingAddons || isLoadingSubscriptions if (!computeSize) return null return ( setOpenState(!open)} openDelay={280}> e.stopPropagation()}>
} className={cn( showUpgradeGlow && 'text-brand-600 border-brand-500 bg-brand/10 gap-1', badgeClassName )} /> {showUpgradeGlow && ( )}
e.stopPropagation()} >
Compute size
{isLoading ? ( <>
) : ( <>
{computeSize === 'nano' ? ( <> ) : meta !== undefined ? ( <> ) : null}
)}
{(!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.'}

)}
) }