StatusPageBanner.utils.ts72 lines · main
1import type { IncidentCache } from '@/lib/api/incident-status'
2
3type BannerIncident = { id: string; cache?: IncidentCache | null }
4
5/**
6 * Determines whether the incident status banner should be shown to a given user,
7 * given all active incidents and the user's project state.
8 *
9 * Returns true if any incident matches the user's context.
10 *
11 * @param incidents - Active incidents from the incident-status endpoint
12 * @param hasProjects - Whether the user has any projects at all
13 * @param userRegions - Deduplicated set of regions of all databases (primary and read replicas) owned by the user
14 * @param hasUnknownRegions - True when region data is incomplete (org has >100 projects).
15 * When true, the region check is skipped and a match is assumed.
16 */
17export function shouldShowBanner({
18 incidents,
19 hasProjects,
20 userRegions,
21 hasUnknownRegions = false,
22}: {
23 incidents: Array<BannerIncident>
24 hasProjects: boolean
25 userRegions: Set<string>
26 hasUnknownRegions?: boolean
27}): boolean {
28 return incidents.some((incident) => {
29 // Forced incidents are shown unconditionally, regardless of regions or project state
30 if (incident.cache?.force) return true
31
32 const affectedRegions = incident.cache?.affected_regions ?? []
33 const affectsProjectCreation = incident.cache?.affects_project_creation ?? false
34
35 // Users with no projects only see the banner if the incident affects project creation
36 // and has no specific region targeting (inline notice in RegionSelector handles region-specific incidents)
37 if (!hasProjects) return affectsProjectCreation && affectedRegions.length === 0
38
39 // User has projects: if no region restriction, always show
40 if (affectedRegions.length === 0) return true
41
42 // Region data is incomplete — assume the user has a database in an affected region
43 if (hasUnknownRegions) return true
44
45 // Region restriction: only show if the user has a database in an affected region
46 return affectedRegions.some((region) => userRegions.has(region.toLowerCase()))
47 })
48}
49
50/**
51 * Returns the IDs of incidents that are relevant to the given user.
52 *
53 * An incident is considered relevant if it would trigger banner visibility for
54 * the user, per the same logic as shouldShowBanner.
55 */
56export function getRelevantIncidentIds({
57 incidents,
58 hasProjects,
59 userRegions,
60 hasUnknownRegions = false,
61}: {
62 incidents: Array<BannerIncident>
63 hasProjects: boolean
64 userRegions: Set<string>
65 hasUnknownRegions?: boolean
66}): Array<string> {
67 return incidents
68 .filter((incident) =>
69 shouldShowBanner({ incidents: [incident], hasProjects, userRegions, hasUnknownRegions })
70 )
71 .map((incident) => incident.id)
72}