@jfvilas/plugin-kwirth-metrics 0.13.4 → 0.13.5

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/README.md CHANGED
@@ -74,44 +74,63 @@ If everyting is correctly configured and tagged, the user should see a list of c
74
74
 
75
75
 
76
76
  ## Configuration: Entity Pages
77
- 1. Add the KwirthMetrics plugin as a tab in your Entity pages:
78
77
 
79
- Firstly, import the plugin module.
78
+ #### 1. Add the KwirthMetrics plugin as a tab in your Entity pages:
80
79
 
81
- ```typescript
82
- // In packages/app/src/components/catalog/EntityPage.tsx
83
- import { EntityKwirthMetricsContent, isKwirthAvailable } from '@jfvilas/plugin-kwirth-metrics';
84
- ```
80
+ Firstly, import the plugin module.
85
81
 
86
- Then, add a tab to your EntityPage (the 'if' is optional, you can keep the 'KwirthMetrics' tab always visible if you prefer to do it that way).
87
- ```jsx
88
- // Note: Add to any other Pages as well (e.g. defaultEntityPage or webSiteEntityPage, for example)
89
- const serviceEntityPage = (
90
- <EntityLayout>
91
- {/* other tabs... */}
92
- <EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
93
- <EntityKwirthMetricsContent allMetrics={true} enableRestart={false} />
94
- </EntityLayout.Route>
95
- </EntityLayout>
96
- )
97
- ```
98
- You can setup some default *viewing* options on the `EntityKwirthMetricsContent` component, so, when the entity loads the default options will be set. These options are:
82
+ ```typescript
83
+ // In packages/app/src/components/catalog/EntityPage.tsx
84
+ import { EntityKwirthMetricsContent, isKwirthAvailable } from '@jfvilas/plugin-kwirth-metrics';
85
+ ```
86
+
87
+ Then, add a tab to your EntityPage (the 'if' is optional, you can keep the 'KwirthMetrics' tab always visible if you prefer to do it that way).
88
+
89
+ ```jsx
90
+ // Note: Add to any other Pages as well (e.g. defaultEntityPage or webSiteEntityPage, for example)
91
+ const serviceEntityPage = (
92
+ <EntityLayout>
93
+ {/* other tabs... */}
94
+ <EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
95
+ <EntityKwirthMetricsContent allMetrics={true} enableRestart={false} />
96
+ </EntityLayout.Route>
97
+ </EntityLayout>
98
+ )
99
+ ```
100
+
101
+ You can setup some default *viewing* options on the `EntityKwirthMetricsContent` component, so, when the entity loads the default options will be set. These options are:
99
102
  - `depth`
100
103
  - `width`
101
104
  - `interval`
102
105
  - `chart`
103
- (The meaning of these properties are explained at the end of this document)
104
-
105
- If you want to setup a long-running histogram as a default chart for your entities you should setup your entity page like this:
106
- ```jsx
107
- ...
108
- <EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
109
- <EntityKwirthMetricsContent allMetrics={true} enableRestart={false} depth={100} chart={'bar'}/>
110
- </EntityLayout.Route>
111
- ...
112
- ```
106
+ (The meaning of these properties are explained at the end of this document)
107
+
108
+ If you want to setup a long-running histogram as a default chart for your entities you should setup your entity page like this:
109
+
110
+ ```jsx
111
+ ...
112
+ <EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
113
+ <EntityKwirthMetricsContent allMetrics={true} enableRestart={false} depth={100} chart={'bar'}/>
114
+ </EntityLayout.Route>
115
+ ...
116
+ ```
117
+
118
+ You can also set some behaviour options for the plugin:
119
+ - `hideVersion` (optional `boolean`) if set to `true`, version information updates will not be shown.
120
+ - `excludeContainers` (optional `string[]`), an array of container names that will be excluded from visualization. For example, if you have pods that include sidecars, you can exclude sidecard log messages using this property. What follows is an example on how to use these properties:
121
+ - `defaultMetrics` (optional `boolean`) if set to `true`, version information updates will not be shown.
122
+
123
+
124
+ ```jsx
125
+ ...
126
+ <EntityLayout.Route if={isKwirthAvailable} path="/kwirthlog" title="KwirthLog">
127
+ <EntityKwirthLogContent hideVersion excludeContainers={['istio-proxy', 'nginx']} defaultMetrics={['kwirth_container_cpu_percentage', 'kwirth_container_memory_percentage']} />
128
+ </EntityLayout.Route>
129
+ ...
130
+ ```
113
131
 
114
- 2. Label your catalog-info according to one of these two startegies:
132
+ #### 2. Label your catalog-info
133
+ Use one of these startegies:
115
134
 
116
135
  - **Strategy 1: one-to-one**. Add `backstage.io/kubernetes-id` annotation to your `catalog-info.yaml` for the entities deployed to Kubernetes you want to work with on Backstage. This is the same annotation that the Kubernetes core plugin uses, so, maybe you already have added it to your components. Exmaple:
117
136
 
@@ -0,0 +1,224 @@
1
+ import React, { useState } from 'react';
2
+ import { Alert, Stack, Typography, Card, CardContent, Tooltip as Tooltip$1, IconButton } from '@mui/material';
3
+ import { ResponsiveContainer, Treemap, Tooltip, PieChart, Legend, Pie, Cell, BarChart, CartesianGrid, XAxis, YAxis, Bar, LabelList, AreaChart, Area, LineChart, Line } from 'recharts';
4
+ import { MenuChart, MenuChartOption } from './MenuChart.esm.js';
5
+ import { MoreVert } from '@material-ui/icons';
6
+
7
+ const METRICSCOLOURS = [
8
+ "#6e5bb8",
9
+ // morado oscuro
10
+ "#4a9076",
11
+ // verde oscuro
12
+ "#b56c52",
13
+ // naranja oscuro
14
+ "#7f6b97",
15
+ // color lavanda oscuro
16
+ "#b0528f",
17
+ // rosa oscuro
18
+ "#b0b052",
19
+ // amarillo oscuro
20
+ "#b05252",
21
+ // rojo oscuro
22
+ "#5285b0",
23
+ // azul oscuro
24
+ "#a38ad6",
25
+ // morado pastel
26
+ "#89c1a0",
27
+ // verde pastel
28
+ "#e4a28a",
29
+ // naranja pastel
30
+ "#b09dbd",
31
+ // lavanda pastel
32
+ "#e2a4c6",
33
+ // rosa pastel
34
+ "#c5c89e",
35
+ // amarillo pastel
36
+ "#e2a4a4",
37
+ // rojo pastel
38
+ "#90b7e2",
39
+ // azul pastel
40
+ "#f8d5e1",
41
+ // rosa claro pastel
42
+ "#b2d7f0",
43
+ // azul muy claro pastel
44
+ "#f7e1b5",
45
+ // amarillo muy claro pastel
46
+ "#d0f0c0",
47
+ // verde muy claro pastel
48
+ "#f5b0a1",
49
+ // coral pastel
50
+ "#d8a7db",
51
+ // lavanda muy claro pastel
52
+ "#f4c2c2",
53
+ // rosa suave pastel
54
+ "#e6c7b9",
55
+ // marron claro pastel
56
+ "#f0e2b6",
57
+ // crema pastel
58
+ "#a7c7e7",
59
+ // azul palido pastel
60
+ "#f5e6a5",
61
+ // amarillo palido pastel
62
+ "#e3c8f5",
63
+ // lilas pastel
64
+ "#d0c4e8",
65
+ // lila palido pastel
66
+ "#b8d8b8",
67
+ // verde claro pastel
68
+ "#d2ebfa",
69
+ // azul muy claro pastel
70
+ "#f1c1d2"
71
+ // rosa bebe pastel
72
+ ];
73
+ const Chart = (props) => {
74
+ const [anchorMenuChart, setAnchorMenuChart] = useState(null);
75
+ const [chartType, setChartType] = useState(props.viewConfig?.chartType ? props.viewConfig.chartType : props.chartType);
76
+ const [stack, setStack] = useState(props.viewConfig?.stack ? props.viewConfig.stack : props.stack);
77
+ const [tooltip, setTooltip] = useState(props.viewConfig?.tooltip ? props.viewConfig.tooltip : props.tooltip);
78
+ const [labels, setLabels] = useState(props.viewConfig?.labels ? props.viewConfig.labels : props.labels);
79
+ let result;
80
+ let height = 300;
81
+ let dataSummarized;
82
+ const mergeSeries = (names, series) => {
83
+ if (!names || names.length === 0) return [];
84
+ let resultSeries = [];
85
+ for (var i = 0; i < series[0].length; i++) {
86
+ var item = {};
87
+ for (var j = 0; j < series.length; j++) {
88
+ if (series[j][i]) {
89
+ item["timestamp"] = series[0][i].timestamp;
90
+ item[names[j]] = series[j][i].value;
91
+ }
92
+ }
93
+ resultSeries.push(item);
94
+ }
95
+ return resultSeries;
96
+ };
97
+ const CustomizedContent = (props2) => {
98
+ const { root, depth, x, y, width, height: height2, index, name } = props2;
99
+ return /* @__PURE__ */ React.createElement("g", null, /* @__PURE__ */ React.createElement(
100
+ "rect",
101
+ {
102
+ x,
103
+ y,
104
+ width,
105
+ height: height2,
106
+ style: {
107
+ fill: depth < 2 ? METRICSCOLOURS[Math.floor(index / root.children.length * 6)] : "#ffffff00",
108
+ stroke: "#fff",
109
+ strokeWidth: 2 / (depth + 1e-10),
110
+ strokeOpacity: 1 / (depth + 1e-10)
111
+ }
112
+ }
113
+ ), depth === 1 ? /* @__PURE__ */ React.createElement("text", { x: x + width / 2, y: y + height2 / 2 + 7, textAnchor: "middle", fill: "#fff", fontSize: 14, fontFamily: "Roboto, Helvetica, Arial, sans-serif" }, name) : null);
114
+ };
115
+ const menuChartOptionSelected = (opt, data) => {
116
+ setAnchorMenuChart(null);
117
+ switch (opt) {
118
+ case MenuChartOption.Stack:
119
+ setStack(!stack);
120
+ break;
121
+ case MenuChartOption.Remove:
122
+ break;
123
+ case MenuChartOption.Export:
124
+ if (!props.names?.length || !props.series?.length) return;
125
+ const headers = ["timestamp", ...props.names];
126
+ const timestamps = props.series[0].map((point) => point.timestamp);
127
+ const rows = timestamps.map((timestamp, idx) => {
128
+ const values = props.series.map((serie) => serie[idx]?.value ?? "");
129
+ return [timestamp, ...values];
130
+ });
131
+ const separator = ",";
132
+ const csvContent = headers.join(separator) + "\n" + rows.map((r) => r.join(separator)).join("\n");
133
+ const blob = new Blob(["\uFEFF" + csvContent], { type: "text/csv;charset=utf-8;" });
134
+ const link = document.createElement("a");
135
+ link.href = URL.createObjectURL(blob);
136
+ link.download = `${props.metricDefinition.metric}.csv`;
137
+ link.click();
138
+ break;
139
+ case MenuChartOption.Tooltip:
140
+ setTooltip(!tooltip);
141
+ break;
142
+ case MenuChartOption.Labels:
143
+ setLabels(!labels);
144
+ break;
145
+ case MenuChartOption.Default:
146
+ if (props.onSetDefault) {
147
+ props.onSetDefault(props.metricDefinition.metric, {
148
+ displayName: props.metricDefinition.metric,
149
+ chartType,
150
+ stack,
151
+ tooltip,
152
+ labels
153
+ });
154
+ }
155
+ break;
156
+ default:
157
+ setChartType(data);
158
+ break;
159
+ }
160
+ };
161
+ const renderLabel = (data) => {
162
+ var values = props.series.map((s) => s[data.index]);
163
+ var total = values.reduce((acc, value) => acc + value.value, 0);
164
+ return /* @__PURE__ */ React.createElement("text", { x: data.x + data.width / 3.5, y: data.y - 10 }, total.toPrecision(3).replace(/0+$/, "").replace(/\.+$/, ""));
165
+ };
166
+ switch (chartType) {
167
+ case "value" /* ValueChart */:
168
+ result = /* @__PURE__ */ React.createElement("div", { style: { padding: "30px", height: height * 0.8, alignItems: "center", justifyContent: "center", display: "flex" } }, /* @__PURE__ */ React.createElement(Stack, { direction: "row", alignItems: "center", justifyContent: "center", spacing: 2, sx: { flexWrap: "wrap" } }, props.series.map((serie, index) => {
169
+ let value = serie[serie.length - 1].value;
170
+ if (!value) return;
171
+ let valueStr = value.toString();
172
+ if (value) {
173
+ valueStr = value.toFixed(3);
174
+ if (value > 10) valueStr = value.toFixed(2);
175
+ if (value > 100) valueStr = value.toFixed(1);
176
+ if (value > 1e3) valueStr = value.toFixed(0);
177
+ }
178
+ return /* @__PURE__ */ React.createElement(Stack, { key: index, direction: "column" }, /* @__PURE__ */ React.createElement(Typography, { mt: 2, textAlign: "center", width: "100%", fontSize: Math.min(48, 192 / props.series.length), color: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, valueStr), /* @__PURE__ */ React.createElement(Typography, { textAlign: "center", width: "100%", fontSize: 12, color: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, props.names[index]));
179
+ })));
180
+ break;
181
+ case "line" /* LineChart */:
182
+ result = /* @__PURE__ */ React.createElement(LineChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map((_serie, index) => /* @__PURE__ */ React.createElement(Line, { key: index, name: props.names[index], type: "monotone", dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], activeDot: { r: 8 } })));
183
+ break;
184
+ case "area" /* AreaChart */:
185
+ result = /* @__PURE__ */ React.createElement(AreaChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement("defs", null, props.series.map((_serie, index) => {
186
+ return /* @__PURE__ */ React.createElement("linearGradient", { key: index, id: `color${props.series.length === 1 ? props.colour : METRICSCOLOURS[index]}`, x1: "0", y1: "0", x2: "0", y2: "1" }, /* @__PURE__ */ React.createElement("stop", { offset: "7%", stopColor: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], stopOpacity: 0.8 }), /* @__PURE__ */ React.createElement("stop", { offset: "93%", stopColor: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], stopOpacity: 0 }));
187
+ })), /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map((_serie, index) => /* @__PURE__ */ React.createElement(Area, { key: index, name: props.names[index], type: "monotone", ...stack ? { stackId: "1" } : {}, dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], fill: `url(#color${props.series.length === 1 ? props.colour : METRICSCOLOURS[index]})` })));
188
+ break;
189
+ case "bar" /* BarChart */:
190
+ result = /* @__PURE__ */ React.createElement(BarChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map(
191
+ (_serie, index) => /* @__PURE__ */ React.createElement(Bar, { key: index, name: props.names[index], ...stack ? { stackId: "1" } : {}, dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], fill: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, index === props.series.length - 1 && props.series.length > 1 && labels ? /* @__PURE__ */ React.createElement(LabelList, { dataKey: props.names[index], position: "insideTop", content: renderLabel }) : null)
192
+ ));
193
+ break;
194
+ case "pie" /* PieChart */:
195
+ dataSummarized = props.names.map((name, index) => {
196
+ return { name, value: props.series[index].reduce((ac, val) => ac + val.value, 0) };
197
+ });
198
+ result = /* @__PURE__ */ React.createElement(PieChart, null, tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, { layout: "vertical", align: "right", verticalAlign: "middle" }), /* @__PURE__ */ React.createElement(Pie, { key: "asd", data: dataSummarized, dataKey: "value", fill: METRICSCOLOURS[0], innerRadius: 0, outerRadius: 90 }, dataSummarized.map((_entry, index) => /* @__PURE__ */ React.createElement(Cell, { key: `cell-${index}`, fill: METRICSCOLOURS[index % METRICSCOLOURS.length] }))));
199
+ break;
200
+ case "treemap" /* TreemapChart */:
201
+ dataSummarized = props.names.map((name, index) => {
202
+ return { name, value: props.series[index].reduce((ac, val) => ac + val.value, 0) };
203
+ });
204
+ result = /* @__PURE__ */ React.createElement("div", { style: { paddingLeft: "32px", height: height * 0.8, alignItems: "center", justifyContent: "center", display: "flex" } }, /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%" }, /* @__PURE__ */ React.createElement(Treemap, { data: dataSummarized, dataKey: "value", nameKey: "name", aspectRatio: 4 / 3, stroke: "#ffffff", fill: "#6e5bb8", content: React.createElement(CustomizedContent) }, tooltip && /* @__PURE__ */ React.createElement(Tooltip, null))));
205
+ break;
206
+ default:
207
+ result = /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, "Unsupported chart type '", props.chartType, "'");
208
+ break;
209
+ }
210
+ let title = props.metricDefinition.metric.replaceAll("_", " ");
211
+ title = title[0].toLocaleUpperCase() + title.substring(1);
212
+ title = title.replaceAll("cpu", "CPU");
213
+ title = title.replaceAll(" fs ", " FS ");
214
+ title = title.replaceAll(" io ", " IO ");
215
+ title = title.replaceAll("oom", "OOM");
216
+ title = title.replaceAll("nvm", "NVM");
217
+ title = title.replaceAll("rss", "RSS");
218
+ title = title.replaceAll("failcnt", "fail count");
219
+ title = title.replaceAll("mbps", "Mbps");
220
+ return /* @__PURE__ */ React.createElement(Card, { sx: { paddingRight: "32px", width: "100%" } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Stack, { direction: "column", alignItems: "center", width: "100%", sx: { mb: "32px" } }, /* @__PURE__ */ React.createElement(Stack, { direction: "row", alignItems: "center" }, /* @__PURE__ */ React.createElement(Tooltip$1, { key: "tooltip" + props.metricDefinition.metric + JSON.stringify(props.names), title: /* @__PURE__ */ React.createElement(Typography, { style: { fontSize: 12 } }, /* @__PURE__ */ React.createElement("b", null, props.metricDefinition.metric), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), props.metricDefinition.help) }, /* @__PURE__ */ React.createElement(Typography, { align: "center" }, title)), /* @__PURE__ */ React.createElement(IconButton, { onClick: (event) => setAnchorMenuChart(event.currentTarget) }, /* @__PURE__ */ React.createElement(MoreVert, { fontSize: "small" })), anchorMenuChart && /* @__PURE__ */ React.createElement(MenuChart, { onClose: () => setAnchorMenuChart(null), optionSelected: menuChartOptionSelected, anchorMenu: anchorMenuChart, selected: chartType, stacked: stack, tooltip, labels, numSeries: props.numSeries, setDefault: false })), /* @__PURE__ */ React.createElement("div", { style: { width: "100%" } }, /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%", height, key: props.metricDefinition.metric + JSON.stringify(props.names) }, result)))));
221
+ };
222
+
223
+ export { Chart, METRICSCOLOURS };
224
+ //# sourceMappingURL=Chart.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chart.esm.js","sources":["../../src/components/Chart.tsx"],"sourcesContent":["import React, { useState } from 'react'\r\nimport { Alert, Card, CardContent, Stack, Typography } from '@mui/material'\r\nimport { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, LabelList, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, Treemap, XAxis, YAxis } from 'recharts'\r\nimport { Tooltip as MUITooltip, IconButton } from '@mui/material'\r\nimport { MenuChart, MenuChartOption } from './MenuChart'\r\nimport { MoreVert } from '@material-ui/icons'\r\nimport { TreemapNode } from 'recharts/types/util/types'\r\nimport { MetricDefinition } from '@jfvilas/plugin-kwirth-common'\r\n\r\nexport interface IMetricViewConfig {\r\n displayName: string\r\n chartType: ChartType\r\n tooltip: boolean\r\n labels: boolean\r\n stack: boolean\r\n}\r\n\r\nenum ChartType {\r\n LineChart='line',\r\n BarChart='bar',\r\n AreaChart='area',\r\n ValueChart='value',\r\n PieChart='pie',\r\n TreemapChart='treemap',\r\n}\r\n\r\nexport interface ISample {\r\n timestamp:string\r\n value:number\r\n}\r\n\r\nexport const METRICSCOLOURS = [\r\n \"#6e5bb8\", // morado oscuro\r\n \"#4a9076\", // verde oscuro\r\n \"#b56c52\", // naranja oscuro\r\n \"#7f6b97\", // color lavanda oscuro\r\n \"#b0528f\", // rosa oscuro\r\n \"#b0b052\", // amarillo oscuro\r\n \"#b05252\", // rojo oscuro\r\n \"#5285b0\", // azul oscuro\r\n \"#a38ad6\", // morado pastel\r\n \"#89c1a0\", // verde pastel\r\n \"#e4a28a\", // naranja pastel\r\n \"#b09dbd\", // lavanda pastel\r\n \"#e2a4c6\", // rosa pastel\r\n \"#c5c89e\", // amarillo pastel\r\n \"#e2a4a4\", // rojo pastel\r\n \"#90b7e2\", // azul pastel\r\n \"#f8d5e1\", // rosa claro pastel\r\n \"#b2d7f0\", // azul muy claro pastel\r\n \"#f7e1b5\", // amarillo muy claro pastel\r\n \"#d0f0c0\", // verde muy claro pastel\r\n \"#f5b0a1\", // coral pastel\r\n \"#d8a7db\", // lavanda muy claro pastel\r\n \"#f4c2c2\", // rosa suave pastel\r\n \"#e6c7b9\", // marron claro pastel\r\n \"#f0e2b6\", // crema pastel\r\n \"#a7c7e7\", // azul palido pastel\r\n \"#f5e6a5\", // amarillo palido pastel\r\n \"#e3c8f5\", // lilas pastel\r\n \"#d0c4e8\", // lila palido pastel\r\n \"#b8d8b8\", // verde claro pastel\r\n \"#d2ebfa\", // azul muy claro pastel\r\n \"#f1c1d2\" // rosa bebe pastel\r\n]\r\n\r\nexport interface IChartProps {\r\n metricDefinition: MetricDefinition,\r\n names: string[],\r\n series: ISample[][],\r\n colour: string,\r\n chartType: ChartType,\r\n stack: boolean\r\n tooltip: boolean\r\n labels: boolean\r\n numSeries: number\r\n viewConfig : IMetricViewConfig\r\n onSetDefault?: (name:string, mvc: IMetricViewConfig) => void\r\n}\r\n\r\nexport const Chart: React.FC<IChartProps> = (props:IChartProps) => {\r\n const [anchorMenuChart, setAnchorMenuChart] = useState<null | HTMLElement>(null)\r\n const [chartType, setChartType] = useState<ChartType>(props.viewConfig?.chartType? props.viewConfig.chartType : props.chartType)\r\n const [stack, setStack] = useState<boolean>(props.viewConfig?.stack? props.viewConfig.stack : props.stack)\r\n const [tooltip, setTooltip] = useState<boolean>(props.viewConfig?.tooltip? props.viewConfig.tooltip : props.tooltip)\r\n const [labels, setLabels] = useState<boolean>(props.viewConfig?.labels? props.viewConfig.labels : props.labels)\r\n\r\n let result\r\n let height=300\r\n let dataSummarized:any[]\r\n\r\n const mergeSeries = (names:string[], series:ISample[][]) => {\r\n // names is an array of names of series\r\n // series is an array of arrays of samples\r\n // example:\r\n // [default, ingress-nginx]\r\n // [ [ {timestamp:'dad',value:1}, {timestamp:'dad',value:2} ], [ {timestamp:'dad',value:4}, {timestamp:'dad',value:0} ] ]\r\n if (!names || names.length===0) return []\r\n let resultSeries = []\r\n\r\n for (var i=0; i<series[0].length; i++) {\r\n var item: { [key: string]: string|number } = {}\r\n for (var j=0; j<series.length; j++ ) {\r\n if (series[j][i]) {\r\n item['timestamp'] = series[0][i].timestamp\r\n item[names[j]] = series[j][i].value\r\n }\r\n }\r\n resultSeries.push(item)\r\n }\r\n\r\n // result is:\r\n // [ \r\n // {timestamp: '09:16:27', default: 0.21, ingress-nginx: 0.93}\r\n // {timestamp: '09:16:32', default: 0.5, ingress-nginx: 0.04}\r\n // ]\r\n return resultSeries\r\n }\r\n\r\n const CustomizedContent: React.FC<TreemapNode> = (props) => {\r\n const { root, depth, x, y, width, height, index, name } = props\r\n\r\n return (\r\n <g>\r\n <rect x={x} y={y} width={width} height={height}\r\n style={{\r\n fill: depth < 2 ? METRICSCOLOURS[Math.floor((index / root.children.length) * 6)] : '#ffffff00',\r\n stroke: '#fff',\r\n strokeWidth: 2 / (depth + 1e-10),\r\n strokeOpacity: 1 / (depth + 1e-10),\r\n }}\r\n />\r\n {depth === 1 ? (\r\n <text x={x + width / 2} y={y + height / 2 + 7} textAnchor=\"middle\" fill=\"#fff\" fontSize={14} fontFamily='Roboto, Helvetica, Arial, sans-serif'>\r\n {name}\r\n </text>\r\n ) : null}\r\n\r\n </g>\r\n )\r\n }\r\n\r\n const menuChartOptionSelected = (opt:MenuChartOption, data:any) => {\r\n setAnchorMenuChart(null)\r\n switch (opt) {\r\n case MenuChartOption.Stack:\r\n setStack(!stack)\r\n break\r\n case MenuChartOption.Remove:\r\n //+++ pending implementation on parent\r\n break\r\n case MenuChartOption.Export:\r\n if (!props.names?.length || !props.series?.length) return\r\n\r\n const headers = [\"timestamp\", ...props.names]\r\n const timestamps = props.series[0].map(point => point.timestamp)\r\n const rows = timestamps.map((timestamp, idx) => {\r\n const values = props.series.map(serie => serie[idx]?.value ?? \"\")\r\n return [timestamp, ...values];\r\n })\r\n const separator = \",\";\r\n const csvContent = headers.join(separator) + \"\\n\" + rows.map(r => r.join(separator)).join(\"\\n\")\r\n const blob = new Blob([\"\\uFEFF\" + csvContent], { type: \"text/csv;charset=utf-8;\" })\r\n \r\n const link = document.createElement(\"a\")\r\n link.href = URL.createObjectURL(blob)\r\n link.download = `${props.metricDefinition.metric}.csv`\r\n link.click()\r\n break\r\n case MenuChartOption.Tooltip:\r\n setTooltip(!tooltip)\r\n break\r\n case MenuChartOption.Labels:\r\n setLabels(!labels)\r\n break\r\n case MenuChartOption.Default:\r\n if (props.onSetDefault) {\r\n props.onSetDefault(props.metricDefinition.metric, {\r\n displayName: props.metricDefinition.metric,\r\n chartType: chartType,\r\n stack: stack,\r\n tooltip: tooltip,\r\n labels: labels\r\n })\r\n }\r\n break\r\n default:\r\n setChartType(data as ChartType)\r\n break\r\n } \r\n }\r\n\r\n const renderLabel = (data:any) => {\r\n var values:any[] = props.series.map (s => s[data.index])\r\n var total:number = values.reduce((acc,value) => acc+value.value, 0)\r\n return <text x={data.x + data.width/3.5} y={data.y-10}>{total.toPrecision(3).replace(/0+$/, '').replace(/\\.+$/, '')}</text>\r\n }\r\n\r\n switch (chartType) {\r\n case ChartType.ValueChart:\r\n result = (\r\n <div style={{padding:'30px', height:height*0.8, alignItems:'center', justifyContent:'center', display:'flex'}}>\r\n <Stack direction={'row'} alignItems={'center'} justifyContent={'center'} spacing={2} sx={{ flexWrap: 'wrap'}}>\r\n { props.series.map( (serie,index) => {\r\n let value = serie[serie.length-1].value\r\n if (!value) return //+++\r\n let valueStr = value.toString()\r\n if (value) {\r\n valueStr = value.toFixed(3)\r\n if (value>10) valueStr=value.toFixed(2)\r\n if (value>100) valueStr=value.toFixed(1)\r\n if (value>1000) valueStr=value.toFixed(0)\r\n }\r\n return (\r\n <Stack key={index} direction={'column'}>\r\n <Typography mt={2} textAlign={'center'} width={'100%'} fontSize={Math.min(48, 192/props.series.length)} color={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n {valueStr}\r\n </Typography>\r\n <Typography textAlign={'center'} width={'100%'} fontSize={12} color={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n {props.names[index]}\r\n </Typography>\r\n </Stack>\r\n )\r\n })}\r\n </Stack>\r\n </div>\r\n )\r\n break\r\n case ChartType.LineChart:\r\n result = (\r\n <LineChart data={mergeSeries(props.names, props.series)}>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis/>\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) => <Line key={index} name={props.names[index]} type='monotone' dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} activeDot={{ r: 8 }} />) }\r\n </LineChart>\r\n )\r\n break\r\n case ChartType.AreaChart:\r\n result = (\r\n <AreaChart data={mergeSeries(props.names, props.series)}>\r\n <defs>\r\n {\r\n props.series.map( (_serie,index) => {\r\n return (\r\n <linearGradient key={index} id={`color${props.series.length===1?props.colour:METRICSCOLOURS[index]}`} x1='0' y1='0' x2='0' y2='1'>\r\n <stop offset='7%' stopColor={props.series.length===1?props.colour:METRICSCOLOURS[index]} stopOpacity={0.8}/>\r\n <stop offset='93%' stopColor={props.series.length===1?props.colour:METRICSCOLOURS[index]} stopOpacity={0}/>\r\n </linearGradient>\r\n )\r\n })\r\n }\r\n </defs>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis />\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) => \r\n <Area key={index} name={props.names[index]} type='monotone' {...(stack? {stackId:'1'}:{})} dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} fill={`url(#color${props.series.length===1?props.colour:METRICSCOLOURS[index]})`}/> )\r\n }\r\n </AreaChart>\r\n )\r\n break\r\n case ChartType.BarChart:\r\n result = (\r\n <BarChart data={mergeSeries(props.names, props.series)}>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis />\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) =>\r\n <Bar key={index} name={props.names[index]} {...(stack? {stackId:'1'}:{})} dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} fill={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n { index === props.series.length-1 && props.series.length > 1 && labels ? <LabelList dataKey={props.names[index]} position='insideTop' content={renderLabel}/> : null }\r\n </Bar>\r\n )}\r\n </BarChart>\r\n )\r\n break\r\n case ChartType.PieChart:\r\n dataSummarized= props.names.map( (name,index) => {\r\n return { name, value:(props.series[index] as ISample[]).reduce((ac,val) => ac+val.value,0)}\r\n })\r\n result = (\r\n <PieChart>\r\n { tooltip && <Tooltip /> }\r\n <Legend layout='vertical' align='right' verticalAlign='middle'/>\r\n <Pie key={'asd'} data={dataSummarized} dataKey={'value'} fill={METRICSCOLOURS[0]} innerRadius={0} outerRadius={90}>\r\n {dataSummarized.map((_entry, index) => (\r\n <Cell key={`cell-${index}`} fill={METRICSCOLOURS[index % METRICSCOLOURS.length]} />\r\n ))}\r\n </Pie>\r\n </PieChart>\r\n )\r\n break\r\n case ChartType.TreemapChart:\r\n dataSummarized = props.names.map( (name,index) => {\r\n return { name, value:(props.series[index] as ISample[]).reduce((ac,val) => ac+val.value,0)}\r\n })\r\n result = (\r\n <div style={{paddingLeft:'32px', height:height*0.8, alignItems:'center', justifyContent:'center', display:'flex'}}>\r\n <ResponsiveContainer width='100%'>\r\n <Treemap data={dataSummarized} dataKey='value' nameKey='name' aspectRatio={4 / 3} stroke=\"#ffffff\" fill=\"#6e5bb8\" content={React.createElement(CustomizedContent)}>\r\n { tooltip && <Tooltip /> }\r\n </Treemap>\r\n </ResponsiveContainer>\r\n </div>\r\n )\r\n break\r\n default:\r\n result = <Alert severity='error'>Unsupported chart type '{props.chartType}'</Alert>\r\n break\r\n }\r\n\r\n let title = props.metricDefinition.metric.replaceAll('_',' ')\r\n title = title[0].toLocaleUpperCase()+ title.substring(1)\r\n title = title.replaceAll('cpu', 'CPU')\r\n title = title.replaceAll(' fs ', ' FS ')\r\n title = title.replaceAll(' io ', ' IO ')\r\n title = title.replaceAll('oom', 'OOM')\r\n title = title.replaceAll('nvm', 'NVM')\r\n title = title.replaceAll('rss', 'RSS')\r\n title = title.replaceAll('failcnt', 'fail count')\r\n title = title.replaceAll('mbps', 'Mbps')\r\n\r\n // +++ review\r\n return (\r\n <Card sx={{paddingRight:'32px', width:'100%'}}>\r\n <CardContent>\r\n <Stack direction='column' alignItems='center' width='100%' sx={{mb:'32px'}}>\r\n <Stack direction={'row'} alignItems={'center'}>\r\n <MUITooltip key={'tooltip'+props.metricDefinition.metric+JSON.stringify(props.names)} title={<Typography style={{fontSize:12}}><b>{props.metricDefinition.metric}</b><br/><br/>{props.metricDefinition.help}</Typography>}>\r\n <Typography align='center'>{title}</Typography>\r\n </MUITooltip>\r\n <IconButton onClick={(event) => setAnchorMenuChart(event.currentTarget)}><MoreVert fontSize='small'/></IconButton> \r\n { anchorMenuChart && <MenuChart onClose={() => setAnchorMenuChart(null)} optionSelected={menuChartOptionSelected} anchorMenu={anchorMenuChart} selected={chartType} stacked={stack} tooltip={tooltip} labels={labels} numSeries={props.numSeries} setDefault={false}/>}\r\n </Stack>\r\n <div style={{width:'100%'}}>\r\n <ResponsiveContainer width='100%' height={height} key={props.metricDefinition.metric+JSON.stringify(props.names)}>\r\n {result}\r\n </ResponsiveContainer>\r\n </div>\r\n </Stack>\r\n </CardContent>\r\n </Card>\r\n )\r\n}\r\n"],"names":["props","height","MUITooltip"],"mappings":";;;;;;AA+BO,MAAM,cAAA,GAAiB;AAAA,EAC1B,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA,SAAA;AAAA;AAAA,EACA;AAAA;AACJ;AAgBO,MAAM,KAAA,GAA+B,CAAC,KAAA,KAAsB;AAC/D,EAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAI,SAA6B,IAAI,CAAA;AAC/E,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA,CAAoB,KAAA,CAAM,UAAA,EAAY,SAAA,GAAW,KAAA,CAAM,UAAA,CAAW,SAAA,GAAY,KAAA,CAAM,SAAS,CAAA;AAC/H,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAAkB,KAAA,CAAM,UAAA,EAAY,KAAA,GAAO,KAAA,CAAM,UAAA,CAAW,KAAA,GAAQ,KAAA,CAAM,KAAK,CAAA;AACzG,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAkB,KAAA,CAAM,UAAA,EAAY,OAAA,GAAS,KAAA,CAAM,UAAA,CAAW,OAAA,GAAU,KAAA,CAAM,OAAO,CAAA;AACnH,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAkB,KAAA,CAAM,UAAA,EAAY,MAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,MAAA,GAAS,KAAA,CAAM,MAAM,CAAA;AAE9G,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,MAAA,GAAO,GAAA;AACX,EAAA,IAAK,cAAA;AAEL,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,EAAgB,MAAA,KAAuB;AAMxD,IAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,MAAA,KAAS,CAAA,SAAU,EAAC;AACxC,IAAA,IAAI,eAAe,EAAC;AAEpB,IAAA,KAAA,IAAS,IAAE,CAAA,EAAG,CAAA,GAAE,OAAO,CAAC,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AACnC,MAAA,IAAI,OAAyC,EAAC;AAC9C,MAAA,KAAA,IAAS,CAAA,GAAE,CAAA,EAAG,CAAA,GAAE,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAM;AACjC,QAAA,IAAI,MAAA,CAAO,CAAC,CAAA,CAAE,CAAC,CAAA,EAAG;AACd,UAAA,IAAA,CAAK,WAAW,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,CAAE,CAAC,CAAA,CAAE,SAAA;AACjC,UAAA,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,OAAO,CAAC,CAAA,CAAE,CAAC,CAAA,CAAE,KAAA;AAAA,QAClC;AAAA,MACJ;AACA,MAAA,YAAA,CAAa,KAAK,IAAI,CAAA;AAAA,IAC1B;AAOA,IAAA,OAAO,YAAA;AAAA,EACX,CAAA;AAEA,EAAA,MAAM,iBAAA,GAA2C,CAACA,MAAAA,KAAU;AACxD,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,CAAA,EAAG,CAAA,EAAG,OAAO,MAAA,EAAAC,OAAAA,EAAQ,KAAA,EAAO,IAAA,EAAK,GAAID,MAAAA;AAE1D,IAAA,2CACK,GAAA,EAAA,IAAA,kBACG,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QAAK,CAAA;AAAA,QAAM,CAAA;AAAA,QAAM,KAAA;AAAA,QAAc,MAAA,EAAQC,OAAAA;AAAA,QACpC,KAAA,EAAO;AAAA,UACH,IAAA,EAAM,KAAA,GAAQ,CAAA,GAAI,cAAA,CAAe,IAAA,CAAK,KAAA,CAAO,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,MAAA,GAAU,CAAC,CAAC,CAAA,GAAI,WAAA;AAAA,UACnF,MAAA,EAAQ,MAAA;AAAA,UACR,WAAA,EAAa,KAAK,KAAA,GAAQ,KAAA,CAAA;AAAA,UAC1B,aAAA,EAAe,KAAK,KAAA,GAAQ,KAAA;AAAA;AAChC;AAAA,KACJ,EACC,KAAA,KAAU,CAAA,mBACP,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,GAAG,CAAA,GAAI,KAAA,GAAQ,CAAA,EAAG,CAAA,EAAG,CAAA,GAAIA,OAAAA,GAAS,IAAI,CAAA,EAAG,UAAA,EAAW,QAAA,EAAS,IAAA,EAAK,MAAA,EAAO,QAAA,EAAU,IAAI,UAAA,EAAW,sCAAA,EAAA,EACvG,IACD,CAAA,GACA,IAER,CAAA;AAAA,EAER,CAAA;AAEA,EAAA,MAAM,uBAAA,GAA0B,CAAC,GAAA,EAAqB,IAAA,KAAa;AAC/D,IAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,IAAA,QAAQ,GAAA;AAAK,MACT,KAAK,eAAA,CAAgB,KAAA;AACjB,QAAA,QAAA,CAAS,CAAC,KAAK,CAAA;AACf,QAAA;AAAA,MACJ,KAAK,eAAA,CAAgB,MAAA;AAEjB,QAAA;AAAA,MACJ,KAAK,eAAA,CAAgB,MAAA;AACjB,QAAA,IAAI,CAAC,KAAA,CAAM,KAAA,EAAO,UAAU,CAAC,KAAA,CAAM,QAAQ,MAAA,EAAQ;AAEnD,QAAA,MAAM,OAAA,GAAU,CAAC,WAAA,EAAa,GAAG,MAAM,KAAK,CAAA;AAC5C,QAAA,MAAM,UAAA,GAAa,MAAM,MAAA,CAAO,CAAC,EAAE,GAAA,CAAI,CAAA,KAAA,KAAS,MAAM,SAAS,CAAA;AAC/D,QAAA,MAAM,IAAA,GAAO,UAAA,CAAW,GAAA,CAAI,CAAC,WAAW,GAAA,KAAQ;AAC5C,UAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,GAAA,CAAI,WAAS,KAAA,CAAM,GAAG,CAAA,EAAG,KAAA,IAAS,EAAE,CAAA;AAChE,UAAA,OAAO,CAAC,SAAA,EAAW,GAAG,MAAM,CAAA;AAAA,QAChC,CAAC,CAAA;AACD,QAAA,MAAM,SAAA,GAAY,GAAA;AAClB,QAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,IAAA,CAAK,SAAS,IAAI,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAA,CAAK,SAAS,CAAC,CAAA,CAAE,KAAK,IAAI,CAAA;AAC9F,QAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAC,QAAA,GAAW,UAAU,CAAA,EAAG,EAAE,IAAA,EAAM,yBAAA,EAA2B,CAAA;AAElF,QAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AACvC,QAAA,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,eAAA,CAAgB,IAAI,CAAA;AACpC,QAAA,IAAA,CAAK,QAAA,GAAW,CAAA,EAAG,KAAA,CAAM,gBAAA,CAAiB,MAAM,CAAA,IAAA,CAAA;AAChD,QAAA,IAAA,CAAK,KAAA,EAAM;AACX,QAAA;AAAA,MACJ,KAAK,eAAA,CAAgB,OAAA;AACjB,QAAA,UAAA,CAAW,CAAC,OAAO,CAAA;AACnB,QAAA;AAAA,MACJ,KAAK,eAAA,CAAgB,MAAA;AACjB,QAAA,SAAA,CAAU,CAAC,MAAM,CAAA;AACjB,QAAA;AAAA,MACJ,KAAK,eAAA,CAAgB,OAAA;AACjB,QAAA,IAAI,MAAM,YAAA,EAAc;AACpB,UAAA,KAAA,CAAM,YAAA,CAAa,KAAA,CAAM,gBAAA,CAAiB,MAAA,EAAQ;AAAA,YAC9C,WAAA,EAAa,MAAM,gBAAA,CAAiB,MAAA;AAAA,YACpC,SAAA;AAAA,YACA,KAAA;AAAA,YACA,OAAA;AAAA,YACA;AAAA,WACH,CAAA;AAAA,QACL;AACA,QAAA;AAAA,MACJ;AACI,QAAA,YAAA,CAAa,IAAiB,CAAA;AAC9B,QAAA;AAAA;AACR,EACJ,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,IAAA,KAAa;AAC9B,IAAA,IAAI,MAAA,GAAe,MAAM,MAAA,CAAO,GAAA,CAAK,OAAK,CAAA,CAAE,IAAA,CAAK,KAAK,CAAC,CAAA;AACvD,IAAA,IAAI,KAAA,GAAe,OAAO,MAAA,CAAO,CAAC,KAAI,KAAA,KAAU,GAAA,GAAI,KAAA,CAAM,KAAA,EAAO,CAAC,CAAA;AAClE,IAAA,uBAAO,KAAA,CAAA,aAAA,CAAC,UAAK,CAAA,EAAG,IAAA,CAAK,IAAI,IAAA,CAAK,KAAA,GAAM,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,CAAA,GAAE,MAAK,KAAA,CAAM,WAAA,CAAY,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,CAAE,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAE,CAAA;AAAA,EACxH,CAAA;AAEA,EAAA,QAAQ,SAAA;AAAW,IACf,KAAK,OAAA;AACD,MAAA,MAAA,uCACK,KAAA,EAAA,EAAI,KAAA,EAAO,EAAC,OAAA,EAAQ,QAAQ,MAAA,EAAO,MAAA,GAAO,GAAA,EAAK,UAAA,EAAW,UAAU,cAAA,EAAe,QAAA,EAAU,OAAA,EAAQ,MAAA,sBACtG,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAW,KAAA,EAAO,YAAY,QAAA,EAAU,cAAA,EAAgB,QAAA,EAAU,OAAA,EAAS,GAAG,EAAA,EAAI,EAAE,QAAA,EAAU,MAAA,MAC/F,KAAA,CAAM,MAAA,CAAO,GAAA,CAAK,CAAC,OAAM,KAAA,KAAU;AACjC,QAAA,IAAI,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,MAAA,GAAO,CAAC,CAAA,CAAE,KAAA;AAClC,QAAA,IAAI,CAAC,KAAA,EAAO;AACZ,QAAA,IAAI,QAAA,GAAW,MAAM,QAAA,EAAS;AAC9B,QAAA,IAAI,KAAA,EAAO;AACP,UAAA,QAAA,GAAW,KAAA,CAAM,QAAQ,CAAC,CAAA;AAC1B,UAAA,IAAI,KAAA,GAAM,EAAA,EAAI,QAAA,GAAS,KAAA,CAAM,QAAQ,CAAC,CAAA;AACtC,UAAA,IAAI,KAAA,GAAM,GAAA,EAAK,QAAA,GAAS,KAAA,CAAM,QAAQ,CAAC,CAAA;AACvC,UAAA,IAAI,KAAA,GAAM,GAAA,EAAM,QAAA,GAAS,KAAA,CAAM,QAAQ,CAAC,CAAA;AAAA,QAC5C;AACA,QAAA,uBACI,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,GAAA,EAAK,KAAA,EAAO,SAAA,EAAW,QAAA,EAAA,kBAC1B,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,EAAA,EAAI,CAAA,EAAG,SAAA,EAAW,QAAA,EAAU,KAAA,EAAO,QAAQ,QAAA,EAAU,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,GAAA,GAAI,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,EAAG,KAAA,EAAO,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,MAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAA,EACnK,QACL,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,SAAA,EAAW,QAAA,EAAU,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,KAAA,CAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAA,EACzH,KAAA,CAAM,KAAA,CAAM,KAAK,CACtB,CACJ,CAAA;AAAA,MAER,CAAC,CACL,CACA,CAAA;AAEJ,MAAA;AAAA,IACJ,KAAK,MAAA;AACD,MAAA,MAAA,mBACI,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,EAAU,IAAA,EAAM,WAAA,CAAY,MAAM,KAAA,EAAO,KAAA,CAAM,MAAM,CAAA,EAAA,kBAClD,KAAA,CAAA,aAAA,CAAC,aAAA,EAAA,EAAc,eAAA,EAAgB,KAAA,EAAK,mBACpC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,OAAA,EAAQ,WAAA,EAAY,QAAA,EAAU,CAAA,EAAE,CAAA,kBACvC,KAAA,CAAA,aAAA,CAAC,WAAK,CAAA,EACJ,OAAA,oBAAW,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,IAAQ,CAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAM,CAAA,EACL,MAAM,MAAA,CAAO,GAAA,CAAK,CAAC,MAAA,EAAO,KAAA,qBAAU,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,GAAA,EAAK,OAAO,IAAA,EAAM,KAAA,CAAM,KAAA,CAAM,KAAK,CAAA,EAAG,IAAA,EAAK,UAAA,EAAW,OAAA,EAAS,MAAM,KAAA,CAAM,KAAK,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,MAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAG,WAAW,EAAE,CAAA,EAAG,CAAA,EAAE,EAAG,CAAE,CAC9N,CAAA;AAEJ,MAAA;AAAA,IACJ,KAAK,MAAA;AACD,MAAA,MAAA,uCACK,SAAA,EAAA,EAAU,IAAA,EAAM,WAAA,CAAY,KAAA,CAAM,OAAO,KAAA,CAAM,MAAM,CAAA,EAAA,kBAClD,KAAA,CAAA,aAAA,CAAC,cAEO,KAAA,CAAM,MAAA,CAAO,GAAA,CAAK,CAAC,QAAO,KAAA,KAAU;AAChC,QAAA,uBACI,KAAA,CAAA,aAAA,CAAC,gBAAA,EAAA,EAAe,GAAA,EAAK,KAAA,EAAO,EAAA,EAAI,CAAA,KAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,KAAA,CAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAC,CAAA,CAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAI,EAAA,EAAG,GAAA,EAAA,kBAC1H,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,IAAA,EAAK,WAAW,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,KAAA,CAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAG,WAAA,EAAa,GAAA,EAAI,CAAA,kBAC1G,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAK,MAAA,EAAO,KAAA,EAAM,SAAA,EAAW,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,KAAA,CAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAG,WAAA,EAAa,CAAA,EAAE,CAC7G,CAAA;AAAA,MAER,CAAC,CAET,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,aAAA,EAAA,EAAc,iBAAgB,KAAA,EAAK,CAAA,kBACpC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,SAAQ,WAAA,EAAY,QAAA,EAAU,CAAA,EAAE,CAAA,sCACtC,KAAA,EAAA,IAAM,CAAA,EACL,OAAA,oBAAW,KAAA,CAAA,aAAA,CAAC,aAAQ,CAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAM,CAAA,EACL,MAAM,MAAA,CAAO,GAAA,CAAK,CAAC,MAAA,EAAO,0BACxB,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,KAAK,KAAA,EAAO,IAAA,EAAM,MAAM,KAAA,CAAM,KAAK,CAAA,EAAG,IAAA,EAAK,YAAY,GAAI,KAAA,GAAO,EAAC,OAAA,EAAQ,KAAG,GAAE,EAAC,EAAI,OAAA,EAAS,MAAM,KAAA,CAAM,KAAK,CAAA,EAAG,MAAA,EAAQ,MAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,KAAA,CAAM,SAAO,cAAA,CAAe,KAAK,CAAA,EAAG,IAAA,EAAM,aAAa,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,MAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAC,CAAA,CAAA,CAAA,EAAI,CAAG,CAExR,CAAA;AAEJ,MAAA;AAAA,IACJ,KAAK,KAAA;AACD,MAAA,MAAA,mBACI,KAAA,CAAA,aAAA,CAAC,QAAA,EAAA,EAAS,IAAA,EAAM,WAAA,CAAY,MAAM,KAAA,EAAO,KAAA,CAAM,MAAM,CAAA,EAAA,kBACjD,KAAA,CAAA,aAAA,CAAC,aAAA,EAAA,EAAc,eAAA,EAAgB,KAAA,EAAK,mBACpC,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,OAAA,EAAQ,WAAA,EAAY,QAAA,EAAU,CAAA,EAAE,CAAA,kBACvC,KAAA,CAAA,aAAA,CAAC,WAAM,CAAA,EACL,OAAA,oBAAW,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,IAAQ,CAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,IAAM,CAAA,EACL,MAAM,MAAA,CAAO,GAAA;AAAA,QAAK,CAAC,MAAA,EAAO,KAAA,qBACxB,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,KAAA,CAAM,KAAA,CAAM,KAAK,CAAA,EAAI,GAAI,KAAA,GAAO,EAAC,OAAA,EAAQ,GAAA,EAAG,GAAE,EAAC,EAAI,OAAA,EAAS,MAAM,KAAA,CAAM,KAAK,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,CAAA,GAAE,MAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAG,IAAA,EAAM,KAAA,CAAM,MAAA,CAAO,MAAA,KAAS,IAAE,KAAA,CAAM,MAAA,GAAO,cAAA,CAAe,KAAK,CAAA,EAAA,EACpO,KAAA,KAAU,KAAA,CAAM,MAAA,CAAO,SAAO,CAAA,IAAK,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS,CAAA,IAAK,MAAA,mBAAS,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,EAAU,SAAS,KAAA,CAAM,KAAA,CAAM,KAAK,CAAA,EAAG,QAAA,EAAS,WAAA,EAAY,OAAA,EAAS,WAAA,EAAY,IAAK,IACpK;AAAA,OAER,CAAA;AAEJ,MAAA;AAAA,IACJ,KAAK,KAAA;AACD,MAAA,cAAA,GAAgB,KAAA,CAAM,KAAA,CAAM,GAAA,CAAK,CAAC,MAAK,KAAA,KAAU;AAC7C,QAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,CAAM,OAAO,KAAK,CAAA,CAAgB,MAAA,CAAO,CAAC,IAAG,GAAA,KAAQ,EAAA,GAAG,GAAA,CAAI,KAAA,EAAM,CAAC,CAAA,EAAC;AAAA,MAC9F,CAAC,CAAA;AACD,MAAA,MAAA,mBACI,KAAA,CAAA,aAAA,CAAC,gBACK,OAAA,oBAAW,KAAA,CAAA,aAAA,CAAC,aAAQ,CAAA,kBACtB,KAAA,CAAA,aAAA,CAAC,MAAA,EAAA,EAAO,MAAA,EAAO,UAAA,EAAW,KAAA,EAAM,SAAQ,aAAA,EAAc,QAAA,EAAQ,CAAA,kBAC9D,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,EAAI,GAAA,EAAK,OAAO,IAAA,EAAM,cAAA,EAAgB,OAAA,EAAS,OAAA,EAAS,IAAA,EAAM,cAAA,CAAe,CAAC,CAAA,EAAG,WAAA,EAAa,GAAG,WAAA,EAAa,EAAA,EAAA,EAC1G,eAAe,GAAA,CAAI,CAAC,MAAA,EAAQ,KAAA,qBACzB,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,KAAK,CAAA,KAAA,EAAQ,KAAK,CAAA,CAAA,EAAI,IAAA,EAAM,cAAA,CAAe,KAAA,GAAQ,eAAe,MAAM,CAAA,EAAG,CACpF,CACL,CACJ,CAAA;AAEJ,MAAA;AAAA,IACJ,KAAK,SAAA;AACD,MAAA,cAAA,GAAiB,KAAA,CAAM,KAAA,CAAM,GAAA,CAAK,CAAC,MAAK,KAAA,KAAU;AAC9C,QAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,CAAM,OAAO,KAAK,CAAA,CAAgB,MAAA,CAAO,CAAC,IAAG,GAAA,KAAQ,EAAA,GAAG,GAAA,CAAI,KAAA,EAAM,CAAC,CAAA,EAAC;AAAA,MAC9F,CAAC,CAAA;AACD,MAAA,MAAA,mBACI,KAAA,CAAA,aAAA,CAAC,SAAI,KAAA,EAAO,EAAC,aAAY,MAAA,EAAQ,MAAA,EAAO,SAAO,GAAA,EAAK,UAAA,EAAW,UAAU,cAAA,EAAe,QAAA,EAAU,SAAQ,MAAA,EAAM,EAAA,sCAC3G,mBAAA,EAAA,EAAoB,KAAA,EAAM,0BACvB,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,EAAQ,MAAM,cAAA,EAAgB,OAAA,EAAQ,SAAQ,OAAA,EAAQ,MAAA,EAAO,aAAa,CAAA,GAAI,CAAA,EAAG,QAAO,SAAA,EAAU,IAAA,EAAK,WAAU,OAAA,EAAS,KAAA,CAAM,cAAc,iBAAiB,CAAA,EAAA,EAC1J,2BAAW,KAAA,CAAA,aAAA,CAAC,OAAA,EAAA,IAAQ,CAC1B,CACJ,CACJ,CAAA;AAEJ,MAAA;AAAA,IACJ;AACI,MAAA,MAAA,uCAAU,KAAA,EAAA,EAAM,QAAA,EAAS,WAAQ,0BAAA,EAAyB,KAAA,CAAM,WAAU,GAAC,CAAA;AAC3E,MAAA;AAAA;AAGR,EAAA,IAAI,QAAQ,KAAA,CAAM,gBAAA,CAAiB,MAAA,CAAO,UAAA,CAAW,KAAI,GAAG,CAAA;AAC5D,EAAA,KAAA,GAAQ,MAAM,CAAC,CAAA,CAAE,mBAAkB,GAAG,KAAA,CAAM,UAAU,CAAC,CAAA;AACvD,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AACrC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AACvC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AACvC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AACrC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AACrC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,KAAA,EAAO,KAAK,CAAA;AACrC,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,SAAA,EAAW,YAAY,CAAA;AAChD,EAAA,KAAA,GAAQ,KAAA,CAAM,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AAGvC,EAAA,uBACI,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,EAAK,EAAA,EAAI,EAAC,YAAA,EAAa,MAAA,EAAQ,KAAA,EAAM,MAAA,EAAM,EAAA,kBACxC,KAAA,CAAA,aAAA,CAAC,WAAA,EAAA,IAAA,kBACG,KAAA,CAAA,aAAA,CAAC,SAAM,SAAA,EAAU,QAAA,EAAS,UAAA,EAAW,QAAA,EAAS,KAAA,EAAM,MAAA,EAAO,EAAA,EAAI,EAAC,IAAG,MAAA,EAAM,EAAA,kBACrE,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAM,SAAA,EAAW,KAAA,EAAO,UAAA,EAAY,QAAA,EAAA,sCAChCC,SAAA,EAAA,EAAW,GAAA,EAAK,SAAA,GAAU,KAAA,CAAM,gBAAA,CAAiB,MAAA,GAAO,IAAA,CAAK,SAAA,CAAU,MAAM,KAAK,CAAA,EAAG,KAAA,kBAAO,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,KAAA,EAAO,EAAC,QAAA,EAAS,IAAE,EAAA,kBAAG,KAAA,CAAA,aAAA,CAAC,GAAA,EAAA,IAAA,EAAG,KAAA,CAAM,gBAAA,CAAiB,MAAO,CAAA,kBAAI,KAAA,CAAA,aAAA,CAAC,UAAE,CAAA,kBAAE,KAAA,CAAA,aAAA,CAAC,IAAA,EAAA,IAAE,CAAA,EAAG,KAAA,CAAM,gBAAA,CAAiB,IAAK,CAAA,EAAA,sCACnM,UAAA,EAAA,EAAW,KAAA,EAAM,QAAA,EAAA,EAAU,KAAM,CAC1C,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAS,CAAC,KAAA,KAAU,kBAAA,CAAmB,KAAA,CAAM,aAAa,CAAA,EAAA,kBAAG,KAAA,CAAA,aAAA,CAAC,YAAS,QAAA,EAAS,OAAA,EAAO,CAAE,CAAA,EACnG,eAAA,oBAAmB,KAAA,CAAA,aAAA,CAAC,SAAA,EAAA,EAAU,OAAA,EAAS,MAAM,kBAAA,CAAmB,IAAI,CAAA,EAAG,cAAA,EAAgB,uBAAA,EAAyB,UAAA,EAAY,eAAA,EAAiB,QAAA,EAAU,WAAW,OAAA,EAAS,KAAA,EAAO,OAAA,EAAkB,MAAA,EAAgB,SAAA,EAAW,KAAA,CAAM,SAAA,EAAW,UAAA,EAAY,OAAM,CACxQ,CAAA,kBACA,KAAA,CAAA,aAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAC,KAAA,EAAM,MAAA,sBACf,KAAA,CAAA,aAAA,CAAC,mBAAA,EAAA,EAAoB,KAAA,EAAM,MAAA,EAAO,MAAA,EAAgB,GAAA,EAAK,KAAA,CAAM,gBAAA,CAAiB,SAAO,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,KAAK,CAAA,EAAA,EAC1G,MACL,CACJ,CACJ,CACJ,CACJ,CAAA;AAER;;;;"}