Files
supabase/apps/studio/components/interfaces/Support/useSupportStatus.ts
T
Charis 8b8569bd1a feat(studio): status page client data layer + support form (#50984)
## Summary

* Adds the client query layer for `/api/status-page`
(`statusPageQueryOptions` in `data/platform/status-page-query.ts`) and a
shared normalization module (`lib/status-page/status-page.utils.ts`)
that maps the endpoint response into region- and project-creation-aware
`StatusItem`s — later PRs in this stack (assistant tool,
project-creation admonition, global banner) build on this same module.
* Wires the support form's status pill and incident admonition to the
new data behind the `incidentIoStatusPage` ConfigCat flag.
`useSupportStatus` branches between the new endpoint and the existing
`useIncidentStatusQuery`/`processIncidentData` path, both mapped to the
same `SupportStatus` shape.
* `IncidentAdmonition` becomes purely presentational; the status link
now points at the `pageUrl` returned by the endpoint instead of a
hardcoded URL.

Part of
[FE-4057](https://linear.app/supabase/issue/FE-4057/frontend-bannerbot-reconfigured)
— see Linear for full design context.

## Test plan

- [X] `pnpm --filter studio run typecheck`
- [X] `pnpm --filter studio run lint:ratchet`
- [X] `pnpm knip --workspace apps/studio`
- [X] `pnpm test:prettier`
- [X] `pnpm --filter studio exec vitest run` (touched files) — 70 tests
passing, including a flag-on case for `SupportFormPage`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Support pages now display current incident and maintenance
information, including relevant status descriptions and links to the
status page.
* Status details can reflect items affecting a user’s region or
project-creation services. Upcoming maintenance is excluded from active
alerts.
* **Bug Fixes**
* Status labels and alerts now account for loading, errors, incidents,
and maintenance consistently across support pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-29 07:53:28 -04:00

83 lines
2.7 KiB
TypeScript

import { useQuery } from '@tanstack/react-query'
import { useFlag } from 'common'
import {
DEFAULT_STATUS_PAGE_URL,
getStatusDescription,
getStatusPillLabel,
getSupportAdmonitionCopy,
prioritizeActiveItems,
type SupportStatus,
} from './SupportStatus.utils'
import { useIncidentStatusQuery } from '@/data/platform/incident-status-query'
import { processIncidentData } from '@/data/platform/incident-status-utils'
import { statusPageQueryOptions } from '@/data/platform/status-page-query'
import { IS_PLATFORM } from '@/lib/constants'
import { normalizeStatusPage } from '@/lib/status-page/status-page.utils'
function getLegacyPillLabel(hasActiveIncidents: boolean, isMaintenance: boolean): string {
if (hasActiveIncidents) return 'Active incident ongoing'
if (isMaintenance) return 'Scheduled maintenance'
return 'All systems operational'
}
export function useSupportStatus(): SupportStatus {
const useNewStatusPage = useFlag('incidentIoStatusPage') === true
const newStatusQuery = useQuery({
...statusPageQueryOptions(),
select: normalizeStatusPage,
enabled: useNewStatusPage && IS_PLATFORM,
})
const legacyQuery = useIncidentStatusQuery({ enabled: !useNewStatusPage })
if (useNewStatusPage) {
if (newStatusQuery.isPending) return { status: 'pending' }
if (newStatusQuery.isError) return { status: 'error' }
const { pageUrl, items } = newStatusQuery.data
const activeItems = prioritizeActiveItems(items)
return {
status: 'success',
hasActiveIncidents: activeItems.some((item) => item.kind === 'incident'),
pillLabel: getStatusPillLabel(items),
admonition: getSupportAdmonitionCopy(items),
pageUrl,
}
}
if (legacyQuery.isPending) return { status: 'pending' }
if (legacyQuery.isError) return { status: 'error' }
const { incidents = [], maintenanceEvents = [] } = legacyQuery.data ?? {}
const hasActiveIncidents = incidents.length > 0
const isMaintenance = maintenanceEvents.length > 0
const { hasMultipleIncidents, mostCriticalIncident, overallStatus, allSameStatus } =
processIncidentData(incidents)
const admonitionTitle =
(mostCriticalIncident?.name ?? '') +
(hasMultipleIncidents
? ` and ${incidents.length - 1} other issue${incidents.length > 2 ? 's' : ''}`
: '')
return {
status: 'success',
hasActiveIncidents,
pillLabel: getLegacyPillLabel(hasActiveIncidents, isMaintenance),
admonition: hasActiveIncidents
? {
title: admonitionTitle,
description: getStatusDescription({
status: overallStatus,
hasMultipleIncidents,
allSameStatus,
}),
}
: null,
pageUrl: DEFAULT_STATUS_PAGE_URL,
}
}