@iotready/nextjs-components-library 1.0.0-preview27 → 1.0.0-preview29
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.js +177 -34
- package/components/groups/GroupsDevices.js +6 -11
- package/components/users/UsersDataGrid.d.ts +5 -2
- package/components/users/UsersDataGrid.js +49 -32
- package/package.json +3 -1
- package/server-actions/trackle.d.ts +4 -0
- package/server-actions/trackle.js +20 -6
|
@@ -33,5 +33,5 @@ export default function UserMenuAccount({ userInfo, handleClick, handleSignOut,
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
}
|
|
36
|
-
}, children: [_jsxs(ListItem, { sx: { display: 'flex', alignItems: 'center' }, children: [_jsx(ListItemIcon, { sx: { minWidth: 46 }, children: _jsx(Avatar, {}) }), _jsxs(Box, { children: [_jsx(Typography, { variant: "subtitle2", children: userInfo.name?.replace('/', ' ') }), _jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 'normal' }, children: userInfo.email })] })] }), _jsxs(MenuItem, { onClick: () => { router.push(accountRoute); handleClick(); handleClose(); }, sx: { py: 1 }, children: [_jsx(ListItemIcon, { sx: { mr: 1 }, children: _jsx(Person, { fontSize: "small" }) }), " Manage account"] }), _jsxs(MenuItem, { onClick: async () => await handleSignOut(), sx: { py: 1 }, children: [_jsx(ListItemIcon, { sx: { mr: 1 }, children: _jsx(Logout, { fontSize: "small" }) }), " Sign Out"] })] }), _jsx(Tooltip, { title: "Open menu", children: _jsx(IconButton, { onClick: handleAccountClick, children: _jsx(Avatar, { sx: { bgcolor: 'secondary.main', color: 'secondary.contrastText' }, alt: userInfo.name
|
|
36
|
+
}, children: [_jsxs(ListItem, { sx: { display: 'flex', alignItems: 'center' }, children: [_jsx(ListItemIcon, { sx: { minWidth: 46 }, children: _jsx(Avatar, {}) }), _jsxs(Box, { children: [_jsx(Typography, { variant: "subtitle2", children: userInfo.name?.replace('/', ' ') }), _jsx(Typography, { variant: "subtitle2", sx: { fontWeight: 'normal' }, children: userInfo.email })] })] }), _jsxs(MenuItem, { onClick: () => { router.push(accountRoute); handleClick(); handleClose(); }, sx: { py: 1 }, children: [_jsx(ListItemIcon, { sx: { mr: 1 }, children: _jsx(Person, { fontSize: "small" }) }), " Manage account"] }), _jsxs(MenuItem, { onClick: async () => await handleSignOut(), sx: { py: 1 }, children: [_jsx(ListItemIcon, { sx: { mr: 1 }, children: _jsx(Logout, { fontSize: "small" }) }), " Sign Out"] })] }), _jsx(Tooltip, { title: "Open menu", children: _jsx(IconButton, { onClick: handleAccountClick, children: _jsx(Avatar, { sx: { bgcolor: 'secondary.main', color: 'secondary.contrastText' }, alt: userInfo.name !== " " ? userInfo.name : userInfo.email, src: userInfo.picture, children: userInfo.picture ? '' : userInfo.name !== " " ? userInfo.name?.charAt(0).toUpperCase() : userInfo.email?.charAt(0).toUpperCase() }) }) })] }) }));
|
|
37
37
|
}
|
|
@@ -74,7 +74,7 @@ const AccountProfile = ({ userAccount, handleSignOut, handleUpdateUser, handleUp
|
|
|
74
74
|
setLoadingUpdateButton(true);
|
|
75
75
|
if (userInfo) {
|
|
76
76
|
try {
|
|
77
|
-
await handleUpdateUser(userInfo.id, {
|
|
77
|
+
await handleUpdateUser(userInfo.id, { firstname: userInfo.firstname, lastname: userInfo.lastname });
|
|
78
78
|
if (afterUpdateCallback) {
|
|
79
79
|
afterUpdateCallback(userInfo);
|
|
80
80
|
}
|
|
@@ -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";
|
|
@@ -23,6 +23,126 @@ import MuiTooltip from '@mui/material/Tooltip';
|
|
|
23
23
|
import EditNoteIcon from '@mui/icons-material/EditNote';
|
|
24
24
|
// import AspectRatioIcon from '@mui/icons-material/AspectRatio';
|
|
25
25
|
import { FilterTagMode } from '../../server-actions/types';
|
|
26
|
+
import { getRelativePosition } from 'chart.js/helpers';
|
|
27
|
+
function findIndexForX(arr, t) {
|
|
28
|
+
if (!arr || arr.length === 0)
|
|
29
|
+
return -1;
|
|
30
|
+
if (t <= arr[0].x)
|
|
31
|
+
return 0;
|
|
32
|
+
let lo = 0;
|
|
33
|
+
let hi = arr.length - 1;
|
|
34
|
+
let res = -1;
|
|
35
|
+
while (lo <= hi) {
|
|
36
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
37
|
+
const midX = arr[mid]?.x;
|
|
38
|
+
if (midX === undefined)
|
|
39
|
+
return -1;
|
|
40
|
+
if (midX === t)
|
|
41
|
+
return mid;
|
|
42
|
+
if (midX < t) {
|
|
43
|
+
res = mid;
|
|
44
|
+
lo = mid + 1;
|
|
45
|
+
}
|
|
46
|
+
else
|
|
47
|
+
hi = mid - 1;
|
|
48
|
+
}
|
|
49
|
+
return res;
|
|
50
|
+
}
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
Tooltip.positioners.myCustomPositioner = function (elements, eventPosition) {
|
|
53
|
+
const chart = this.chart;
|
|
54
|
+
const activePos = chart.__activePos;
|
|
55
|
+
if (!activePos || !activePos.closestPoint) {
|
|
56
|
+
return { x: eventPosition.x, y: eventPosition.y };
|
|
57
|
+
}
|
|
58
|
+
const closest = activePos.closestPoint.element;
|
|
59
|
+
const pixelX = activePos.closestPoint.pixelX;
|
|
60
|
+
const pixelY = closest?.y ?? eventPosition.y;
|
|
61
|
+
return { x: pixelX, y: pixelY };
|
|
62
|
+
};
|
|
63
|
+
// Definisci la modalità solo se Interaction è disponibile
|
|
64
|
+
if (Interaction && Interaction.modes) {
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
Interaction.modes.myCustomMode = function (chart, e, options, useFinalPosition) {
|
|
67
|
+
const pos = getRelativePosition(e, chart);
|
|
68
|
+
const xScale = chart.scales?.x;
|
|
69
|
+
const hoveredX = xScale && typeof xScale.getValueForPixel === "function"
|
|
70
|
+
? xScale.getValueForPixel(pos.x)
|
|
71
|
+
: null;
|
|
72
|
+
const items = [];
|
|
73
|
+
let closestPoint = null;
|
|
74
|
+
let closestDistance = Infinity;
|
|
75
|
+
for (let datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
|
|
76
|
+
const meta = chart.getDatasetMeta(datasetIndex);
|
|
77
|
+
if (!meta || !meta.data || meta.data.length === 0)
|
|
78
|
+
continue;
|
|
79
|
+
let found = null;
|
|
80
|
+
let pointPixelX = null;
|
|
81
|
+
for (let i = 0; i < meta.data.length; i++) {
|
|
82
|
+
const el = meta.data[i];
|
|
83
|
+
if (el && typeof el.inXRange === "function" && el.inXRange(pos.x, useFinalPosition)) {
|
|
84
|
+
found = { element: el, datasetIndex, index: i };
|
|
85
|
+
pointPixelX = el.x;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!found && hoveredX != null) {
|
|
90
|
+
const dataArr = chart.data.datasets[datasetIndex]?.data || [];
|
|
91
|
+
const idx = findIndexForX(dataArr, hoveredX);
|
|
92
|
+
if (idx >= 0 && meta.data[idx]) {
|
|
93
|
+
found = { element: meta.data[idx], datasetIndex, index: idx };
|
|
94
|
+
const valX = dataArr[idx].x;
|
|
95
|
+
pointPixelX = xScale.getPixelForValue(valX);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (found && pointPixelX != null) {
|
|
99
|
+
items.push(found);
|
|
100
|
+
const xMin = xScale?.left ?? 0;
|
|
101
|
+
const xMax = xScale?.right ?? chart.width;
|
|
102
|
+
console.log({ pointPixelX, xMin, xMax });
|
|
103
|
+
if (pointPixelX < xMin || pointPixelX > xMax) {
|
|
104
|
+
continue; // ignora questo punto
|
|
105
|
+
}
|
|
106
|
+
// Aggiorna il closestPoint globale per la barra verticale
|
|
107
|
+
const distance = Math.abs(pointPixelX - pos.x);
|
|
108
|
+
if (distance < closestDistance) {
|
|
109
|
+
closestDistance = distance;
|
|
110
|
+
closestPoint = { ...found, pixelX: pointPixelX };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
chart.__activePos = hoveredX != null
|
|
115
|
+
? {
|
|
116
|
+
value: hoveredX,
|
|
117
|
+
px: closestPoint ? closestPoint.pixelX : pos.x,
|
|
118
|
+
hasPoint: !!closestPoint
|
|
119
|
+
}
|
|
120
|
+
: null;
|
|
121
|
+
return items;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function getValueAtX(datasetData, t) {
|
|
125
|
+
if (!datasetData || datasetData.length === 0)
|
|
126
|
+
return null;
|
|
127
|
+
// se t è prima del primo punto -> primo valore
|
|
128
|
+
if (t <= datasetData[0].x)
|
|
129
|
+
return datasetData[0].y;
|
|
130
|
+
// binary search per ultimo index con x <= t
|
|
131
|
+
let lo = 0;
|
|
132
|
+
let hi = datasetData.length - 1;
|
|
133
|
+
let res = -1;
|
|
134
|
+
while (lo <= hi) {
|
|
135
|
+
const mid = (lo + hi) >> 1;
|
|
136
|
+
const midX = datasetData[mid].x;
|
|
137
|
+
if (midX <= t) {
|
|
138
|
+
res = mid;
|
|
139
|
+
lo = mid + 1;
|
|
140
|
+
}
|
|
141
|
+
else
|
|
142
|
+
hi = mid - 1;
|
|
143
|
+
}
|
|
144
|
+
return res >= 0 ? datasetData[res].y : null;
|
|
145
|
+
}
|
|
26
146
|
const lineOptions = {
|
|
27
147
|
parsing: false,
|
|
28
148
|
normalized: true,
|
|
@@ -33,14 +153,38 @@ const lineOptions = {
|
|
|
33
153
|
maintainAspectRatio: false,
|
|
34
154
|
interaction: {
|
|
35
155
|
intersect: false,
|
|
36
|
-
mode: '
|
|
156
|
+
mode: 'myCustomMode',
|
|
37
157
|
axis: 'x'
|
|
38
158
|
},
|
|
39
159
|
plugins: {
|
|
40
160
|
tooltip: {
|
|
161
|
+
position: 'myCustomPositioner',
|
|
41
162
|
callbacks: {
|
|
42
163
|
label: (context) => {
|
|
43
|
-
|
|
164
|
+
const ds = context.dataset || {};
|
|
165
|
+
const parsed = context.parsed || {};
|
|
166
|
+
const unit = ds.unit ?? '';
|
|
167
|
+
const chart = context.chart;
|
|
168
|
+
// 1) se parsed.y è disponibile usalo (gestisci stepped senza toFixed)
|
|
169
|
+
if (parsed.y !== null && parsed.y !== undefined) {
|
|
170
|
+
// se la serie è stepped vogliamo mostrare il valore grezzo (es. 1/0)
|
|
171
|
+
if (ds.stepped)
|
|
172
|
+
return `${ds.label}: ${parsed.y} ${unit}`;
|
|
173
|
+
// numero normale -> 3 decimali
|
|
174
|
+
return `${ds.label}: ${Number(parsed.y).toFixed(3)} ${unit}`;
|
|
175
|
+
}
|
|
176
|
+
// 2) fallback: usa parsed.x o context.parsed.x per calcolare il valore via getValueAtX
|
|
177
|
+
const xVal = parsed.x ?? (context.parsed && context.parsed.x) ?? null;
|
|
178
|
+
if (xVal != null && Array.isArray(ds.data)) {
|
|
179
|
+
const val = getValueAtX(ds.data, xVal);
|
|
180
|
+
if (val !== null && val !== undefined) {
|
|
181
|
+
if (ds.stepped)
|
|
182
|
+
return `${ds.label}: ${val} ${unit}`;
|
|
183
|
+
return `${ds.label}: ${Number(val).toFixed(3)} ${unit}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// 3) fallback finale
|
|
187
|
+
return `${ds.label}: - ${unit}`;
|
|
44
188
|
}
|
|
45
189
|
},
|
|
46
190
|
},
|
|
@@ -487,7 +631,7 @@ const TrendChart = ({ filter, measures1, annotationsDataFn, measures2, enableDat
|
|
|
487
631
|
return acc;
|
|
488
632
|
}, {});
|
|
489
633
|
}
|
|
490
|
-
//
|
|
634
|
+
// Combina statiche + dinamiche
|
|
491
635
|
const annotations = {
|
|
492
636
|
// ...staticAnnotations,
|
|
493
637
|
...dynamicAnnotations
|
|
@@ -574,37 +718,36 @@ const TrendChart = ({ filter, measures1, annotationsDataFn, measures2, enableDat
|
|
|
574
718
|
ChartJS.register(Colors, Legend, Title, Tooltip, PointElement, LineElement, LinearScale, TimeScale, annotationPlugin, zoomPlugin, {
|
|
575
719
|
id: 'uniqueid5',
|
|
576
720
|
afterDraw: function (chart) {
|
|
577
|
-
if (chart.
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
const topY = Math.max(topY1, topY2);
|
|
598
|
-
const bottomY = Math.min(bottomY1, bottomY2);
|
|
599
|
-
ctx.save();
|
|
600
|
-
ctx.beginPath();
|
|
601
|
-
ctx.moveTo(x, topY);
|
|
602
|
-
ctx.lineTo(x, bottomY);
|
|
603
|
-
ctx.lineWidth = 1;
|
|
604
|
-
ctx.strokeStyle = '#722257';
|
|
605
|
-
ctx.stroke();
|
|
606
|
-
ctx.restore();
|
|
721
|
+
if (!chart.__activePos?.hasPoint)
|
|
722
|
+
return; // disegna solo sui punti
|
|
723
|
+
const ctx = chart.ctx;
|
|
724
|
+
const x = chart.__activePos.px; // pixel esatto del punto attivo
|
|
725
|
+
let topY1 = 0;
|
|
726
|
+
let topY2 = 0;
|
|
727
|
+
let bottomY1 = 0;
|
|
728
|
+
let bottomY2 = 0;
|
|
729
|
+
if (chart.scales.y1?.top) {
|
|
730
|
+
topY1 = chart.scales.y1.top;
|
|
731
|
+
}
|
|
732
|
+
if (chart.scales.y2?.top) {
|
|
733
|
+
topY2 = chart.scales.y2.top;
|
|
734
|
+
}
|
|
735
|
+
if (chart.scales.y1?.bottom) {
|
|
736
|
+
bottomY1 = chart.scales.y1.bottom;
|
|
737
|
+
}
|
|
738
|
+
if (chart.scales.y2?.bottom) {
|
|
739
|
+
bottomY2 = chart.scales.y2.bottom;
|
|
607
740
|
}
|
|
741
|
+
const topY = Math.max(topY1, topY2);
|
|
742
|
+
const bottomY = Math.min(bottomY1, bottomY2);
|
|
743
|
+
ctx.save();
|
|
744
|
+
ctx.beginPath();
|
|
745
|
+
ctx.moveTo(x, topY);
|
|
746
|
+
ctx.lineTo(x, bottomY);
|
|
747
|
+
ctx.lineWidth = 1;
|
|
748
|
+
ctx.strokeStyle = '#722257';
|
|
749
|
+
ctx.stroke();
|
|
750
|
+
ctx.restore();
|
|
608
751
|
}
|
|
609
752
|
});
|
|
610
753
|
setChartJsLoaded(true);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { Box, CircularProgress, Autocomplete, TextField, Input, IconButton, Button, Card, CardContent, CardHeader, Modal, Typography, Stack, Tab } from "@mui/material";
|
|
4
|
-
import { cloneElement, isValidElement, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { cloneElement, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
5
|
import AddIcon from '@mui/icons-material/Add';
|
|
6
6
|
import PlaylistAddIcon from '@mui/icons-material/PlaylistAdd';
|
|
7
7
|
import EditIcon from '@mui/icons-material/Edit';
|
|
@@ -10,7 +10,7 @@ import ReadMoreIcon from '@mui/icons-material/ReadMore';
|
|
|
10
10
|
import PlaylistRemoveIcon from '@mui/icons-material/PlaylistRemove';
|
|
11
11
|
import CloseIcon from '@mui/icons-material/Close';
|
|
12
12
|
import { LoadingButton, TabContext, TabList, TabPanel } from "@mui/lab";
|
|
13
|
-
import { DataGrid, GridToolbar, GridToolbarExport, GridToolbarQuickFilter } from "@mui/x-data-grid";
|
|
13
|
+
import { DataGrid, GridToolbar, GridToolbarExport, GridToolbarFilterButton, GridToolbarQuickFilter } from "@mui/x-data-grid";
|
|
14
14
|
import GroupUpdate from "./GroupUpdate";
|
|
15
15
|
import BackIcon from '@mui/icons-material/ArrowBack';
|
|
16
16
|
import dynamic from "next/dynamic";
|
|
@@ -113,9 +113,7 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
|
113
113
|
}
|
|
114
114
|
}, [userInfo]);
|
|
115
115
|
useEffect(() => {
|
|
116
|
-
|
|
117
|
-
getUsersList();
|
|
118
|
-
}
|
|
116
|
+
getUsersList();
|
|
119
117
|
}, [usersGroup]);
|
|
120
118
|
useEffect(() => {
|
|
121
119
|
if (currentGroup && !checkboxSelection) {
|
|
@@ -221,11 +219,11 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
|
221
219
|
setDeviceSelectedObjs([]);
|
|
222
220
|
}
|
|
223
221
|
}, [devicesToAdd]);
|
|
224
|
-
const CustomToolbar = () => (_jsxs("div", { style: { display: 'flex', padding: '8px', justifyContent: 'space-between', paddingTop: '2px' }, children: [_jsxs("div", { style: { display: 'flex', gap: 8, marginRight: '20px' }, children: [customToolbar && customToolbar.includes('filters') ?
|
|
225
|
-
_jsx(
|
|
222
|
+
const CustomToolbar = useCallback((props) => (_jsxs("div", { style: { display: 'flex', padding: '8px', justifyContent: 'space-between', paddingTop: '2px' }, children: [_jsxs("div", { style: { display: 'flex', gap: 8, marginRight: '20px' }, children: [customToolbar && customToolbar.includes('filters') ?
|
|
223
|
+
_jsx(GridToolbarFilterButton, {})
|
|
226
224
|
: '', customToolbar && customToolbar.includes('export') ?
|
|
227
225
|
_jsx(GridToolbarExport, {})
|
|
228
|
-
: ''] }), customToolbar && customToolbar.includes('quickfilter') && _jsx(GridToolbarQuickFilter, {})] }));
|
|
226
|
+
: ''] }), customToolbar && customToolbar.includes('quickfilter') && _jsx(GridToolbarQuickFilter, {})] })), []);
|
|
229
227
|
const renderDataGrid = (_jsx("div", { style: { display: 'flex', flexDirection: 'column', minHeight: 323 }, children: _jsx(DataGrid, { checkboxSelection: checkboxSelection,
|
|
230
228
|
// Se siamo in modalità aggiunta, includi gli ID già nel gruppo
|
|
231
229
|
rowSelectionModel: (checkboxSelection && currentGroup === 'all')
|
|
@@ -279,9 +277,6 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
|
279
277
|
printOptions: { disableToolbarButton: true },
|
|
280
278
|
},
|
|
281
279
|
}, initialState: {
|
|
282
|
-
sorting: {
|
|
283
|
-
sortModel: [{ field: 'online', sort: 'desc' }],
|
|
284
|
-
},
|
|
285
280
|
pagination: { paginationModel: { pageSize: 100 } },
|
|
286
281
|
}, ...propsDatagrid }) }));
|
|
287
282
|
const renderTabContext = (_jsxs(TabContext, { value: valueTab, children: [_jsx(Box, { sx: { borderBottom: 1, borderColor: 'divider' }, children: _jsxs(TabList, { onChange: handleChangeTab, sx: { minHeight: 20 }, children: [_jsx(Tab, { label: "List", value: "1", sx: { minHeight: 20 } }), _jsx(Tab, { label: "Map", value: "2", sx: { minHeight: 20 } })] }) }), _jsx(TabPanel, { value: "1", sx: { p: 0 }, children: renderDataGrid }), _jsx(TabPanel, { value: "2", sx: { p: 0 }, children: renderMaps })] }));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { GridSortModel } from '@mui/x-data-grid';
|
|
1
2
|
import { UserType } from '../../types/user';
|
|
2
3
|
import { Theme } from '@emotion/react';
|
|
3
|
-
declare const UsersDataGrid: ({ handleGetUsersList, pageSize, container, containerProps, props, loadingComponent, theme }: {
|
|
4
|
-
handleGetUsersList: ({ page, pageSize, filter }: {
|
|
4
|
+
declare const UsersDataGrid: ({ handleGetUsersList, pageSize, container, containerProps, props, loadingComponent, theme, columns }: {
|
|
5
|
+
handleGetUsersList: ({ page, pageSize, filter, sortModel }: {
|
|
5
6
|
page: number;
|
|
6
7
|
pageSize: number;
|
|
7
8
|
filter?: {
|
|
@@ -9,6 +10,7 @@ declare const UsersDataGrid: ({ handleGetUsersList, pageSize, container, contain
|
|
|
9
10
|
operator?: string;
|
|
10
11
|
value: string;
|
|
11
12
|
};
|
|
13
|
+
sortModel: GridSortModel;
|
|
12
14
|
}) => Promise<{
|
|
13
15
|
users: UserType[];
|
|
14
16
|
rowCount: number;
|
|
@@ -19,5 +21,6 @@ declare const UsersDataGrid: ({ handleGetUsersList, pageSize, container, contain
|
|
|
19
21
|
props?: object | undefined;
|
|
20
22
|
loadingComponent?: React.ReactNode;
|
|
21
23
|
theme: Theme;
|
|
24
|
+
columns: any[];
|
|
22
25
|
}) => string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<import("react").ReactNode> | Promise<import("react").AwaitedReactNode> | null;
|
|
23
26
|
export default UsersDataGrid;
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { DataGrid,
|
|
4
|
-
import { Card, Box,
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
const UsersDataGrid = ({ handleGetUsersList, pageSize, container = 'Box', containerProps = {}, props = {}, loadingComponent = _jsx(CircularProgress, {}), theme }) => {
|
|
9
|
-
const router = useRouter();
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import { DataGrid, GridLogicOperator, GridToolbarExport } from '@mui/x-data-grid';
|
|
4
|
+
import { Card, Box, CircularProgress, ThemeProvider, FormControl, IconButton, InputLabel, MenuItem, Select, TextField } from '@mui/material';
|
|
5
|
+
import { ClearIcon } from "@mui/x-date-pickers";
|
|
6
|
+
import debounce from 'lodash.debounce';
|
|
7
|
+
const UsersDataGrid = ({ handleGetUsersList, pageSize, container = 'Box', containerProps = {}, props = {}, loadingComponent = _jsx(CircularProgress, {}), theme, columns }) => {
|
|
10
8
|
const [usersList, setUsersList] = useState(undefined);
|
|
11
9
|
const [userCount, setUserCount] = useState(undefined);
|
|
10
|
+
const filterType = {
|
|
11
|
+
email: 'Email',
|
|
12
|
+
name: 'Full name',
|
|
13
|
+
role: 'Role'
|
|
14
|
+
};
|
|
15
|
+
const [selectedFilterValue, setSelectedFilterValue] = useState(undefined);
|
|
16
|
+
const [selectedFilterType, setSelectedFilterType] = useState('email');
|
|
12
17
|
const [isLoading, setIsLoading] = useState(true);
|
|
18
|
+
const [sortModel, setSortModel] = useState([
|
|
19
|
+
{ field: "email", sort: "desc" },
|
|
20
|
+
]);
|
|
13
21
|
const [paginationModel, setPaginationModel] = useState({
|
|
14
22
|
page: 0,
|
|
15
23
|
pageSize: pageSize || 1,
|
|
@@ -29,7 +37,8 @@ const UsersDataGrid = ({ handleGetUsersList, pageSize, container = 'Box', contai
|
|
|
29
37
|
{ value: filterModel.quickFilterValues[0] }
|
|
30
38
|
: filterModel.items.length > 0 ?
|
|
31
39
|
{ field: filterModel.items[0].field, operator: filterModel.items[0].operator, value: filterModel.items[0].value }
|
|
32
|
-
: undefined
|
|
40
|
+
: undefined,
|
|
41
|
+
sortModel
|
|
33
42
|
});
|
|
34
43
|
setUsersList(usersList.users);
|
|
35
44
|
setUserCount(usersList.rowCount);
|
|
@@ -41,36 +50,44 @@ const UsersDataGrid = ({ handleGetUsersList, pageSize, container = 'Box', contai
|
|
|
41
50
|
setIsLoading(false);
|
|
42
51
|
}
|
|
43
52
|
};
|
|
53
|
+
const debouncedUpdateFilter = useMemo(() => debounce((value) => {
|
|
54
|
+
setFilterModel({
|
|
55
|
+
items: [{
|
|
56
|
+
field: selectedFilterType || 'email',
|
|
57
|
+
operator: 'startsWith',
|
|
58
|
+
value: value
|
|
59
|
+
}],
|
|
60
|
+
});
|
|
61
|
+
}, 500), [selectedFilterType]);
|
|
62
|
+
const handleFilterModelChange = (value) => {
|
|
63
|
+
setSelectedFilterValue(value);
|
|
64
|
+
// const value = newModel.quickFilterValues?.[0] || '';
|
|
65
|
+
if (value.length >= 3 || value.length === 0) {
|
|
66
|
+
debouncedUpdateFilter(value);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
44
69
|
useEffect(() => {
|
|
45
70
|
getUsersList();
|
|
46
|
-
}, [paginationModel, filterModel]);
|
|
71
|
+
}, [paginationModel, filterModel, sortModel]);
|
|
47
72
|
if (!usersList && isLoading) {
|
|
48
73
|
return loadingComponent;
|
|
49
74
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
field: 'email', filterOperators: getGridStringOperators().filter((operator) => operator.value === 'contains' || operator.value === 'equals'), display: 'flex', flex: 0.400, minWidth: 180, headerName: 'Email address', renderCell: (params) => (_jsx(_Fragment, { children: params.value }))
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
field: 'lastSignInAt', filterable: false, disableColumnMenu: true, display: 'flex', minWidth: 160, headerName: 'Last Signed In', renderCell: (params) => (_jsx(_Fragment, { children: params.value ? moment(params.value).format('DD/MM/YYYY HH:mm:ss') : '' }))
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
field: 'id', filterable: false, disableExport: true, disableColumnMenu: true, headerName: '', display: 'flex', align: 'right', renderCell: (params) => (_jsxs(Button, { onClick: () => router.push(`/users/${params.value}`), variant: 'contained', color: 'secondary', size: 'small', children: [_jsx(EditIcon, { fontSize: 'small', sx: { mr: 1 } }), " EDIT"] }))
|
|
65
|
-
}
|
|
66
|
-
], rows: usersList, slots: {
|
|
67
|
-
toolbar: GridToolbar,
|
|
75
|
+
const handleSortModelChange = (model) => {
|
|
76
|
+
setSortModel(model);
|
|
77
|
+
setPaginationModel(prev => ({ ...prev, page: 0 }));
|
|
78
|
+
};
|
|
79
|
+
const CustomToolbar = () => (_jsx(Box, { sx: { display: 'flex', alignItems: 'center', p: 1, justifyContent: 'space-between' }, children: _jsx(GridToolbarExport, {}) }));
|
|
80
|
+
const renderUsersList = () => (_jsx("div", { style: { display: 'flex', flexDirection: 'column', minHeight: 323 }, children: _jsx(DataGrid, { disableDensitySelector: true, disableColumnSelector: true, disableRowSelectionOnClick: true, onSortModelChange: handleSortModelChange, disableColumnResize: true, columns: columns, rows: usersList, slots: {
|
|
81
|
+
toolbar: CustomToolbar,
|
|
68
82
|
}, slotProps: {
|
|
69
83
|
toolbar: {
|
|
70
84
|
printOptions: { disableToolbarButton: true },
|
|
71
|
-
showQuickFilter:
|
|
85
|
+
showQuickFilter: false,
|
|
72
86
|
},
|
|
73
|
-
}, rowCount: userCount, loading: isLoading, pageSizeOptions: [], paginationModel: paginationModel, paginationMode: "server", onPaginationModelChange: setPaginationModel,
|
|
74
|
-
|
|
87
|
+
}, rowCount: userCount, loading: isLoading, pageSizeOptions: [], paginationModel: paginationModel, paginationMode: "server", onPaginationModelChange: setPaginationModel, ...props }) }));
|
|
88
|
+
const renderFilter = () => (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', justifyContent: 'flex-end', mb: 2 }, children: [_jsxs(FormControl, { children: [_jsx(InputLabel, { shrink: true, id: "filter-by-select-label", children: "Filter by" }), _jsx(Select, { value: selectedFilterType || '', onChange: (e) => setSelectedFilterType(e.target.value), labelId: "filter-by-select-label", label: "Filter by", size: "small", sx: { mr: 2 }, children: Object.entries(filterType).map(([key, label]) => (_jsx(MenuItem, { value: key, children: label }, key))) })] }), _jsx(TextField, { value: selectedFilterValue || "", onChange: (e) => handleFilterModelChange(e.target.value), placeholder: "Search...", size: "small", InputProps: {
|
|
89
|
+
endAdornment: selectedFilterValue ? (_jsx(IconButton, { size: "small", onClick: () => handleFilterModelChange(""), children: _jsx(ClearIcon, { fontSize: "small" }) })) : null,
|
|
90
|
+
} })] }));
|
|
91
|
+
return _jsx(ThemeProvider, { theme: theme, children: container === "Card" ? (_jsxs(_Fragment, { children: [renderFilter(), _jsx(Card, { ...containerProps, children: renderUsersList() })] })) : (_jsxs(_Fragment, { children: [renderFilter(), _jsx(Box, { ...containerProps, children: renderUsersList() })] })) });
|
|
75
92
|
};
|
|
76
93
|
export default UsersDataGrid;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iotready/nextjs-components-library",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-preview29",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rm -rf dist && tsc --project tsconfig.build.json && cp package.json dist/",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"firebase-admin": "^13.4.0",
|
|
24
24
|
"leaflet": "^1.9.4",
|
|
25
25
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
|
26
|
+
"lodash.debounce": "^4.0.8",
|
|
26
27
|
"material-ui-confirm": "^3.0.16",
|
|
27
28
|
"moment": "^2.30.1",
|
|
28
29
|
"next": "^14.2.9",
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/leaflet": "^1.9.13",
|
|
39
|
+
"@types/lodash.debounce": "^4.0.9",
|
|
38
40
|
"@types/node": "^20",
|
|
39
41
|
"@types/react": "^18",
|
|
40
42
|
"@types/react-csv": "^1.1.10",
|
|
@@ -15,6 +15,10 @@ export declare function createCustomer(trackleConfig: TrackleConfig, uid: string
|
|
|
15
15
|
organization: string;
|
|
16
16
|
uid: string;
|
|
17
17
|
}>;
|
|
18
|
+
export declare function removeCustomer(trackleConfig: TrackleConfig, uid: string): Promise<{
|
|
19
|
+
organization: string;
|
|
20
|
+
uid: string;
|
|
21
|
+
}>;
|
|
18
22
|
export declare function getDevices(trackleConfig: TrackleConfig, productId?: number, uid?: string, query?: string): Promise<{
|
|
19
23
|
devices: any[];
|
|
20
24
|
}>;
|
|
@@ -55,14 +55,28 @@ export async function createCustomer(trackleConfig, uid) {
|
|
|
55
55
|
.post({ uid })
|
|
56
56
|
.json();
|
|
57
57
|
}
|
|
58
|
+
export async function removeCustomer(trackleConfig, uid) {
|
|
59
|
+
return await wretch(`${trackleConfig.apiUrl}/orgs/${trackleConfig.orgName}/customers/${uid}`)
|
|
60
|
+
.headers({
|
|
61
|
+
Authorization: `Basic ${btoa(`${trackleConfig.clientId}:${trackleConfig.clientSecret}`)}`
|
|
62
|
+
})
|
|
63
|
+
.delete().json();
|
|
64
|
+
}
|
|
58
65
|
export async function getDevices(trackleConfig, productId, uid, query) {
|
|
59
66
|
const api = uid ? wretchApi(trackleConfig, uid) : wretchApi(trackleConfig);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
let response;
|
|
68
|
+
try {
|
|
69
|
+
response = await api
|
|
70
|
+
.get(uid
|
|
71
|
+
? `/devices${query ? "?" + query : ""}`
|
|
72
|
+
: `/products/${productId}/devices${query ? "?" + query : ""}`)
|
|
73
|
+
.setTimeout(trackleConfig.apiTimeout)
|
|
74
|
+
.json();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error("Error fetching devices:", error);
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
66
80
|
return (uid ? { devices: response } : response);
|
|
67
81
|
}
|
|
68
82
|
export async function getDevice(trackleConfig, id, productId, uid) {
|