From 9597ce0b50c683180c1fcf54d3ee7bbca1e45b90 Mon Sep 17 00:00:00 2001 From: ArthurGamby Date: Thu, 2 Apr 2026 11:04:27 +0200 Subject: [PATCH 1/2] fix: status indicator now reflects unresolved incidents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status indicator only checked /api/v2/status.json which reflects aggregate component health. During incident monitoring phases, components get marked operational again while the incident remains unresolved — causing the indicator to show "All Systems Operational" despite an active incident visible on prisma-status.com. Now also fetches /api/v2/incidents/unresolved.json and uses the higher severity between the summary and worst unresolved incident. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/docs/src/components/status-indicator.tsx | 44 ++++++++++++++-- packages/ui/src/components/pdp-status.tsx | 52 ++++++++++++++++--- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/apps/docs/src/components/status-indicator.tsx b/apps/docs/src/components/status-indicator.tsx index 0f6655ba7c..46dec4f1a9 100644 --- a/apps/docs/src/components/status-indicator.tsx +++ b/apps/docs/src/components/status-indicator.tsx @@ -12,6 +12,14 @@ interface StatusResponse { }; } +interface Incident { + impact: StatusIndicator; +} + +interface IncidentsResponse { + incidents: Incident[]; +} + const dotColors: Record = { none: "bg-green-500", minor: "bg-yellow-500", @@ -19,6 +27,13 @@ const dotColors: Record = { critical: "bg-red-500", }; +const SEVERITY: Record = { + none: 0, + minor: 1, + major: 2, + critical: 3, +}; + const POLL_INTERVAL = 5 * 60 * 1000; // 5 minutes export function StatusIndicator() { @@ -26,9 +41,32 @@ export function StatusIndicator() { useEffect(() => { const fetchStatus = () => { - fetch("https://www.prisma-status.com/api/v2/status.json") - .then((res) => res.json()) - .then((data: StatusResponse) => setStatus(data.status)) + Promise.all([ + fetch("https://www.prisma-status.com/api/v2/status.json").then( + (res) => res.json() as Promise, + ), + fetch("https://www.prisma-status.com/api/v2/incidents/unresolved.json").then( + (res) => res.json() as Promise, + ), + ]) + .then(([statusData, incidentsData]) => { + const summaryIndicator = statusData.status.indicator; + const incidents = incidentsData.incidents ?? []; + const worstIncidentIndicator = incidents.reduce( + (worst, incident) => + SEVERITY[incident.impact] > SEVERITY[worst] ? incident.impact : worst, + "none", + ); + + if (SEVERITY[worstIncidentIndicator] > SEVERITY[summaryIndicator]) { + setStatus({ + indicator: worstIncidentIndicator, + description: incidents.length === 1 ? "Active Incident" : "Active Incidents", + }); + } else { + setStatus(statusData.status); + } + }) .catch(() => setStatus(null)); }; diff --git a/packages/ui/src/components/pdp-status.tsx b/packages/ui/src/components/pdp-status.tsx index ba2cff4824..5423341831 100644 --- a/packages/ui/src/components/pdp-status.tsx +++ b/packages/ui/src/components/pdp-status.tsx @@ -2,6 +2,23 @@ import { useEffect, useState } from "react"; import { cn } from "../lib/cn"; +type StatusIndicator = "none" | "minor" | "major" | "critical"; + +interface Incident { + impact: StatusIndicator; +} + +interface IncidentsResponse { + incidents: Incident[]; +} + +const SEVERITY: Record = { + none: 0, + minor: 1, + major: 2, + critical: 3, +}; + const indicatorStatus: Record = { "-": "[&>div]:bg-gray-500 text-foreground-neutral-weak", none: "[&>div]:bg-background-success-reverse-strong text-background-success-reverse-strong", @@ -19,14 +36,35 @@ const PDPStatus = ({ className }: { className?: string }) => { }); useEffect(() => { - fetch("https://www.prisma-status.com/api/v2/status.json") - .then((response) => response.json()) - .then((json) => { - setPdpStatus(json); + Promise.all([ + fetch("https://www.prisma-status.com/api/v2/status.json").then( + (res) => res.json(), + ), + fetch("https://www.prisma-status.com/api/v2/incidents/unresolved.json").then( + (res) => res.json() as Promise, + ), + ]) + .then(([statusJson, incidentsData]) => { + const summaryIndicator: StatusIndicator = statusJson.status.indicator ?? "none"; + const incidents: Incident[] = incidentsData.incidents ?? []; + const worstIncidentIndicator = incidents.reduce( + (worst, incident) => + SEVERITY[incident.impact] > SEVERITY[worst] ? incident.impact : worst, + "none", + ); + + if (SEVERITY[worstIncidentIndicator] > SEVERITY[summaryIndicator]) { + setPdpStatus({ + status: { + indicator: worstIncidentIndicator, + description: incidents.length === 1 ? "Active Incident" : "Active Incidents", + }, + }); + } else { + setPdpStatus(statusJson); + } }) - .catch((error) => - console.log("PDP Status fetch failed " + error.message), - ); + .catch((error) => console.log("PDP Status fetch failed " + error.message)); }, []); const indicator = pdpStatus.status.indicator || "-"; From 9f82fd9d73822371fcf6f4664eb801e6879f04b9 Mon Sep 17 00:00:00 2001 From: ArthurGamby Date: Thu, 2 Apr 2026 11:15:59 +0200 Subject: [PATCH 2/2] fix: treat incidents fetch as optional in status indicator Use Promise.allSettled so a failing incidents endpoint doesn't hide the widget. The status summary is still shown when incidents/unresolved.json is unavailable. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/docs/src/components/status-indicator.tsx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/apps/docs/src/components/status-indicator.tsx b/apps/docs/src/components/status-indicator.tsx index 46dec4f1a9..d48bf31066 100644 --- a/apps/docs/src/components/status-indicator.tsx +++ b/apps/docs/src/components/status-indicator.tsx @@ -41,33 +41,39 @@ export function StatusIndicator() { useEffect(() => { const fetchStatus = () => { - Promise.all([ + Promise.allSettled([ fetch("https://www.prisma-status.com/api/v2/status.json").then( (res) => res.json() as Promise, ), fetch("https://www.prisma-status.com/api/v2/incidents/unresolved.json").then( (res) => res.json() as Promise, ), - ]) - .then(([statusData, incidentsData]) => { - const summaryIndicator = statusData.status.indicator; - const incidents = incidentsData.incidents ?? []; - const worstIncidentIndicator = incidents.reduce( - (worst, incident) => - SEVERITY[incident.impact] > SEVERITY[worst] ? incident.impact : worst, - "none", - ); - - if (SEVERITY[worstIncidentIndicator] > SEVERITY[summaryIndicator]) { - setStatus({ - indicator: worstIncidentIndicator, - description: incidents.length === 1 ? "Active Incident" : "Active Incidents", - }); - } else { - setStatus(statusData.status); - } - }) - .catch(() => setStatus(null)); + ]).then(([statusResult, incidentsResult]) => { + if (statusResult.status === "rejected") { + setStatus(null); + return; + } + + const statusData = statusResult.value; + const incidents = + incidentsResult.status === "fulfilled" + ? (incidentsResult.value.incidents ?? []) + : []; + const worstIncidentIndicator = incidents.reduce( + (worst, incident) => + SEVERITY[incident.impact] > SEVERITY[worst] ? incident.impact : worst, + "none", + ); + + if (SEVERITY[worstIncidentIndicator] > SEVERITY[statusData.status.indicator]) { + setStatus({ + indicator: worstIncidentIndicator, + description: incidents.length === 1 ? "Active Incident" : "Active Incidents", + }); + } else { + setStatus(statusData.status); + } + }); }; fetchStatus();