feat: separate concerns

This commit is contained in:
kemal committed 2026-03-31 15:17:08 +01:00
1 parent e728620a40
commit e5d2e2b275
4 files changed
+57 -25

No files matched your search

@@ -47,6 +47,12 @@ vi.mock('common', () => ({
mergeRefs:
(..._refs: any[]) =>
(_value: unknown) => {},
IS_PLATFORM: false,
LOCAL_STORAGE_KEYS: {
FREE_MICRO_UPGRADE_BANNER_DISMISSED: (ref: string) =>
`free-micro-upgrade-banner-dismissed-${ref}`,
},
isFeatureEnabled: () => false,
}))
vi.mock('framer-motion', () => ({
@@ -121,6 +127,27 @@ vi.mock('@/hooks/custom-content/useCustomContent', () => ({
useCustomContent: () => ({ appTitle: 'Supabase' }),
}))
vi.mock('@/hooks/misc/useCheckEntitlements', () => ({
useCheckEntitlements: () => ({ hasAccess: false, isLoading: false }),
}))
vi.mock('@/hooks/misc/useLocalStorage', () => ({
useLocalStorageQuery: () => [false, vi.fn()],
}))
vi.mock('@/components/ui/BannerStack/BannerStackProvider', () => ({
BANNER_ID: { FREE_MICRO_UPGRADE: 'free-micro-upgrade-banner' },
useBannerStack: () => ({ addBanner: vi.fn(), dismissBanner: vi.fn(), banners: [] }),
}))
vi.mock('@/components/ui/BannerStack/Banners/BannerFreeMicroUpgrade', () => ({
BannerFreeMicroUpgrade: () => null,
}))
vi.mock('data/usage/resource-warnings-query', () => ({
useResourceWarningsQuery: () => ({ data: undefined }),
}))
vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
useSelectedOrganizationQuery: () => ({
data: { name: 'Organization 1', slug: 'org-1' },
@@ -1,4 +1,5 @@
import { mergeRefs, useParams } from 'common'
import { LOCAL_STORAGE_KEYS, mergeRefs, useParams } from 'common'
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
import { AnimatePresence, motion } from 'framer-motion'
import Head from 'next/head'
import { useRouter } from 'next/router'
@@ -45,10 +46,8 @@ import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { withAuth } from '@/hooks/misc/withAuth'
import { LOCAL_STORAGE_KEYS } from 'common'
import { PROJECT_STATUS } from '@/lib/constants'
import { buildStudioPageTitle } from '@/lib/page-title'
import { getPathnameWithoutQuery } from '@/lib/pathname.utils'
@@ -126,11 +125,11 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
)
const isComputeNearExhaustion =
!!projectResourceWarnings?.cpu_exhaustion ||
!!projectResourceWarnings?.memory_and_swap_exhaustion
!!projectResourceWarnings?.memory_and_swap_exhaustion ||
!!projectResourceWarnings?.disk_space_exhaustion
const isEligibleForFreeUpgrade =
entitledUpdateCompute &&
selectedProject?.infra_compute_size === 'nano' &&
isComputeNearExhaustion
entitledUpdateCompute && selectedProject?.infra_compute_size === 'nano'
const showUpgradeBanner = isEligibleForFreeUpgrade && isComputeNearExhaustion
const [isFreeMicroUpgradeBannerDismissed] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.FREE_MICRO_UPGRADE_BANNER_DISMISSED(selectedProject?.ref ?? ''),
false
@@ -180,7 +179,7 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
useEffect(() => {
if (!selectedProject?.ref) return
if (isEligibleForFreeUpgrade && !isFreeMicroUpgradeBannerDismissed) {
if (showUpgradeBanner && !isFreeMicroUpgradeBannerDismissed) {
addBanner({
id: BANNER_ID.FREE_MICRO_UPGRADE,
isDismissed: false,
@@ -192,7 +191,7 @@ export const ProjectLayout = forwardRef<HTMLDivElement, PropsWithChildren<Projec
}
}, [
selectedProject?.ref,
isEligibleForFreeUpgrade,
showUpgradeBanner,
isFreeMicroUpgradeBannerDismissed,
addBanner,
dismissBanner,
@@ -41,10 +41,10 @@ export const BannerFreeMicroUpgrade = () => {
</div>
</div>
<div className="flex flex-col gap-y-1 mb-2">
<p className="text-sm font-medium">Free upgrade to Micro available</p>
<p className="text-sm font-medium">Your project is running low on resources</p>
<p className="text-xs text-foreground-lighter text-balance">
Your Pro plan includes a free upgrade from Nano to Micro — double the memory at no extra
cost.
Your Nano compute is approaching its limits. Upgrade to Micro for free — it's included
in your Pro plan.
</p>
</div>
<div className="flex gap-2">
@@ -106,30 +106,32 @@ export const ComputeBadgeWrapper = ({
const isEligibleForFreeUpgrade = !!data && data.plan.id !== 'free' && computeSize === 'nano'
const isLoading = isLoadingAddons || isLoadingSubscriptions
const isComputeNearExhaustion =
!!resourceWarnings?.cpu_exhaustion || !!resourceWarnings?.memory_and_swap_exhaustion
const hasUpgradeAvailable = (isFreeOnNano || isEligibleForFreeUpgrade) && isComputeNearExhaustion
!!resourceWarnings?.cpu_exhaustion ||
!!resourceWarnings?.memory_and_swap_exhaustion ||
!!resourceWarnings?.disk_space_exhaustion
const showUpgradeGlow = isEligibleForFreeUpgrade && isComputeNearExhaustion
if (!computeSize) return null
return (
<HoverCard onOpenChange={() => setOpenState(!open)} openDelay={280}>
<HoverCardTrigger asChild className="group" onClick={(e) => e.stopPropagation()}>
<div className={cn('flex items-center', hasUpgradeAvailable && 'animate-badge-pulse')}>
<div className={cn('flex items-center', showUpgradeGlow && 'animate-badge-pulse')}>
<div
className={cn(
'flex',
hasUpgradeAvailable && 'relative inline-flex overflow-hidden rounded'
showUpgradeGlow && 'relative inline-flex overflow-hidden rounded'
)}
>
<ComputeBadge
infraComputeSize={computeSize}
icon={hasUpgradeAvailable && <ChevronsUpAnimated />}
icon={showUpgradeGlow && <ChevronsUpAnimated />}
className={cn(
hasUpgradeAvailable && 'text-brand-600 border-brand-500 bg-brand/10 gap-1',
showUpgradeGlow && 'text-brand-600 border-brand-500 bg-brand/10 gap-1',
badgeClassName
)}
/>
{hasUpgradeAvailable && (
{showUpgradeGlow && (
<span className="animate-badge-shimmer pointer-events-none absolute inset-0 bg-gradient-to-br from-transparent via-brand/20 to-transparent blur-md" />
)}
</div>
@@ -186,16 +188,20 @@ export const ComputeBadgeWrapper = ({
<p className="text-foreground">
{isFreeOnNano
? 'Double your memory for free'
: isEligibleForFreeUpgrade
? 'Free upgrade to Micro available'
: 'Unlock more compute'}
: isEligibleForFreeUpgrade && isComputeNearExhaustion
? 'Your project is running low on resources'
: isEligibleForFreeUpgrade
? 'Free upgrade to Micro available'
: 'Unlock more compute'}
</p>
<p className="text-foreground-light">
{isFreeOnNano
? 'Upgrade to Pro and get a free Micro compute upgrade — double the memory at no extra cost.'
: isEligibleForFreeUpgrade
? 'Your Pro plan includes a free upgrade from Nano to Micro compute.'
: 'Scale your project up to 64 cores and 256 GB RAM.'}
: isEligibleForFreeUpgrade && isComputeNearExhaustion
? 'Your Nano compute is approaching its limits. Your Pro plan includes a free upgrade to Micro — double the memory at no extra cost.'
: isEligibleForFreeUpgrade
? 'Your Pro plan includes a free upgrade from Nano to Micro compute.'
: 'Scale your project up to 64 cores and 256 GB RAM.'}
</p>
</div>
<div>