@oneuptime/common 10.0.37 → 10.0.38
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.
- package/Server/API/TelemetryAPI.ts +10 -0
- package/Server/Services/LogAggregationService.ts +24 -1
- package/Server/Utils/Telemetry/Telemetry.ts +38 -19
- package/UI/Components/Charts/Area/AreaChart.tsx +81 -0
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +106 -63
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +986 -0
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/Utils/ChartColors.ts +18 -1
- package/UI/Components/Charts/Utils/XAxis.ts +26 -21
- package/UI/Components/ConditionsTable/ConditionsTable.tsx +86 -67
- package/UI/Components/Dictionary/DictionaryOfStingsViewer.tsx +48 -28
- package/UI/Components/Filters/FiltersForm.tsx +19 -13
- package/UI/Components/InfoCard/InfoCard.tsx +3 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +9 -4
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +29 -2
- package/UI/Components/LogsViewer/types.ts +1 -0
- package/build/dist/Server/API/TelemetryAPI.js +8 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +12 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +29 -15
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +39 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +28 -9
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +376 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js +15 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/ChartColors.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +25 -21
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js +51 -30
- package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js +23 -11
- package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js.map +1 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js +10 -6
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +5 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +17 -2
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -158,6 +158,10 @@ router.post(
|
|
|
158
158
|
? (body["spanIds"] as Array<string>)
|
|
159
159
|
: undefined;
|
|
160
160
|
|
|
161
|
+
const attributes: Record<string, string> | undefined = body["attributes"]
|
|
162
|
+
? (body["attributes"] as Record<string, string>)
|
|
163
|
+
: undefined;
|
|
164
|
+
|
|
161
165
|
const request: HistogramRequest = {
|
|
162
166
|
projectId: databaseProps.tenantId,
|
|
163
167
|
startTime,
|
|
@@ -168,6 +172,7 @@ router.post(
|
|
|
168
172
|
bodySearchText,
|
|
169
173
|
traceIds,
|
|
170
174
|
spanIds,
|
|
175
|
+
attributes,
|
|
171
176
|
};
|
|
172
177
|
|
|
173
178
|
const buckets: Array<HistogramBucket> =
|
|
@@ -242,6 +247,10 @@ router.post(
|
|
|
242
247
|
? (body["spanIds"] as Array<string>)
|
|
243
248
|
: undefined;
|
|
244
249
|
|
|
250
|
+
const attributes: Record<string, string> | undefined = body["attributes"]
|
|
251
|
+
? (body["attributes"] as Record<string, string>)
|
|
252
|
+
: undefined;
|
|
253
|
+
|
|
245
254
|
const facets: Record<string, Array<FacetValue>> = {};
|
|
246
255
|
|
|
247
256
|
for (const facetKey of facetKeys) {
|
|
@@ -256,6 +265,7 @@ router.post(
|
|
|
256
265
|
bodySearchText,
|
|
257
266
|
traceIds,
|
|
258
267
|
spanIds,
|
|
268
|
+
attributes,
|
|
259
269
|
};
|
|
260
270
|
|
|
261
271
|
facets[facetKey] = await LogAggregationService.getFacetValues(request);
|
|
@@ -25,6 +25,7 @@ export interface HistogramRequest {
|
|
|
25
25
|
bodySearchText?: string | undefined;
|
|
26
26
|
traceIds?: Array<string> | undefined;
|
|
27
27
|
spanIds?: Array<string> | undefined;
|
|
28
|
+
attributes?: Record<string, string> | undefined;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export interface FacetValue {
|
|
@@ -43,6 +44,7 @@ export interface FacetRequest {
|
|
|
43
44
|
bodySearchText?: string | undefined;
|
|
44
45
|
traceIds?: Array<string> | undefined;
|
|
45
46
|
spanIds?: Array<string> | undefined;
|
|
47
|
+
attributes?: Record<string, string> | undefined;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export type AnalyticsChartType = "timeseries" | "toplist" | "table";
|
|
@@ -589,7 +591,12 @@ export class LogAggregationService {
|
|
|
589
591
|
statement: Statement,
|
|
590
592
|
request: Pick<
|
|
591
593
|
HistogramRequest,
|
|
592
|
-
|
|
594
|
+
| "serviceIds"
|
|
595
|
+
| "severityTexts"
|
|
596
|
+
| "bodySearchText"
|
|
597
|
+
| "traceIds"
|
|
598
|
+
| "spanIds"
|
|
599
|
+
| "attributes"
|
|
593
600
|
>,
|
|
594
601
|
): void {
|
|
595
602
|
if (request.serviceIds && request.serviceIds.length > 0) {
|
|
@@ -640,6 +647,22 @@ export class LogAggregationService {
|
|
|
640
647
|
}}`,
|
|
641
648
|
);
|
|
642
649
|
}
|
|
650
|
+
|
|
651
|
+
if (request.attributes && Object.keys(request.attributes).length > 0) {
|
|
652
|
+
for (const [attrKey, attrValue] of Object.entries(request.attributes)) {
|
|
653
|
+
LogAggregationService.validateFacetKey(attrKey);
|
|
654
|
+
|
|
655
|
+
statement.append(
|
|
656
|
+
SQL` AND attributes[${{
|
|
657
|
+
type: TableColumnType.Text,
|
|
658
|
+
value: attrKey,
|
|
659
|
+
}}] = ${{
|
|
660
|
+
type: TableColumnType.Text,
|
|
661
|
+
value: attrValue,
|
|
662
|
+
}}`,
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
643
666
|
}
|
|
644
667
|
|
|
645
668
|
@CaptureSpan()
|
|
@@ -211,33 +211,50 @@ export default class TelemetryUtil {
|
|
|
211
211
|
const jsonValue: JSONObject = value as JSONObject;
|
|
212
212
|
|
|
213
213
|
if (jsonValue && typeof jsonValue === "object") {
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
// Handle both camelCase (JSON encoding) and snake_case (protobuf via protobufjs)
|
|
215
|
+
if (
|
|
216
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "stringValue") ||
|
|
217
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "string_value")
|
|
218
|
+
) {
|
|
219
|
+
const stringValue: JSONValue =
|
|
220
|
+
jsonValue["stringValue"] ?? jsonValue["string_value"];
|
|
216
221
|
finalObj =
|
|
217
222
|
stringValue !== undefined && stringValue !== null
|
|
218
223
|
? (stringValue as string)
|
|
219
224
|
: "";
|
|
220
|
-
} else if (
|
|
221
|
-
|
|
225
|
+
} else if (
|
|
226
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "intValue") ||
|
|
227
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "int_value")
|
|
228
|
+
) {
|
|
229
|
+
const intValue: JSONValue =
|
|
230
|
+
jsonValue["intValue"] ?? jsonValue["int_value"];
|
|
222
231
|
if (intValue !== undefined && intValue !== null) {
|
|
223
232
|
finalObj = intValue as number;
|
|
224
233
|
}
|
|
225
234
|
} else if (
|
|
226
|
-
Object.prototype.hasOwnProperty.call(jsonValue, "doubleValue")
|
|
235
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "doubleValue") ||
|
|
236
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "double_value")
|
|
227
237
|
) {
|
|
228
|
-
const doubleValue: JSONValue =
|
|
238
|
+
const doubleValue: JSONValue =
|
|
239
|
+
jsonValue["doubleValue"] ?? jsonValue["double_value"];
|
|
229
240
|
if (doubleValue !== undefined && doubleValue !== null) {
|
|
230
241
|
finalObj = doubleValue as number;
|
|
231
242
|
}
|
|
232
|
-
} else if (Object.prototype.hasOwnProperty.call(jsonValue, "boolValue")) {
|
|
233
|
-
finalObj = jsonValue["boolValue"] as boolean;
|
|
234
243
|
} else if (
|
|
235
|
-
jsonValue
|
|
236
|
-
(jsonValue
|
|
244
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "boolValue") ||
|
|
245
|
+
Object.prototype.hasOwnProperty.call(jsonValue, "bool_value")
|
|
246
|
+
) {
|
|
247
|
+
finalObj = (jsonValue["boolValue"] ??
|
|
248
|
+
jsonValue["bool_value"]) as boolean;
|
|
249
|
+
} else if (
|
|
250
|
+
(jsonValue["arrayValue"] &&
|
|
251
|
+
(jsonValue["arrayValue"] as JSONObject)["values"]) ||
|
|
252
|
+
(jsonValue["array_value"] &&
|
|
253
|
+
(jsonValue["array_value"] as JSONObject)["values"])
|
|
237
254
|
) {
|
|
238
|
-
const
|
|
239
|
-
"
|
|
240
|
-
] as JSONArray;
|
|
255
|
+
const arrayVal: JSONObject = (jsonValue["arrayValue"] ||
|
|
256
|
+
jsonValue["array_value"]) as JSONObject;
|
|
257
|
+
const values: JSONArray = arrayVal["values"] as JSONArray;
|
|
241
258
|
finalObj = values.map((v: JSONObject) => {
|
|
242
259
|
return this.getAttributeValues(
|
|
243
260
|
prefixKeysWithString,
|
|
@@ -290,17 +307,19 @@ export default class TelemetryUtil {
|
|
|
290
307
|
|
|
291
308
|
finalObj = flattenedFields;
|
|
292
309
|
} else if (
|
|
293
|
-
jsonValue["kvlistValue"] &&
|
|
294
|
-
|
|
310
|
+
(jsonValue["kvlistValue"] &&
|
|
311
|
+
(jsonValue["kvlistValue"] as JSONObject)["values"]) ||
|
|
312
|
+
(jsonValue["kvlist_value"] &&
|
|
313
|
+
(jsonValue["kvlist_value"] as JSONObject)["values"])
|
|
295
314
|
) {
|
|
296
|
-
const
|
|
297
|
-
"
|
|
298
|
-
] as JSONArray;
|
|
315
|
+
const kvlistVal: JSONObject = (jsonValue["kvlistValue"] ||
|
|
316
|
+
jsonValue["kvlist_value"]) as JSONObject;
|
|
317
|
+
const values: JSONArray = kvlistVal["values"] as JSONArray;
|
|
299
318
|
finalObj = this.getAttributes({
|
|
300
319
|
prefixKeysWithString,
|
|
301
320
|
items: values,
|
|
302
321
|
});
|
|
303
|
-
} else if ("nullValue" in jsonValue) {
|
|
322
|
+
} else if ("nullValue" in jsonValue || "null_value" in jsonValue) {
|
|
304
323
|
finalObj = null;
|
|
305
324
|
}
|
|
306
325
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AreaChart } from "../ChartLibrary/AreaChart/AreaChart";
|
|
2
|
+
import React, { FunctionComponent, ReactElement, useEffect } from "react";
|
|
3
|
+
import SeriesPoint from "../Types/SeriesPoints";
|
|
4
|
+
import { XAxis } from "../Types/XAxis/XAxis";
|
|
5
|
+
import YAxis from "../Types/YAxis/YAxis";
|
|
6
|
+
import ChartCurve from "../Types/ChartCurve";
|
|
7
|
+
import ChartDataPoint from "../ChartLibrary/Types/ChartDataPoint";
|
|
8
|
+
import DataPointUtil from "../Utils/DataPoint";
|
|
9
|
+
|
|
10
|
+
export interface ComponentProps {
|
|
11
|
+
data: Array<SeriesPoint>;
|
|
12
|
+
xAxis: XAxis;
|
|
13
|
+
yAxis: YAxis;
|
|
14
|
+
curve: ChartCurve;
|
|
15
|
+
sync: boolean;
|
|
16
|
+
heightInPx?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AreaInternalProps extends ComponentProps {
|
|
20
|
+
syncid: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
24
|
+
props: AreaInternalProps,
|
|
25
|
+
): ReactElement => {
|
|
26
|
+
const [records, setRecords] = React.useState<Array<ChartDataPoint>>([]);
|
|
27
|
+
|
|
28
|
+
const categories: Array<string> = props.data.map((item: SeriesPoint) => {
|
|
29
|
+
return item.seriesName;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!props.data || props.data.length === 0) {
|
|
34
|
+
setRecords([]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const records: Array<ChartDataPoint> = DataPointUtil.getChartDataPoints({
|
|
38
|
+
seriesPoints: props.data,
|
|
39
|
+
xAxis: props.xAxis,
|
|
40
|
+
yAxis: props.yAxis,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
setRecords(records);
|
|
44
|
+
}, [props.data]);
|
|
45
|
+
|
|
46
|
+
const className: string = props.heightInPx ? `` : "h-80";
|
|
47
|
+
const style: React.CSSProperties = props.heightInPx
|
|
48
|
+
? { height: `${props.heightInPx}px` }
|
|
49
|
+
: {};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<AreaChart
|
|
53
|
+
className={className}
|
|
54
|
+
style={style}
|
|
55
|
+
data={records}
|
|
56
|
+
tickGap={1}
|
|
57
|
+
index={"Time"}
|
|
58
|
+
categories={categories}
|
|
59
|
+
colors={[
|
|
60
|
+
"blue",
|
|
61
|
+
"emerald",
|
|
62
|
+
"violet",
|
|
63
|
+
"amber",
|
|
64
|
+
"cyan",
|
|
65
|
+
"pink",
|
|
66
|
+
"lime",
|
|
67
|
+
"fuchsia",
|
|
68
|
+
"indigo",
|
|
69
|
+
"rose",
|
|
70
|
+
]}
|
|
71
|
+
valueFormatter={props.yAxis.options.formatter || undefined}
|
|
72
|
+
showTooltip={true}
|
|
73
|
+
connectNulls={true}
|
|
74
|
+
curve={props.curve || ChartCurve.MONOTONE}
|
|
75
|
+
syncid={props.sync ? props.syncid : undefined}
|
|
76
|
+
yAxisWidth={60}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default AreaChartElement;
|
|
@@ -3,6 +3,9 @@ import LineChart, { ComponentProps as LineChartProps } from "../Line/LineChart";
|
|
|
3
3
|
import BarChartElement, {
|
|
4
4
|
ComponentProps as BarChartProps,
|
|
5
5
|
} from "../Bar/BarChart";
|
|
6
|
+
import AreaChartElement, {
|
|
7
|
+
ComponentProps as AreaChartProps,
|
|
8
|
+
} from "../Area/AreaChart";
|
|
6
9
|
import React, { FunctionComponent, ReactElement } from "react";
|
|
7
10
|
|
|
8
11
|
export enum ChartType {
|
|
@@ -16,7 +19,7 @@ export interface Chart {
|
|
|
16
19
|
title: string;
|
|
17
20
|
description?: string | undefined;
|
|
18
21
|
type: ChartType;
|
|
19
|
-
props: LineChartProps | BarChartProps;
|
|
22
|
+
props: LineChartProps | BarChartProps | AreaChartProps;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export interface ComponentProps {
|
|
@@ -31,71 +34,111 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
31
34
|
): ReactElement => {
|
|
32
35
|
const syncId: string = Text.generateRandomText(10);
|
|
33
36
|
|
|
37
|
+
const isLastChart: (index: number) => boolean = (index: number): boolean => {
|
|
38
|
+
return index === props.charts.length - 1;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type GetChartContentFunction = (chart: Chart, index: number) => ReactElement;
|
|
42
|
+
|
|
43
|
+
const getChartContent: GetChartContentFunction = (
|
|
44
|
+
chart: Chart,
|
|
45
|
+
index: number,
|
|
46
|
+
): ReactElement => {
|
|
47
|
+
switch (chart.type) {
|
|
48
|
+
case ChartType.LINE:
|
|
49
|
+
return (
|
|
50
|
+
<LineChart
|
|
51
|
+
key={index}
|
|
52
|
+
{...(chart.props as LineChartProps)}
|
|
53
|
+
syncid={syncId}
|
|
54
|
+
heightInPx={props.heightInPx}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
case ChartType.BAR:
|
|
58
|
+
return (
|
|
59
|
+
<BarChartElement
|
|
60
|
+
key={index}
|
|
61
|
+
{...(chart.props as BarChartProps)}
|
|
62
|
+
syncid={syncId}
|
|
63
|
+
heightInPx={props.heightInPx}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
case ChartType.AREA:
|
|
67
|
+
return (
|
|
68
|
+
<AreaChartElement
|
|
69
|
+
key={index}
|
|
70
|
+
{...(chart.props as AreaChartProps)}
|
|
71
|
+
syncid={syncId}
|
|
72
|
+
heightInPx={props.heightInPx}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
default:
|
|
76
|
+
return <></>;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// When hideCard is true, render charts in a clean vertical stack with dividers
|
|
81
|
+
if (props.hideCard) {
|
|
82
|
+
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
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
{getChartContent(chart, index)}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
})}
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// When showing cards, use the grid layout
|
|
111
|
+
const gridCols: string =
|
|
112
|
+
props.charts.length > 1 ? "lg:grid-cols-2" : "lg:grid-cols-1";
|
|
113
|
+
|
|
34
114
|
return (
|
|
35
|
-
<div
|
|
115
|
+
<div
|
|
116
|
+
className={`grid grid-cols-1 ${gridCols} gap-4 space-y-4 lg:space-y-0`}
|
|
117
|
+
>
|
|
36
118
|
{props.charts.map((chart: Chart, index: number) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
data-testid="card-description"
|
|
54
|
-
className="mt-1 text-sm text-gray-500 w-full hidden md:block"
|
|
55
|
-
>
|
|
56
|
-
{chart.description}
|
|
57
|
-
</p>
|
|
58
|
-
)}
|
|
59
|
-
<LineChart
|
|
60
|
-
key={index}
|
|
61
|
-
{...(chart.props as LineChartProps)}
|
|
62
|
-
syncid={syncId}
|
|
63
|
-
heightInPx={props.heightInPx}
|
|
64
|
-
/>
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
case ChartType.BAR:
|
|
68
|
-
return (
|
|
69
|
-
<div
|
|
70
|
-
key={index}
|
|
71
|
-
className={`p-6 ${props.hideCard ? "" : "rounded-md bg-white shadow"} ${props.chartCssClass || ""}`}
|
|
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"
|
|
128
|
+
>
|
|
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"
|
|
72
135
|
>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
</h2>
|
|
80
|
-
{chart.description && (
|
|
81
|
-
<p
|
|
82
|
-
data-testid="card-description"
|
|
83
|
-
className="mt-1 text-sm text-gray-500 w-full hidden md:block"
|
|
84
|
-
>
|
|
85
|
-
{chart.description}
|
|
86
|
-
</p>
|
|
87
|
-
)}
|
|
88
|
-
<BarChartElement
|
|
89
|
-
key={index}
|
|
90
|
-
{...(chart.props as BarChartProps)}
|
|
91
|
-
syncid={syncId}
|
|
92
|
-
heightInPx={props.heightInPx}
|
|
93
|
-
/>
|
|
94
|
-
</div>
|
|
95
|
-
);
|
|
96
|
-
default:
|
|
97
|
-
return <></>;
|
|
98
|
-
}
|
|
136
|
+
{chart.description}
|
|
137
|
+
</p>
|
|
138
|
+
)}
|
|
139
|
+
{getChartContent(chart, index)}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
99
142
|
})}
|
|
100
143
|
</div>
|
|
101
144
|
);
|