@oneuptime/common 10.0.43 → 10.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/Models/DatabaseModels/Workflow.ts +29 -0
  2. package/Server/API/StatusPageAPI.ts +48 -0
  3. package/Server/EnvironmentConfig.ts +5 -8
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.ts +22 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  6. package/Server/Services/AlertService.ts +45 -0
  7. package/Server/Services/IncidentService.ts +81 -13
  8. package/Server/Services/LogAggregationService.ts +1 -0
  9. package/Server/Services/WorkflowService.ts +28 -1
  10. package/Server/Types/Workflow/Components/Webhook.ts +28 -5
  11. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +29 -13
  12. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +163 -26
  13. package/Server/Utils/Monitor/MonitorMetricUtil.ts +92 -0
  14. package/Server/Utils/Profiling.ts +101 -0
  15. package/Server/Utils/VM/VMRunner.ts +88 -0
  16. package/Types/Dashboard/DashboardTemplates.ts +1149 -0
  17. package/Types/Exception/ExceptionMetricType.ts +15 -0
  18. package/Types/IsolatedVM/ReturnResult.ts +3 -0
  19. package/Types/Metrics/MetricDashboardMetricType.ts +28 -0
  20. package/Types/Metrics/MetricsQuery.ts +2 -1
  21. package/Types/Monitor/CustomCodeMonitor/CapturedMetric.ts +7 -0
  22. package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +2 -0
  23. package/Types/Monitor/UptimeBarTooltipIncident.ts +21 -0
  24. package/Types/Profile/ProfileMetricType.ts +16 -0
  25. package/Types/Span/SpanMetricType.ts +17 -0
  26. package/UI/Components/Charts/Area/AreaChart.tsx +40 -33
  27. package/UI/Components/Charts/Bar/BarChart.tsx +37 -30
  28. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +196 -51
  29. package/UI/Components/Charts/ChartGroup/NoDataMessage.tsx +13 -0
  30. package/UI/Components/Charts/Line/LineChart.tsx +39 -32
  31. package/UI/Components/Forms/BasicForm.tsx +1 -1
  32. package/UI/Components/Graphs/DayUptimeGraph.tsx +88 -35
  33. package/UI/Components/Graphs/UptimeBarTooltip.tsx +547 -0
  34. package/UI/Components/MonitorGraphs/Uptime.tsx +7 -0
  35. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +225 -0
  36. package/UI/Components/RadioButtons/GroupRadioButtons.tsx +1 -0
  37. package/UI/Components/Tooltip/Tooltip.tsx +29 -4
  38. package/UI/Components/Workflow/ComponentSettingsModal.tsx +2 -0
  39. package/UI/Components/Workflow/DocumentationViewer.tsx +5 -0
  40. package/UI/Components/Workflow/Workflow.tsx +2 -0
  41. package/Utils/Alerts/AlertMetricType.ts +98 -0
  42. package/Utils/Incident/IncidentMetricType.ts +130 -0
  43. package/build/dist/Models/DatabaseModels/Workflow.js +30 -0
  44. package/build/dist/Models/DatabaseModels/Workflow.js.map +1 -1
  45. package/build/dist/Server/API/StatusPageAPI.js +42 -0
  46. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  47. package/build/dist/Server/EnvironmentConfig.js +2 -2
  48. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.js +14 -0
  50. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774559064920-MigrationName.js.map +1 -0
  51. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  52. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  53. package/build/dist/Server/Services/AlertService.js +34 -0
  54. package/build/dist/Server/Services/AlertService.js.map +1 -1
  55. package/build/dist/Server/Services/IncidentService.js +52 -9
  56. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  57. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  58. package/build/dist/Server/Services/WorkflowService.js +25 -0
  59. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  60. package/build/dist/Server/Types/Workflow/Components/Webhook.js +23 -5
  61. package/build/dist/Server/Types/Workflow/Components/Webhook.js.map +1 -1
  62. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +21 -7
  63. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  64. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +120 -21
  65. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  66. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +68 -1
  67. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  68. package/build/dist/Server/Utils/Profiling.js +80 -0
  69. package/build/dist/Server/Utils/Profiling.js.map +1 -0
  70. package/build/dist/Server/Utils/VM/VMRunner.js +68 -0
  71. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  72. package/build/dist/Types/Dashboard/DashboardTemplates.js +1095 -0
  73. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  74. package/build/dist/Types/Exception/ExceptionMetricType.js +16 -0
  75. package/build/dist/Types/Exception/ExceptionMetricType.js.map +1 -0
  76. package/build/dist/Types/Metrics/MetricDashboardMetricType.js +26 -0
  77. package/build/dist/Types/Metrics/MetricDashboardMetricType.js.map +1 -0
  78. package/build/dist/Types/Monitor/CustomCodeMonitor/CapturedMetric.js +2 -0
  79. package/build/dist/Types/Monitor/CustomCodeMonitor/CapturedMetric.js.map +1 -0
  80. package/build/dist/Types/Monitor/UptimeBarTooltipIncident.js +2 -0
  81. package/build/dist/Types/Monitor/UptimeBarTooltipIncident.js.map +1 -0
  82. package/build/dist/Types/Profile/ProfileMetricType.js +17 -0
  83. package/build/dist/Types/Profile/ProfileMetricType.js.map +1 -0
  84. package/build/dist/Types/Span/SpanMetricType.js +18 -0
  85. package/build/dist/Types/Span/SpanMetricType.js.map +1 -0
  86. package/build/dist/UI/Components/Charts/Area/AreaChart.js +21 -16
  87. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  88. package/build/dist/UI/Components/Charts/Bar/BarChart.js +20 -15
  89. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  90. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +73 -15
  91. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  92. package/build/dist/UI/Components/Charts/ChartGroup/NoDataMessage.js +7 -0
  93. package/build/dist/UI/Components/Charts/ChartGroup/NoDataMessage.js.map +1 -0
  94. package/build/dist/UI/Components/Charts/Line/LineChart.js +20 -15
  95. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  96. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  97. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  98. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +46 -20
  99. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  100. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +303 -0
  101. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -0
  102. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +1 -1
  103. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  104. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +118 -0
  105. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -0
  106. package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js +1 -1
  107. package/build/dist/UI/Components/RadioButtons/GroupRadioButtons.js.map +1 -1
  108. package/build/dist/UI/Components/Tooltip/Tooltip.js +13 -3
  109. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  110. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +1 -1
  111. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  112. package/build/dist/UI/Components/Workflow/DocumentationViewer.js +1 -0
  113. package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
  114. package/build/dist/UI/Components/Workflow/Workflow.js +1 -1
  115. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  116. package/build/dist/Utils/Alerts/AlertMetricType.js +84 -0
  117. package/build/dist/Utils/Alerts/AlertMetricType.js.map +1 -0
  118. package/build/dist/Utils/Incident/IncidentMetricType.js +114 -0
  119. package/build/dist/Utils/Incident/IncidentMetricType.js.map +1 -0
  120. package/package.json +3 -2
@@ -0,0 +1,547 @@
1
+ import OneUptimeDate from "../../../Types/Date";
2
+ import Color from "../../../Types/Color";
3
+ import UptimeBarTooltipIncident from "../../../Types/Monitor/UptimeBarTooltipIncident";
4
+ import React, { FunctionComponent, ReactElement } from "react";
5
+
6
+ export interface StatusDuration {
7
+ label: string;
8
+ seconds: number;
9
+ color: Color;
10
+ isDowntime: boolean;
11
+ }
12
+
13
+ export interface ComponentProps {
14
+ date: Date;
15
+ uptimePercent: number;
16
+ hasEvents: boolean;
17
+ statusDurations: Array<StatusDuration>;
18
+ incidents: Array<UptimeBarTooltipIncident>;
19
+ onIncidentClick?: ((incidentId: string) => void) | undefined;
20
+ }
21
+
22
+ const UptimeBarTooltip: FunctionComponent<ComponentProps> = (
23
+ props: ComponentProps,
24
+ ): ReactElement => {
25
+ const dateStr: string =
26
+ OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(props.date, true);
27
+
28
+ // Color tiers
29
+ const isGood: boolean = props.uptimePercent >= 99.9;
30
+ const isWarn: boolean = !isGood && props.uptimePercent >= 99;
31
+
32
+ const uptimeColor: string = isGood
33
+ ? "#059669"
34
+ : isWarn
35
+ ? "#d97706"
36
+ : "#dc2626";
37
+
38
+ // Sort: downtime first, then by duration desc
39
+ const sortedDurations: Array<StatusDuration> = [
40
+ ...props.statusDurations,
41
+ ].sort((a: StatusDuration, b: StatusDuration) => {
42
+ if (a.isDowntime !== b.isDowntime) {
43
+ return a.isDowntime ? -1 : 1;
44
+ }
45
+ return b.seconds - a.seconds;
46
+ });
47
+
48
+ const totalSeconds: number = sortedDurations.reduce(
49
+ (sum: number, d: StatusDuration) => {
50
+ return sum + d.seconds;
51
+ },
52
+ 0,
53
+ );
54
+
55
+ const hasIncidents: boolean = props.incidents.length > 0;
56
+ const hasStatuses: boolean = sortedDurations.length > 0;
57
+
58
+ return (
59
+ <div
60
+ style={{
61
+ minWidth: "280px",
62
+ maxWidth: "340px",
63
+ fontFamily:
64
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
65
+ }}
66
+ >
67
+ {/* ── Date header ── */}
68
+ <div
69
+ style={{
70
+ display: "flex",
71
+ alignItems: "center",
72
+ gap: "8px",
73
+ paddingBottom: "10px",
74
+ marginBottom: "10px",
75
+ borderBottom: "1px solid #f0f0f0",
76
+ }}
77
+ >
78
+ <svg
79
+ width="14"
80
+ height="14"
81
+ viewBox="0 0 24 24"
82
+ fill="none"
83
+ stroke="#9ca3af"
84
+ strokeWidth="2"
85
+ strokeLinecap="round"
86
+ strokeLinejoin="round"
87
+ style={{ flexShrink: 0 }}
88
+ >
89
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
90
+ <line x1="16" y1="2" x2="16" y2="6" />
91
+ <line x1="8" y1="2" x2="8" y2="6" />
92
+ <line x1="3" y1="10" x2="21" y2="10" />
93
+ </svg>
94
+ <span
95
+ style={{
96
+ fontWeight: 600,
97
+ fontSize: "13px",
98
+ color: "#111827",
99
+ letterSpacing: "-0.01em",
100
+ }}
101
+ >
102
+ {dateStr}
103
+ </span>
104
+ </div>
105
+
106
+ {/* ── Uptime ── */}
107
+ {props.hasEvents && (
108
+ <div
109
+ style={{
110
+ marginBottom: hasStatuses || hasIncidents ? "12px" : "0",
111
+ }}
112
+ >
113
+ <div
114
+ style={{
115
+ display: "flex",
116
+ alignItems: "baseline",
117
+ justifyContent: "space-between",
118
+ marginBottom: "8px",
119
+ }}
120
+ >
121
+ <span
122
+ style={{
123
+ fontSize: "11px",
124
+ color: "#6b7280",
125
+ fontWeight: 500,
126
+ }}
127
+ >
128
+ Uptime
129
+ </span>
130
+ <div style={{ display: "flex", alignItems: "baseline" }}>
131
+ <span
132
+ style={{
133
+ fontSize: "14px",
134
+ fontWeight: 600,
135
+ color: uptimeColor,
136
+ fontVariantNumeric: "tabular-nums",
137
+ lineHeight: 1,
138
+ letterSpacing: "-0.03em",
139
+ }}
140
+ >
141
+ {props.uptimePercent >= 100
142
+ ? "100"
143
+ : props.uptimePercent.toFixed(2)}
144
+ </span>
145
+ <span
146
+ style={{
147
+ fontSize: "11px",
148
+ fontWeight: 600,
149
+ color: uptimeColor,
150
+ marginLeft: "1px",
151
+ opacity: 0.7,
152
+ }}
153
+ >
154
+ %
155
+ </span>
156
+ </div>
157
+ </div>
158
+ {/* Segmented bar */}
159
+ {totalSeconds > 0 && sortedDurations.length > 1 ? (
160
+ <div
161
+ style={{
162
+ width: "100%",
163
+ height: "4px",
164
+ borderRadius: "100px",
165
+ overflow: "hidden",
166
+ display: "flex",
167
+ gap: "1px",
168
+ backgroundColor: "#e5e7eb",
169
+ }}
170
+ >
171
+ {sortedDurations.map((status: StatusDuration, index: number) => {
172
+ const widthPercent: number =
173
+ (status.seconds / totalSeconds) * 100;
174
+ if (widthPercent < 0.5) {
175
+ return null;
176
+ }
177
+ return (
178
+ <div
179
+ key={index}
180
+ style={{
181
+ width: `${widthPercent}%`,
182
+ height: "100%",
183
+ backgroundColor: status.color.toString(),
184
+ }}
185
+ />
186
+ );
187
+ })}
188
+ </div>
189
+ ) : (
190
+ <div
191
+ style={{
192
+ width: "100%",
193
+ height: "4px",
194
+ backgroundColor: "#e5e7eb",
195
+ borderRadius: "100px",
196
+ overflow: "hidden",
197
+ }}
198
+ >
199
+ <div
200
+ style={{
201
+ width: `${Math.min(props.uptimePercent, 100)}%`,
202
+ height: "100%",
203
+ backgroundColor: uptimeColor,
204
+ borderRadius: "100px",
205
+ }}
206
+ />
207
+ </div>
208
+ )}
209
+ </div>
210
+ )}
211
+
212
+ {/* ── No data ── */}
213
+ {!props.hasEvents && (
214
+ <div
215
+ style={{
216
+ backgroundColor: "#f9fafb",
217
+ borderRadius: "8px",
218
+ padding: "16px",
219
+ textAlign: "center",
220
+ }}
221
+ >
222
+ <svg
223
+ width="18"
224
+ height="18"
225
+ viewBox="0 0 24 24"
226
+ fill="none"
227
+ stroke="#d1d5db"
228
+ strokeWidth="1.5"
229
+ strokeLinecap="round"
230
+ strokeLinejoin="round"
231
+ style={{ margin: "0 auto 6px" }}
232
+ >
233
+ <circle cx="12" cy="12" r="10" />
234
+ <line x1="12" y1="8" x2="12" y2="12" />
235
+ <line x1="12" y1="16" x2="12.01" y2="16" />
236
+ </svg>
237
+ <div
238
+ style={{
239
+ fontSize: "12px",
240
+ color: "#9ca3af",
241
+ fontWeight: 500,
242
+ }}
243
+ >
244
+ No monitoring data for this day
245
+ </div>
246
+ </div>
247
+ )}
248
+
249
+ {/* ── Status breakdown ── */}
250
+ {hasStatuses && (
251
+ <div
252
+ style={{
253
+ paddingBottom: hasIncidents ? "10px" : "0",
254
+ marginBottom: hasIncidents ? "10px" : "0",
255
+ borderBottom: hasIncidents ? "1px solid #f0f0f0" : "none",
256
+ }}
257
+ >
258
+ {sortedDurations.map((status: StatusDuration, index: number) => {
259
+ const pct: number =
260
+ totalSeconds > 0 ? (status.seconds / totalSeconds) * 100 : 0;
261
+ return (
262
+ <div
263
+ key={index}
264
+ style={{
265
+ display: "flex",
266
+ alignItems: "center",
267
+ padding: "4px 0",
268
+ gap: "8px",
269
+ }}
270
+ >
271
+ {/* Color dot + label */}
272
+ <div
273
+ style={{
274
+ display: "flex",
275
+ alignItems: "center",
276
+ gap: "6px",
277
+ width: "100px",
278
+ flexShrink: 0,
279
+ }}
280
+ >
281
+ <span
282
+ style={{
283
+ width: "6px",
284
+ height: "6px",
285
+ borderRadius: "50%",
286
+ backgroundColor: status.color.toString(),
287
+ display: "inline-block",
288
+ flexShrink: 0,
289
+ }}
290
+ />
291
+ <span
292
+ style={{
293
+ fontSize: "12px",
294
+ color: "#374151",
295
+ fontWeight: 500,
296
+ whiteSpace: "nowrap",
297
+ overflow: "hidden",
298
+ textOverflow: "ellipsis",
299
+ }}
300
+ >
301
+ {status.label}
302
+ </span>
303
+ </div>
304
+ {/* Mini bar */}
305
+ <div
306
+ style={{
307
+ flex: 1,
308
+ height: "4px",
309
+ backgroundColor: "#f3f4f6",
310
+ borderRadius: "100px",
311
+ overflow: "hidden",
312
+ }}
313
+ >
314
+ <div
315
+ style={{
316
+ width: `${pct}%`,
317
+ height: "100%",
318
+ backgroundColor: status.color.toString(),
319
+ borderRadius: "100px",
320
+ opacity: 0.7,
321
+ }}
322
+ />
323
+ </div>
324
+ {/* Duration */}
325
+ <span
326
+ style={{
327
+ fontSize: "11px",
328
+ color: status.isDowntime ? "#dc2626" : "#6b7280",
329
+ fontWeight: status.isDowntime ? 600 : 400,
330
+ fontVariantNumeric: "tabular-nums",
331
+ whiteSpace: "nowrap",
332
+ flexShrink: 0,
333
+ }}
334
+ >
335
+ {OneUptimeDate.secondsToFormattedFriendlyTimeString(
336
+ status.seconds,
337
+ )}
338
+ </span>
339
+ </div>
340
+ );
341
+ })}
342
+ </div>
343
+ )}
344
+
345
+ {/* ── Incidents ── */}
346
+ {hasIncidents && (
347
+ <div>
348
+ <div
349
+ style={{
350
+ display: "flex",
351
+ alignItems: "center",
352
+ gap: "6px",
353
+ marginBottom: "8px",
354
+ }}
355
+ >
356
+ <span
357
+ style={{
358
+ fontSize: "11px",
359
+ color: "#6b7280",
360
+ fontWeight: 500,
361
+ flex: 1,
362
+ }}
363
+ >
364
+ Incidents
365
+ </span>
366
+ <span
367
+ style={{
368
+ fontSize: "10px",
369
+ fontWeight: 600,
370
+ color: "#dc2626",
371
+ backgroundColor: "#fef2f2",
372
+ padding: "1px 7px",
373
+ borderRadius: "9999px",
374
+ lineHeight: "1.6",
375
+ }}
376
+ >
377
+ {props.incidents.length}
378
+ </span>
379
+ </div>
380
+
381
+ {props.incidents
382
+ .slice(0, 3)
383
+ .map((incident: UptimeBarTooltipIncident) => {
384
+ const isClickable: boolean = Boolean(props.onIncidentClick);
385
+
386
+ return (
387
+ <div
388
+ key={incident.id}
389
+ onClick={
390
+ isClickable
391
+ ? (e: React.MouseEvent) => {
392
+ e.stopPropagation();
393
+ props.onIncidentClick!(incident.id);
394
+ }
395
+ : undefined
396
+ }
397
+ style={{
398
+ borderLeft: `3px solid ${incident.incidentSeverity ? incident.incidentSeverity.color.toString() : "#dc2626"}`,
399
+ padding: "6px 10px",
400
+ marginBottom: "6px",
401
+ cursor: isClickable ? "pointer" : "default",
402
+ transition: "background-color 0.12s ease",
403
+ backgroundColor: "#fafafa",
404
+ borderRadius: "0 6px 6px 0",
405
+ }}
406
+ onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {
407
+ if (isClickable) {
408
+ (
409
+ e.currentTarget as HTMLDivElement
410
+ ).style.backgroundColor = "#f3f4f6";
411
+ }
412
+ }}
413
+ onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {
414
+ if (isClickable) {
415
+ (
416
+ e.currentTarget as HTMLDivElement
417
+ ).style.backgroundColor = "#fafafa";
418
+ }
419
+ }}
420
+ >
421
+ {/* Title row */}
422
+ <div
423
+ style={{
424
+ display: "flex",
425
+ alignItems: "center",
426
+ gap: "6px",
427
+ }}
428
+ >
429
+ <div
430
+ style={{
431
+ flex: 1,
432
+ minWidth: 0,
433
+ fontSize: "12px",
434
+ color: "#111827",
435
+ fontWeight: 600,
436
+ lineHeight: "1.3",
437
+ overflow: "hidden",
438
+ textOverflow: "ellipsis",
439
+ whiteSpace: "nowrap",
440
+ }}
441
+ >
442
+ {incident.title}
443
+ </div>
444
+ {isClickable && (
445
+ <svg
446
+ width="10"
447
+ height="10"
448
+ viewBox="0 0 16 16"
449
+ fill="none"
450
+ style={{ flexShrink: 0, opacity: 0.35 }}
451
+ >
452
+ <path
453
+ d="M6 3l5 5-5 5"
454
+ stroke="#6b7280"
455
+ strokeWidth="1.5"
456
+ strokeLinecap="round"
457
+ strokeLinejoin="round"
458
+ />
459
+ </svg>
460
+ )}
461
+ </div>
462
+ {/* Meta row: badges + time */}
463
+ <div
464
+ style={{
465
+ display: "flex",
466
+ alignItems: "center",
467
+ gap: "4px",
468
+ flexWrap: "wrap",
469
+ marginTop: "4px",
470
+ }}
471
+ >
472
+ {incident.incidentSeverity && (
473
+ <span
474
+ style={{
475
+ fontSize: "9px",
476
+ fontWeight: 600,
477
+ color: incident.incidentSeverity.color.toString(),
478
+ backgroundColor:
479
+ incident.incidentSeverity.color.toString() + "14",
480
+ padding: "1px 5px",
481
+ borderRadius: "3px",
482
+ lineHeight: "1.6",
483
+ textTransform: "uppercase",
484
+ letterSpacing: "0.03em",
485
+ }}
486
+ >
487
+ {incident.incidentSeverity.name}
488
+ </span>
489
+ )}
490
+ {incident.currentIncidentState && (
491
+ <span
492
+ style={{
493
+ fontSize: "9px",
494
+ fontWeight: 600,
495
+ color: incident.currentIncidentState.color.toString(),
496
+ backgroundColor:
497
+ incident.currentIncidentState.color.toString() +
498
+ "14",
499
+ padding: "1px 5px",
500
+ borderRadius: "3px",
501
+ lineHeight: "1.6",
502
+ textTransform: "uppercase",
503
+ letterSpacing: "0.03em",
504
+ }}
505
+ >
506
+ {incident.currentIncidentState.name}
507
+ </span>
508
+ )}
509
+ <span
510
+ style={{
511
+ fontSize: "10px",
512
+ color: "#b0b0b0",
513
+ marginLeft: "auto",
514
+ whiteSpace: "nowrap",
515
+ }}
516
+ >
517
+ {OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(
518
+ incident.declaredAt,
519
+ false,
520
+ )}
521
+ </span>
522
+ </div>
523
+ </div>
524
+ );
525
+ })}
526
+
527
+ {props.incidents.length > 3 && (
528
+ <div
529
+ style={{
530
+ fontSize: "11px",
531
+ color: "#9ca3af",
532
+ textAlign: "center",
533
+ padding: "4px 0 0",
534
+ fontWeight: 500,
535
+ }}
536
+ >
537
+ +{props.incidents.length - 3} more incident
538
+ {props.incidents.length - 3 !== 1 ? "s" : ""}
539
+ </div>
540
+ )}
541
+ </div>
542
+ )}
543
+ </div>
544
+ );
545
+ };
546
+
547
+ export default UptimeBarTooltip;
@@ -7,6 +7,7 @@ import CommonMonitorEvent from "../../../Utils/Uptime/MonitorEvent";
7
7
  import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
8
8
  import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
9
9
  import StatusPageHistoryChartBarColorRule from "../../../Models/DatabaseModels/StatusPageHistoryChartBarColorRule";
10
+ import UptimeBarTooltipIncident from "../../../Types/Monitor/UptimeBarTooltipIncident";
10
11
  import React, {
11
12
  FunctionComponent,
12
13
  ReactElement,
@@ -27,6 +28,9 @@ export interface ComponentProps {
27
28
  barColorRules?: Array<StatusPageHistoryChartBarColorRule> | undefined;
28
29
  downtimeMonitorStatuses: Array<MonitorStatus> | undefined;
29
30
  defaultBarColor: Color;
31
+ incidents?: Array<UptimeBarTooltipIncident> | undefined;
32
+ onBarClick?: (date: Date, incidents: Array<UptimeBarTooltipIncident>) => void;
33
+ onIncidentClick?: ((incidentId: string) => void) | undefined;
30
34
  }
31
35
 
32
36
  const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
@@ -83,6 +87,9 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
83
87
  return status.id!;
84
88
  }) || []
85
89
  }
90
+ incidents={props.incidents}
91
+ onBarClick={props.onBarClick}
92
+ onIncidentClick={props.onIncidentClick}
86
93
  />
87
94
  );
88
95
  };