Skip to content

Commit cf1e95d

Browse files
authored
studio: maintenance banner for shared pooler 2026-05-13 (supabase#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 -->
1 parent 7328697 commit cf1e95d

2 files changed

Lines changed: 81 additions & 1 deletion

File tree

apps/studio/components/interfaces/App/AppBannerWrapper.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ import { PropsWithChildren } from 'react'
33

44
import { OrganizationResourceBanner } from '../Organization/HeaderBanner'
55
import { ClockSkewBanner } from '@/components/layouts/AppLayout/ClockSkewBanner'
6-
import { NoticeBanner } from '@/components/layouts/AppLayout/NoticeBanner'
6+
import { NoticeBanner, NoticeBanner2 } from '@/components/layouts/AppLayout/NoticeBanner'
77
import { StatusPageBanner } from '@/components/layouts/AppLayout/StatusPageBanner'
88

99
export const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => {
1010
const showNoticeBanner = useFlag('showNoticeBanner')
11+
const showNoticeBanner2 = useFlag('showNoticeBanner2')
1112
const clockSkewBanner = useFlag('clockSkewBanner')
1213

1314
return (
1415
<div className="flex flex-col">
1516
<div className="shrink-0">
1617
<StatusPageBanner />
1718
{showNoticeBanner && <NoticeBanner />}
19+
{showNoticeBanner2 && <NoticeBanner2 />}
1820
<OrganizationResourceBanner />
1921
{/* Disabled until reintroduced or removed altogether. */}
2022
{/* <TaxIdBanner /> */}

apps/studio/components/layouts/AppLayout/NoticeBanner.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useQueries } from '@tanstack/react-query'
12
import { LOCAL_STORAGE_KEYS } from 'common'
23
import { useRouter } from 'next/router'
34
import {
@@ -17,6 +18,12 @@ import {
1718

1819
import { HeaderBanner } from '@/components/interfaces/Organization/HeaderBanner'
1920
import { InlineLink, InlineLinkClassName } from '@/components/ui/InlineLink'
21+
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
22+
import { projectKeys } from '@/data/projects/keys'
23+
import {
24+
getOrganizationProjects,
25+
type OrgProject,
26+
} from '@/data/projects/org-projects-infinite-query'
2027
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
2128

2229
// Update this whenever the banner content below changes so old client bundles
@@ -53,6 +60,77 @@ export const NoticeBanner = () => {
5360
)
5461
}
5562

63+
const MAINTENANCE_REGIONS = new Set(['ap-southeast-1', 'sa-east-1'])
64+
65+
export const NoticeBanner2 = () => {
66+
const id = 'maintenance-2026-05-13'
67+
const expiry = new Date('2026-05-14T23:59:00Z')
68+
const isExpired = new Date() > expiry
69+
70+
const router = useRouter()
71+
72+
const [bannerAcknowledged, setBannerAcknowledged, { isSuccess }] = useLocalStorageQuery(
73+
LOCAL_STORAGE_KEYS.MAINTENANCE_BANNER_DISMISSED(id),
74+
false
75+
)
76+
77+
const shouldEvaluate =
78+
!router.pathname.includes('sign-in') && isSuccess && !bannerAcknowledged && !isExpired
79+
80+
const { data: organizations } = useOrganizationsQuery({ enabled: shouldEvaluate })
81+
const orgProjectsQueries = useQueries({
82+
queries: (organizations ?? []).map((org) => ({
83+
queryKey: projectKeys.bannerProjectsByOrg(org.slug),
84+
queryFn: () => getOrganizationProjects({ slug: org.slug, limit: 100 }),
85+
staleTime: 30 * 60 * 1000,
86+
enabled: shouldEvaluate,
87+
})),
88+
})
89+
90+
const isProjectsFetched =
91+
organizations !== undefined &&
92+
(organizations.length === 0 || orgProjectsQueries.every((q) => q.isFetched))
93+
94+
const hasMaintenanceRegionProject = orgProjectsQueries
95+
.flatMap((q) => q.data?.projects ?? [])
96+
.some((project: OrgProject) =>
97+
project.databases.some((db) => MAINTENANCE_REGIONS.has(db.region))
98+
)
99+
100+
if (!shouldEvaluate || !isProjectsFetched || !hasMaintenanceRegionProject) {
101+
return null
102+
}
103+
104+
return (
105+
<HeaderBanner
106+
variant="note"
107+
title="Upcoming maintenance"
108+
description={
109+
<>
110+
Shared pooler maintenance in{' '}
111+
<a
112+
target="_blank"
113+
rel="noopener referrer"
114+
href="https://status.supabase.com/incidents/hxf8876zl69x"
115+
>
116+
ap-southeast-1
117+
</a>{' '}
118+
and{' '}
119+
<a
120+
target="_blank"
121+
rel="noopener noreferrer"
122+
href="https://status.supabase.com/incidents/jqsj3pb3mnx7"
123+
>
124+
sa-east-1
125+
</a>{' '}
126+
on May 13-14.
127+
</>
128+
}
129+
onDismiss={() => setBannerAcknowledged(true)}
130+
/>
131+
)
132+
}
133+
56134
const UpdatedTermsOfServiceDialog = ({ onDismiss }: { onDismiss: () => void }) => {
57135
return (
58136
<Dialog>

0 commit comments

Comments
 (0)