Files
supabase/apps/studio/components/layouts/AppLayout/StatusPageBanner.utils.ts
Charis befc817f94 feat: version who-knows-what of incident banner (#43726)
Feature

## What is the current behavior?

Incident banner logic depends on StatusPage and Supabase project for
metadata.

## What is the new behavior?

New incident banner logic that depends only on incident.io. Displays in
non-production environments for now because I haven't wired up the rest
of the workflow. This is just to allow a total end-to-end
testing/playground for test incidents <-> Slack <-> preview dashboard
for people to try out the UX.

## Additional context

You can test using my [test
incident](https://app.incident.io/supabase/incidents/405). This has
severity minor, so the preview site should have a banner. Toggle to
informative, hard refresh dashboard with cache off, and banner should
disappear. Toggle back to minor, hard refresh without cache again, and
banner should reappear. Same thing if you edit the "Banner shown" field
from 1 to -1 and back.
2026-03-12 16:55:53 -04:00

73 lines
2.6 KiB
TypeScript

import type { IncidentCache } from 'lib/api/incident-status'
type BannerIncident = { id: string; cache?: IncidentCache | null }
/**
* Determines whether the incident status banner should be shown to a given user,
* given all active incidents and the user's project state.
*
* Returns true if any incident matches the user's context.
*
* @param incidents - Active incidents from the incident-status endpoint
* @param hasProjects - Whether the user has any projects at all
* @param userRegions - Deduplicated set of regions of all databases (primary and read replicas) owned by the user
* @param hasUnknownRegions - True when region data is incomplete (org has >100 projects).
* When true, the region check is skipped and a match is assumed.
*/
export function shouldShowBanner({
incidents,
hasProjects,
userRegions,
hasUnknownRegions = false,
}: {
incidents: Array<BannerIncident>
hasProjects: boolean
userRegions: Set<string>
hasUnknownRegions?: boolean
}): boolean {
return incidents.some((incident) => {
// Forced incidents are shown unconditionally, regardless of regions or project state
if (incident.cache?.force) return true
const affectedRegions = incident.cache?.affected_regions ?? []
const affectsProjectCreation = incident.cache?.affects_project_creation ?? false
// Users with no projects only see the banner if the incident affects project creation
// and has no specific region targeting (inline notice in RegionSelector handles region-specific incidents)
if (!hasProjects) return affectsProjectCreation && affectedRegions.length === 0
// User has projects: if no region restriction, always show
if (affectedRegions.length === 0) return true
// Region data is incomplete — assume the user has a database in an affected region
if (hasUnknownRegions) return true
// Region restriction: only show if the user has a database in an affected region
return affectedRegions.some((region) => userRegions.has(region.toLowerCase()))
})
}
/**
* Returns the IDs of incidents that are relevant to the given user.
*
* An incident is considered relevant if it would trigger banner visibility for
* the user, per the same logic as shouldShowBanner.
*/
export function getRelevantIncidentIds({
incidents,
hasProjects,
userRegions,
hasUnknownRegions = false,
}: {
incidents: Array<BannerIncident>
hasProjects: boolean
userRegions: Set<string>
hasUnknownRegions?: boolean
}): Array<string> {
return incidents
.filter((incident) =>
shouldShowBanner({ incidents: [incident], hasProjects, userRegions, hasUnknownRegions })
)
.map((incident) => incident.id)
}