mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: add onIncidentClick handler to various components for incident navigation; enhance Tooltip with animation support
This commit is contained in:
@@ -33,6 +33,9 @@ export interface ComponentProps {
|
||||
onBarClick?:
|
||||
| ((date: Date, incidents: Array<UptimeBarTooltipIncident>) => void)
|
||||
| undefined;
|
||||
onIncidentClick?:
|
||||
| ((incidentId: string) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const DayUptimeGraph: FunctionComponent<ComponentProps> = (
|
||||
@@ -252,6 +255,7 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
|
||||
hasEvents={hasEvents}
|
||||
statusDurations={statusDurations}
|
||||
incidents={dayIncidents}
|
||||
onIncidentClick={props.onIncidentClick}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -16,6 +16,9 @@ export interface ComponentProps {
|
||||
hasEvents: boolean;
|
||||
statusDurations: Array<StatusDuration>;
|
||||
incidents: Array<UptimeBarTooltipIncident>;
|
||||
onIncidentClick?:
|
||||
| ((incidentId: string) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
@@ -45,35 +48,60 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
? "#fef9c3"
|
||||
: "#fee2e2";
|
||||
|
||||
// Sort: downtime statuses first so they're prominent
|
||||
const sortedDurations: Array<StatusDuration> = [
|
||||
...props.statusDurations,
|
||||
].sort((a: StatusDuration, b: StatusDuration) => {
|
||||
if (a.isDowntime && !b.isDowntime) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.isDowntime && b.isDowntime) {
|
||||
return 1;
|
||||
}
|
||||
return b.seconds - a.seconds;
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ minWidth: "260px", maxWidth: "340px" }}>
|
||||
{/* Date header */}
|
||||
<div style={{ minWidth: "270px", maxWidth: "340px" }}>
|
||||
{/* ── Header ── */}
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: "8px",
|
||||
marginBottom: "8px",
|
||||
borderBottom: "1px solid #f3f4f6",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingBottom: "10px",
|
||||
marginBottom: props.hasEvents ? "0" : "8px",
|
||||
borderBottom: props.hasEvents ? "none" : "1px solid #e5e7eb",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 600,
|
||||
fontSize: "13px",
|
||||
color: "#111827",
|
||||
}}
|
||||
>
|
||||
<span style={{ fontWeight: 600, fontSize: "13px", color: "#111827" }}>
|
||||
{dateStr}
|
||||
</div>
|
||||
</span>
|
||||
{props.hasEvents && props.incidents.length === 0 && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
fontWeight: 600,
|
||||
color: uptimeColor,
|
||||
backgroundColor: uptimeBgColor,
|
||||
padding: "2px 8px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.5",
|
||||
}}
|
||||
>
|
||||
{props.uptimePercent >= 100 ? "100%" : props.uptimePercent.toFixed(2) + "%"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Uptime card */}
|
||||
{/* ── Uptime meter ── */}
|
||||
{props.hasEvents && (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: uptimeBgColor,
|
||||
borderRadius: "8px",
|
||||
padding: "10px 12px",
|
||||
marginBottom: "10px",
|
||||
borderRadius: "10px",
|
||||
padding: "12px 14px",
|
||||
marginBottom: sortedDurations.length > 0 || props.incidents.length > 0 ? "12px" : "0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -81,24 +109,33 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "baseline",
|
||||
marginBottom: "6px",
|
||||
marginBottom: "8px",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{ fontSize: "11px", color: "#6b7280", fontWeight: 500 }}
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
color: "#6b7280",
|
||||
fontWeight: 500,
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.04em",
|
||||
}}
|
||||
>
|
||||
Uptime
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
fontSize: "20px",
|
||||
fontWeight: 700,
|
||||
color: uptimeColor,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
{props.uptimePercent.toFixed(2)}%
|
||||
{props.uptimePercent >= 100
|
||||
? "100"
|
||||
: props.uptimePercent.toFixed(2)}
|
||||
<span style={{ fontSize: "13px", fontWeight: 600 }}>%</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@@ -106,7 +143,7 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
width: "100%",
|
||||
height: "6px",
|
||||
backgroundColor: uptimeTrackColor,
|
||||
borderRadius: "3px",
|
||||
borderRadius: "100px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
@@ -115,34 +152,43 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
width: `${Math.min(props.uptimePercent, 100)}%`,
|
||||
height: "100%",
|
||||
backgroundColor: uptimeColor,
|
||||
borderRadius: "3px",
|
||||
borderRadius: "100px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── No data ── */}
|
||||
{!props.hasEvents && (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#f9fafb",
|
||||
borderRadius: "8px",
|
||||
padding: "12px",
|
||||
borderRadius: "10px",
|
||||
padding: "16px",
|
||||
textAlign: "center",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: "12px", color: "#9ca3af", fontWeight: 500 }}>
|
||||
No data available for this day
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#9ca3af",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
No monitoring data for this day
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Status breakdown */}
|
||||
{props.statusDurations.length > 0 && (
|
||||
{/* ── Status breakdown ── */}
|
||||
{sortedDurations.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
marginBottom: props.incidents.length > 0 ? "10px" : "0",
|
||||
marginBottom: props.incidents.length > 0 ? "0" : "0",
|
||||
paddingBottom: props.incidents.length > 0 ? "10px" : "0",
|
||||
borderBottom:
|
||||
props.incidents.length > 0 ? "1px solid #e5e7eb" : "none",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -152,78 +198,71 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: "0.06em",
|
||||
fontWeight: 600,
|
||||
marginBottom: "4px",
|
||||
marginBottom: "6px",
|
||||
}}
|
||||
>
|
||||
Status Breakdown
|
||||
</div>
|
||||
{props.statusDurations.map(
|
||||
(status: StatusDuration, index: number) => {
|
||||
return (
|
||||
{sortedDurations.map((status: StatusDuration, index: number) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "4px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "4px 0",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
width: "8px",
|
||||
height: "8px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: status.color.toString(),
|
||||
display: "inline-block",
|
||||
flexShrink: 0,
|
||||
boxShadow: `0 0 0 2px ${status.color.toString()}25`,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: "8px",
|
||||
height: "8px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: status.color.toString(),
|
||||
display: "inline-block",
|
||||
flexShrink: 0,
|
||||
boxShadow: `0 0 0 2px ${status.color.toString()}30`,
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#374151",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{status.label}
|
||||
</span>
|
||||
</div>
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: status.isDowntime ? "#dc2626" : "#6b7280",
|
||||
fontWeight: status.isDowntime ? 600 : 400,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
color: "#374151",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{OneUptimeDate.secondsToFormattedFriendlyTimeString(
|
||||
status.seconds,
|
||||
)}
|
||||
{status.label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
<span
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: status.isDowntime ? "#dc2626" : "#6b7280",
|
||||
fontWeight: status.isDowntime ? 600 : 400,
|
||||
fontVariantNumeric: "tabular-nums",
|
||||
}}
|
||||
>
|
||||
{OneUptimeDate.secondsToFormattedFriendlyTimeString(
|
||||
status.seconds,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Incidents section */}
|
||||
{/* ── Incidents ── */}
|
||||
{props.incidents.length > 0 && (
|
||||
<div
|
||||
style={{
|
||||
borderTop: "1px solid #f3f4f6",
|
||||
paddingTop: "10px",
|
||||
}}
|
||||
>
|
||||
<div style={{ paddingTop: "10px" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
@@ -246,47 +285,121 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
fontWeight: 600,
|
||||
fontWeight: 700,
|
||||
color: "#dc2626",
|
||||
backgroundColor: "#fef2f2",
|
||||
padding: "1px 8px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
minWidth: "20px",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{props.incidents.length}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{props.incidents.slice(0, 3).map(
|
||||
(incident: UptimeBarTooltipIncident) => {
|
||||
const isClickable: boolean = Boolean(props.onIncidentClick);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={incident.id}
|
||||
onClick={
|
||||
isClickable
|
||||
? (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
props.onIncidentClick!(incident.id);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
style={{
|
||||
backgroundColor: "#f9fafb",
|
||||
border: "1px solid #f3f4f6",
|
||||
border: "1px solid #e5e7eb",
|
||||
borderRadius: "8px",
|
||||
padding: "8px 10px",
|
||||
marginBottom: "6px",
|
||||
cursor: isClickable ? "pointer" : "default",
|
||||
transition: "all 0.15s ease",
|
||||
}}
|
||||
onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (isClickable) {
|
||||
(e.currentTarget as HTMLDivElement).style.backgroundColor =
|
||||
"#f3f4f6";
|
||||
(e.currentTarget as HTMLDivElement).style.borderColor =
|
||||
"#d1d5db";
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (isClickable) {
|
||||
(e.currentTarget as HTMLDivElement).style.backgroundColor =
|
||||
"#f9fafb";
|
||||
(e.currentTarget as HTMLDivElement).style.borderColor =
|
||||
"#e5e7eb";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#111827",
|
||||
fontWeight: 600,
|
||||
marginBottom: "4px",
|
||||
lineHeight: "1.4",
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
{incident.title}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: isClickable ? "#2563eb" : "#111827",
|
||||
fontWeight: 600,
|
||||
lineHeight: "1.4",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{incident.title}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "#9ca3af",
|
||||
marginTop: "2px",
|
||||
}}
|
||||
>
|
||||
{OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
|
||||
incident.declaredAt,
|
||||
false,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isClickable && (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
style={{ flexShrink: 0, marginTop: "2px" }}
|
||||
>
|
||||
<path
|
||||
d="M6 3l5 5-5 5"
|
||||
stroke="#9ca3af"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "6px",
|
||||
gap: "5px",
|
||||
flexWrap: "wrap",
|
||||
marginTop: "5px",
|
||||
}}
|
||||
>
|
||||
{incident.incidentSeverity && (
|
||||
@@ -296,9 +409,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
fontWeight: 600,
|
||||
color: incident.incidentSeverity.color.toString(),
|
||||
backgroundColor:
|
||||
incident.incidentSeverity.color.toString() + "15",
|
||||
border: `1px solid ${incident.incidentSeverity.color.toString()}30`,
|
||||
padding: "1px 8px",
|
||||
incident.incidentSeverity.color.toString() + "12",
|
||||
border: `1px solid ${incident.incidentSeverity.color.toString()}25`,
|
||||
padding: "1px 7px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
@@ -315,9 +428,9 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
incident.currentIncidentState.color.toString(),
|
||||
backgroundColor:
|
||||
incident.currentIncidentState.color.toString() +
|
||||
"15",
|
||||
border: `1px solid ${incident.currentIncidentState.color.toString()}30`,
|
||||
padding: "1px 8px",
|
||||
"12",
|
||||
border: `1px solid ${incident.currentIncidentState.color.toString()}25`,
|
||||
padding: "1px 7px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
@@ -330,13 +443,14 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
);
|
||||
},
|
||||
)}
|
||||
|
||||
{props.incidents.length > 3 && (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "11px",
|
||||
color: "#9ca3af",
|
||||
color: "#6b7280",
|
||||
textAlign: "center",
|
||||
padding: "2px 0",
|
||||
padding: "4px 0 2px",
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
@@ -344,18 +458,6 @@ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
|
||||
{props.incidents.length - 3 !== 1 ? "s" : ""}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "#9ca3af",
|
||||
textAlign: "center",
|
||||
marginTop: "8px",
|
||||
fontWeight: 500,
|
||||
letterSpacing: "0.02em",
|
||||
}}
|
||||
>
|
||||
Click bar to view details
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,9 @@ export interface ComponentProps {
|
||||
defaultBarColor: Color;
|
||||
incidents?: Array<UptimeBarTooltipIncident> | undefined;
|
||||
onBarClick?: (date: Date, incidents: Array<UptimeBarTooltipIncident>) => void;
|
||||
onIncidentClick?:
|
||||
| ((incidentId: string) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
|
||||
@@ -88,6 +91,7 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
incidents={props.incidents}
|
||||
onBarClick={props.onBarClick}
|
||||
onIncidentClick={props.onIncidentClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,9 @@ export interface ComponentProps {
|
||||
date: Date;
|
||||
incidents: Array<UptimeBarTooltipIncident>;
|
||||
onClose: () => void;
|
||||
onIncidentClick?:
|
||||
| ((incidentId: string) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const UptimeBarDayModal: FunctionComponent<ComponentProps> = (
|
||||
@@ -17,45 +20,179 @@ const UptimeBarDayModal: FunctionComponent<ComponentProps> = (
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`Incidents on ${dateStr}`}
|
||||
title={`Incidents - ${dateStr}`}
|
||||
description={
|
||||
props.incidents.length > 0
|
||||
? `${props.incidents.length} incident${props.incidents.length !== 1 ? "s" : ""} reported on this day`
|
||||
: undefined
|
||||
}
|
||||
onClose={props.onClose}
|
||||
modalWidth={ModalWidth.Medium}
|
||||
closeButtonText="Close"
|
||||
>
|
||||
<div>
|
||||
{props.incidents.length === 0 && (
|
||||
<div className="text-gray-500 text-sm py-4 text-center">
|
||||
No incidents on this day.
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
padding: "32px 16px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "48px",
|
||||
height: "48px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "#f0fdf4",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
margin: "0 auto 12px",
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#16a34a"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M20 6L9 17l-5-5" />
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
color: "#111827",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
No incidents
|
||||
</div>
|
||||
<div style={{ fontSize: "13px", color: "#6b7280" }}>
|
||||
No incidents were reported on this day.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.incidents.map((incident: UptimeBarTooltipIncident) => {
|
||||
const isClickable: boolean = Boolean(props.onIncidentClick);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={incident.id}
|
||||
className="border border-gray-200 rounded-lg p-4 mb-3"
|
||||
onClick={
|
||||
isClickable
|
||||
? () => {
|
||||
props.onIncidentClick!(incident.id);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
style={{
|
||||
border: "1px solid #e5e7eb",
|
||||
borderRadius: "10px",
|
||||
padding: "14px 16px",
|
||||
marginBottom: "10px",
|
||||
cursor: isClickable ? "pointer" : "default",
|
||||
transition: "all 0.15s ease",
|
||||
backgroundColor: "#ffffff",
|
||||
}}
|
||||
onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (isClickable) {
|
||||
(e.currentTarget as HTMLDivElement).style.backgroundColor =
|
||||
"#f9fafb";
|
||||
(e.currentTarget as HTMLDivElement).style.borderColor =
|
||||
"#d1d5db";
|
||||
(e.currentTarget as HTMLDivElement).style.boxShadow =
|
||||
"0 1px 3px rgba(0,0,0,0.06)";
|
||||
}
|
||||
}}
|
||||
onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (isClickable) {
|
||||
(e.currentTarget as HTMLDivElement).style.backgroundColor =
|
||||
"#ffffff";
|
||||
(e.currentTarget as HTMLDivElement).style.borderColor =
|
||||
"#e5e7eb";
|
||||
(e.currentTarget as HTMLDivElement).style.boxShadow = "none";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="font-medium text-base text-gray-900">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
justifyContent: "space-between",
|
||||
gap: "12px",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
color: isClickable ? "#2563eb" : "#111827",
|
||||
lineHeight: "1.4",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
{incident.title}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 mt-1">
|
||||
Declared at{" "}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "#6b7280",
|
||||
}}
|
||||
>
|
||||
Declared{" "}
|
||||
{OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
|
||||
incident.declaredAt,
|
||||
false,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isClickable && (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
style={{ flexShrink: 0, marginTop: "3px" }}
|
||||
>
|
||||
<path
|
||||
d="M6 3l5 5-5 5"
|
||||
stroke="#9ca3af"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3 mt-2">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
flexWrap: "wrap",
|
||||
marginTop: "8px",
|
||||
}}
|
||||
>
|
||||
{incident.incidentSeverity && (
|
||||
<span
|
||||
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
|
||||
style={{
|
||||
backgroundColor:
|
||||
incident.incidentSeverity.color.toString() + "20",
|
||||
fontSize: "11px",
|
||||
fontWeight: 600,
|
||||
color: incident.incidentSeverity.color.toString(),
|
||||
backgroundColor:
|
||||
incident.incidentSeverity.color.toString() + "12",
|
||||
border: `1px solid ${incident.incidentSeverity.color.toString()}25`,
|
||||
padding: "2px 10px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{incident.incidentSeverity.name}
|
||||
@@ -63,11 +200,16 @@ const UptimeBarDayModal: FunctionComponent<ComponentProps> = (
|
||||
)}
|
||||
{incident.currentIncidentState && (
|
||||
<span
|
||||
className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium"
|
||||
style={{
|
||||
backgroundColor:
|
||||
incident.currentIncidentState.color.toString() + "20",
|
||||
fontSize: "11px",
|
||||
fontWeight: 600,
|
||||
color: incident.currentIncidentState.color.toString(),
|
||||
backgroundColor:
|
||||
incident.currentIncidentState.color.toString() + "12",
|
||||
border: `1px solid ${incident.currentIncidentState.color.toString()}25`,
|
||||
padding: "2px 10px",
|
||||
borderRadius: "9999px",
|
||||
lineHeight: "1.6",
|
||||
}}
|
||||
>
|
||||
{incident.currentIncidentState.name}
|
||||
|
||||
@@ -2,6 +2,7 @@ import Tippy from "@tippyjs/react";
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import "tippy.js/dist/tippy.css";
|
||||
import "tippy.js/themes/light-border.css";
|
||||
import "tippy.js/animations/shift-away-subtle.css";
|
||||
|
||||
export interface ComponentProps {
|
||||
text?: string | undefined;
|
||||
@@ -22,20 +23,29 @@ const Tooltip: FunctionComponent<ComponentProps> = (
|
||||
<span>{props.text}</span>
|
||||
);
|
||||
|
||||
const themeProps: { theme: string } | Record<string, never> =
|
||||
props.richContent ? { theme: "light-border" } : {};
|
||||
const isRich: boolean = Boolean(props.richContent);
|
||||
|
||||
const themeProps: { theme: string } | Record<string, never> = isRich
|
||||
? { theme: "light-border" }
|
||||
: {};
|
||||
|
||||
const animationProps: { animation: string } | Record<string, never> = isRich
|
||||
? { animation: "shift-away-subtle" }
|
||||
: {};
|
||||
|
||||
return (
|
||||
<Tippy
|
||||
key={Math.random()}
|
||||
content={tooltipContent}
|
||||
interactive={true}
|
||||
interactive={isRich}
|
||||
trigger="mouseenter focus"
|
||||
hideOnClick={false}
|
||||
maxWidth={props.richContent ? 380 : 350}
|
||||
delay={[80, 0]}
|
||||
duration={[150, 100]}
|
||||
maxWidth={isRich ? 380 : 350}
|
||||
delay={isRich ? [120, 80] : [0, 0]}
|
||||
duration={[200, 150]}
|
||||
placement={isRich ? "top" : "top"}
|
||||
{...themeProps}
|
||||
{...animationProps}
|
||||
aria={{
|
||||
content: "describedby",
|
||||
expanded: "auto",
|
||||
|
||||
Reference in New Issue
Block a user