@iotready/nextjs-components-library 1.0.0-preview2 → 1.0.0-preview21
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/charts/TrendChart.d.ts +14 -5
- package/components/charts/TrendChart.js +397 -156
- package/components/groups/GroupUpdate.d.ts +1 -1
- package/components/groups/GroupUpdate.js +1 -1
- package/components/groups/GroupsDevices.d.ts +2 -2
- package/components/groups/GroupsDevices.js +27 -13
- package/package.json +3 -2
- package/server-actions/annotations.d.ts +4 -0
- package/server-actions/annotations.js +15 -0
- package/server-actions/groups.d.ts +1 -8
- package/server-actions/index.d.ts +1 -0
- package/server-actions/index.js +1 -0
- package/server-actions/influx.d.ts +5 -13
- package/server-actions/influx.js +202 -98
- package/server-actions/trackle.d.ts +3 -2
- package/server-actions/trackle.js +4 -17
- package/server-actions/types.d.ts +24 -0
- package/server-actions/types.js +6 -0
- package/types/user.d.ts +1 -0
@@ -12,7 +12,7 @@ declare const GroupUpdate: ({ userInfo, groupInfo, usersGroup, usersList, device
|
|
12
12
|
handleAddUserToGroup: (groupID: string, userName: string, userID: string) => Promise<any>;
|
13
13
|
handleRemoveUserFromGroup: (groupID: string, userID: string) => Promise<any>;
|
14
14
|
handleUpdateDevice: (id: string, body: any, user: UserType) => Promise<any>;
|
15
|
-
handleGetGroups: (
|
15
|
+
handleGetGroups: (userInfo?: UserType) => Promise<any>;
|
16
16
|
handleUpdateGroup: (id: string, group: any) => Promise<void>;
|
17
17
|
handleDeleteGroup: (id: string) => Promise<void>;
|
18
18
|
container?: "Box" | "Card";
|
@@ -27,7 +27,7 @@ const GroupUpdate = ({ userInfo, groupInfo, usersGroup, usersList, devicesList,
|
|
27
27
|
if (response) {
|
28
28
|
if (devicesList.length > 0) {
|
29
29
|
// should check if device is present in other groups the user is member of
|
30
|
-
const groupsData = await handleGetGroups(
|
30
|
+
const groupsData = await handleGetGroups({ role: 'support', uid: userId });
|
31
31
|
const userGroupIds = groupsData.map((group) => group.id);
|
32
32
|
// for all devices in the group set the selectedUser as manager
|
33
33
|
for (const device of devicesList) {
|
@@ -11,10 +11,10 @@ declare const GroupsDevices: ({ userInfo, handleGetUsersList, handleAddUserToGro
|
|
11
11
|
}>;
|
12
12
|
handleAddUserToGroup: (groupID: string, userName: string, userID: string) => Promise<any>;
|
13
13
|
handleRemoveUserFromGroup: (groupID: string, userID: string) => Promise<any>;
|
14
|
-
handleGetGroups: (
|
14
|
+
handleGetGroups: (userInfo?: UserType) => Promise<any>;
|
15
15
|
handleGetUsersGroup: (groupID: string) => Promise<any>;
|
16
16
|
handleCreateGroup: (group: any) => Promise<any>;
|
17
|
-
handleGetDevices: (user: UserType,
|
17
|
+
handleGetDevices: (user: UserType, query: string) => Promise<any>;
|
18
18
|
handleGetPositions: (devices: any) => Promise<any>;
|
19
19
|
handleAddDevicesToGroup: (user: UserType, group: string, devicesToPatch: any[]) => Promise<any>;
|
20
20
|
handleRemoveDevicesFromGroup: (user: UserType, group: string, devicesToPatch: any[]) => Promise<any>;
|
@@ -28,7 +28,7 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
28
28
|
const [selectedGroup, setSelectedGroup] = useState(group);
|
29
29
|
const [groups, setGroups] = useState(null);
|
30
30
|
const [usersList, setUsersList] = useState([]);
|
31
|
-
const [usersGroup, setUsersGroup] = useState(
|
31
|
+
const [usersGroup, setUsersGroup] = useState(null);
|
32
32
|
const [groupInfo, setGroupInfo] = useState(null);
|
33
33
|
const [openAdd, setOpenAdd] = useState(false);
|
34
34
|
const [groupName, setGroupName] = useState('');
|
@@ -60,7 +60,7 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
60
60
|
moreData = usersList.users.length === pageSize;
|
61
61
|
page++;
|
62
62
|
}
|
63
|
-
const userGroupsUserIds = usersGroup.map((ug) => ug.user.userId);
|
63
|
+
const userGroupsUserIds = usersGroup && usersGroup.map((ug) => ug.user.userId) || [];
|
64
64
|
setUsersList(usersData.filter((user) => user.role === 'support' && !userGroupsUserIds.includes(user.uid)).map((user) => {
|
65
65
|
const userName = user.name ? user.name.replace("/", " ") : "";
|
66
66
|
return { label: `${userName} (${user.email})`, id: user.uid };
|
@@ -73,7 +73,20 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
73
73
|
const fetchDevices = async (group, selected) => {
|
74
74
|
try {
|
75
75
|
setLoadingDevices(true);
|
76
|
-
|
76
|
+
let queryParams = "last_handshake_at!=null";
|
77
|
+
if (group !== "all") {
|
78
|
+
queryParams = `last_handshake_at!=null&state.groups=/${group}/`;
|
79
|
+
if (userInfo.role === "admin") {
|
80
|
+
queryParams += "&quarantined=false";
|
81
|
+
}
|
82
|
+
}
|
83
|
+
else if (selected !== "all") {
|
84
|
+
queryParams = `last_handshake_at!=null&state.groups!=/${selected}/`;
|
85
|
+
if (userInfo.role === "admin") {
|
86
|
+
queryParams += "&quarantined=false";
|
87
|
+
}
|
88
|
+
}
|
89
|
+
const devices = await handleGetDevices(userInfo, queryParams);
|
77
90
|
setDevices(devices);
|
78
91
|
if (enableMaps) {
|
79
92
|
const positions = await handleGetPositions(devices);
|
@@ -89,7 +102,7 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
89
102
|
};
|
90
103
|
const getGroups = async () => {
|
91
104
|
try {
|
92
|
-
const responseData = await handleGetGroups(
|
105
|
+
const responseData = await handleGetGroups(userInfo);
|
93
106
|
setGroups(responseData);
|
94
107
|
}
|
95
108
|
catch (err) {
|
@@ -119,7 +132,9 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
119
132
|
if (currentGroup && !checkboxSelection) {
|
120
133
|
setSelectedGroup(currentGroup);
|
121
134
|
setGroupInfo(groups && groups.find((g) => g.id === currentGroup));
|
122
|
-
|
135
|
+
if (currentGroup !== 'all') {
|
136
|
+
getUsersGroup(currentGroup);
|
137
|
+
}
|
123
138
|
fetchDevices(currentGroup, currentGroup);
|
124
139
|
}
|
125
140
|
}, [currentGroup]);
|
@@ -130,7 +145,6 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
130
145
|
const newGroup = {
|
131
146
|
name: groupName,
|
132
147
|
description,
|
133
|
-
productID: 1008, // Assuming this is the correct productID
|
134
148
|
};
|
135
149
|
const id = await handleCreateGroup(newGroup);
|
136
150
|
await getGroups();
|
@@ -177,13 +191,13 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
177
191
|
const devicesPatched = await handleAddDevicesToGroup(userInfo, selectedGroup, devicesToPatch);
|
178
192
|
if (devicesPatched && devicesPatched.length > 0) {
|
179
193
|
// get all members of the group
|
180
|
-
const userIdsToAdd = usersGroup.map((ug) => ug.user.userId);
|
181
|
-
if (userIdsToAdd.length > 0) {
|
194
|
+
const userIdsToAdd = usersGroup && usersGroup.map((ug) => ug.user.userId) || [];
|
195
|
+
if (userIdsToAdd && userIdsToAdd.length > 0) {
|
182
196
|
for (const device of devicesToPatch) {
|
183
197
|
// add members to devices managers
|
184
198
|
await handleUpdateDevice(device.id, {
|
185
199
|
managers: [
|
186
|
-
...device
|
200
|
+
...(device?.managers || []),
|
187
201
|
...userIdsToAdd
|
188
202
|
]
|
189
203
|
}, userInfo);
|
@@ -210,12 +224,12 @@ const GroupsDevices = ({ userInfo, handleGetUsersList, handleAddUserToGroup, han
|
|
210
224
|
const devicesPatched = await handleRemoveDevicesFromGroup(userInfo, selectedGroup, devicesToPatch);
|
211
225
|
if (devicesPatched && devicesPatched.length > 0) {
|
212
226
|
for (const device of devicesToPatch) {
|
213
|
-
if (device.managers.length > 0) {
|
214
|
-
let managersToKeep = device.managers
|
227
|
+
if (device.managers && device.managers.length > 0) {
|
228
|
+
let managersToKeep = device.managers;
|
215
229
|
for (const manager of device.managers) {
|
216
230
|
// should check if device is present in other groups the user is member of
|
217
|
-
const groupsData = await handleGetGroups(
|
218
|
-
const userGroupIds = groupsData.map((group) => group.id);
|
231
|
+
const groupsData = await handleGetGroups({ role: 'support', uid: manager });
|
232
|
+
const userGroupIds = groupsData && groupsData.map((group) => group.id) || [];
|
219
233
|
const found = device.groups && device.groups.some((gID) => userGroupIds.includes(gID));
|
220
234
|
if (!found) {
|
221
235
|
// @ts-ignore
|
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-preview21",
|
4
4
|
"main": "index.js",
|
5
5
|
"scripts": {
|
6
6
|
"build": "rm -rf dist && tsc --project tsconfig.build.json && cp package.json dist/",
|
@@ -19,6 +19,7 @@
|
|
19
19
|
"chartjs-adapter-moment": "^1.0.1",
|
20
20
|
"chartjs-plugin-annotation": "^3.1.0",
|
21
21
|
"chartjs-plugin-zoom": "^2.0.1",
|
22
|
+
"csv-parse": "^5.6.0",
|
22
23
|
"firebase": "^10.13.1",
|
23
24
|
"leaflet": "^1.9.4",
|
24
25
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
@@ -42,4 +43,4 @@
|
|
42
43
|
"eslint-config-next": "14.2.9",
|
43
44
|
"typescript": "^5"
|
44
45
|
}
|
45
|
-
}
|
46
|
+
}
|
@@ -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
|
-
|
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
|
}[]>;
|
package/server-actions/index.js
CHANGED
@@ -1,13 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
measurement: string;
|
7
|
-
dbName: string;
|
8
|
-
username: string;
|
9
|
-
password: string;
|
10
|
-
};
|
11
|
-
export declare function getInfluxDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string, timeGroup: string, raw: boolean): Promise<any>;
|
12
|
-
export declare function getManyMeasuresV1(influxConfig: InfluxConfig, fields: string[], limit: number, offset: number, sort: any, deviceID: string, timeStart?: number, timeEnd?: number): Promise<any>;
|
13
|
-
export declare function getFirstTimestamp(influxConfig: InfluxConfig, deviceID: string): Promise<any>;
|
1
|
+
import { FilterTagMode, 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 getDwSlots(influxConfig: InfluxConfig, timeStart: number, timeEnd: number, deviceID: string): Promise<any>;
|
4
|
+
export declare function getInfluxDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string, timeGroup: string, raw: boolean, fill?: InfluxFillType, filterTag?: number, tagInclude?: FilterTagMode): Promise<any>;
|
5
|
+
export declare function exportDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string): Promise<any>;
|
package/server-actions/influx.js
CHANGED
@@ -1,55 +1,175 @@
|
|
1
1
|
"use server";
|
2
|
+
import { parse } from "csv-parse";
|
2
3
|
import moment from "moment";
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
import { FilterTagMode } from "./types";
|
5
|
+
export async function getInfluxAlerts(influxConfig, fields, limit, offset, sort, deviceID, timeStart, timeEnd) {
|
6
|
+
const conditions = fields
|
7
|
+
.map((field) => `r["valueName"] == "${field}"`)
|
8
|
+
.join(" or ");
|
9
|
+
let query = `
|
10
|
+
import "contrib/tomhollingworth/events"
|
11
|
+
from(bucket: "${influxConfig.bucket}")`;
|
12
|
+
if (timeStart && timeEnd) {
|
13
|
+
query += ` |> range(start: ${timeStart}, stop: ${timeEnd})`;
|
14
|
+
}
|
15
|
+
query += `
|
16
|
+
|> filter(fn: (r) => r["_measurement"] == "${influxConfig.measurement}" and r["deviceid"] == "${deviceID}" and (${conditions}))
|
17
|
+
`;
|
18
|
+
// Add filter for tag d === 3
|
19
|
+
query += `
|
20
|
+
|> filter(fn: (r) => r["d"] == "3" or r["d"] !~ /./) // Include only events with tag d == 3 or no tag d
|
21
|
+
`;
|
22
|
+
const queryCount = `${query} |> group() |> count()`;
|
23
|
+
query += `
|
24
|
+
|> sort(columns: ["_time"]) // Ordina gli eventi cronologicamente
|
25
|
+
|> group(columns: ["valueName"]) // Raggruppa per il tag
|
26
|
+
|> events.duration(unit: 1s, stop: 2020-01-02T00:00:00Z)
|
27
|
+
|> keep(columns: ["_time", "valueName", "_value", "duration"])
|
28
|
+
|> group() // Raggruppa tutti i dati in un unico gruppo
|
29
|
+
`;
|
30
|
+
if (sort && sort.field === "time" && sort.sort === "desc") {
|
31
|
+
query += ` |> sort(columns: ["_time"], desc: true)`;
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
query += ` |> sort(columns: ["_time"])`;
|
35
|
+
}
|
36
|
+
query += ` |> limit(n:${limit}, offset:${offset})`;
|
37
|
+
const responses = await Promise.all([
|
38
|
+
fetch(encodeURI(`${influxConfig.url}/api/v2/query?org=${influxConfig.orgId}`), {
|
39
|
+
method: "POST",
|
40
|
+
headers: {
|
41
|
+
Authorization: `Token ${influxConfig.accessToken}`,
|
42
|
+
"Content-Type": "application/json"
|
43
|
+
},
|
44
|
+
body: JSON.stringify({
|
45
|
+
query: query,
|
46
|
+
type: "flux"
|
47
|
+
})
|
48
|
+
}),
|
49
|
+
fetch(encodeURI(`${influxConfig.url}/api/v2/query?org=${influxConfig.orgId}`), {
|
50
|
+
method: "POST",
|
51
|
+
headers: {
|
52
|
+
Authorization: `Token ${influxConfig.accessToken}`,
|
53
|
+
"Content-Type": "application/json"
|
54
|
+
},
|
55
|
+
body: JSON.stringify({
|
56
|
+
query: queryCount,
|
57
|
+
type: "flux"
|
58
|
+
})
|
59
|
+
})
|
60
|
+
]);
|
61
|
+
if (!responses[0].ok) {
|
62
|
+
throw new Error(`Failed to fetch data from InfluxDB: ${responses[0].statusText}`);
|
63
|
+
}
|
64
|
+
const data = await responses[0].text();
|
65
|
+
const count = await responses[1].text();
|
66
|
+
const rows = [];
|
67
|
+
const parsedData = parse(data.trim(), { columns: true });
|
68
|
+
parsedData.forEach((row) => {
|
69
|
+
rows.push({
|
70
|
+
time: row["_time"],
|
71
|
+
duration: parseInt(row["duration"], 10),
|
72
|
+
value: parseInt(row["_value"], 10),
|
73
|
+
valueName: row["valueName"]
|
74
|
+
});
|
75
|
+
});
|
76
|
+
const parsedCount = count.split("\n")[1].split(",");
|
77
|
+
let countData = parsedCount[5];
|
78
|
+
return {
|
79
|
+
data: rows,
|
80
|
+
count: countData
|
81
|
+
};
|
82
|
+
}
|
83
|
+
export async function getDwSlots(influxConfig, timeStart, timeEnd, deviceID) {
|
84
|
+
const field = "dw";
|
85
|
+
const query = `
|
86
|
+
SELECT ("value") FROM "${influxConfig.measurement}"
|
87
|
+
WHERE "deviceid" = '${deviceID}'
|
88
|
+
AND "valueName" = '${field}'
|
89
|
+
AND time >= '${moment.unix(timeStart).toISOString()}'
|
90
|
+
AND time <= '${moment.unix(timeEnd).toISOString()}'
|
12
91
|
`;
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
}
|
40
|
-
export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, deviceID, timeGroup, raw) {
|
92
|
+
const response = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${query}`), {
|
93
|
+
headers: {
|
94
|
+
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
95
|
+
}
|
96
|
+
});
|
97
|
+
if (!response.ok) {
|
98
|
+
throw new Error(`Failed to fetch DW data: ${response.statusText}`);
|
99
|
+
}
|
100
|
+
const data = await response.json();
|
101
|
+
const series = data?.results?.[0]?.series?.[0];
|
102
|
+
if (!series?.values || !Array.isArray(series.values)) {
|
103
|
+
return data; // niente da fare, ritorna i dati grezzi
|
104
|
+
}
|
105
|
+
const values = series.values;
|
106
|
+
// Modifiche ai valori
|
107
|
+
if (values.length > 0) {
|
108
|
+
// Aggiungi punto all'inizio se il primo valore è 0
|
109
|
+
if (values[0][1] === 0) {
|
110
|
+
values.unshift([moment.unix(timeStart).unix(), 1]);
|
111
|
+
}
|
112
|
+
// Aggiungi punto alla fine se l'ultimo valore è 1
|
113
|
+
if (values[values.length - 1][1] === 1) {
|
114
|
+
values.push([moment.unix(timeEnd).unix(), 0]);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
return data; // stessa struttura di Influx, con values modificati
|
118
|
+
}
|
119
|
+
export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, deviceID, timeGroup, raw, fill, filterTag, tagInclude = FilterTagMode.Include) {
|
41
120
|
let query;
|
121
|
+
let preStartValue = null;
|
122
|
+
// Costruzione filtro tag
|
123
|
+
let filterTagCondition = "";
|
124
|
+
if (filterTag !== undefined && filterTag !== null) {
|
125
|
+
switch (tagInclude) {
|
126
|
+
case FilterTagMode.Include:
|
127
|
+
filterTagCondition = ` AND ("d" = '${filterTag}' OR "d" !~ /./)`;
|
128
|
+
break;
|
129
|
+
case FilterTagMode.Exclude:
|
130
|
+
filterTagCondition = ` AND ("d" != '${filterTag}' OR "d" !~ /./)`;
|
131
|
+
break;
|
132
|
+
case FilterTagMode.DW:
|
133
|
+
filterTagCondition = ` AND "d" = '${filterTag}'`;
|
134
|
+
break;
|
135
|
+
}
|
136
|
+
}
|
42
137
|
if (raw) {
|
43
|
-
query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
|
138
|
+
query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time >= '${moment
|
44
139
|
.unix(timeStart)
|
45
140
|
.toISOString()}' AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
46
141
|
}
|
47
142
|
else {
|
48
|
-
query = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
|
143
|
+
query = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time >= '${moment
|
49
144
|
.unix(timeStart)
|
50
145
|
.toISOString()}' AND time <= '${moment
|
51
146
|
.unix(timeEnd)
|
52
|
-
.toISOString()}' GROUP BY time(${timeGroup})
|
147
|
+
.toISOString()}' GROUP BY time(${timeGroup})`;
|
148
|
+
if (fill === "none") {
|
149
|
+
query += ` fill(none)`;
|
150
|
+
}
|
151
|
+
else if (fill === "previous") {
|
152
|
+
query += ` fill(previous)`;
|
153
|
+
}
|
154
|
+
else {
|
155
|
+
query += ` fill(null)`;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
if (fill) {
|
159
|
+
// Query to get the last data point before timeStart
|
160
|
+
const preStartQuery = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}'${filterTagCondition} AND time < '${moment
|
161
|
+
.unix(timeStart)
|
162
|
+
.toISOString()}'`;
|
163
|
+
const preStartResponse = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${preStartQuery}`), {
|
164
|
+
headers: {
|
165
|
+
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
166
|
+
}
|
167
|
+
});
|
168
|
+
if (!preStartResponse.ok) {
|
169
|
+
throw new Error(`Failed to fetch pre-start data from InfluxDB: ${preStartResponse.statusText}`);
|
170
|
+
}
|
171
|
+
const preStartData = await preStartResponse.json();
|
172
|
+
preStartValue = preStartData.results[0].series?.[0]?.values?.[0]?.[1];
|
53
173
|
}
|
54
174
|
const response = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${query}`), {
|
55
175
|
headers: {
|
@@ -57,7 +177,6 @@ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, d
|
|
57
177
|
}
|
58
178
|
});
|
59
179
|
if (!response.ok) {
|
60
|
-
console.log(response);
|
61
180
|
throw new Error(`Failed to fetch data from InfluxDB: ${response.statusText}`);
|
62
181
|
}
|
63
182
|
const data = await response.json();
|
@@ -72,61 +191,34 @@ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, d
|
|
72
191
|
}
|
73
192
|
];
|
74
193
|
}
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
.join(" OR ");
|
88
|
-
let queryCount = `SELECT count(*) FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND (${conditions})`;
|
89
|
-
let queryPagination = `SELECT "valueName", "value" FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND (${conditions})`;
|
90
|
-
if (timeStart) {
|
91
|
-
queryCount += ` AND time >= '${moment.unix(timeStart).toISOString()}'`;
|
92
|
-
queryPagination += ` AND time >= '${moment
|
93
|
-
.unix(timeStart)
|
94
|
-
.toISOString()}'`;
|
95
|
-
}
|
96
|
-
if (timeEnd) {
|
97
|
-
queryCount += ` AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
98
|
-
queryPagination += ` AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
99
|
-
}
|
100
|
-
if (sort && sort.field === "time") {
|
101
|
-
queryPagination = `${queryPagination} ORDER BY "${sort.field}" ${sort.sort}`;
|
102
|
-
}
|
103
|
-
queryPagination = `${queryPagination} LIMIT ${limit} OFFSET ${offset}`;
|
104
|
-
const responses = await Promise.all([
|
105
|
-
fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${queryPagination}`), {
|
106
|
-
headers: {
|
107
|
-
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
108
|
-
}
|
109
|
-
}),
|
110
|
-
fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${queryCount}`), {
|
111
|
-
headers: {
|
112
|
-
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
113
|
-
}
|
114
|
-
})
|
115
|
-
]);
|
116
|
-
if (!responses[0].ok) {
|
117
|
-
throw new Error(`Failed to fetch data from InfluxDB: ${responses[0].statusText}`);
|
194
|
+
// Always override the name to be the field
|
195
|
+
data.results[0].series.forEach((series) => {
|
196
|
+
series.name = field; // Force the series name to be the field name
|
197
|
+
});
|
198
|
+
// 1000000 REMOVED AND ADDED TO MOVE THE POINT AWAY IN THE CHART
|
199
|
+
if (fill) {
|
200
|
+
if (preStartValue !== null && preStartValue !== undefined) {
|
201
|
+
// Insert the pre-start value at the beginning of the dataset
|
202
|
+
data.results[0].series[0].values.unshift([
|
203
|
+
timeStart - 1000000,
|
204
|
+
preStartValue
|
205
|
+
]);
|
118
206
|
}
|
119
|
-
|
120
|
-
const
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
207
|
+
// Set the last point as the timeEnd and last value of the dataset
|
208
|
+
const lastSeries = data.results[0].series[0];
|
209
|
+
const lastValue = lastSeries?.values &&
|
210
|
+
lastSeries.values.length > 0 &&
|
211
|
+
lastSeries.values.slice(-1)[0].length > 1
|
212
|
+
? lastSeries.values.slice(-1)[0][1]
|
213
|
+
: null;
|
214
|
+
data.results[0].series[0].values.push([timeEnd + 1000000, lastValue]);
|
125
215
|
}
|
216
|
+
return data;
|
126
217
|
}
|
127
|
-
export async function
|
128
|
-
|
129
|
-
|
218
|
+
export async function exportDataV1(influxConfig, field, timeStart, timeEnd, deviceID) {
|
219
|
+
const query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
|
220
|
+
.unix(timeStart)
|
221
|
+
.toISOString()}' AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
130
222
|
const response = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${query}`), {
|
131
223
|
headers: {
|
132
224
|
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
@@ -136,10 +228,22 @@ export async function getFirstTimestamp(influxConfig, deviceID) {
|
|
136
228
|
throw new Error(`Failed to fetch data from InfluxDB: ${response.statusText}`);
|
137
229
|
}
|
138
230
|
const data = await response.json();
|
139
|
-
//
|
140
|
-
if (data
|
141
|
-
//
|
142
|
-
|
231
|
+
// Ensure the name is manually set to the field
|
232
|
+
if (!data.results[0].series) {
|
233
|
+
// Set default value with null time and null value
|
234
|
+
data.results[0].series = [
|
235
|
+
{
|
236
|
+
name: field, // Manually set the series name as the field
|
237
|
+
columns: ["time", "value"],
|
238
|
+
values: [] // Set null point for time and value
|
239
|
+
}
|
240
|
+
];
|
241
|
+
}
|
242
|
+
else {
|
243
|
+
// Always override the name to be the field
|
244
|
+
data.results[0].series.forEach((series) => {
|
245
|
+
series.name = field; // Force the series name to be the field name
|
246
|
+
});
|
143
247
|
}
|
144
|
-
return
|
248
|
+
return data;
|
145
249
|
}
|
@@ -1,19 +1,20 @@
|
|
1
1
|
export type TrackleConfig = {
|
2
|
+
orgName: string;
|
2
3
|
apiUrl: string;
|
3
|
-
createCustomerUrl: string;
|
4
4
|
clientId: string;
|
5
5
|
clientSecret: string;
|
6
6
|
tokenUrl: string;
|
7
7
|
cookieName: string;
|
8
8
|
cookieSecure: boolean;
|
9
9
|
apiTimeout: number;
|
10
|
+
productID: number;
|
10
11
|
};
|
11
12
|
export declare function logOut(trackleConfig: TrackleConfig): Promise<void>;
|
12
13
|
export declare function createCustomer(trackleConfig: TrackleConfig, uid: string): Promise<{
|
13
14
|
organization: string;
|
14
15
|
uid: string;
|
15
16
|
}>;
|
16
|
-
export declare function getDevices(trackleConfig: TrackleConfig, productId?: number, uid?: string,
|
17
|
+
export declare function getDevices(trackleConfig: TrackleConfig, productId?: number, uid?: string, query?: string): Promise<{
|
17
18
|
devices: any[];
|
18
19
|
}>;
|
19
20
|
export declare function getDevice(trackleConfig: TrackleConfig, id: string, productId?: number, uid?: string): Promise<{
|
@@ -48,32 +48,19 @@ export async function logOut(trackleConfig) {
|
|
48
48
|
cookies().delete(trackleConfig.cookieName);
|
49
49
|
}
|
50
50
|
export async function createCustomer(trackleConfig, uid) {
|
51
|
-
return await wretch(trackleConfig.
|
51
|
+
return await wretch(`${trackleConfig.apiUrl}/orgs/${trackleConfig.orgName}/customers`)
|
52
52
|
.headers({
|
53
53
|
Authorization: `Basic ${btoa(`${trackleConfig.clientId}:${trackleConfig.clientSecret}`)}`
|
54
54
|
})
|
55
55
|
.post({ uid })
|
56
56
|
.json();
|
57
57
|
}
|
58
|
-
export async function getDevices(trackleConfig, productId, uid,
|
59
|
-
let queryParams = "last_handshake_at!=null";
|
60
|
-
if (group !== "all") {
|
61
|
-
queryParams = `last_handshake_at!=null&state.groups=/${group}/`;
|
62
|
-
if (!uid) {
|
63
|
-
queryParams += "&quarantined=false";
|
64
|
-
}
|
65
|
-
}
|
66
|
-
else if (selected !== "all") {
|
67
|
-
queryParams = `last_handshake_at!=null&state.groups!=/${selected}/`;
|
68
|
-
if (!uid) {
|
69
|
-
queryParams += "&quarantined=false";
|
70
|
-
}
|
71
|
-
}
|
58
|
+
export async function getDevices(trackleConfig, productId, uid, query) {
|
72
59
|
const api = uid ? wretchApi(trackleConfig, uid) : wretchApi(trackleConfig);
|
73
60
|
const response = await api
|
74
61
|
.get(uid
|
75
|
-
? `/devices
|
76
|
-
: `/products/${productId}/devices
|
62
|
+
? `/devices${query ? "?" + query : ""}`
|
63
|
+
: `/products/${productId}/devices${query ? "?" + query : ""}`)
|
77
64
|
.setTimeout(trackleConfig.apiTimeout)
|
78
65
|
.json();
|
79
66
|
return (uid ? { devices: response } : response);
|