StatusPageBanner.utils.ts72 lines · main
| 1 | import type { IncidentCache } from '@/lib/api/incident-status' |
| 2 | |
| 3 | type 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 | */ |
| 17 | export 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 | */ |
| 56 | export 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 | } |