StatusPageBanner.utils.ts 2.6 KB

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