@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, handleGetFirstTimestamp, theme, ...props }: {
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: true, // enable for all datasets
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
- // Create rows from timestampMap
164
- Object.values(timestampMap).forEach((entry) => {
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 or just the timestamp, add it to csvData
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, handleGetFirstTimestamp, theme, ...props }) => {
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(firstTimestamp - 86400);
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
- const polltime = getPollTime(intervalInSeconds, measure.polltime);
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, false);
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
- spanGaps: false,
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 values = [];
303
+ let time;
304
+ let minTime = null;
292
305
  datasets.forEach(dataset => {
293
- values = [...values, ...dataset.data.map((point) => point.y).filter((data) => data !== null)];
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(timeStart * 1000),
397
- // format="DD/MM/YY hh:mm A"
398
- onChange: (newValue) => {
399
- setTimeStart((moment(newValue).unix()));
400
- }, maxDate: moment(timeEnd * 1000), slotProps: {
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
- // format="DD/MM/YY hh:mm A"
404
- onChange: (newValue) => {
405
- setTimeEnd((moment(newValue).unix()));
406
- }, minDate: moment(timeStart * 1000), slotProps: {
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
- if (datePickerUsed) {
415
- setZoomed(false);
416
- }
417
- else if (chartPeriod === "ALL") {
418
- setZoomed(false);
426
+ setZoomed(false);
427
+ if (chartPeriod === "ALL") {
428
+ setDatePickerUsed(false);
429
+ setTimeStart(1577854800);
419
430
  setTimeEnd(moment().unix());
420
- setTimeStart(firstTimestamp - 86400);
431
+ }
432
+ else if (datePickerUsed) {
433
+ setTimeStart(pickerTimeStart);
434
+ setTimeEnd(pickerTimeEnd);
421
435
  }
422
436
  else {
423
- setZoomed(false);
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', xl: '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: chartPeriod, exclusive: true, onChange: handleChange, size: "small",
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: [] }]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iotready/nextjs-components-library",
3
- "version": "1.0.0-preview2",
3
+ "version": "1.0.0-preview4",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && tsc --project tsconfig.build.json && cp package.json dist/",