@iotready/nextjs-components-library 1.0.0-preview19 → 1.0.0-preview20

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.
@@ -1,6 +1,7 @@
1
1
  import 'chartjs-adapter-moment';
2
2
  import 'moment/locale/it';
3
3
  import { Theme } from "@emotion/react";
4
+ import { InfluxFillType } from '../../server-actions/types';
4
5
  type Measure = {
5
6
  name: string;
6
7
  description: string;
@@ -8,15 +9,19 @@ type Measure = {
8
9
  unit: string;
9
10
  stepped: boolean;
10
11
  };
11
- declare const TrendChart: ({ deviceId, measures1, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme, initialTimeStart, initialTimeEnd }: {
12
+ declare const TrendChart: ({ deviceId, measures1, annotationsData, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme, initialTimeStart, initialTimeEnd, dwEnabled, dwCallback, dwHandled }: {
12
13
  deviceId: string;
14
+ annotationsData?: Array<[any, any]>;
13
15
  measures1?: Array<Measure>;
14
16
  measures2?: Array<Measure>;
15
17
  enableDatePicker: boolean;
16
- handleGetInfluxData: (measure: string, timeStart: number, timeEnd: number, deviceId: string, timeGroup: string, raw: boolean, fill?: boolean) => Promise<any>;
18
+ handleGetInfluxData: (measure: string, timeStart: number, timeEnd: number, deviceId: string, timeGroup: string, raw: boolean, fill?: InfluxFillType) => Promise<any>;
17
19
  handleExportDataCB?: (measure: string, timeStart: number, timeEnd: number, deviceId: string) => Promise<any>;
18
20
  theme: Theme;
19
21
  initialTimeStart?: number;
20
22
  initialTimeEnd?: number;
23
+ dwEnabled?: boolean;
24
+ dwCallback?: (val: boolean) => void;
25
+ dwHandled?: boolean;
21
26
  }) => import("react/jsx-runtime").JSX.Element;
22
27
  export default TrendChart;
@@ -20,6 +20,8 @@ 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';
23
25
  const lineOptions = {
24
26
  parsing: false,
25
27
  normalized: true,
@@ -186,8 +188,9 @@ function getCsvData(data, measures) {
186
188
  return csvData.map(row => row.join(',')).join('\n');
187
189
  }
188
190
  // eslint-disable-next-line no-unused-vars
189
- const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme, initialTimeStart, initialTimeEnd }) => {
191
+ const TrendChart = ({ deviceId, measures1, annotationsData, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme, initialTimeStart, initialTimeEnd, dwEnabled, dwCallback, dwHandled }) => {
190
192
  const [chartJsLoaded, setChartJsLoaded] = useState(false);
193
+ const [annotentionsEnabled, setAnnotationsEnabled] = useState(true);
191
194
  const [dataMeasures, setDataMeasures] = useState(null);
192
195
  const [chartPeriod, setChartPeriod] = useState('1D');
193
196
  const [chartPeriodConfig, setChartPeriodConfig] = useState(chartConfigByPeriod['1D']);
@@ -258,22 +261,24 @@ const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGe
258
261
  setSpanGapsOption(spanG);
259
262
  };
260
263
  const handleChange = (event, newPeriod) => {
261
- setChartLoading(true);
262
- setZoomed(false);
263
- setDatePickerUsed(false);
264
- setCsvData('');
265
- if (newPeriod === "ALL") {
266
- setTimeStart(1577854800);
267
- setTimeEnd(moment().unix());
268
- setChartPeriod(newPeriod);
269
- return;
270
- }
271
- const periodConfig = chartConfigByPeriod[newPeriod];
272
- if (periodConfig) {
273
- setTimeStart(Math.round(moment().subtract(periodConfig.from).valueOf() / 1000));
274
- setTimeEnd(Math.round(moment().valueOf() / 1000));
275
- setChartPeriod(newPeriod);
276
- setChartPeriodConfig(periodConfig);
264
+ if (newPeriod && newPeriod !== chartPeriod) {
265
+ setChartLoading(true);
266
+ setZoomed(false);
267
+ setDatePickerUsed(false);
268
+ setCsvData('');
269
+ if (newPeriod === "ALL") {
270
+ setTimeStart(1577854800);
271
+ setTimeEnd(moment().unix());
272
+ setChartPeriod(newPeriod);
273
+ return;
274
+ }
275
+ const periodConfig = chartConfigByPeriod[newPeriod];
276
+ if (periodConfig) {
277
+ setTimeStart(Math.round(moment().subtract(periodConfig.from).valueOf() / 1000));
278
+ setTimeEnd(Math.round(moment().valueOf() / 1000));
279
+ setChartPeriod(newPeriod);
280
+ setChartPeriodConfig(periodConfig);
281
+ }
277
282
  }
278
283
  };
279
284
  const handleExportData = async () => {
@@ -302,7 +307,7 @@ const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGe
302
307
  ];
303
308
  const datasetsPromises = allMeasures.map(async (measure) => {
304
309
  const polltime = getPollTime(intervalInSeconds, measure.polltime || 30);
305
- const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, !measure.polltime && rawQuery, !measure.polltime);
310
+ const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, !measure.polltime && rawQuery, dwEnabled ? "previous" : !measure.polltime ? "none" : "null");
306
311
  const points = getChartPoints(influxData);
307
312
  return {
308
313
  label: measure.description || measure.name,
@@ -357,33 +362,106 @@ const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGe
357
362
  };
358
363
  const { paddedMin: paddedMin1, paddedMax: paddedMax1 } = getPaddedMinMax(min1, max1);
359
364
  const { paddedMin: paddedMin2, paddedMax: paddedMax2 } = getPaddedMinMax(min2, max2);
365
+ // Handle undefined/null annotationsData
366
+ let dynamicAnnotations = {};
367
+ if (Array.isArray(annotationsData) && annotationsData.length > 0 && annotentionsEnabled) {
368
+ dynamicAnnotations = annotationsData.reduce((acc, [timestamp, label], index) => {
369
+ const yVal = paddedMin1 !== null && paddedMin1 !== undefined
370
+ ? paddedMin1 + 0.01 * (paddedMax1 - paddedMin1)
371
+ : paddedMin2 !== null && paddedMin2 !== undefined
372
+ ? paddedMin2 + 0.01 * (paddedMax2 - paddedMin2)
373
+ : 0;
374
+ acc[`line${index}`] = {
375
+ type: 'line',
376
+ xMin: timestamp,
377
+ xMax: timestamp,
378
+ borderColor: 'rgba(255, 225, 0, 0.8)',
379
+ borderWidth: 2,
380
+ drawTime: 'afterDatasetsDraw',
381
+ label: {
382
+ content: label,
383
+ enabled: false
384
+ }
385
+ };
386
+ acc[`triangle${index}`] = {
387
+ type: 'point',
388
+ xValue: timestamp,
389
+ yValue: yVal,
390
+ backgroundColor: 'rgba(255, 225, 0, 0.8)',
391
+ pointStyle: 'triangle',
392
+ radius: 6,
393
+ rotation: 0,
394
+ tooltip: {
395
+ enabled: true,
396
+ callbacks: {
397
+ label: () => label
398
+ }
399
+ }
400
+ };
401
+ acc[`label${index}`] = {
402
+ type: 'label',
403
+ xValue: timestamp,
404
+ yValue: yVal + 0.1 * (((paddedMax1 ?? paddedMax2 ?? 0) - (paddedMax1 ?? paddedMax2 ?? 0))),
405
+ xAdjust: 0,
406
+ yAdjust: -20,
407
+ backgroundColor: 'rgba(245,245,245)',
408
+ borderColor: 'rgba(255, 225, 0, 0.8)',
409
+ borderWidth: 1,
410
+ content: [label],
411
+ textAlign: 'start',
412
+ font: {
413
+ size: 10,
414
+ weight: 'bold'
415
+ },
416
+ callout: {
417
+ display: false,
418
+ }
419
+ };
420
+ return acc;
421
+ }, {});
422
+ }
423
+ // 👇 Combina statiche + dinamiche
424
+ const annotations = {
425
+ // ...staticAnnotations,
426
+ ...dynamicAnnotations
427
+ };
428
+ // 👇 Configurazione completa
360
429
  setDataMeasures(datasets);
361
430
  setTimeStartPicker(minTime || timeStart);
362
- const scalesOptions = {
363
- ...options.scales,
364
- y1: {
365
- ...options.scales.y1,
366
- min: paddedMin1,
367
- max: paddedMax1,
368
- position: "left",
369
- display: "auto"
370
- },
371
- y2: {
372
- ...options.scales.y2,
373
- min: paddedMin2,
374
- max: paddedMax2,
375
- position: "right",
376
- display: "auto"
377
- },
378
- x: {
379
- ...options.scales.x,
380
- min: moment.unix(minTime || timeStart).toString(),
381
- max: moment.unix(timeEnd).toString()
382
- }
383
- };
384
431
  setOptions({
385
432
  ...options,
386
- scales: scalesOptions
433
+ scales: {
434
+ ...options.scales,
435
+ y1: {
436
+ ...options.scales.y1,
437
+ min: paddedMin1,
438
+ max: paddedMax1,
439
+ position: "left",
440
+ display: "auto"
441
+ },
442
+ y2: {
443
+ ...options.scales.y2,
444
+ min: paddedMin2,
445
+ max: paddedMax2,
446
+ position: "right",
447
+ display: "auto"
448
+ },
449
+ x: {
450
+ ...options.scales.x,
451
+ min: moment.unix(minTime || timeStart).toString(),
452
+ max: moment.unix(timeEnd).toString()
453
+ }
454
+ },
455
+ plugins: {
456
+ ...options.plugins,
457
+ annotation: {
458
+ interaction: {
459
+ mode: 'nearest',
460
+ intersect: true
461
+ },
462
+ annotations
463
+ }
464
+ }
387
465
  });
388
466
  };
389
467
  useEffect(() => {
@@ -422,7 +500,7 @@ const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGe
422
500
  loadDatasets(chartPeriod).then(() => {
423
501
  setChartLoading(false);
424
502
  });
425
- }, [measures1, timeEnd, timeStart, measures2]);
503
+ }, [measures1, timeEnd, timeStart, measures2, annotentionsEnabled, annotationsData, dwEnabled]);
426
504
  useEffect(() => {
427
505
  const loadZoomPlugin = async () => {
428
506
  const zoomPlugin = (await import('chartjs-plugin-zoom')).default;
@@ -514,7 +592,7 @@ const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGe
514
592
  '& .MuiToggleButton-root': {
515
593
  color: 'text.primary', fontSize: '0.95rem', fontWeight: 'normal', paddingTop: '6px', paddingBottom: '6px'
516
594
  }
517
- }, 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: { mt: 2, height: '100%' }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
595
+ }, 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: 1 }, children: _jsx(TimelineIcon, {}) }) }), annotationsData !== undefined && (_jsx(MuiTooltip, { placement: "top", arrow: true, title: "Show annotations", children: _jsx(ToggleButton, { value: "check", color: "primary", size: "small", selected: annotentionsEnabled, disabled: chartLoading, onChange: () => setAnnotationsEnabled(!annotentionsEnabled), sx: { ml: 1 }, children: _jsx(EditNoteIcon, {}) }) })), dwCallback !== undefined && dwEnabled != undefined && (_jsx(MuiTooltip, { placement: "top", arrow: true, title: "Show data window data", children: _jsx(ToggleButton, { value: "check", color: "primary", size: "small", selected: dwEnabled, disabled: chartLoading || !dwHandled, onChange: () => dwCallback(!dwEnabled), sx: { ml: 1 }, children: _jsx(AspectRatioIcon, {}) }) }))] })] }), _jsx(Box, { component: 'div', className: "chart-container", sx: { mt: 2, height: '100%' }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
518
596
  _jsx(_Fragment, { children: dataMeasures && (dataMeasures.length > 1 || (dataMeasures.length === 1 && dataMeasures[0].data?.length)) ?
519
597
  (_jsx(Line, { options: options, data: {
520
598
  // 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-preview19",
3
+ "version": "1.0.0-preview20",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "build": "rm -rf dist && tsc --project tsconfig.build.json && cp package.json dist/",
@@ -43,4 +43,4 @@
43
43
  "eslint-config-next": "14.2.9",
44
44
  "typescript": "^5"
45
45
  }
46
- }
46
+ }
@@ -0,0 +1,4 @@
1
+ import { FirebaseConfig } from "./types";
2
+ export declare const getAnnotations: (firebaseConfig: FirebaseConfig, deviceID: number) => Promise<{
3
+ id: string;
4
+ }[]>;
@@ -0,0 +1,15 @@
1
+ "use server";
2
+ import { initializeApp } from "firebase/app";
3
+ import { collection, query, orderBy, getDocs, where } from "@firebase/firestore";
4
+ import { getFirestore } from "@firebase/firestore";
5
+ export const getAnnotations = async (firebaseConfig, deviceID) => {
6
+ // Initialize Firebase
7
+ const app = initializeApp(firebaseConfig);
8
+ const db = getFirestore(app);
9
+ const annotationsQuery = query(collection(db, "annotations"), where("target", "==", deviceID), orderBy("createdAt", "desc"));
10
+ const groupsSnapshot = await getDocs(annotationsQuery);
11
+ return groupsSnapshot.docs.map((doc) => ({
12
+ id: doc.id,
13
+ ...doc.data()
14
+ }));
15
+ };
@@ -1,11 +1,4 @@
1
- export type FirebaseConfig = {
2
- apiKey: string;
3
- authDomain: string;
4
- projectId: string;
5
- storageBucket: string;
6
- messagingSenderId: string;
7
- appId: string;
8
- };
1
+ import { FirebaseConfig } from "./types";
9
2
  export declare const getGroups: (firebaseConfig: FirebaseConfig, productID: number, userID?: string) => Promise<{
10
3
  id: string;
11
4
  }[]>;
@@ -2,3 +2,4 @@ export * from "./groups";
2
2
  export * from "./influx";
3
3
  export * from "./logto";
4
4
  export * from "./trackle";
5
+ export * from "./annotations";
@@ -3,3 +3,4 @@ export * from "./groups";
3
3
  export * from "./influx";
4
4
  export * from "./logto";
5
5
  export * from "./trackle";
6
+ export * from "./annotations";
@@ -1,13 +1,4 @@
1
- export type InfluxConfig = {
2
- url: string;
3
- accessToken: string;
4
- bucket: string;
5
- orgId: string;
6
- measurement: string;
7
- dbName: string;
8
- username: string;
9
- password: string;
10
- };
11
- export declare function getInfluxAlarms(influxConfig: InfluxConfig, fields: string[], limit: number, offset: number, sort: any, deviceID: string, timeStart: number, timeEnd: number): Promise<any>;
12
- export declare function getInfluxDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string, timeGroup: string, raw: boolean, fill?: boolean): Promise<any>;
1
+ import { InfluxConfig, InfluxFillType } from "./types";
2
+ export declare function getInfluxAlerts(influxConfig: InfluxConfig, fields: string[], limit: number, offset: number, sort: any, deviceID: string, timeStart: number, timeEnd: number): Promise<any>;
3
+ export declare function getInfluxDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string, timeGroup: string, raw: boolean, fill?: InfluxFillType, filterTag?: number): Promise<any>;
13
4
  export declare function exportDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string): Promise<any>;
@@ -1,7 +1,7 @@
1
1
  "use server";
2
2
  import { parse } from "csv-parse";
3
3
  import moment from "moment";
4
- export async function getInfluxAlarms(influxConfig, fields, limit, offset, sort, deviceID, timeStart, timeEnd) {
4
+ export async function getInfluxAlerts(influxConfig, fields, limit, offset, sort, deviceID, timeStart, timeEnd) {
5
5
  const conditions = fields
6
6
  .map((field) => `r["valueName"] == "${field}"`)
7
7
  .join(" or ");
@@ -13,6 +13,10 @@ export async function getInfluxAlarms(influxConfig, fields, limit, offset, sort,
13
13
  }
14
14
  query += `
15
15
  |> filter(fn: (r) => r["_measurement"] == "${influxConfig.measurement}" and r["deviceid"] == "${deviceID}" and (${conditions}))
16
+ `;
17
+ // Add filter for tag d === 3
18
+ query += `
19
+ |> filter(fn: (r) => r["d"] == "3" or r["d"] !~ /./) // Include only events with tag d == 3 or no tag d
16
20
  `;
17
21
  const queryCount = `${query} |> group() |> count()`;
18
22
  query += `
@@ -70,35 +74,44 @@ export async function getInfluxAlarms(influxConfig, fields, limit, offset, sort,
70
74
  });
71
75
  const parsedCount = count.split("\n")[1].split(",");
72
76
  let countData = parsedCount[5];
77
+ console.log("Count Data:", countData);
78
+ console.log("Rows:", rows);
73
79
  return {
74
80
  data: rows,
75
81
  count: countData
76
82
  };
77
83
  }
78
- export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, deviceID, timeGroup, raw, fill) {
84
+ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, deviceID, timeGroup, raw, fill, filterTag) {
79
85
  let query;
80
86
  let preStartValue = null;
87
+ // Build filter for filterTag if provided
88
+ const filterTagCondition = filterTag !== undefined && filterTag !== null
89
+ ? ` AND ("d" = '${filterTag}' OR "d" !~ /./)`
90
+ : "";
81
91
  if (raw) {
82
- query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
92
+ query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time >= '${moment
83
93
  .unix(timeStart)
84
94
  .toISOString()}' AND time <= '${moment.unix(timeEnd).toISOString()}'`;
85
95
  }
86
96
  else {
87
- query = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
97
+ query = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time >= '${moment
88
98
  .unix(timeStart)
89
99
  .toISOString()}' AND time <= '${moment
90
100
  .unix(timeEnd)
91
101
  .toISOString()}' GROUP BY time(${timeGroup})`;
92
- if (fill) {
102
+ if (fill === "none") {
93
103
  query += ` fill(none)`;
94
104
  }
105
+ else if (fill === "previous") {
106
+ query += ` fill(previous)`;
107
+ }
95
108
  else {
96
109
  query += ` fill(null)`;
97
110
  }
98
111
  }
99
112
  if (fill) {
100
113
  // Query to get the last data point before timeStart
101
- const preStartQuery = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time < '${moment
114
+ const preStartQuery = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time < '${moment
102
115
  .unix(timeStart)
103
116
  .toISOString()}'`;
104
117
  const preStartResponse = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${preStartQuery}`), {
@@ -0,0 +1,19 @@
1
+ export type FirebaseConfig = {
2
+ apiKey: string;
3
+ authDomain: string;
4
+ projectId: string;
5
+ storageBucket: string;
6
+ messagingSenderId: string;
7
+ appId: string;
8
+ };
9
+ export type InfluxConfig = {
10
+ url: string;
11
+ accessToken: string;
12
+ bucket: string;
13
+ orgId: string;
14
+ measurement: string;
15
+ dbName: string;
16
+ username: string;
17
+ password: string;
18
+ };
19
+ export type InfluxFillType = "null" | "none" | "previous";
@@ -0,0 +1 @@
1
+ export {};
package/types/user.d.ts CHANGED
@@ -4,6 +4,7 @@ export type UserType = {
4
4
  name: string;
5
5
  picture?: string;
6
6
  role: string;
7
+ demo?: any;
7
8
  lastSignInAt?: number;
8
9
  firstname?: string;
9
10
  lastname?: string;