@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.
@@ -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 || userInfo.email, src: userInfo.picture, children: userInfo.picture ? '' : userInfo.name?.charAt(0).toUpperCase() || userInfo.email?.charAt(0).toUpperCase() }) }) })] }) }));
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, { name: userInfo.name, firstname: userInfo.firstname, lastname: userInfo.lastname });
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: 'nearest',
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
- return `${context.dataset.label}: ${context.parsed.y.toFixed(3)} ${context.dataset.unit}`;
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
- // 👇 Combina statiche + dinamiche
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.tooltip?._active && chart.tooltip?._active.length) {
578
- const activePoint = chart.tooltip._active[0];
579
- const ctx = chart.ctx;
580
- const x = activePoint.element.x;
581
- let topY1 = 0;
582
- let topY2 = 0;
583
- let bottomY1 = 0;
584
- let bottomY2 = 0;
585
- if (chart.scales.y1?.top) {
586
- topY1 = chart.scales.y1.top;
587
- }
588
- if (chart.scales.y2?.top) {
589
- topY2 = chart.scales.y2.top;
590
- }
591
- if (chart.scales.y1?.bottom) {
592
- bottomY1 = chart.scales.y1.bottom;
593
- }
594
- if (chart.scales.y2?.bottom) {
595
- bottomY2 = chart.scales.y2.bottom;
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
- if (usersGroup) {
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(GridToolbarExport, { sx: { mr: 2 } })
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, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from 'react';
3
- import { DataGrid, getGridStringOperators, GridLogicOperator, GridToolbar } from '@mui/x-data-grid';
4
- import { Card, Box, Button, CircularProgress, ThemeProvider } from '@mui/material';
5
- import moment from 'moment';
6
- import EditIcon from '@mui/icons-material/Edit';
7
- import { useRouter } from 'next/navigation';
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 renderUsersList = () => (_jsx("div", { style: { display: 'flex', flexDirection: 'column', minHeight: 323 }, children: _jsx(DataGrid, { disableDensitySelector: true, disableColumnSelector: true, disableRowSelectionOnClick: true, disableColumnResize: true, disableColumnSorting: true, columns: [
51
- {
52
- field: 'role', filterable: false, disableColumnMenu: true, display: 'flex', flex: 0.125, minWidth: 80, headerName: 'Role', renderCell: (params) => (_jsx(_Fragment, { children: params.value ? params.value.charAt(0).toUpperCase() + params.value.slice(1) : '' }))
53
- },
54
- {
55
- field: 'name', filterOperators: getGridStringOperators().filter((operator) => operator.value === 'contains' || operator.value === 'equals'), display: 'flex', flex: 0.400, minWidth: 140, headerName: 'Full name', renderCell: (params) => (_jsx(_Fragment, { children: params.value ? params.value.replace('/', ' ') : '' }))
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: true,
85
+ showQuickFilter: false,
72
86
  },
73
- }, rowCount: userCount, loading: isLoading, pageSizeOptions: [], paginationModel: paginationModel, paginationMode: "server", onPaginationModelChange: setPaginationModel, filterMode: "server", onFilterModelChange: setFilterModel, filterModel: filterModel, ...props }) }));
74
- return _jsx(ThemeProvider, { theme: theme, children: container === "Card" ? (_jsx(Card, { ...containerProps, children: renderUsersList() })) : (_jsx(Box, { ...containerProps, children: renderUsersList() })) });
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-preview27",
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
- const response = await api
61
- .get(uid
62
- ? `/devices${query ? "?" + query : ""}`
63
- : `/products/${productId}/devices${query ? "?" + query : ""}`)
64
- .setTimeout(trackleConfig.apiTimeout)
65
- .json();
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) {