studio: maintenance banner for shared pooler 2026-05-13 (#45695)

Add a second notice banner (because we need the first one to show the
current ToS update). Scoped to ap-southeast-1 and sa-east-1.

Haven't linked to the StatusPage maintenance entry yet as it's not up;
the placeholder link is just to the generic StatusPage.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a second notice banner that alerts users to upcoming maintenance
for affected databases in specific regions; it appears conditionally
(based on affected projects) and can be dismissed—dismissal prevents it
from reappearing.
* The existing “Updated Terms of Service” notice remains unchanged and
continues to display on non–sign-in routes until acknowledged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Charis authored and GitHub committed 2026-05-08 15:35:32 +00:00
1 parent 73286972fb
commit cf1e95dcd7
2 files changed
+81 -1

No files matched your search

@@ -3,11 +3,12 @@ import { PropsWithChildren } from 'react'
import { OrganizationResourceBanner } from '../Organization/HeaderBanner'
import { ClockSkewBanner } from '@/components/layouts/AppLayout/ClockSkewBanner'
import { NoticeBanner } from '@/components/layouts/AppLayout/NoticeBanner'
import { NoticeBanner, NoticeBanner2 } from '@/components/layouts/AppLayout/NoticeBanner'
import { StatusPageBanner } from '@/components/layouts/AppLayout/StatusPageBanner'
export const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
const showNoticeBanner = useFlag('showNoticeBanner')
const showNoticeBanner2 = useFlag('showNoticeBanner2')
const clockSkewBanner = useFlag('clockSkewBanner')
return (
@@ -15,6 +16,7 @@ export const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
<div className="shrink-0">
<StatusPageBanner />
{showNoticeBanner && <NoticeBanner />}
{showNoticeBanner2 && <NoticeBanner2 />}
<OrganizationResourceBanner />
{/* Disabled until reintroduced or removed altogether. */}
{/* <TaxIdBanner /> */}
@@ -1,3 +1,4 @@
import { useQueries } from '@tanstack/react-query'
import { LOCAL_STORAGE_KEYS } from 'common'
import { useRouter } from 'next/router'
import {
@@ -17,6 +18,12 @@ import {
import { HeaderBanner } from '@/components/interfaces/Organization/HeaderBanner'
import { InlineLink, InlineLinkClassName } from '@/components/ui/InlineLink'
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
import { projectKeys } from '@/data/projects/keys'
import {
getOrganizationProjects,
type OrgProject,
} from '@/data/projects/org-projects-infinite-query'
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
// Update this whenever the banner content below changes so old client bundles
@@ -53,6 +60,77 @@ export const NoticeBanner = () => {
)
}
const MAINTENANCE_REGIONS = new Set(['ap-southeast-1', 'sa-east-1'])
export const NoticeBanner2 = () => {
const id = 'maintenance-2026-05-13'
const expiry = new Date('2026-05-14T23:59:00Z')
const isExpired = new Date() > expiry
const router = useRouter()
const [bannerAcknowledged, setBannerAcknowledged, { isSuccess }] = useLocalStorageQuery(
LOCAL_STORAGE_KEYS.MAINTENANCE_BANNER_DISMISSED(id),
false
)
const shouldEvaluate =
!router.pathname.includes('sign-in') && isSuccess && !bannerAcknowledged && !isExpired
const { data: organizations } = useOrganizationsQuery({ enabled: shouldEvaluate })
const orgProjectsQueries = useQueries({
queries: (organizations ?? []).map((org) => ({
queryKey: projectKeys.bannerProjectsByOrg(org.slug),
queryFn: () => getOrganizationProjects({ slug: org.slug, limit: 100 }),
staleTime: 30 * 60 * 1000,
enabled: shouldEvaluate,
})),
})
const isProjectsFetched =
organizations !== undefined &&
(organizations.length === 0 || orgProjectsQueries.every((q) => q.isFetched))
const hasMaintenanceRegionProject = orgProjectsQueries
.flatMap((q) => q.data?.projects ?? [])
.some((project: OrgProject) =>
project.databases.some((db) => MAINTENANCE_REGIONS.has(db.region))
)
if (!shouldEvaluate || !isProjectsFetched || !hasMaintenanceRegionProject) {
return null
}
return (
<HeaderBanner
variant="note"
title="Upcoming maintenance"
description={
<>
Shared pooler maintenance in{' '}
<a
target="_blank"
rel="noopener referrer"
href="https://status.supabase.com/incidents/hxf8876zl69x"
>
ap-southeast-1
</a>{' '}
and{' '}
<a
target="_blank"
rel="noopener noreferrer"
href="https://status.supabase.com/incidents/jqsj3pb3mnx7"
>
sa-east-1
</a>{' '}
on May 13-14.
</>
}
onDismiss={() => setBannerAcknowledged(true)}
/>
)
}
const UpdatedTermsOfServiceDialog = ({ onDismiss }: { onDismiss: () => void }) => {
return (
<Dialog>