@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,15 @@
1
+ enum ExceptionMetricType {
2
+ ExceptionCount = "oneuptime.exception.count",
3
+ ExceptionRate = "oneuptime.exception.rate",
4
+ ExceptionCountByType = "oneuptime.exception.count.by.type",
5
+ ExceptionCountByService = "oneuptime.exception.count.by.service",
6
+ UnresolvedExceptionCount = "oneuptime.exception.unresolved.count",
7
+ ResolvedExceptionCount = "oneuptime.exception.resolved.count",
8
+ MutedExceptionCount = "oneuptime.exception.muted.count",
9
+ ExceptionFirstSeenTime = "oneuptime.exception.first.seen.time",
10
+ ExceptionLastSeenTime = "oneuptime.exception.last.seen.time",
11
+ ExceptionOccurrenceCount = "oneuptime.exception.occurrence.count",
12
+ ExceptionAffectedServiceCount = "oneuptime.exception.affected.service.count",
13
+ }
14
+
15
+ export default ExceptionMetricType;
@@ -1,4 +1,7 @@
1
+ import CapturedMetric from "../Monitor/CustomCodeMonitor/CapturedMetric";
2
+
1
3
  export default interface ReturnResult {
2
4
  returnValue: any;
3
5
  logMessages: string[];
6
+ capturedMetrics: CapturedMetric[];
4
7
  }
@@ -0,0 +1,28 @@
1
+ enum MetricDashboardMetricType {
2
+ // HTTP metrics
3
+ HttpRequestDuration = "http.server.request.duration",
4
+ HttpRequestCount = "http.server.request.count",
5
+ HttpRequestErrorRate = "http.server.request.error.rate",
6
+ HttpResponseSize = "http.server.response.body.size",
7
+ HttpRequestSize = "http.server.request.body.size",
8
+ HttpActiveRequests = "http.server.active_requests",
9
+
10
+ // System metrics
11
+ SystemCpuUtilization = "system.cpu.utilization",
12
+ SystemMemoryUsage = "system.memory.usage",
13
+ SystemDiskIo = "system.disk.io",
14
+ SystemNetworkIo = "system.network.io",
15
+
16
+ // Runtime metrics
17
+ ProcessCpuUtilization = "process.cpu.utilization",
18
+ ProcessMemoryUsage = "process.runtime.jvm.memory.usage",
19
+ GcDuration = "process.runtime.jvm.gc.duration",
20
+ ThreadCount = "process.runtime.jvm.threads.count",
21
+
22
+ // Custom application metrics
23
+ CustomCounter = "custom.counter",
24
+ CustomGauge = "custom.gauge",
25
+ CustomHistogram = "custom.histogram",
26
+ }
27
+
28
+ export default MetricDashboardMetricType;
@@ -1,9 +1,10 @@
1
+ import Search from "../BaseDatabase/Search";
1
2
  import Dictionary from "../Dictionary";
2
3
  import MetricsAggregationType from "./MetricsAggregationType";
3
4
 
4
5
  export default interface MetricsQuery {
5
6
  metricName: string;
6
- attributes: Dictionary<string | boolean | number>;
7
+ attributes: Dictionary<string | boolean | number | Search<string>>;
7
8
  aggegationType: MetricsAggregationType;
8
9
  aggregateBy: Dictionary<boolean>;
9
10
 
@@ -0,0 +1,7 @@
1
+ import { JSONObject } from "../../JSON";
2
+
3
+ export default interface CapturedMetric {
4
+ name: string;
5
+ value: number;
6
+ attributes?: JSONObject | undefined;
7
+ }
@@ -1,8 +1,10 @@
1
+ import CapturedMetric from "./CapturedMetric";
1
2
  import { JSONObject } from "../../JSON";
2
3
 
3
4
  export default interface CustomCodeMonitorResponse {
4
5
  result: string | number | boolean | JSONObject | undefined;
5
6
  scriptError?: string | undefined;
6
7
  logMessages: string[];
8
+ capturedMetrics: CapturedMetric[];
7
9
  executionTimeInMS: number;
8
10
  }
@@ -0,0 +1,21 @@
1
+ import Color from "../Color";
2
+ import ObjectID from "../ObjectID";
3
+
4
+ export default interface UptimeBarTooltipIncident {
5
+ id: string;
6
+ title: string;
7
+ declaredAt: Date;
8
+ incidentSeverity?:
9
+ | {
10
+ name: string;
11
+ color: Color;
12
+ }
13
+ | undefined;
14
+ currentIncidentState?:
15
+ | {
16
+ name: string;
17
+ color: Color;
18
+ }
19
+ | undefined;
20
+ monitorIds: Array<ObjectID>;
21
+ }
@@ -0,0 +1,16 @@
1
+ enum ProfileMetricType {
2
+ CpuProfileDuration = "oneuptime.profile.cpu.duration",
3
+ CpuProfileSampleCount = "oneuptime.profile.cpu.sample.count",
4
+ WallClockDuration = "oneuptime.profile.wall.duration",
5
+ MemoryAllocationSize = "oneuptime.profile.memory.allocation.size",
6
+ MemoryAllocationCount = "oneuptime.profile.memory.allocation.count",
7
+ HeapUsage = "oneuptime.profile.heap.usage",
8
+ GoroutineCount = "oneuptime.profile.goroutine.count",
9
+ ThreadCount = "oneuptime.profile.thread.count",
10
+ ProfileSampleRate = "oneuptime.profile.sample.rate",
11
+ ProfileCount = "oneuptime.profile.count",
12
+ TopFunctionCpuTime = "oneuptime.profile.top.function.cpu.time",
13
+ TopFunctionAllocations = "oneuptime.profile.top.function.allocations",
14
+ }
15
+
16
+ export default ProfileMetricType;
@@ -0,0 +1,17 @@
1
+ enum SpanMetricType {
2
+ SpanCount = "oneuptime.span.count",
3
+ SpanDuration = "oneuptime.span.duration",
4
+ SpanErrorCount = "oneuptime.span.error.count",
5
+ SpanErrorRate = "oneuptime.span.error.rate",
6
+ SpanRequestRate = "oneuptime.span.request.rate",
7
+ SpanP50Duration = "oneuptime.span.duration.p50",
8
+ SpanP90Duration = "oneuptime.span.duration.p90",
9
+ SpanP95Duration = "oneuptime.span.duration.p95",
10
+ SpanP99Duration = "oneuptime.span.duration.p99",
11
+ SpanStatusOk = "oneuptime.span.status.ok",
12
+ SpanStatusError = "oneuptime.span.status.error",
13
+ SpanStatusUnset = "oneuptime.span.status.unset",
14
+ SpanThroughput = "oneuptime.span.throughput",
15
+ }
16
+
17
+ export default SpanMetricType;
@@ -7,6 +7,7 @@ import ChartCurve from "../Types/ChartCurve";
7
7
  import ChartDataPoint from "../ChartLibrary/Types/ChartDataPoint";
8
8
  import DataPointUtil from "../Utils/DataPoint";
9
9
  import ChartReferenceLineProps from "../Types/ReferenceLineProps";
10
+ import NoDataMessage from "../ChartGroup/NoDataMessage";
10
11
 
11
12
  export interface ComponentProps {
12
13
  data: Array<SeriesPoint>;
@@ -32,12 +33,8 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
32
33
  });
33
34
 
34
35
  useEffect(() => {
35
- if (!props.data || props.data.length === 0) {
36
- setRecords([]);
37
- }
38
-
39
36
  const records: Array<ChartDataPoint> = DataPointUtil.getChartDataPoints({
40
- seriesPoints: props.data,
37
+ seriesPoints: props.data || [],
41
38
  xAxis: props.xAxis,
42
39
  yAxis: props.yAxis,
43
40
  });
@@ -50,35 +47,45 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
50
47
  ? { height: `${props.heightInPx}px` }
51
48
  : {};
52
49
 
50
+ const hasNoData: boolean =
51
+ !props.data ||
52
+ props.data.length === 0 ||
53
+ props.data.every((series: SeriesPoint) => {
54
+ return series.data.length === 0;
55
+ });
56
+
53
57
  return (
54
- <AreaChart
55
- className={className}
56
- style={style}
57
- data={records}
58
- tickGap={1}
59
- index={"Time"}
60
- categories={categories}
61
- colors={[
62
- "blue",
63
- "emerald",
64
- "violet",
65
- "amber",
66
- "cyan",
67
- "pink",
68
- "lime",
69
- "fuchsia",
70
- "indigo",
71
- "rose",
72
- ]}
73
- valueFormatter={props.yAxis.options.formatter || undefined}
74
- showTooltip={true}
75
- connectNulls={true}
76
- curve={props.curve || ChartCurve.MONOTONE}
77
- syncid={props.sync ? props.syncid : undefined}
78
- yAxisWidth={60}
79
- onValueChange={() => {}}
80
- referenceLines={props.referenceLines}
81
- />
58
+ <div className="relative">
59
+ <AreaChart
60
+ className={className}
61
+ style={style}
62
+ data={records}
63
+ tickGap={1}
64
+ index={"Time"}
65
+ categories={categories}
66
+ colors={[
67
+ "blue",
68
+ "emerald",
69
+ "violet",
70
+ "amber",
71
+ "cyan",
72
+ "pink",
73
+ "lime",
74
+ "fuchsia",
75
+ "indigo",
76
+ "rose",
77
+ ]}
78
+ valueFormatter={props.yAxis.options.formatter || undefined}
79
+ showTooltip={true}
80
+ connectNulls={true}
81
+ curve={props.curve || ChartCurve.MONOTONE}
82
+ syncid={props.sync ? props.syncid : undefined}
83
+ yAxisWidth={60}
84
+ onValueChange={() => {}}
85
+ referenceLines={props.referenceLines}
86
+ />
87
+ {hasNoData && <NoDataMessage />}
88
+ </div>
82
89
  );
83
90
  };
84
91
 
@@ -6,6 +6,7 @@ import YAxis from "../Types/YAxis/YAxis";
6
6
  import ChartDataPoint from "../ChartLibrary/Types/ChartDataPoint";
7
7
  import DataPointUtil from "../Utils/DataPoint";
8
8
  import ChartReferenceLineProps from "../Types/ReferenceLineProps";
9
+ import NoDataMessage from "../ChartGroup/NoDataMessage";
9
10
 
10
11
  export interface ComponentProps {
11
12
  data: Array<SeriesPoint>;
@@ -30,12 +31,8 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
30
31
  });
31
32
 
32
33
  useEffect(() => {
33
- if (!props.data || props.data.length === 0) {
34
- setRecords([]);
35
- }
36
-
37
34
  const records: Array<ChartDataPoint> = DataPointUtil.getChartDataPoints({
38
- seriesPoints: props.data,
35
+ seriesPoints: props.data || [],
39
36
  xAxis: props.xAxis,
40
37
  yAxis: props.yAxis,
41
38
  });
@@ -48,32 +45,42 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
48
45
  ? { height: `${props.heightInPx}px` }
49
46
  : {};
50
47
 
48
+ const hasNoData: boolean =
49
+ !props.data ||
50
+ props.data.length === 0 ||
51
+ props.data.every((series: SeriesPoint) => {
52
+ return series.data.length === 0;
53
+ });
54
+
51
55
  return (
52
- <BarChart
53
- className={className}
54
- style={style}
55
- data={records}
56
- tickGap={1}
57
- index={"Time"}
58
- categories={categories}
59
- colors={[
60
- "indigo",
61
- "rose",
62
- "emerald",
63
- "amber",
64
- "cyan",
65
- "gray",
66
- "pink",
67
- "lime",
68
- "fuchsia",
69
- ]}
70
- valueFormatter={props.yAxis.options.formatter || undefined}
71
- showTooltip={true}
72
- yAxisWidth={60}
73
- syncid={props.sync ? props.syncid : undefined}
74
- onValueChange={() => {}}
75
- referenceLines={props.referenceLines}
76
- />
56
+ <div className="relative">
57
+ <BarChart
58
+ className={className}
59
+ style={style}
60
+ data={records}
61
+ tickGap={1}
62
+ index={"Time"}
63
+ categories={categories}
64
+ colors={[
65
+ "indigo",
66
+ "rose",
67
+ "emerald",
68
+ "amber",
69
+ "cyan",
70
+ "gray",
71
+ "pink",
72
+ "lime",
73
+ "fuchsia",
74
+ ]}
75
+ valueFormatter={props.yAxis.options.formatter || undefined}
76
+ showTooltip={true}
77
+ yAxisWidth={60}
78
+ syncid={props.sync ? props.syncid : undefined}
79
+ onValueChange={() => {}}
80
+ referenceLines={props.referenceLines}
81
+ />
82
+ {hasNoData && <NoDataMessage />}
83
+ </div>
77
84
  );
78
85
  };
79
86
 
@@ -1,4 +1,5 @@
1
1
  import Text from "../../../../Types/Text";
2
+ import Dictionary from "../../../../Types/Dictionary";
2
3
  import LineChart, { ComponentProps as LineChartProps } from "../Line/LineChart";
3
4
  import BarChartElement, {
4
5
  ComponentProps as BarChartProps,
@@ -6,7 +7,10 @@ import BarChartElement, {
6
7
  import AreaChartElement, {
7
8
  ComponentProps as AreaChartProps,
8
9
  } from "../Area/AreaChart";
9
- import React, { FunctionComponent, ReactElement } from "react";
10
+ import Icon, { SizeProp } from "../../Icon/Icon";
11
+ import IconProp from "../../../../Types/Icon/IconProp";
12
+ import Modal, { ModalWidth } from "../../Modal/Modal";
13
+ import React, { FunctionComponent, ReactElement, useState } from "react";
10
14
 
11
15
  export enum ChartType {
12
16
  LINE = "line",
@@ -14,12 +18,21 @@ export enum ChartType {
14
18
  AREA = "area",
15
19
  }
16
20
 
21
+ export interface ChartMetricInfo {
22
+ metricName: string;
23
+ aggregationType: string;
24
+ attributes?: Dictionary<string> | undefined;
25
+ groupByAttribute?: string | undefined;
26
+ unit?: string | undefined;
27
+ }
28
+
17
29
  export interface Chart {
18
30
  id: string;
19
31
  title: string;
20
32
  description?: string | undefined;
21
33
  type: ChartType;
22
34
  props: LineChartProps | BarChartProps | AreaChartProps;
35
+ metricInfo?: ChartMetricInfo | undefined;
23
36
  }
24
37
 
25
38
  export interface ComponentProps {
@@ -33,6 +46,8 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
33
46
  props: ComponentProps,
34
47
  ): ReactElement => {
35
48
  const syncId: string = Text.generateRandomText(10);
49
+ const [metricInfoModalChart, setMetricInfoModalChart] =
50
+ useState<ChartMetricInfo | null>(null);
36
51
 
37
52
  const isLastChart: (index: number) => boolean = (index: number): boolean => {
38
53
  return index === props.charts.length - 1;
@@ -77,33 +92,157 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
77
92
  }
78
93
  };
79
94
 
95
+ type GetInfoIconFunction = (chart: Chart) => ReactElement;
96
+
97
+ const getInfoIcon: GetInfoIconFunction = (chart: Chart): ReactElement => {
98
+ if (!chart.metricInfo) {
99
+ return <></>;
100
+ }
101
+
102
+ return (
103
+ <button
104
+ type="button"
105
+ className="ml-1.5 inline-flex items-center justify-center rounded-full w-5 h-5 text-gray-300 hover:text-gray-500 hover:bg-gray-100 transition-all duration-150"
106
+ title="View metric details"
107
+ onClick={() => {
108
+ setMetricInfoModalChart(chart.metricInfo || null);
109
+ }}
110
+ >
111
+ <Icon
112
+ icon={IconProp.InformationCircle}
113
+ size={SizeProp.Smaller}
114
+ className="h-3.5 w-3.5"
115
+ />
116
+ </button>
117
+ );
118
+ };
119
+
120
+ const renderMetricInfoModal: () => ReactElement = (): ReactElement => {
121
+ if (!metricInfoModalChart) {
122
+ return <></>;
123
+ }
124
+
125
+ const attributes: Dictionary<string> =
126
+ metricInfoModalChart.attributes || {};
127
+ const attributeKeys: Array<string> = Object.keys(attributes);
128
+
129
+ return (
130
+ <Modal
131
+ title="Metric Details"
132
+ onClose={() => {
133
+ setMetricInfoModalChart(null);
134
+ }}
135
+ onSubmit={() => {
136
+ setMetricInfoModalChart(null);
137
+ }}
138
+ submitButtonText="Close"
139
+ modalWidth={ModalWidth.Normal}
140
+ >
141
+ <div className="space-y-4">
142
+ <div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
143
+ <table className="w-full text-sm">
144
+ <tbody>
145
+ <tr className="border-b border-gray-200">
146
+ <td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
147
+ Metric Name
148
+ </td>
149
+ <td className="py-2.5 text-gray-900 font-mono text-xs">
150
+ {metricInfoModalChart.metricName}
151
+ </td>
152
+ </tr>
153
+ <tr className="border-b border-gray-200">
154
+ <td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
155
+ Aggregation
156
+ </td>
157
+ <td className="py-2.5 text-gray-900">
158
+ {metricInfoModalChart.aggregationType}
159
+ </td>
160
+ </tr>
161
+ {metricInfoModalChart.unit && (
162
+ <tr className="border-b border-gray-200">
163
+ <td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
164
+ Unit
165
+ </td>
166
+ <td className="py-2.5 text-gray-900">
167
+ {metricInfoModalChart.unit}
168
+ </td>
169
+ </tr>
170
+ )}
171
+ {metricInfoModalChart.groupByAttribute && (
172
+ <tr className="border-b border-gray-200">
173
+ <td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap">
174
+ Grouped By
175
+ </td>
176
+ <td className="py-2.5 text-gray-900 font-mono text-xs">
177
+ {metricInfoModalChart.groupByAttribute}
178
+ </td>
179
+ </tr>
180
+ )}
181
+ {attributeKeys.length > 0 && (
182
+ <tr>
183
+ <td className="py-2.5 pr-4 font-medium text-gray-500 whitespace-nowrap align-top">
184
+ Attributes
185
+ </td>
186
+ <td className="py-2.5">
187
+ <div className="space-y-1.5">
188
+ {attributeKeys.map((key: string) => {
189
+ return (
190
+ <div key={key} className="flex items-center gap-2">
191
+ <span className="inline-flex items-center rounded bg-gray-200 px-2 py-0.5 text-xs font-mono text-gray-700">
192
+ {key}
193
+ </span>
194
+ <span className="text-gray-400">=</span>
195
+ <span className="text-xs text-gray-900 font-mono">
196
+ {attributes[key]}
197
+ </span>
198
+ </div>
199
+ );
200
+ })}
201
+ </div>
202
+ </td>
203
+ </tr>
204
+ )}
205
+ </tbody>
206
+ </table>
207
+ </div>
208
+ </div>
209
+ </Modal>
210
+ );
211
+ };
212
+
80
213
  // When hideCard is true, render charts in a clean vertical stack with dividers
81
214
  if (props.hideCard) {
82
215
  return (
83
- <div className="space-y-0">
84
- {props.charts.map((chart: Chart, index: number) => {
85
- return (
86
- <div
87
- key={index}
88
- className={`${!isLastChart(index) ? "border-b border-gray-100" : ""} ${props.chartCssClass || ""}`}
89
- >
90
- <div className="px-1 pt-5 pb-4">
91
- <div className="mb-1">
92
- <h3 className="text-sm font-semibold text-gray-700 tracking-tight">
93
- {chart.title}
94
- </h3>
95
- {chart.description && (
96
- <p className="mt-0.5 text-xs text-gray-400 hidden md:block">
97
- {chart.description}
98
- </p>
99
- )}
216
+ <>
217
+ {renderMetricInfoModal()}
218
+ <div className="space-y-0">
219
+ {props.charts.map((chart: Chart, index: number) => {
220
+ return (
221
+ <div
222
+ key={index}
223
+ className={`${!isLastChart(index) ? "border-b border-gray-100" : ""} ${props.chartCssClass || ""}`}
224
+ >
225
+ <div className="px-1 pt-5 pb-4">
226
+ <div className="mb-1">
227
+ <div className="flex items-center">
228
+ <h3 className="text-sm font-semibold text-gray-700 tracking-tight">
229
+ {chart.title}
230
+ </h3>
231
+ {getInfoIcon(chart)}
232
+ </div>
233
+ {chart.description && (
234
+ <p className="mt-0.5 text-xs text-gray-400 hidden md:block">
235
+ {chart.description}
236
+ </p>
237
+ )}
238
+ </div>
239
+ {getChartContent(chart, index)}
100
240
  </div>
101
- {getChartContent(chart, index)}
102
241
  </div>
103
- </div>
104
- );
105
- })}
106
- </div>
242
+ );
243
+ })}
244
+ </div>
245
+ </>
107
246
  );
108
247
  }
109
248
 
@@ -112,35 +251,41 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
112
251
  props.charts.length > 1 ? "lg:grid-cols-2" : "lg:grid-cols-1";
113
252
 
114
253
  return (
115
- <div
116
- className={`grid grid-cols-1 ${gridCols} gap-4 space-y-4 lg:space-y-0`}
117
- >
118
- {props.charts.map((chart: Chart, index: number) => {
119
- return (
120
- <div
121
- key={index}
122
- className={`p-5 rounded-lg border border-gray-200 bg-white shadow-sm ${props.chartCssClass || ""}`}
123
- >
124
- <h2
125
- data-testid="card-details-heading"
126
- id="card-details-heading"
127
- className="text-base font-semibold leading-6 text-gray-900"
254
+ <>
255
+ {renderMetricInfoModal()}
256
+ <div
257
+ className={`grid grid-cols-1 ${gridCols} gap-4 space-y-4 lg:space-y-0`}
258
+ >
259
+ {props.charts.map((chart: Chart, index: number) => {
260
+ return (
261
+ <div
262
+ key={index}
263
+ className={`p-5 rounded-lg border border-gray-200 bg-white shadow-sm ${props.chartCssClass || ""}`}
128
264
  >
129
- {chart.title}
130
- </h2>
131
- {chart.description && (
132
- <p
133
- data-testid="card-description"
134
- className="mt-0.5 text-sm text-gray-500 w-full hidden md:block"
135
- >
136
- {chart.description}
137
- </p>
138
- )}
139
- {getChartContent(chart, index)}
140
- </div>
141
- );
142
- })}
143
- </div>
265
+ <div className="flex items-center">
266
+ <h2
267
+ data-testid="card-details-heading"
268
+ id="card-details-heading"
269
+ className="text-base font-semibold leading-6 text-gray-900"
270
+ >
271
+ {chart.title}
272
+ </h2>
273
+ {getInfoIcon(chart)}
274
+ </div>
275
+ {chart.description && (
276
+ <p
277
+ data-testid="card-description"
278
+ className="mt-0.5 text-sm text-gray-500 w-full hidden md:block"
279
+ >
280
+ {chart.description}
281
+ </p>
282
+ )}
283
+ {getChartContent(chart, index)}
284
+ </div>
285
+ );
286
+ })}
287
+ </div>
288
+ </>
144
289
  );
145
290
  };
146
291
 
@@ -0,0 +1,13 @@
1
+ import React, { FunctionComponent, ReactElement } from "react";
2
+
3
+ const NoDataMessage: FunctionComponent = (): ReactElement => {
4
+ return (
5
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
6
+ <span className="text-sm text-gray-400 bg-white/90 border border-gray-100 rounded-full px-4 py-1.5 shadow-sm">
7
+ No data available
8
+ </span>
9
+ </div>
10
+ );
11
+ };
12
+
13
+ export default NoDataMessage;