feat: compute upgrade banner

This commit is contained in:
kemal committed 2026-03-10 13:46:11 +00:00
1 parent 8cfdc8576e
commit 4e0b5ced36
4 files changed
+135 -1

No files matched your search

@@ -1,6 +1,8 @@
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { AppBannerWrapper } from 'components/interfaces/App/AppBannerWrapper'
import { Sidebar } from 'components/interfaces/Sidebar'
import { BannerComputeExhaustion } from 'components/ui/BannerStack/Banners/BannerComputeExhaustion'
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useCheckLatestDeploy } from 'hooks/use-check-latest-deploy'
import { useRouter } from 'next/router'
@@ -9,7 +11,7 @@ import { useAppStateSnapshot } from 'state/app-state'
import { ResizablePanel, ResizablePanelGroup, SidebarProvider } from 'ui'
import { BannerStack } from '../ui/BannerStack/BannerStack'
import { BannerStackProvider } from '../ui/BannerStack/BannerStackProvider'
import { BannerStackProvider, useBannerStack } from '../ui/BannerStack/BannerStackProvider'
import { LayoutHeader } from './ProjectLayout/LayoutHeader/LayoutHeader'
import { LayoutSidebar } from './ProjectLayout/LayoutSidebar'
import { LayoutSidebarProvider } from './ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
@@ -17,6 +19,43 @@ import MobileNavigationBar from './ProjectLayout/NavigationBar/MobileNavigationB
import { MobileSheetProvider } from './ProjectLayout/NavigationBar/MobileSheetContext'
import { ProjectContextProvider } from './ProjectLayout/ProjectContext'
const ComputeExhaustionBannerController = () => {
const { ref } = useParams()
const { addBanner, dismissBanner } = useBannerStack()
const [isBannerDismissed] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.COMPUTE_EXHAUSTION_BANNER_DISMISSED(ref ?? ''),
false
)
const { data: resourceWarnings } = useResourceWarningsQuery({ ref })
const projectWarnings = resourceWarnings?.find((w) => w.project === ref)
const cpuLevel = projectWarnings?.cpu_exhaustion ?? null
const memoryLevel = projectWarnings?.memory_and_swap_exhaustion ?? null
const worstLevel =
cpuLevel === 'critical' || memoryLevel === 'critical'
? 'critical'
: cpuLevel === 'warning' || memoryLevel === 'warning'
? 'warning'
: null
useEffect(() => {
if (worstLevel && !isBannerDismissed) {
addBanner({
id: 'compute-exhaustion-banner',
isDismissed: false,
content: <BannerComputeExhaustion isCritical={worstLevel === 'critical'} />,
priority: 10,
})
} else {
dismissBanner('compute-exhaustion-banner')
}
}, [worstLevel, isBannerDismissed, addBanner, dismissBanner])
return null
}
export interface DefaultLayoutProps {
headerTitle?: string
hideMobileMenu?: boolean
@@ -77,6 +116,7 @@ export const DefaultLayout = ({
<ProjectContextProvider projectRef={ref}>
<MobileSheetProvider>
<BannerStackProvider>
<ComputeExhaustionBannerController />
<div className="flex flex-col h-screen w-screen">
{/* Top Banner */}
<AppBannerWrapper />
@@ -0,0 +1,65 @@
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useTrack } from 'lib/telemetry/track'
import { Cpu } from 'lucide-react'
import Link from 'next/link'
import { Button } from 'ui'
import { BannerCard } from '../BannerCard'
import { useBannerStack } from '../BannerStackProvider'
interface BannerComputeExhaustionProps {
isCritical: boolean
}
export const BannerComputeExhaustion = ({ isCritical }: BannerComputeExhaustionProps) => {
const track = useTrack()
const { ref } = useParams()
const { dismissBanner } = useBannerStack()
const [, setIsDismissed] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.COMPUTE_EXHAUSTION_BANNER_DISMISSED(ref ?? ''),
false
)
const title = isCritical ? 'Compute is maxed out' : 'Compute is running high'
const description = isCritical
? 'Your project CPU or memory is at 100%. Performance is severely affected — consider upgrading your compute.'
: 'Your project is experiencing high CPU or memory usage. Upgrading your compute can prevent downtime.'
return (
<BannerCard
onDismiss={() => {
setIsDismissed(true)
dismissBanner('compute-exhaustion-banner')
track('compute_exhaustion_banner_dismiss_button_clicked', { is_critical: isCritical })
}}
>
<div className="flex flex-col gap-y-4">
<div className="flex flex-col gap-y-2 items-start">
<div
className={`p-2 rounded-lg ${isCritical ? 'bg-destructive-300 text-destructive' : 'bg-warning-300 text-warning'}`}
>
<Cpu size={16} />
</div>
</div>
<div className="flex flex-col gap-y-1 mb-2">
<p className="text-sm font-medium">{title}</p>
<p className="text-xs text-foreground-lighter text-balance">{description}</p>
</div>
<div className="flex gap-2">
<Button asChild type="primary" size="tiny">
<Link
href={`/project/${ref}/settings/compute-and-disk`}
onClick={() =>
track('compute_exhaustion_banner_upgrade_button_clicked', {
is_critical: isCritical,
})
}
>
Upgrade compute
</Link>
</Button>
</div>
</div>
</BannerCard>
)
}
@@ -103,6 +103,9 @@ export const LOCAL_STORAGE_KEYS = {
// Observability banner dismissed
OBSERVABILITY_BANNER_DISMISSED: (ref: string) => `observability-banner-dismissed-${ref}`,
// Compute exhaustion banner dismissed
COMPUTE_EXHAUSTION_BANNER_DISMISSED: (ref: string) =>
`compute-exhaustion-banner-dismissed-${ref}`,
TABLE_EDITOR_NEW_FILTER_BANNER_DISMISSED: (ref: string) =>
`table-editor-new-filter-banner-dismissed-${ref}`,
+26
View File
@@ -1188,6 +1188,30 @@ export interface IndexAdvisorBannerDismissButtonClickedEvent {
groups: TelemetryGroups
}
/**
* Compute exhaustion banner dismiss button clicked event.
*
* @group Events
* @source studio
*/
export interface ComputeExhaustionBannerDismissButtonClickedEvent {
action: 'compute_exhaustion_banner_dismiss_button_clicked'
properties: { is_critical: boolean }
groups: TelemetryGroups
}
/**
* Compute exhaustion banner upgrade button clicked event.
*
* @group Events
* @source studio
*/
export interface ComputeExhaustionBannerUpgradeButtonClickedEvent {
action: 'compute_exhaustion_banner_upgrade_button_clicked'
properties: { is_critical: boolean }
groups: TelemetryGroups
}
/**
* Index Advisor tab clicked event.
*
@@ -2842,3 +2866,5 @@ export type TelemetryEvent =
| IntegrationInstallFailedEvent
| IntegrationUninstallCompletedEvent
| RlsEventTriggerBannerCreateButtonClickedEvent
| ComputeExhaustionBannerDismissButtonClickedEvent
| ComputeExhaustionBannerUpgradeButtonClickedEvent