mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 12:55:06 +03:00
feat: compute upgrade banner
This commit is contained in:
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}`,
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user