diff --git a/Common/UI/Components/Graphs/UptimeBarTooltip.tsx b/Common/UI/Components/Graphs/UptimeBarTooltip.tsx index 4dd2b16800..0723aff047 100644 --- a/Common/UI/Components/Graphs/UptimeBarTooltip.tsx +++ b/Common/UI/Components/Graphs/UptimeBarTooltip.tsx @@ -27,88 +27,92 @@ const UptimeBarTooltip: FunctionComponent = ( const dateStr: string = OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(props.date, true); - const uptimeColor: string = - props.uptimePercent >= 99.9 - ? "#16a34a" - : props.uptimePercent >= 99 - ? "#ca8a04" - : "#dc2626"; + // Color tiers + const isGood: boolean = props.uptimePercent >= 99.9; + const isWarn: boolean = !isGood && props.uptimePercent >= 99; + const uptimeColor: string = isGood + ? "#059669" + : isWarn + ? "#d97706" + : "#dc2626"; - const uptimeBgColor: string = - props.uptimePercent >= 99.9 - ? "#f0fdf4" - : props.uptimePercent >= 99 - ? "#fefce8" - : "#fef2f2"; + const uptimeBg: string = isGood + ? "#ecfdf5" + : isWarn + ? "#fffbeb" + : "#fef2f2"; - const uptimeTrackColor: string = - props.uptimePercent >= 99.9 - ? "#dcfce7" - : props.uptimePercent >= 99 - ? "#fef9c3" - : "#fee2e2"; + const uptimeTrack: string = isGood + ? "#d1fae5" + : isWarn + ? "#fef3c7" + : "#fecaca"; - // Sort: downtime statuses first so they're prominent + // Sort: downtime first, then by duration desc const sortedDurations: Array = [ ...props.statusDurations, ].sort((a: StatusDuration, b: StatusDuration) => { - if (a.isDowntime && !b.isDowntime) { - return -1; - } - if (!a.isDowntime && b.isDowntime) { - return 1; + if (a.isDowntime !== b.isDowntime) { + return a.isDowntime ? -1 : 1; } return b.seconds - a.seconds; }); + const totalSeconds: number = sortedDurations.reduce( + (sum: number, d: StatusDuration) => { + return sum + d.seconds; + }, + 0, + ); + + const hasIncidents: boolean = props.incidents.length > 0; + const hasStatuses: boolean = sortedDurations.length > 0; + return ( -
- {/* ── Header ── */} +
+ {/* ── Date header ── */}
- + {dateStr} - {props.hasEvents && props.incidents.length === 0 && ( - - {props.uptimePercent >= 100 ? "100%" : props.uptimePercent.toFixed(2) + "%"} - - )}
{/* ── Uptime meter ── */} {props.hasEvents && (
0 || props.incidents.length > 0 ? "12px" : "0", + padding: "10px 12px", + marginBottom: hasStatuses || hasIncidents ? "12px" : "0", }} >
@@ -116,46 +120,97 @@ const UptimeBarTooltip: FunctionComponent = ( style={{ fontSize: "11px", color: "#6b7280", - fontWeight: 500, + fontWeight: 600, textTransform: "uppercase", - letterSpacing: "0.04em", + letterSpacing: "0.05em", }} > Uptime {props.uptimePercent >= 100 ? "100" : props.uptimePercent.toFixed(2)} - % + + % +
-
+ {/* Segmented bar showing all statuses proportionally */} + {totalSeconds > 0 && sortedDurations.length > 1 ? (
-
+ > + {sortedDurations.map( + (status: StatusDuration, index: number) => { + const widthPercent: number = + (status.seconds / totalSeconds) * 100; + if (widthPercent < 0.5) { + return null; + } + return ( +
+ ); + }, + )} +
+ ) : ( +
+
+
+ )}
)} @@ -165,10 +220,25 @@ const UptimeBarTooltip: FunctionComponent = ( style={{ backgroundColor: "#f9fafb", borderRadius: "10px", - padding: "16px", + padding: "14px 16px", textAlign: "center", }} > + + + + +
= ( )} {/* ── Status breakdown ── */} - {sortedDurations.length > 0 && ( + {hasStatuses && (
0 ? "0" : "0", - paddingBottom: props.incidents.length > 0 ? "10px" : "0", - borderBottom: - props.incidents.length > 0 ? "1px solid #e5e7eb" : "none", + paddingBottom: hasIncidents ? "10px" : "0", + marginBottom: hasIncidents ? "2px" : "0", + borderBottom: hasIncidents ? "1px solid #f3f4f6" : "none", }} >
= ( textTransform: "uppercase", letterSpacing: "0.06em", fontWeight: 600, - marginBottom: "6px", + marginBottom: "4px", }} > Status Breakdown
{sortedDurations.map((status: StatusDuration, index: number) => { + const pct: string = + totalSeconds > 0 + ? ((status.seconds / totalSeconds) * 100).toFixed(1) + : "0"; return (
= ( display: "flex", alignItems: "center", justifyContent: "space-between", - padding: "4px 0", + padding: "3px 0", }} >
= ( > {status.label} + + {pct}% +
{OneUptimeDate.secondsToFormattedFriendlyTimeString( @@ -261,42 +346,51 @@ const UptimeBarTooltip: FunctionComponent = ( )} {/* ── Incidents ── */} - {props.incidents.length > 0 && ( + {hasIncidents && (
-
+ + + Incidents -
-
+ {props.incidents.length} -
+
{props.incidents.slice(0, 3).map( @@ -315,77 +409,65 @@ const UptimeBarTooltip: FunctionComponent = ( : undefined } style={{ - backgroundColor: "#f9fafb", - border: "1px solid #e5e7eb", + border: "1px solid #f3f4f6", borderRadius: "8px", - padding: "8px 10px", - marginBottom: "6px", + padding: "7px 10px", + marginBottom: "5px", cursor: isClickable ? "pointer" : "default", - transition: "all 0.15s ease", + transition: "all 0.12s ease", + backgroundColor: "#ffffff", }} onMouseEnter={(e: React.MouseEvent) => { if (isClickable) { - (e.currentTarget as HTMLDivElement).style.backgroundColor = - "#f3f4f6"; - (e.currentTarget as HTMLDivElement).style.borderColor = - "#d1d5db"; + const el: HTMLDivElement = + e.currentTarget as HTMLDivElement; + el.style.backgroundColor = "#f9fafb"; + el.style.borderColor = "#e5e7eb"; } }} onMouseLeave={(e: React.MouseEvent) => { if (isClickable) { - (e.currentTarget as HTMLDivElement).style.backgroundColor = - "#f9fafb"; - (e.currentTarget as HTMLDivElement).style.borderColor = - "#e5e7eb"; + const el: HTMLDivElement = + e.currentTarget as HTMLDivElement; + el.style.backgroundColor = "#ffffff"; + el.style.borderColor = "#f3f4f6"; } }} > + {/* Title row */}
-
-
- {incident.title} -
-
- {OneUptimeDate.getDateAsUserFriendlyLocalFormattedString( - incident.declaredAt, - false, - )} -
+
+ {incident.title}
{isClickable && ( = ( )}
+ {/* Meta row: badges */}
{incident.incidentSeverity && ( @@ -409,11 +492,10 @@ const UptimeBarTooltip: FunctionComponent = ( fontWeight: 600, color: incident.incidentSeverity.color.toString(), backgroundColor: - incident.incidentSeverity.color.toString() + "12", - border: `1px solid ${incident.incidentSeverity.color.toString()}25`, - padding: "1px 7px", - borderRadius: "9999px", - lineHeight: "1.6", + incident.incidentSeverity.color.toString() + "10", + padding: "0px 6px", + borderRadius: "4px", + lineHeight: "1.7", }} > {incident.incidentSeverity.name} @@ -428,16 +510,28 @@ const UptimeBarTooltip: FunctionComponent = ( incident.currentIncidentState.color.toString(), backgroundColor: incident.currentIncidentState.color.toString() + - "12", - border: `1px solid ${incident.currentIncidentState.color.toString()}25`, - padding: "1px 7px", - borderRadius: "9999px", - lineHeight: "1.6", + "10", + padding: "0px 6px", + borderRadius: "4px", + lineHeight: "1.7", }} > {incident.currentIncidentState.name} )} + + {OneUptimeDate.getDateAsUserFriendlyLocalFormattedString( + incident.declaredAt, + false, + )} +
); @@ -448,14 +542,13 @@ const UptimeBarTooltip: FunctionComponent = (
- +{props.incidents.length - 3} more incident - {props.incidents.length - 3 !== 1 ? "s" : ""} + +{props.incidents.length - 3} more
)}