@iotready/nextjs-components-library 1.0.0-preview2 → 1.0.0-preview4
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.
@@ -6,7 +6,7 @@ type Measure = {
|
|
6
6
|
polltime: number;
|
7
7
|
unit: string;
|
8
8
|
};
|
9
|
-
declare const TrendChart: ({ deviceId, measures, enableExportData, enableDatePicker, handleGetInfluxData,
|
9
|
+
declare const TrendChart: ({ deviceId, measures, enableExportData, enableDatePicker, handleGetInfluxData, theme, ...props }: {
|
10
10
|
deviceId: string;
|
11
11
|
measures: Array<Measure>;
|
12
12
|
enableenableExportData: boolean;
|
@@ -18,10 +18,12 @@ import 'moment/locale/it';
|
|
18
18
|
// import 'moment/locale/en-gb'; // TODO set locale based on browser
|
19
19
|
import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
|
20
20
|
import { ThemeProvider } from '@mui/material/styles';
|
21
|
+
import TimelineIcon from '@mui/icons-material/Timeline';
|
22
|
+
import MuiTooltip from '@mui/material/Tooltip';
|
21
23
|
const lineOptions = {
|
22
24
|
parsing: false,
|
23
25
|
normalized: true,
|
24
|
-
spanGaps:
|
26
|
+
spanGaps: false, // enable for all datasets
|
25
27
|
// showLine: false, // disable for all datasets
|
26
28
|
animation: false,
|
27
29
|
responsive: true,
|
@@ -160,8 +162,11 @@ function getCsvData(data, measures) {
|
|
160
162
|
});
|
161
163
|
});
|
162
164
|
});
|
163
|
-
//
|
164
|
-
Object.
|
165
|
+
// Sort timestamps in ascending order
|
166
|
+
const sortedTimestamps = Object.keys(timestampMap).sort((a, b) => new Date(a).getTime() - new Date(b).getTime());
|
167
|
+
// Create rows from sorted timestampMap
|
168
|
+
sortedTimestamps.forEach(timestamp => {
|
169
|
+
const entry = timestampMap[timestamp];
|
165
170
|
const row = [entry.timestamp];
|
166
171
|
measures.forEach(measure => {
|
167
172
|
// Push the corresponding value or an empty string if undefined
|
@@ -169,7 +174,7 @@ function getCsvData(data, measures) {
|
|
169
174
|
});
|
170
175
|
// Check if the row contains only empty values (besides the timestamp)
|
171
176
|
const hasNonEmptyValues = row.slice(1).some(value => value !== null && value !== '' && value !== undefined);
|
172
|
-
// If the row has at least one non-empty value
|
177
|
+
// If the row has at least one non-empty value, add it to csvData
|
173
178
|
if (hasNonEmptyValues || row.length === 1) {
|
174
179
|
csvData.push(row);
|
175
180
|
}
|
@@ -178,19 +183,22 @@ function getCsvData(data, measures) {
|
|
178
183
|
return csvData.map(row => row.join(',')).join('\n');
|
179
184
|
}
|
180
185
|
// eslint-disable-next-line no-unused-vars
|
181
|
-
const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, handleGetInfluxData,
|
186
|
+
const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, handleGetInfluxData, theme, ...props }) => {
|
182
187
|
const [chartJsLoaded, setChartJsLoaded] = useState(false);
|
183
188
|
const [dataMeasures, setDataMeasures] = useState(null);
|
184
189
|
const [chartPeriod, setChartPeriod] = useState('1D');
|
185
190
|
const [chartPeriodConfig, setChartPeriodConfig] = useState(chartConfigByPeriod['1D']);
|
186
191
|
const [chartLoading, setChartLoading] = useState(false);
|
192
|
+
const [timeStartPicker, setTimeStartPicker] = useState(moment().subtract(1, 'day').unix());
|
187
193
|
const [timeStart, setTimeStart] = useState(moment().subtract(1, 'day').unix());
|
188
194
|
const [timeEnd, setTimeEnd] = useState(moment().unix());
|
189
|
-
const [firstTimestamp, setFirstTimestamp] = useState();
|
190
195
|
const [datePickerUsed, setDatePickerUsed] = useState(false);
|
196
|
+
const [pickerTimeStart, setPickerTimeStart] = useState(moment().subtract(1, 'day').unix());
|
197
|
+
const [pickerTimeEnd, setPickerTimeEnd] = useState(moment().unix());
|
191
198
|
const [loadingButton, setLoadingButton] = useState(false);
|
192
199
|
const csvLinkRef = useRef(null);
|
193
200
|
const [csvData, setCsvData] = useState('');
|
201
|
+
const [spanGapsOption, setSpanGapsOption] = useState(false);
|
194
202
|
const [options, setOptions] = useState({
|
195
203
|
...lineOptions,
|
196
204
|
plugins: {
|
@@ -229,12 +237,19 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
229
237
|
setDataMeasures([...[]]);
|
230
238
|
setCsvData("");
|
231
239
|
};
|
240
|
+
const handleSpanGaps = (spanG) => {
|
241
|
+
setOptions({
|
242
|
+
...options,
|
243
|
+
spanGaps: spanG,
|
244
|
+
});
|
245
|
+
setSpanGapsOption(spanG);
|
246
|
+
};
|
232
247
|
const handleChange = (event, newPeriod) => {
|
233
248
|
setZoomed(false);
|
234
249
|
setDatePickerUsed(false);
|
235
250
|
setCsvData('');
|
236
251
|
if (newPeriod === "ALL") {
|
237
|
-
setTimeStart(
|
252
|
+
setTimeStart(1577854800);
|
238
253
|
setTimeEnd(moment().unix());
|
239
254
|
setChartPeriod(newPeriod);
|
240
255
|
return;
|
@@ -249,10 +264,8 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
249
264
|
};
|
250
265
|
const handleExportData = async () => {
|
251
266
|
setLoadingButton(true);
|
252
|
-
const intervalInSeconds = timeEnd - timeStart;
|
253
267
|
const data = await Promise.all(measures.map(async (measure) => {
|
254
|
-
|
255
|
-
return await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, true);
|
268
|
+
return await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, "0s", true);
|
256
269
|
}));
|
257
270
|
const csvData = getCsvData(data, measures);
|
258
271
|
setCsvData(csvData);
|
@@ -268,19 +281,18 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
268
281
|
const intervalInSeconds = timeEnd - timeStart;
|
269
282
|
// Inizializza un array di promesse per ottenere i dati per ciascuna misura
|
270
283
|
const datasetsPromises = measures.map(async (measure) => {
|
271
|
-
const polltime = getPollTime(intervalInSeconds, measure.polltime);
|
272
|
-
const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime,
|
284
|
+
const polltime = measure.polltime ? getPollTime(intervalInSeconds, measure.polltime) : "0s";
|
285
|
+
const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, !!!measure.polltime);
|
273
286
|
const points = GetPoints(influxData);
|
274
287
|
return {
|
275
288
|
label: measure.name,
|
276
289
|
data: points,
|
277
290
|
unit: measure.unit,
|
278
|
-
// borderColor: `hsl(${index * 50}, 70%, 50%)`, // Colore unico per ogni dataset
|
279
291
|
borderWidth: 2,
|
280
292
|
pointRadius: 1,
|
281
293
|
pointHoverRadius: 5,
|
282
294
|
pointHoverBackgroundColor: 'rgba(52, 125, 236, 0.5)',
|
283
|
-
|
295
|
+
// showLine: measure.polltime ? true : false, //<- set this
|
284
296
|
fill: false
|
285
297
|
};
|
286
298
|
});
|
@@ -288,15 +300,21 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
288
300
|
const datasets = await Promise.all(datasetsPromises);
|
289
301
|
let min = null;
|
290
302
|
let max = null;
|
291
|
-
let
|
303
|
+
let time;
|
304
|
+
let minTime = null;
|
292
305
|
datasets.forEach(dataset => {
|
293
|
-
values =
|
306
|
+
const values = dataset.data.map((point) => point.y).filter((data) => data !== null);
|
307
|
+
if (chartPeriod === "ALL" && !datePickerUsed && !zoomed && values.length) {
|
308
|
+
time = dataset.data.filter((data) => data.y !== null).map((data) => data.x)[0];
|
309
|
+
}
|
294
310
|
const datasetMin = Math.min(...values);
|
295
311
|
const datasetMax = Math.max(...values);
|
296
312
|
if (min === null || datasetMin < min)
|
297
313
|
min = datasetMin;
|
298
314
|
if (max === null || datasetMax > max)
|
299
315
|
max = datasetMax;
|
316
|
+
if (time && (minTime === null || time.unix() < minTime))
|
317
|
+
minTime = time.unix() - 86400;
|
300
318
|
});
|
301
319
|
let paddedMin = null;
|
302
320
|
let paddedMax = null;
|
@@ -306,6 +324,7 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
306
324
|
paddedMax = max + diff;
|
307
325
|
}
|
308
326
|
setDataMeasures([...datasets]);
|
327
|
+
setTimeStartPicker(minTime || timeStart);
|
309
328
|
setOptions({
|
310
329
|
...options,
|
311
330
|
scales: {
|
@@ -317,7 +336,7 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
317
336
|
},
|
318
337
|
x: {
|
319
338
|
...options.scales.x,
|
320
|
-
min: moment.unix(timeStart).toString(),
|
339
|
+
min: moment.unix(minTime || timeStart).toString(),
|
321
340
|
max: moment.unix(timeEnd).toString(),
|
322
341
|
time: {
|
323
342
|
...options.scales.x.time,
|
@@ -327,15 +346,6 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
327
346
|
}
|
328
347
|
});
|
329
348
|
};
|
330
|
-
useEffect(() => {
|
331
|
-
const fetchFirstTimestamp = async () => {
|
332
|
-
const response = await handleGetFirstTimestamp(deviceId);
|
333
|
-
if (response) {
|
334
|
-
setFirstTimestamp(response);
|
335
|
-
}
|
336
|
-
};
|
337
|
-
fetchFirstTimestamp();
|
338
|
-
}, []);
|
339
349
|
useEffect(() => {
|
340
350
|
const timeDifference = Math.abs(moment(timeStart).valueOf() - moment(timeEnd).valueOf()); // Convert milliseconds to seconds
|
341
351
|
let newChartPeriod = '1D'; // Default to 1 day
|
@@ -393,34 +403,38 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
393
403
|
loadZoomPlugin();
|
394
404
|
resetChart();
|
395
405
|
}, []);
|
396
|
-
const datePicker = _jsx(Box, { sx: { display: 'flex', alignItems: 'center', mr: { xs: 0, lg: 2 } }, children: _jsxs(LocalizationProvider, { dateAdapter: AdapterMoment, adapterLocale: "it", children: [_jsx(DateTimePicker, { value: moment(
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
406
|
+
const datePicker = _jsx(Box, { sx: { display: 'flex', alignItems: 'center', mr: { xs: 0, lg: 2 } }, children: _jsxs(LocalizationProvider, { dateAdapter: AdapterMoment, adapterLocale: "it", children: [_jsx(DateTimePicker, { value: moment(timeStartPicker * 1000), onChange: (newValue) => {
|
407
|
+
setDatePickerUsed(true);
|
408
|
+
setZoomed(false);
|
409
|
+
setPickerTimeStart(moment(newValue).unix());
|
410
|
+
setTimeStart(moment(newValue).unix());
|
411
|
+
}, maxDateTime: moment(timeEnd * 1000), slotProps: {
|
401
412
|
textField: { size: 'small', sx: { width: { sm: 210 } } }
|
402
|
-
} }), " \u00A0\u00A0 ", _jsx(Box, { children: "\u2013" }), " \u00A0\u00A0", _jsx(DateTimePicker, { value: moment(timeEnd * 1000),
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
413
|
+
} }), " \u00A0\u00A0 ", _jsx(Box, { children: "\u2013" }), " \u00A0\u00A0", _jsx(DateTimePicker, { value: moment(timeEnd * 1000), onChange: (newValue) => {
|
414
|
+
setDatePickerUsed(true);
|
415
|
+
setZoomed(false);
|
416
|
+
setPickerTimeEnd(moment(newValue).unix());
|
417
|
+
setTimeEnd(moment(newValue).unix());
|
418
|
+
}, minDateTime: moment(timeStart * 1000), slotProps: {
|
407
419
|
textField: { size: 'small', sx: { width: { sm: 210 } } }
|
408
420
|
} })] }) });
|
409
|
-
return (_jsxs(ThemeProvider, { theme: theme, children: [enableDatePicker && _jsx(Box, { sx: { display: { xs: 'flex', lg: 'none', justifyContent: 'flex-end' }, mb: 2 }, children: datePicker }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs(Box, { children: [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
|
421
|
+
return (_jsxs(ThemeProvider, { theme: theme, children: [enableDatePicker && _jsx(Box, { sx: { display: { xs: 'flex', lg: 'none', justifyContent: 'flex-end' }, mb: 2 }, children: datePicker }), _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].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
|
410
422
|
//@ts-ignore
|
411
423
|
, {
|
412
424
|
//@ts-ignore
|
413
425
|
ref: csvLinkRef, data: csvData, filename: `export_${moment().toISOString()}.csv`, separator: ';' })] })), zoomed && (_jsxs(Button, { sx: { minWidth: '40px !important', boxShadow: 1, px: 1 }, variant: "contained", color: "secondary", size: 'small', onClick: () => {
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
setZoomed(false);
|
426
|
+
setZoomed(false);
|
427
|
+
if (chartPeriod === "ALL") {
|
428
|
+
setDatePickerUsed(false);
|
429
|
+
setTimeStart(1577854800);
|
419
430
|
setTimeEnd(moment().unix());
|
420
|
-
|
431
|
+
}
|
432
|
+
else if (datePickerUsed) {
|
433
|
+
setTimeStart(pickerTimeStart);
|
434
|
+
setTimeEnd(pickerTimeEnd);
|
421
435
|
}
|
422
436
|
else {
|
423
|
-
|
437
|
+
setDatePickerUsed(false);
|
424
438
|
setChartPeriodConfig(chartConfigByPeriod[chartPeriod]);
|
425
439
|
setTimeStart(chartPeriodConfig.from?.days
|
426
440
|
? moment()
|
@@ -429,9 +443,9 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
429
443
|
: moment().subtract(1, 'day').unix());
|
430
444
|
setTimeEnd(moment().unix());
|
431
445
|
}
|
432
|
-
}, children: [_jsx(ZoomOut, { fontSize: 'small' }), _jsx(Box, { sx: { display: { xs: 'none',
|
446
|
+
}, children: [_jsx(ZoomOut, { fontSize: 'small' }), _jsx(Box, { sx: { display: { xs: 'none', lg: 'block' }, ml: { md: 1 } }, children: "Reset" })] }))] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: [enableDatePicker && _jsx(Box, { sx: { display: { xs: 'none', lg: 'flex' } }, children: datePicker }), _jsxs(ToggleButtonGroup, { color: "primary", value: !datePickerUsed && !zoomed ? chartPeriod : null, exclusive: true, onChange: handleChange, size: "small",
|
433
447
|
// sx={{ boxShadow: 1 }}
|
434
|
-
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(Box, { component: 'div', className: "chart-container", sx: { height: { xs: enableDatePicker && props.height ? `calc(${props.height} - 50px)` : props.height, lg: props.height }, minHeight: 300, mt: 2 }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
|
448
|
+
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(ToggleButton, { value: "check", color: "primary", size: "small", selected: spanGapsOption, disabled: chartLoading, onChange: () => handleSpanGaps(!spanGapsOption), sx: { ml: 2 }, children: _jsx(TimelineIcon, {}) }) })] })] }), _jsx(Box, { component: 'div', className: "chart-container", sx: { height: { xs: enableDatePicker && props.height ? `calc(${props.height} - 50px)` : props.height, lg: props.height }, minHeight: 300, mt: 2 }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
|
435
449
|
_jsx(_Fragment, { children: dataMeasures && dataMeasures[0]?.data?.length ?
|
436
450
|
(_jsx(Line, { options: options, data: {
|
437
451
|
datasets: dataMeasures || [{ data: [] }]
|