@iotready/nextjs-components-library 1.0.0-preview3 → 1.0.0-preview31
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/components/accounts/AccountMenu.js +1 -1
- package/components/accounts/AccountProfile.js +1 -1
- package/components/charts/TrendChart.d.ts +27 -6
- package/components/charts/TrendChart.js +580 -170
- package/components/groups/GroupUpdate.d.ts +4 -8
- package/components/groups/GroupUpdate.js +1 -31
- package/components/groups/GroupsDevices.d.ts +15 -10
- package/components/groups/GroupsDevices.js +92 -79
- package/components/groups/Map.d.ts +3 -8
- package/components/settings/DynamicMenu.d.ts +1 -0
- package/components/settings/DynamicMenu.js +2 -1
- package/components/users/UsersDataGrid.d.ts +5 -2
- package/components/users/UsersDataGrid.js +49 -32
- package/package.json +6 -3
- package/server-actions/annotations.d.ts +4 -0
- package/server-actions/annotations.js +12 -0
- package/server-actions/groups.d.ts +12 -16
- package/server-actions/groups.js +71 -72
- package/server-actions/index.d.ts +1 -0
- package/server-actions/index.js +1 -0
- package/server-actions/influx.d.ts +17 -13
- package/server-actions/influx.js +207 -98
- package/server-actions/trackle.d.ts +10 -4
- package/server-actions/trackle.js +29 -24
- package/server-actions/types.d.ts +16 -0
- package/server-actions/types.js +6 -0
- package/types/device.d.ts +19 -0
- package/types/device.js +1 -0
- package/types/index.d.ts +1 -0
- package/types/index.js +1 -0
- package/types/user.d.ts +1 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { Line } from 'react-chartjs-2';
|
|
5
|
-
import { Chart as ChartJS, Colors, Title, LinearScale, Legend, Tooltip, TimeScale, PointElement, LineElement } from 'chart.js';
|
|
5
|
+
import { Chart as ChartJS, Colors, Title, LinearScale, Legend, Tooltip, TimeScale, PointElement, LineElement, Interaction } from 'chart.js';
|
|
6
6
|
import annotationPlugin from 'chartjs-plugin-annotation';
|
|
7
7
|
import 'chartjs-adapter-moment';
|
|
8
8
|
import { ToggleButtonGroup, ToggleButton, Box, Button, Typography } from "@mui/material";
|
|
@@ -20,24 +20,173 @@ import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
|
|
|
20
20
|
import { ThemeProvider } from '@mui/material/styles';
|
|
21
21
|
import TimelineIcon from '@mui/icons-material/Timeline';
|
|
22
22
|
import MuiTooltip from '@mui/material/Tooltip';
|
|
23
|
+
import EditNoteIcon from '@mui/icons-material/EditNote';
|
|
24
|
+
// import AspectRatioIcon from '@mui/icons-material/AspectRatio';
|
|
25
|
+
import { FilterTagMode } from '../../server-actions/types';
|
|
26
|
+
import { getRelativePosition } from 'chart.js/helpers';
|
|
27
|
+
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
|
28
|
+
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
|
|
29
|
+
function findIndexForX(arr, t) {
|
|
30
|
+
if (!arr || arr.length === 0)
|
|
31
|
+
return -1;
|
|
32
|
+
if (t <= arr[0].x)
|
|
33
|
+
return 0;
|
|
34
|
+
let lo = 0;
|
|
35
|
+
let hi = arr.length - 1;
|
|
36
|
+
let res = -1;
|
|
37
|
+
while (lo <= hi) {
|
|
38
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
39
|
+
const midX = arr[mid]?.x;
|
|
40
|
+
if (midX === undefined)
|
|
41
|
+
return -1;
|
|
42
|
+
if (midX === t)
|
|
43
|
+
return mid;
|
|
44
|
+
if (midX < t) {
|
|
45
|
+
res = mid;
|
|
46
|
+
lo = mid + 1;
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
hi = mid - 1;
|
|
50
|
+
}
|
|
51
|
+
return res;
|
|
52
|
+
}
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
Tooltip.positioners.myCustomPositioner = function (elements, eventPosition) {
|
|
55
|
+
const chart = this.chart;
|
|
56
|
+
const activePos = chart.__activePos;
|
|
57
|
+
if (!activePos || !activePos.closestPoint) {
|
|
58
|
+
return { x: eventPosition.x, y: eventPosition.y };
|
|
59
|
+
}
|
|
60
|
+
const closest = activePos.closestPoint.element;
|
|
61
|
+
const pixelX = activePos.closestPoint.pixelX;
|
|
62
|
+
const pixelY = closest?.y ?? eventPosition.y;
|
|
63
|
+
return { x: pixelX, y: pixelY };
|
|
64
|
+
};
|
|
65
|
+
// Definisci la modalità solo se Interaction è disponibile
|
|
66
|
+
if (Interaction && Interaction.modes) {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
Interaction.modes.myCustomMode = function (chart, e, options, useFinalPosition) {
|
|
69
|
+
const pos = getRelativePosition(e, chart);
|
|
70
|
+
const xScale = chart.scales?.x;
|
|
71
|
+
const hoveredX = xScale && typeof xScale.getValueForPixel === "function"
|
|
72
|
+
? xScale.getValueForPixel(pos.x)
|
|
73
|
+
: null;
|
|
74
|
+
const items = [];
|
|
75
|
+
let closestPoint = null;
|
|
76
|
+
let closestDistance = Infinity;
|
|
77
|
+
for (let datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
|
|
78
|
+
const meta = chart.getDatasetMeta(datasetIndex);
|
|
79
|
+
if (!meta || !meta.data || meta.data.length === 0)
|
|
80
|
+
continue;
|
|
81
|
+
let found = null;
|
|
82
|
+
let pointPixelX = null;
|
|
83
|
+
for (let i = 0; i < meta.data.length; i++) {
|
|
84
|
+
const el = meta.data[i];
|
|
85
|
+
if (el && typeof el.inXRange === "function" && el.inXRange(pos.x, useFinalPosition)) {
|
|
86
|
+
found = { element: el, datasetIndex, index: i };
|
|
87
|
+
pointPixelX = el.x;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!found && hoveredX != null) {
|
|
92
|
+
const dataArr = chart.data.datasets[datasetIndex]?.data || [];
|
|
93
|
+
const idx = findIndexForX(dataArr, hoveredX);
|
|
94
|
+
if (idx >= 0 && meta.data[idx]) {
|
|
95
|
+
found = { element: meta.data[idx], datasetIndex, index: idx };
|
|
96
|
+
const valX = dataArr[idx].x;
|
|
97
|
+
pointPixelX = xScale.getPixelForValue(valX);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (found && pointPixelX != null) {
|
|
101
|
+
items.push(found);
|
|
102
|
+
const xMin = xScale?.left ?? 0;
|
|
103
|
+
const xMax = xScale?.right ?? chart.width;
|
|
104
|
+
console.log({ pointPixelX, xMin, xMax });
|
|
105
|
+
if (pointPixelX < xMin || pointPixelX > xMax) {
|
|
106
|
+
continue; // ignora questo punto
|
|
107
|
+
}
|
|
108
|
+
// Aggiorna il closestPoint globale per la barra verticale
|
|
109
|
+
const distance = Math.abs(pointPixelX - pos.x);
|
|
110
|
+
if (distance < closestDistance) {
|
|
111
|
+
closestDistance = distance;
|
|
112
|
+
closestPoint = { ...found, pixelX: pointPixelX };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
chart.__activePos = hoveredX != null
|
|
117
|
+
? {
|
|
118
|
+
value: hoveredX,
|
|
119
|
+
px: closestPoint ? closestPoint.pixelX : pos.x,
|
|
120
|
+
hasPoint: !!closestPoint
|
|
121
|
+
}
|
|
122
|
+
: null;
|
|
123
|
+
return items;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function getValueAtX(datasetData, t) {
|
|
127
|
+
if (!datasetData || datasetData.length === 0)
|
|
128
|
+
return null;
|
|
129
|
+
// se t è prima del primo punto -> primo valore
|
|
130
|
+
if (t <= datasetData[0].x)
|
|
131
|
+
return datasetData[0].y;
|
|
132
|
+
// binary search per ultimo index con x <= t
|
|
133
|
+
let lo = 0;
|
|
134
|
+
let hi = datasetData.length - 1;
|
|
135
|
+
let res = -1;
|
|
136
|
+
while (lo <= hi) {
|
|
137
|
+
const mid = (lo + hi) >> 1;
|
|
138
|
+
const midX = datasetData[mid].x;
|
|
139
|
+
if (midX <= t) {
|
|
140
|
+
res = mid;
|
|
141
|
+
lo = mid + 1;
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
hi = mid - 1;
|
|
145
|
+
}
|
|
146
|
+
return res >= 0 ? datasetData[res].y : null;
|
|
147
|
+
}
|
|
23
148
|
const lineOptions = {
|
|
24
149
|
parsing: false,
|
|
25
150
|
normalized: true,
|
|
26
|
-
spanGaps:
|
|
151
|
+
spanGaps: true, // enable for all datasets
|
|
27
152
|
// showLine: false, // disable for all datasets
|
|
28
153
|
animation: false,
|
|
29
154
|
responsive: true,
|
|
30
155
|
maintainAspectRatio: false,
|
|
31
156
|
interaction: {
|
|
32
157
|
intersect: false,
|
|
33
|
-
mode: '
|
|
158
|
+
mode: 'myCustomMode',
|
|
34
159
|
axis: 'x'
|
|
35
160
|
},
|
|
36
161
|
plugins: {
|
|
37
162
|
tooltip: {
|
|
163
|
+
position: 'myCustomPositioner',
|
|
38
164
|
callbacks: {
|
|
39
165
|
label: (context) => {
|
|
40
|
-
|
|
166
|
+
const ds = context.dataset || {};
|
|
167
|
+
const parsed = context.parsed || {};
|
|
168
|
+
const unit = ds.unit ?? '';
|
|
169
|
+
const chart = context.chart;
|
|
170
|
+
// 1) se parsed.y è disponibile usalo (gestisci stepped senza toFixed)
|
|
171
|
+
if (parsed.y !== null && parsed.y !== undefined) {
|
|
172
|
+
// se la serie è stepped vogliamo mostrare il valore grezzo (es. 1/0)
|
|
173
|
+
if (ds.stepped)
|
|
174
|
+
return `${ds.label}: ${parsed.y} ${unit}`;
|
|
175
|
+
// numero normale -> 3 decimali
|
|
176
|
+
return `${ds.label}: ${Number(parsed.y).toFixed(3)} ${unit}`;
|
|
177
|
+
}
|
|
178
|
+
// 2) fallback: usa parsed.x o context.parsed.x per calcolare il valore via getValueAtX
|
|
179
|
+
const xVal = parsed.x ?? (context.parsed && context.parsed.x) ?? null;
|
|
180
|
+
if (xVal != null && Array.isArray(ds.data)) {
|
|
181
|
+
const val = getValueAtX(ds.data, xVal);
|
|
182
|
+
if (val !== null && val !== undefined) {
|
|
183
|
+
if (ds.stepped)
|
|
184
|
+
return `${ds.label}: ${val} ${unit}`;
|
|
185
|
+
return `${ds.label}: ${Number(val).toFixed(3)} ${unit}`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// 3) fallback finale
|
|
189
|
+
return `${ds.label}: - ${unit}`;
|
|
41
190
|
}
|
|
42
191
|
},
|
|
43
192
|
},
|
|
@@ -64,7 +213,19 @@ const lineOptions = {
|
|
|
64
213
|
day: 'DD MMM YY',
|
|
65
214
|
hour: 'DD MMM HH:mm',
|
|
66
215
|
minute: 'HH:mm'
|
|
67
|
-
}
|
|
216
|
+
},
|
|
217
|
+
y1: {
|
|
218
|
+
type: 'linear',
|
|
219
|
+
position: 'left',
|
|
220
|
+
title: { display: false, text: 'Primary Axis' },
|
|
221
|
+
display: false
|
|
222
|
+
},
|
|
223
|
+
y2: {
|
|
224
|
+
type: 'linear',
|
|
225
|
+
position: 'right',
|
|
226
|
+
title: { display: false, text: 'Secondary Axis' },
|
|
227
|
+
display: false
|
|
228
|
+
},
|
|
68
229
|
},
|
|
69
230
|
title: {
|
|
70
231
|
display: false,
|
|
@@ -76,15 +237,6 @@ const lineOptions = {
|
|
|
76
237
|
drawOnChartArea: false,
|
|
77
238
|
}
|
|
78
239
|
},
|
|
79
|
-
y: {
|
|
80
|
-
ticks: {
|
|
81
|
-
callback: function (value) {
|
|
82
|
-
if (Math.floor(value) === value) {
|
|
83
|
-
return value;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
240
|
},
|
|
89
241
|
};
|
|
90
242
|
const chartConfigByPeriod = {
|
|
@@ -121,15 +273,6 @@ const chartConfigByPeriod = {
|
|
|
121
273
|
scaleUnit: 'year',
|
|
122
274
|
}
|
|
123
275
|
};
|
|
124
|
-
function GetPoints(data) {
|
|
125
|
-
const points = data.results[0].series[0].values.map((row) => {
|
|
126
|
-
return {
|
|
127
|
-
x: moment.unix(row[0]),
|
|
128
|
-
y: row[1]
|
|
129
|
-
};
|
|
130
|
-
});
|
|
131
|
-
return points;
|
|
132
|
-
}
|
|
133
276
|
function getPollTime(intervalInSeconds, pollTime) {
|
|
134
277
|
const CalculatedPollTime = Math.round(intervalInSeconds / 2880);
|
|
135
278
|
if (CalculatedPollTime <= pollTime) {
|
|
@@ -140,60 +283,76 @@ function getPollTime(intervalInSeconds, pollTime) {
|
|
|
140
283
|
}
|
|
141
284
|
}
|
|
142
285
|
function getCsvData(data, measures) {
|
|
143
|
-
//
|
|
144
|
-
const headers = [
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
286
|
+
// Intestazioni CSV
|
|
287
|
+
const headers = [
|
|
288
|
+
'timestamp',
|
|
289
|
+
...measures.map(m => m.description || m.name)
|
|
290
|
+
];
|
|
291
|
+
const csvRows = [headers];
|
|
292
|
+
// Mappa temporale: ISO string → { timestamp, [measureName]: value, … }
|
|
149
293
|
const timestampMap = {};
|
|
150
|
-
//
|
|
151
|
-
data.forEach((
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
});
|
|
294
|
+
// Ogni serie corrisponde a measures[index]
|
|
295
|
+
data.forEach((series, index) => {
|
|
296
|
+
const measure = measures[index];
|
|
297
|
+
if (!measure)
|
|
298
|
+
return; // difesa su mismatch lunghezze
|
|
299
|
+
series.forEach(point => {
|
|
300
|
+
// usa moment per coerenza con prima versione
|
|
301
|
+
const ts = moment(point.x).toISOString();
|
|
302
|
+
if (!timestampMap[ts]) {
|
|
303
|
+
timestampMap[ts] = { timestamp: ts };
|
|
304
|
+
}
|
|
305
|
+
timestampMap[ts][measure.name] = point.y;
|
|
163
306
|
});
|
|
164
307
|
});
|
|
165
|
-
//
|
|
166
|
-
Object.
|
|
308
|
+
// Ordina i timestamp
|
|
309
|
+
const sortedTs = Object.keys(timestampMap)
|
|
310
|
+
.sort((a, b) => new Date(a).getTime() - new Date(b).getTime());
|
|
311
|
+
// Costruisci le righe CSV
|
|
312
|
+
sortedTs.forEach(ts => {
|
|
313
|
+
const entry = timestampMap[ts];
|
|
314
|
+
// prima colonna timestamp, poi i valori nelle colonne in ordine measures[]
|
|
167
315
|
const row = [entry.timestamp];
|
|
168
|
-
measures.forEach(
|
|
169
|
-
|
|
170
|
-
|
|
316
|
+
measures.forEach(m => {
|
|
317
|
+
row.push(entry[m.name] !== undefined
|
|
318
|
+
? entry[m.name]
|
|
319
|
+
: '');
|
|
171
320
|
});
|
|
172
|
-
//
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
csvData.push(row);
|
|
321
|
+
// optional: filtra le righe che non hanno alcun valore utile
|
|
322
|
+
const hasValue = row.slice(1).some(v => v !== '' && v !== null);
|
|
323
|
+
if (hasValue) {
|
|
324
|
+
csvRows.push(row);
|
|
177
325
|
}
|
|
178
326
|
});
|
|
179
|
-
//
|
|
180
|
-
return
|
|
327
|
+
// Unisci in stringa
|
|
328
|
+
return csvRows
|
|
329
|
+
.map(r => r.join(','))
|
|
330
|
+
.join('\n');
|
|
181
331
|
}
|
|
182
332
|
// eslint-disable-next-line no-unused-vars
|
|
183
|
-
const TrendChart = ({
|
|
333
|
+
const TrendChart = ({ filter, measures1, annotationsDataFn, measures2, enableDatePicker, handleGetInfluxData, handleGetDwSlotsCB, handleExportDataCB, theme, initialTimeStart, initialTimeEnd }) => {
|
|
184
334
|
const [chartJsLoaded, setChartJsLoaded] = useState(false);
|
|
335
|
+
// Dichiarazione di annotationsData come funzione che ritorna una Promise<any>
|
|
336
|
+
const [annotationsData, setAnnotationsData] = useState(null);
|
|
337
|
+
const [annotationsEnabled, setAnnotationsEnabled] = useState(true);
|
|
185
338
|
const [dataMeasures, setDataMeasures] = useState(null);
|
|
186
339
|
const [chartPeriod, setChartPeriod] = useState('1D');
|
|
187
340
|
const [chartPeriodConfig, setChartPeriodConfig] = useState(chartConfigByPeriod['1D']);
|
|
188
341
|
const [chartLoading, setChartLoading] = useState(false);
|
|
189
|
-
const [
|
|
190
|
-
const [
|
|
191
|
-
const [
|
|
192
|
-
const [datePickerUsed, setDatePickerUsed] = useState(false);
|
|
342
|
+
const [timeStartPicker, setTimeStartPicker] = useState(initialTimeStart || moment().subtract(1, 'day').unix());
|
|
343
|
+
const [timeStart, setTimeStart] = useState(initialTimeStart || moment().subtract(1, 'day').unix());
|
|
344
|
+
const [timeEnd, setTimeEnd] = useState(initialTimeEnd || moment().unix());
|
|
345
|
+
const [datePickerUsed, setDatePickerUsed] = useState(initialTimeStart || initialTimeEnd ? true : false);
|
|
346
|
+
const [pickerTimeStart, setPickerTimeStart] = useState(initialTimeStart || moment().subtract(1, 'day').unix());
|
|
347
|
+
const [pickerTimeEnd, setPickerTimeEnd] = useState(initialTimeEnd || moment().unix());
|
|
193
348
|
const [loadingButton, setLoadingButton] = useState(false);
|
|
194
349
|
const csvLinkRef = useRef(null);
|
|
195
350
|
const [csvData, setCsvData] = useState('');
|
|
196
|
-
const [spanGapsOption, setSpanGapsOption] = useState(
|
|
351
|
+
const [spanGapsOption, setSpanGapsOption] = useState(true);
|
|
352
|
+
const enableExportData = handleExportDataCB ? true : false;
|
|
353
|
+
const measures = measures1 && measures2 ? [...measures1, ...measures2] :
|
|
354
|
+
measures1 ? [...measures1] :
|
|
355
|
+
measures2 ? [...measures2] : [];
|
|
197
356
|
const [options, setOptions] = useState({
|
|
198
357
|
...lineOptions,
|
|
199
358
|
plugins: {
|
|
@@ -217,13 +376,19 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
217
376
|
}
|
|
218
377
|
});
|
|
219
378
|
const [zoomed, setZoomed] = useState(false);
|
|
379
|
+
const prevMeasures = useRef();
|
|
220
380
|
const resetChart = () => {
|
|
221
381
|
setOptions({
|
|
222
382
|
...options,
|
|
223
383
|
scales: {
|
|
224
384
|
...options.scales,
|
|
225
|
-
|
|
226
|
-
...options.scales.
|
|
385
|
+
y1: {
|
|
386
|
+
...options.scales.y1,
|
|
387
|
+
min: 0,
|
|
388
|
+
max: 1
|
|
389
|
+
},
|
|
390
|
+
y2: {
|
|
391
|
+
...options.scales.y2,
|
|
227
392
|
min: 0,
|
|
228
393
|
max: 1
|
|
229
394
|
}
|
|
@@ -240,114 +405,294 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
240
405
|
setSpanGapsOption(spanG);
|
|
241
406
|
};
|
|
242
407
|
const handleChange = (event, newPeriod) => {
|
|
243
|
-
|
|
244
|
-
setDatePickerUsed(false);
|
|
245
|
-
setCsvData('');
|
|
246
|
-
if (newPeriod === "ALL") {
|
|
247
|
-
setTimeStart(firstTimestamp - 86400);
|
|
248
|
-
setTimeEnd(moment().unix());
|
|
249
|
-
setChartPeriod(newPeriod);
|
|
408
|
+
if (newPeriod == null) {
|
|
250
409
|
return;
|
|
251
410
|
}
|
|
252
|
-
|
|
253
|
-
|
|
411
|
+
if (chartPeriod === newPeriod) {
|
|
412
|
+
setChartLoading(true);
|
|
413
|
+
setZoomed(false);
|
|
414
|
+
setDatePickerUsed(false);
|
|
415
|
+
setCsvData('');
|
|
416
|
+
const periodConfig = chartConfigByPeriod[chartPeriod];
|
|
254
417
|
setTimeStart(Math.round(moment().subtract(periodConfig.from).valueOf() / 1000));
|
|
255
418
|
setTimeEnd(Math.round(moment().valueOf() / 1000));
|
|
256
|
-
setChartPeriod(
|
|
419
|
+
setChartPeriod(chartPeriod);
|
|
257
420
|
setChartPeriodConfig(periodConfig);
|
|
258
421
|
}
|
|
422
|
+
if (newPeriod && newPeriod !== chartPeriod) {
|
|
423
|
+
setChartLoading(true);
|
|
424
|
+
setZoomed(false);
|
|
425
|
+
setDatePickerUsed(false);
|
|
426
|
+
setCsvData('');
|
|
427
|
+
if (newPeriod === "ALL") {
|
|
428
|
+
setTimeStart(1577854800);
|
|
429
|
+
setTimeEnd(moment().unix());
|
|
430
|
+
setChartPeriod(newPeriod);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const periodConfig = chartConfigByPeriod[newPeriod];
|
|
434
|
+
if (periodConfig) {
|
|
435
|
+
setTimeStart(Math.round(moment().subtract(periodConfig.from).valueOf() / 1000));
|
|
436
|
+
setTimeEnd(Math.round(moment().valueOf() / 1000));
|
|
437
|
+
setChartPeriod(newPeriod);
|
|
438
|
+
setChartPeriodConfig(periodConfig);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
259
441
|
};
|
|
260
442
|
const handleExportData = async () => {
|
|
261
443
|
setLoadingButton(true);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
444
|
+
if (handleExportDataCB) {
|
|
445
|
+
const data = await Promise.all(measures.map(async (measure) => {
|
|
446
|
+
return await handleExportDataCB(measure.name, timeStart, timeEnd, filter);
|
|
447
|
+
}));
|
|
448
|
+
const csvData = getCsvData(data, measures);
|
|
449
|
+
setCsvData(csvData);
|
|
450
|
+
setLoadingButton(false);
|
|
451
|
+
}
|
|
270
452
|
};
|
|
271
453
|
useEffect(() => {
|
|
272
454
|
if (csvData.length > 0) {
|
|
273
455
|
csvLinkRef.current?.link.click();
|
|
274
456
|
}
|
|
275
457
|
}, [csvData]);
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
if (annotationsDataFn && annotationsEnabled) {
|
|
460
|
+
(async () => {
|
|
461
|
+
const resp = await annotationsDataFn();
|
|
462
|
+
setAnnotationsData(resp);
|
|
463
|
+
})();
|
|
464
|
+
}
|
|
465
|
+
}, [annotationsDataFn, annotationsEnabled]);
|
|
466
|
+
function getDwSlotsFromValues(dwValues) {
|
|
467
|
+
const slots = [];
|
|
468
|
+
let start = null;
|
|
469
|
+
for (const point of dwValues) {
|
|
470
|
+
if (point.y === 1 && start === null) {
|
|
471
|
+
start = point.x;
|
|
472
|
+
}
|
|
473
|
+
if (point.y === 0 && start !== null) {
|
|
474
|
+
slots.push({ start, end: point.x });
|
|
475
|
+
start = null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return slots;
|
|
479
|
+
}
|
|
276
480
|
const loadDatasets = async (chartPeriod) => {
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
481
|
+
let intervalInSeconds = chartPeriod === "ALL" && !datePickerUsed && !zoomed ? 31536000 : timeEnd - timeStart;
|
|
482
|
+
const rawQuery = intervalInSeconds < 86400;
|
|
483
|
+
let tempSlots = [];
|
|
484
|
+
if (handleGetDwSlotsCB) {
|
|
485
|
+
const dwValues = await handleGetDwSlotsCB(timeStart, timeEnd, filter);
|
|
486
|
+
tempSlots = getDwSlotsFromValues(dwValues);
|
|
487
|
+
}
|
|
488
|
+
// Combine measures and track their source
|
|
489
|
+
const allMeasures = [
|
|
490
|
+
...(measures1?.map(m => ({ ...m, source: 'measures1' })) || []),
|
|
491
|
+
...(measures2?.map(m => ({ ...m, source: 'measures2' })) || [])
|
|
492
|
+
];
|
|
493
|
+
const datasetsPromises = allMeasures.map(async (measure) => {
|
|
494
|
+
const polltime = getPollTime(intervalInSeconds, measure.polltime || 30);
|
|
495
|
+
let dwPoints = [];
|
|
496
|
+
let dwSlots = [];
|
|
497
|
+
if (measure.dwPolltime) {
|
|
498
|
+
const dwPolltime = getPollTime(intervalInSeconds, measure.dwPolltime);
|
|
499
|
+
// Query DW solo tag=100
|
|
500
|
+
const rawDwPoints = await handleGetInfluxData(measure.name, timeStart, timeEnd, filter, dwPolltime, false, "previous", 100, FilterTagMode.DW);
|
|
501
|
+
dwSlots = tempSlots;
|
|
502
|
+
if (dwSlots.length > 0) {
|
|
503
|
+
dwPoints = rawDwPoints.filter((p) => dwSlots.some(slot => p.x >= slot.start && p.x <= slot.end));
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
dwPoints = rawDwPoints;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const stdPoints = await handleGetInfluxData(measure.name, timeStart, timeEnd, filter, polltime, !measure.polltime && rawQuery, !measure.polltime ? "none" : "null", 100, FilterTagMode.Exclude);
|
|
510
|
+
const filtered = dwSlots.length
|
|
511
|
+
? stdPoints.filter((p) => !dwSlots.some(slot => p.x >= slot.start && p.x <= slot.end))
|
|
512
|
+
: stdPoints;
|
|
513
|
+
let combined;
|
|
514
|
+
// 6) Unisco e ordino
|
|
515
|
+
if (dwPoints.length > 2 && dwSlots.length > 0) {
|
|
516
|
+
combined = [...dwPoints, ...filtered]
|
|
517
|
+
.sort((a, b) => a.x - b.x);
|
|
518
|
+
}
|
|
519
|
+
else if (dwPoints.length > 2 && dwSlots.length <= 0) {
|
|
520
|
+
combined = [...dwPoints];
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
combined = [...filtered];
|
|
524
|
+
}
|
|
284
525
|
return {
|
|
285
|
-
label: measure.name,
|
|
286
|
-
data:
|
|
526
|
+
label: measure.description || measure.name,
|
|
527
|
+
data: combined,
|
|
287
528
|
unit: measure.unit,
|
|
288
|
-
// borderColor: `hsl(${index * 50}, 70%, 50%)`, // Colore unico per ogni dataset
|
|
289
529
|
borderWidth: 2,
|
|
290
530
|
pointRadius: 1,
|
|
291
531
|
pointHoverRadius: 5,
|
|
292
532
|
pointHoverBackgroundColor: 'rgba(52, 125, 236, 0.5)',
|
|
293
|
-
|
|
294
|
-
|
|
533
|
+
change: !measure.polltime,
|
|
534
|
+
stepped: measure.stepped,
|
|
535
|
+
yAxisID: measure.source === 'measures1' ? 'y1' : 'y2'
|
|
295
536
|
};
|
|
296
537
|
});
|
|
297
|
-
// Risolvi tutte le promesse per popolare i dataset
|
|
298
538
|
const datasets = await Promise.all(datasetsPromises);
|
|
299
|
-
let
|
|
300
|
-
let
|
|
301
|
-
let
|
|
539
|
+
let min1 = null;
|
|
540
|
+
let max1 = null;
|
|
541
|
+
let min2 = null;
|
|
542
|
+
let max2 = null;
|
|
543
|
+
let time;
|
|
544
|
+
let minTime = null;
|
|
302
545
|
datasets.forEach(dataset => {
|
|
303
|
-
values =
|
|
546
|
+
const values = dataset.data.map((point) => point.y).filter((data) => data !== null);
|
|
547
|
+
if (chartPeriod === "ALL" && !datePickerUsed && !zoomed && values.length) {
|
|
548
|
+
time = dataset.data.filter((data) => data.y !== null).map((data) => data.x)[0];
|
|
549
|
+
}
|
|
304
550
|
const datasetMin = Math.min(...values);
|
|
305
551
|
const datasetMax = Math.max(...values);
|
|
306
|
-
if (
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
552
|
+
if (dataset.yAxisID === 'y1') {
|
|
553
|
+
if (min1 === null || datasetMin < min1)
|
|
554
|
+
min1 = datasetMin;
|
|
555
|
+
if (max1 === null || datasetMax > max1)
|
|
556
|
+
max1 = datasetMax;
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
if (min2 === null || datasetMin < min2)
|
|
560
|
+
min2 = datasetMin;
|
|
561
|
+
if (max2 === null || datasetMax > max2)
|
|
562
|
+
max2 = datasetMax;
|
|
563
|
+
}
|
|
564
|
+
if (time && (minTime === null || time < minTime))
|
|
565
|
+
minTime = Math.floor(time / 1000) - 86400;
|
|
310
566
|
});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const diff =
|
|
315
|
-
|
|
316
|
-
|
|
567
|
+
const getPaddedMinMax = (min, max) => {
|
|
568
|
+
if (min === null || max === null)
|
|
569
|
+
return { paddedMin: null, paddedMax: null };
|
|
570
|
+
const diff = ((max - min) * 0.2) < 0.1 ? 0.1 : (max - min) * 0.2;
|
|
571
|
+
return {
|
|
572
|
+
paddedMin: Math.floor((min - diff) * 10) / 10,
|
|
573
|
+
paddedMax: Math.ceil((max + diff) * 10) / 10
|
|
574
|
+
};
|
|
575
|
+
};
|
|
576
|
+
const { paddedMin: paddedMin1, paddedMax: paddedMax1 } = getPaddedMinMax(min1, max1);
|
|
577
|
+
const { paddedMin: paddedMin2, paddedMax: paddedMax2 } = getPaddedMinMax(min2, max2);
|
|
578
|
+
// Handle undefined/null annotationsData
|
|
579
|
+
let dynamicAnnotations = {};
|
|
580
|
+
if (annotationsData && Array.isArray(annotationsData) && annotationsData.length > 0 && annotationsEnabled) {
|
|
581
|
+
dynamicAnnotations = annotationsData.reduce((acc, [timestamp, label], index) => {
|
|
582
|
+
const yVal = paddedMin1 !== null && paddedMin1 !== undefined
|
|
583
|
+
? paddedMin1 + 0.01 * (paddedMax1 - paddedMin1)
|
|
584
|
+
: paddedMin2 !== null && paddedMin2 !== undefined
|
|
585
|
+
? paddedMin2 + 0.01 * (paddedMax2 - paddedMin2)
|
|
586
|
+
: 0;
|
|
587
|
+
acc[`line${index}`] = {
|
|
588
|
+
type: 'line',
|
|
589
|
+
xMin: timestamp,
|
|
590
|
+
xMax: timestamp,
|
|
591
|
+
borderColor: 'rgba(255, 225, 0, 0.8)',
|
|
592
|
+
borderWidth: 2,
|
|
593
|
+
drawTime: 'afterDatasetsDraw',
|
|
594
|
+
label: {
|
|
595
|
+
content: label,
|
|
596
|
+
enabled: false
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
acc[`triangle${index}`] = {
|
|
600
|
+
type: 'point',
|
|
601
|
+
xValue: timestamp,
|
|
602
|
+
yValue: yVal,
|
|
603
|
+
backgroundColor: 'rgba(255, 225, 0, 0.8)',
|
|
604
|
+
pointStyle: 'triangle',
|
|
605
|
+
radius: 6,
|
|
606
|
+
rotation: 0,
|
|
607
|
+
tooltip: {
|
|
608
|
+
enabled: true,
|
|
609
|
+
callbacks: {
|
|
610
|
+
label: () => label
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
acc[`label${index}`] = {
|
|
615
|
+
type: 'label',
|
|
616
|
+
xValue: timestamp,
|
|
617
|
+
yValue: yVal + 0.1 * (((paddedMax1 ?? paddedMax2 ?? 0) - (paddedMax1 ?? paddedMax2 ?? 0))),
|
|
618
|
+
xAdjust: 0,
|
|
619
|
+
yAdjust: -20,
|
|
620
|
+
backgroundColor: 'rgba(245,245,245)',
|
|
621
|
+
borderColor: 'rgba(255, 225, 0, 0.8)',
|
|
622
|
+
borderWidth: 1,
|
|
623
|
+
content: [label],
|
|
624
|
+
textAlign: 'start',
|
|
625
|
+
font: {
|
|
626
|
+
size: 10,
|
|
627
|
+
weight: 'bold'
|
|
628
|
+
},
|
|
629
|
+
callout: {
|
|
630
|
+
display: false,
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
return acc;
|
|
634
|
+
}, {});
|
|
317
635
|
}
|
|
318
|
-
|
|
636
|
+
// Combina statiche + dinamiche
|
|
637
|
+
const annotations = {
|
|
638
|
+
// ...staticAnnotations,
|
|
639
|
+
...dynamicAnnotations
|
|
640
|
+
};
|
|
641
|
+
// 👇 Configurazione completa
|
|
642
|
+
setDataMeasures(datasets);
|
|
643
|
+
setTimeStartPicker(minTime || timeStart);
|
|
319
644
|
setOptions({
|
|
320
645
|
...options,
|
|
321
646
|
scales: {
|
|
322
647
|
...options.scales,
|
|
323
|
-
|
|
324
|
-
...options.scales.
|
|
325
|
-
min:
|
|
326
|
-
max:
|
|
648
|
+
y1: {
|
|
649
|
+
...options.scales.y1,
|
|
650
|
+
min: paddedMin1,
|
|
651
|
+
max: paddedMax1,
|
|
652
|
+
position: "left",
|
|
653
|
+
display: "auto"
|
|
654
|
+
},
|
|
655
|
+
y2: {
|
|
656
|
+
...options.scales.y2,
|
|
657
|
+
min: paddedMin2,
|
|
658
|
+
max: paddedMax2,
|
|
659
|
+
position: "right",
|
|
660
|
+
display: "auto"
|
|
327
661
|
},
|
|
328
662
|
x: {
|
|
329
663
|
...options.scales.x,
|
|
330
|
-
min: moment.unix(timeStart).toString(),
|
|
331
|
-
max: moment.unix(timeEnd).toString()
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
664
|
+
min: moment.unix(minTime || timeStart).toString(),
|
|
665
|
+
max: moment.unix(timeEnd).toString()
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
plugins: {
|
|
669
|
+
...options.plugins,
|
|
670
|
+
annotation: {
|
|
671
|
+
interaction: {
|
|
672
|
+
mode: 'nearest',
|
|
673
|
+
intersect: true
|
|
674
|
+
},
|
|
675
|
+
annotations
|
|
336
676
|
}
|
|
337
677
|
}
|
|
338
678
|
});
|
|
339
679
|
};
|
|
680
|
+
const shiftChart = ((timeToShift) => {
|
|
681
|
+
setChartLoading(true);
|
|
682
|
+
const timeInRange = timeEnd - timeStart;
|
|
683
|
+
const percentageToShift = 0.1; // 10%
|
|
684
|
+
const shiftAmount = Math.floor(timeInRange * percentageToShift);
|
|
685
|
+
if (timeToShift === 'timeStart') {
|
|
686
|
+
setTimeStart(prev => prev - shiftAmount);
|
|
687
|
+
setTimeEnd(prev => prev - shiftAmount);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
setTimeEnd(prev => prev + shiftAmount);
|
|
691
|
+
setTimeStart(prev => prev + shiftAmount);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
340
694
|
useEffect(() => {
|
|
341
|
-
const
|
|
342
|
-
const response = await handleGetFirstTimestamp(deviceId);
|
|
343
|
-
if (response) {
|
|
344
|
-
setFirstTimestamp(response);
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
fetchFirstTimestamp();
|
|
348
|
-
}, []);
|
|
349
|
-
useEffect(() => {
|
|
350
|
-
const timeDifference = Math.abs(moment(timeStart).valueOf() - moment(timeEnd).valueOf()); // Convert milliseconds to seconds
|
|
695
|
+
const timeDifference = Math.abs(moment(timeEnd).valueOf() - moment(timeStart).valueOf()); // Convert milliseconds to seconds
|
|
351
696
|
let newChartPeriod = '1D'; // Default to 1 day
|
|
352
697
|
if (timeDifference < 86400) { // Less than 1 day
|
|
353
698
|
newChartPeriod = '1H'; // Set to 1 day
|
|
@@ -355,47 +700,70 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
355
700
|
else if (timeDifference < 604800) { // Less than 1 week
|
|
356
701
|
newChartPeriod = '1D'; // Set to 1 day
|
|
357
702
|
}
|
|
358
|
-
else if (timeDifference <
|
|
703
|
+
else if (timeDifference < 2678400) { // Less than 1 month
|
|
359
704
|
newChartPeriod = '1W'; // Set to 1 week
|
|
360
705
|
}
|
|
361
|
-
else if (timeDifference <
|
|
706
|
+
else if (timeDifference < 8035200) { // Less than 3 months
|
|
362
707
|
newChartPeriod = '1M'; // Set to 1 month
|
|
363
708
|
}
|
|
364
|
-
else if (timeDifference <
|
|
709
|
+
else if (timeDifference < 16070400) { // Less than 6 months
|
|
365
710
|
newChartPeriod = '3M'; // Set to 3 months
|
|
366
711
|
}
|
|
367
|
-
else if (timeDifference <
|
|
712
|
+
else if (timeDifference < 31536000) { // Less than 1 year
|
|
368
713
|
newChartPeriod = '6M'; // Set to 6 months
|
|
369
714
|
}
|
|
370
715
|
else {
|
|
371
716
|
newChartPeriod = '1Y'; // Set to 1 year
|
|
372
717
|
}
|
|
373
718
|
setChartPeriodConfig(chartConfigByPeriod[newChartPeriod]);
|
|
719
|
+
// check prev measures value in order to show the loader
|
|
720
|
+
// hide the loader if measure is the same (for interval get measure value)
|
|
721
|
+
const prevMeasuresValue = prevMeasures.current || [];
|
|
722
|
+
//@ts-ignore
|
|
723
|
+
prevMeasures.current = measures;
|
|
724
|
+
if (!prevMeasuresValue || prevMeasuresValue.length !== measures?.length || (prevMeasuresValue[0] && prevMeasuresValue[0].name !== measures[0].name)) {
|
|
725
|
+
setChartLoading(true);
|
|
726
|
+
}
|
|
374
727
|
loadDatasets(chartPeriod).then(() => {
|
|
375
728
|
setChartLoading(false);
|
|
376
729
|
});
|
|
377
|
-
}, [timeEnd, timeStart]);
|
|
730
|
+
}, [measures1, timeEnd, timeStart, measures2, annotationsEnabled, annotationsData]);
|
|
378
731
|
useEffect(() => {
|
|
379
732
|
const loadZoomPlugin = async () => {
|
|
380
733
|
const zoomPlugin = (await import('chartjs-plugin-zoom')).default;
|
|
381
734
|
ChartJS.register(Colors, Legend, Title, Tooltip, PointElement, LineElement, LinearScale, TimeScale, annotationPlugin, zoomPlugin, {
|
|
382
735
|
id: 'uniqueid5',
|
|
383
736
|
afterDraw: function (chart) {
|
|
384
|
-
if (chart.
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
737
|
+
if (!chart.__activePos?.hasPoint)
|
|
738
|
+
return; // disegna solo sui punti
|
|
739
|
+
const ctx = chart.ctx;
|
|
740
|
+
const x = chart.__activePos.px; // pixel esatto del punto attivo
|
|
741
|
+
let topY1 = 0;
|
|
742
|
+
let topY2 = 0;
|
|
743
|
+
let bottomY1 = 0;
|
|
744
|
+
let bottomY2 = 0;
|
|
745
|
+
if (chart.scales.y1?.top) {
|
|
746
|
+
topY1 = chart.scales.y1.top;
|
|
747
|
+
}
|
|
748
|
+
if (chart.scales.y2?.top) {
|
|
749
|
+
topY2 = chart.scales.y2.top;
|
|
750
|
+
}
|
|
751
|
+
if (chart.scales.y1?.bottom) {
|
|
752
|
+
bottomY1 = chart.scales.y1.bottom;
|
|
753
|
+
}
|
|
754
|
+
if (chart.scales.y2?.bottom) {
|
|
755
|
+
bottomY2 = chart.scales.y2.bottom;
|
|
398
756
|
}
|
|
757
|
+
const topY = Math.max(topY1, topY2);
|
|
758
|
+
const bottomY = Math.min(bottomY1, bottomY2);
|
|
759
|
+
ctx.save();
|
|
760
|
+
ctx.beginPath();
|
|
761
|
+
ctx.moveTo(x, topY);
|
|
762
|
+
ctx.lineTo(x, bottomY);
|
|
763
|
+
ctx.lineWidth = 1;
|
|
764
|
+
ctx.strokeStyle = '#722257';
|
|
765
|
+
ctx.stroke();
|
|
766
|
+
ctx.restore();
|
|
399
767
|
}
|
|
400
768
|
});
|
|
401
769
|
setChartJsLoaded(true);
|
|
@@ -403,34 +771,57 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
403
771
|
loadZoomPlugin();
|
|
404
772
|
resetChart();
|
|
405
773
|
}, []);
|
|
406
|
-
const datePicker = _jsx(Box, { sx: { display: 'flex', alignItems: 'center'
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
774
|
+
const datePicker = _jsx(Box, { sx: { display: 'flex', alignItems: 'center' }, children: _jsxs(LocalizationProvider, { dateAdapter: AdapterMoment, adapterLocale: "it", children: [_jsx(DateTimePicker, { value: moment(timeStartPicker * 1000), onChange: (newValue) => {
|
|
775
|
+
setChartLoading(true);
|
|
776
|
+
setDatePickerUsed(true);
|
|
777
|
+
setZoomed(false);
|
|
778
|
+
setPickerTimeStart(moment(newValue).unix());
|
|
779
|
+
setTimeStart(moment(newValue).unix());
|
|
410
780
|
}, maxDateTime: moment(timeEnd * 1000), slotProps: {
|
|
411
781
|
textField: { size: 'small', sx: { width: { sm: 210 } } }
|
|
412
|
-
} }), " \u00A0\u00A0 ", _jsx(Box, { children: "\u2013" }), " \u00A0\u00A0", _jsx(DateTimePicker, { value: moment(timeEnd * 1000),
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
782
|
+
} }), " \u00A0\u00A0 ", _jsx(Box, { children: "\u2013" }), " \u00A0\u00A0", _jsx(DateTimePicker, { value: moment(timeEnd * 1000), onChange: (newValue) => {
|
|
783
|
+
setChartLoading(true);
|
|
784
|
+
setDatePickerUsed(true);
|
|
785
|
+
setZoomed(false);
|
|
786
|
+
setPickerTimeEnd(moment(newValue).unix());
|
|
787
|
+
setTimeEnd(moment(newValue).unix());
|
|
416
788
|
}, minDateTime: moment(timeStart * 1000), slotProps: {
|
|
417
789
|
textField: { size: 'small', sx: { width: { sm: 210 } } }
|
|
418
790
|
} })] }) });
|
|
419
|
-
return (_jsxs(ThemeProvider, { theme: theme, children: [
|
|
791
|
+
return (_jsxs(ThemeProvider, { theme: theme, children: [_jsxs(Box, { sx: { display: { xs: 'flex', lg: 'none', justifyContent: 'center' }, alignItems: 'center', mb: 3, mt: 1 }, children: [_jsx(Button, { sx: {
|
|
792
|
+
borderWidth: 1,
|
|
793
|
+
borderStyle: 'solid',
|
|
794
|
+
borderColor: '#c4c4c4',
|
|
795
|
+
color: 'text.primary',
|
|
796
|
+
":hover": { backgroundColor: 'action.hover' },
|
|
797
|
+
mr: enableDatePicker ? 1 : 0,
|
|
798
|
+
p: 0.85
|
|
799
|
+
}, onClick: () => shiftChart('timeStart'), children: _jsx(KeyboardArrowLeftIcon, {}) }), enableDatePicker && _jsx(Box, { sx: { display: { xs: 'flex', lg: 'none', justifyContent: 'flex-end' } }, children: datePicker }), _jsx(Button, { sx: {
|
|
800
|
+
borderWidth: 1,
|
|
801
|
+
borderStyle: 'solid',
|
|
802
|
+
borderColor: '#c4c4c4',
|
|
803
|
+
color: 'text.primary',
|
|
804
|
+
":hover": { backgroundColor: 'action.hover' },
|
|
805
|
+
ml: enableDatePicker ? 1 : 0,
|
|
806
|
+
p: 0.85
|
|
807
|
+
}, onClick: () => shiftChart('timeEnd'), children: _jsx(KeyboardArrowRightIcon, {}) })] }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center' }, children: [!enableExportData && measures && measures.length === 1 ? _jsx(Typography, { variant: 'body1', sx: { mr: 2 }, children: measures[0].description || measures[0].name }) : '', enableExportData && (_jsxs(_Fragment, { children: [_jsxs(LoadingButton, { sx: { minWidth: '40px !important', mr: 1, px: 1 }, loading: loadingButton, type: "submit", variant: "text", color: "primary", onClick: handleExportData, disabled: !dataMeasures || !dataMeasures.length, size: 'small', children: [_jsx(LoginIcon, { fontSize: 'small', style: { transform: "rotate(90deg)" } }), _jsx(Box, { sx: { display: { xs: 'none', xl: 'block' }, ml: { md: 1 } }, children: "Export" })] }), _jsx(CSVLink
|
|
420
808
|
//@ts-ignore
|
|
421
809
|
, {
|
|
422
810
|
//@ts-ignore
|
|
423
|
-
ref: csvLinkRef, data: csvData, filename: `export_${moment().toISOString()}.csv`, separator: ';' })] })), zoomed && (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
811
|
+
ref: csvLinkRef, data: csvData, filename: `export_${moment().toISOString()}.csv`, separator: ';' })] })), zoomed && measures.length > 0 && (_jsx(Button, { sx: { minWidth: '40px !important', boxShadow: 1, p: 1, mr: 1 }, variant: "contained", color: "primary", size: 'small', onClick: () => {
|
|
812
|
+
setChartLoading(true);
|
|
813
|
+
setZoomed(false);
|
|
814
|
+
if (chartPeriod === "ALL") {
|
|
815
|
+
setDatePickerUsed(false);
|
|
816
|
+
setTimeStart(1577854800);
|
|
429
817
|
setTimeEnd(moment().unix());
|
|
430
|
-
|
|
818
|
+
}
|
|
819
|
+
else if (datePickerUsed) {
|
|
820
|
+
setTimeStart(pickerTimeStart);
|
|
821
|
+
setTimeEnd(pickerTimeEnd);
|
|
431
822
|
}
|
|
432
823
|
else {
|
|
433
|
-
|
|
824
|
+
setDatePickerUsed(false);
|
|
434
825
|
setChartPeriodConfig(chartConfigByPeriod[chartPeriod]);
|
|
435
826
|
setTimeStart(chartPeriodConfig.from?.days
|
|
436
827
|
? moment()
|
|
@@ -439,12 +830,31 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
439
830
|
: moment().subtract(1, 'day').unix());
|
|
440
831
|
setTimeEnd(moment().unix());
|
|
441
832
|
}
|
|
442
|
-
}, children:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
833
|
+
}, children: _jsx(ZoomOut, { fontSize: 'small' }) }))] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: [_jsxs(Box, { sx: { display: { xs: 'none', lg: 'flex' }, alignItems: 'center', mr: 1 }, children: [_jsx(Button, { sx: {
|
|
834
|
+
borderWidth: 1,
|
|
835
|
+
borderStyle: 'solid',
|
|
836
|
+
borderColor: '#c4c4c4',
|
|
837
|
+
color: 'text.primary',
|
|
838
|
+
":hover": { backgroundColor: 'action.hover' },
|
|
839
|
+
mr: enableDatePicker ? 1 : 0,
|
|
840
|
+
p: 0.85
|
|
841
|
+
}, onClick: () => shiftChart('timeStart'), children: _jsx(KeyboardArrowLeftIcon, {}) }), enableDatePicker && _jsx(Box, { sx: { display: { xs: 'none', lg: 'flex' } }, children: datePicker }), _jsx(Button, { sx: {
|
|
842
|
+
borderWidth: 1,
|
|
843
|
+
borderStyle: 'solid',
|
|
844
|
+
borderColor: '#c4c4c4',
|
|
845
|
+
color: 'text.primary',
|
|
846
|
+
":hover": { backgroundColor: 'action.hover' },
|
|
847
|
+
ml: 1,
|
|
848
|
+
p: 0.85
|
|
849
|
+
}, onClick: () => shiftChart('timeEnd'), children: _jsx(KeyboardArrowRightIcon, {}) })] }), _jsxs(ToggleButtonGroup, { color: "primary", value: !datePickerUsed && !zoomed ? chartPeriod : null, exclusive: true, onChange: handleChange, size: "small", sx: {
|
|
850
|
+
'& .MuiToggleButton-root': {
|
|
851
|
+
color: 'text.primary', fontSize: '0.95rem', fontWeight: 'normal', paddingTop: '6px', paddingBottom: '6px'
|
|
852
|
+
}
|
|
853
|
+
}, disabled: chartLoading, children: [_jsx(ToggleButton, { value: "1D", sx: { px: 1 }, children: "1d" }), _jsx(ToggleButton, { value: "1W", sx: { px: 1 }, children: "1w" }), _jsx(ToggleButton, { value: "1M", sx: { px: 1 }, children: "1M" }), _jsx(ToggleButton, { value: "3M", sx: { px: 1 }, children: "3M" }), _jsx(ToggleButton, { value: "6M", sx: { px: 1 }, children: "6M" }), _jsx(ToggleButton, { value: "1Y", sx: { px: 1 }, children: "1Y" }), _jsx(ToggleButton, { value: "ALL", sx: { px: 1 }, children: "ALL" })] }), _jsx(MuiTooltip, { placement: "top", arrow: true, title: "Connect point values", children: _jsx("span", { children: _jsx(ToggleButton, { value: "check", color: "primary", size: "small", selected: spanGapsOption, disabled: chartLoading, onChange: () => handleSpanGaps(!spanGapsOption), sx: { ml: 1 }, children: _jsx(TimelineIcon, {}) }) }) }), annotationsData !== null && (_jsx(MuiTooltip, { placement: "top", arrow: true, title: "Show annotations", children: _jsx("span", { children: _jsx(ToggleButton, { value: "check", color: "primary", size: "small", selected: annotationsEnabled, disabled: chartLoading, onChange: () => setAnnotationsEnabled(!annotationsEnabled), sx: { ml: 1 }, children: _jsx(EditNoteIcon, {}) }) }) }))] })] }), _jsx(Box, { component: 'div', className: "chart-container", sx: { mt: 2, height: '100%' }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
|
|
854
|
+
_jsx(_Fragment, { children: dataMeasures && (dataMeasures.length > 1 || (dataMeasures.length === 1 && dataMeasures[0].data?.length)) ?
|
|
446
855
|
(_jsx(Line, { options: options, data: {
|
|
447
|
-
datasets: dataMeasures || [{ data: [] }]
|
|
856
|
+
// datasets: dataMeasures || [{ data: [] }]
|
|
857
|
+
datasets: dataMeasures.map(d => ({ ...d, showLine: spanGapsOption || !d.change })) || [{ data: [] }]
|
|
448
858
|
} })) : _jsxs(Box, { sx: {
|
|
449
859
|
display: 'flex',
|
|
450
860
|
flexDirection: 'column',
|
|
@@ -458,7 +868,7 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
|
458
868
|
alignItems: 'center',
|
|
459
869
|
justifyContent: 'center',
|
|
460
870
|
textAlign: 'center',
|
|
461
|
-
height: '100%'
|
|
871
|
+
height: 'calc(100% - 50px)'
|
|
462
872
|
}, children: _jsx(CircularProgress, {}) })) })] }));
|
|
463
873
|
};
|
|
464
874
|
export default TrendChart;
|