@adventurelabs/scout-core 1.0.58 → 1.0.60
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/README.md +134 -4
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +2 -2
- package/dist/hooks/useScoutRefresh.d.ts +31 -0
- package/dist/hooks/useScoutRefresh.js +136 -16
- package/dist/providers/ScoutRefreshProvider.js +8 -9
- package/dist/store/hooks.d.ts +7 -0
- package/dist/store/hooks.js +65 -1
- package/dist/store/scout.d.ts +32 -2
- package/dist/store/scout.js +27 -1
- package/dist/types/herd_module.d.ts +6 -0
- package/dist/types/herd_module.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,108 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Scout Core
|
|
2
2
|
|
|
3
3
|
Core utilities and helpers for Adventure Labs Scout applications.
|
|
4
4
|
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Herd Management**: Comprehensive herd and device management
|
|
8
|
+
- **Event Tracking**: Wildlife event monitoring and tagging
|
|
9
|
+
- **Real-time Updates**: Supabase-powered real-time data synchronization
|
|
10
|
+
- **State Management**: Redux-based state management with loading states
|
|
11
|
+
|
|
12
|
+
## Herd Modules Loading State
|
|
13
|
+
|
|
14
|
+
The core provides a global loading state for herd modules, which are essential for many consuming applications. This state tracks whether herd modules are currently loading, have loaded successfully, or failed to load.
|
|
15
|
+
|
|
16
|
+
### Loading State Enum
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { EnumHerdModulesLoadingState } from "@adventurelabs/scout-core";
|
|
20
|
+
|
|
21
|
+
enum EnumHerdModulesLoadingState {
|
|
22
|
+
NOT_LOADING = "NOT_LOADING",
|
|
23
|
+
LOADING = "LOADING",
|
|
24
|
+
SUCCESSFULLY_LOADED = "SUCCESSFULLY_LOADED",
|
|
25
|
+
UNSUCCESSFULLY_LOADED = "UNSUCCESSFULLY_LOADED",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
**Available Hooks:**
|
|
29
|
+
- `useHerdModulesLoadingState()` - Get current loading state
|
|
30
|
+
- `useIsHerdModulesLoading()` - Check if currently loading
|
|
31
|
+
- `useIsHerdModulesLoaded()` - Check if successfully loaded
|
|
32
|
+
- `useIsHerdModulesFailed()` - Check if loading failed
|
|
33
|
+
- `useHerdModulesLoadedAt()` - Get how long the last loading took (in milliseconds)
|
|
34
|
+
- `useHerdModulesLoadingDuration()` - Get loading duration in milliseconds
|
|
35
|
+
- `useHerdModulesLoadingTimeAgo()` - Get formatted time ago since last loaded (e.g., "2.5s ago")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Usage in Components
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import {
|
|
42
|
+
useHerdModulesLoadingState,
|
|
43
|
+
useIsHerdModulesLoading,
|
|
44
|
+
useIsHerdModulesLoaded,
|
|
45
|
+
useIsHerdModulesFailed,
|
|
46
|
+
useHerdModulesLoadedAt,
|
|
47
|
+
useHerdModulesLoadingTimeAgo,
|
|
48
|
+
useHerdModulesLoadingDuration,
|
|
49
|
+
} from "@adventurelabs/scout-core";
|
|
50
|
+
|
|
51
|
+
function MyComponent() {
|
|
52
|
+
const loadingState = useHerdModulesLoadingState();
|
|
53
|
+
const isLoading = useIsHerdModulesLoading();
|
|
54
|
+
const isLoaded = useIsHerdModulesLoaded();
|
|
55
|
+
const isFailed = useIsHerdModulesFailed();
|
|
56
|
+
|
|
57
|
+
if (isLoading) {
|
|
58
|
+
return <div>Loading herd modules...</div>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (isFailed) {
|
|
62
|
+
return <div>Failed to load herd modules</div>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (isLoaded) {
|
|
66
|
+
return <div>Herd modules loaded successfully!</div>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return <div>Not loading</div>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Example with loading duration information
|
|
73
|
+
function HerdModulesStatus() {
|
|
74
|
+
const loadingState = useHerdModulesLoadingState();
|
|
75
|
+
const loadingTimeMs = useHerdModulesLoadedAt();
|
|
76
|
+
const timeAgo = useHerdModulesLoadingTimeAgo();
|
|
77
|
+
const loadingDuration = useHerdModulesLoadingDuration();
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div>
|
|
81
|
+
<div>Status: {loadingState}</div>
|
|
82
|
+
{loadingTimeMs && (
|
|
83
|
+
<>
|
|
84
|
+
<div>Last loading took: {loadingTimeMs}ms</div>
|
|
85
|
+
<div>Loaded: {timeAgo}</div>
|
|
86
|
+
<div>Loading duration: {loadingDuration}ms</div>
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Manual Refresh
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { useScoutRefresh } from "@adventurelabs/scout-core";
|
|
98
|
+
|
|
99
|
+
function RefreshButton() {
|
|
100
|
+
const { handleRefresh } = useScoutRefresh({ autoRefresh: false });
|
|
101
|
+
|
|
102
|
+
return <button onClick={handleRefresh}>Refresh Data</button>;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
5
106
|
## Installation
|
|
6
107
|
|
|
7
108
|
```bash
|
|
@@ -10,9 +111,31 @@ npm install @adventurelabs/scout-core
|
|
|
10
111
|
yarn add @adventurelabs/scout-core
|
|
11
112
|
```
|
|
12
113
|
|
|
13
|
-
##
|
|
114
|
+
## Setup
|
|
115
|
+
|
|
116
|
+
Wrap your app with the ScoutRefreshProvider:
|
|
14
117
|
|
|
15
118
|
```typescript
|
|
119
|
+
import { ScoutRefreshProvider } from "@adventurelabs/scout-core";
|
|
120
|
+
|
|
121
|
+
function App() {
|
|
122
|
+
return (
|
|
123
|
+
<ScoutRefreshProvider>{/* Your app components */}</ScoutRefreshProvider>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Recent Updates
|
|
129
|
+
|
|
130
|
+
- **v1.0.58**: Added global herd modules loading state tracking with timestamps
|
|
131
|
+
- Fixed repeat Supabase client creation logs
|
|
132
|
+
- Enhanced loading state management for better UX
|
|
133
|
+
- Added loading duration and time-ago tracking
|
|
134
|
+
- Added comprehensive edge case handling and race condition prevention
|
|
135
|
+
|
|
136
|
+
## Usage
|
|
137
|
+
|
|
138
|
+
````typescript
|
|
16
139
|
import "../../app/globals.css";
|
|
17
140
|
import StoreProvider from "../../components/Store/StoreProvider";
|
|
18
141
|
import { ScoutRefreshProvider } from "@adventurelabs/scout-core";
|
|
@@ -33,7 +156,6 @@ export default function ScoutLayout({
|
|
|
33
156
|
</StoreProvider>
|
|
34
157
|
);
|
|
35
158
|
}
|
|
36
|
-
```
|
|
37
159
|
|
|
38
160
|
## Available Modules
|
|
39
161
|
|
|
@@ -42,6 +164,7 @@ export default function ScoutLayout({
|
|
|
42
164
|
- Database types from Supabase
|
|
43
165
|
- Herd, Device, Event, User interfaces
|
|
44
166
|
- Request/Response types
|
|
167
|
+
- Herd module loading state enums (`EnumHerdModulesLoadingState`)
|
|
45
168
|
|
|
46
169
|
### Helpers
|
|
47
170
|
|
|
@@ -95,7 +218,7 @@ function ConnectionStatus() {
|
|
|
95
218
|
|
|
96
219
|
return <div>Status: {isConnected ? "Connected" : "Disconnected"}</div>;
|
|
97
220
|
}
|
|
98
|
-
|
|
221
|
+
````
|
|
99
222
|
|
|
100
223
|
### Store
|
|
101
224
|
|
|
@@ -128,3 +251,10 @@ yarn clean
|
|
|
128
251
|
## License
|
|
129
252
|
|
|
130
253
|
GPL-3.0
|
|
254
|
+
|
|
255
|
+
**New Hooks** (in `core/store/hooks.ts`):
|
|
256
|
+
|
|
257
|
+
- `useHerdModulesLoadingState()` - Get current loading state
|
|
258
|
+
- `useIsHerdModulesLoading()` - Check if currently loading
|
|
259
|
+
- `useIsHerdModulesLoaded()` - Check if successfully loaded
|
|
260
|
+
- `
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export { useScoutDbListener } from "./useScoutDbListener";
|
|
2
|
+
export { useScoutRefresh, type UseScoutRefreshOptions, } from "./useScoutRefresh";
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export { useScoutDbListener } from "./useScoutDbListener";
|
|
2
|
+
export { useScoutRefresh, } from "./useScoutRefresh";
|
|
@@ -2,6 +2,37 @@ export interface UseScoutRefreshOptions {
|
|
|
2
2
|
autoRefresh?: boolean;
|
|
3
3
|
onRefreshComplete?: () => void;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* Hook for refreshing scout data with detailed timing measurements
|
|
7
|
+
*
|
|
8
|
+
* @param options - Configuration options for the refresh behavior
|
|
9
|
+
* @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
|
|
10
|
+
* @param options.onRefreshComplete - Callback function called when refresh completes
|
|
11
|
+
*
|
|
12
|
+
* @returns Object containing:
|
|
13
|
+
* - handleRefresh: Function to manually trigger a refresh
|
|
14
|
+
* - getTimingStats: Function to get detailed timing statistics for the last refresh
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const { handleRefresh, getTimingStats } = useScoutRefresh();
|
|
19
|
+
*
|
|
20
|
+
* // Get timing stats after a refresh
|
|
21
|
+
* const stats = getTimingStats();
|
|
22
|
+
* console.log('Herd modules API took:', stats.herdModulesApi, 'ms');
|
|
23
|
+
* console.log('User API took:', stats.userApi, 'ms');
|
|
24
|
+
* console.log('Data processing took:', stats.dataProcessing, 'ms');
|
|
25
|
+
* console.log('LocalStorage operations took:', stats.localStorage, 'ms');
|
|
26
|
+
* console.log('Total duration:', stats.totalDuration, 'ms');
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
5
29
|
export declare function useScoutRefresh(options?: UseScoutRefreshOptions): {
|
|
6
30
|
handleRefresh: () => Promise<void>;
|
|
31
|
+
getTimingStats: () => {
|
|
32
|
+
totalDuration: number;
|
|
33
|
+
herdModulesApi: number;
|
|
34
|
+
userApi: number;
|
|
35
|
+
dataProcessing: number;
|
|
36
|
+
localStorage: number;
|
|
37
|
+
};
|
|
7
38
|
};
|
|
@@ -1,45 +1,165 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
1
|
+
import { useEffect, useCallback, useRef } from "react";
|
|
2
2
|
import { useAppDispatch } from "../store/hooks";
|
|
3
|
-
import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setUser, } from "../store/scout";
|
|
3
|
+
import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setUser, } from "../store/scout";
|
|
4
|
+
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
4
5
|
import { server_load_herd_modules } from "../helpers/herds";
|
|
5
6
|
import { server_get_user } from "../helpers/users";
|
|
7
|
+
/**
|
|
8
|
+
* Hook for refreshing scout data with detailed timing measurements
|
|
9
|
+
*
|
|
10
|
+
* @param options - Configuration options for the refresh behavior
|
|
11
|
+
* @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
|
|
12
|
+
* @param options.onRefreshComplete - Callback function called when refresh completes
|
|
13
|
+
*
|
|
14
|
+
* @returns Object containing:
|
|
15
|
+
* - handleRefresh: Function to manually trigger a refresh
|
|
16
|
+
* - getTimingStats: Function to get detailed timing statistics for the last refresh
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const { handleRefresh, getTimingStats } = useScoutRefresh();
|
|
21
|
+
*
|
|
22
|
+
* // Get timing stats after a refresh
|
|
23
|
+
* const stats = getTimingStats();
|
|
24
|
+
* console.log('Herd modules API took:', stats.herdModulesApi, 'ms');
|
|
25
|
+
* console.log('User API took:', stats.userApi, 'ms');
|
|
26
|
+
* console.log('Data processing took:', stats.dataProcessing, 'ms');
|
|
27
|
+
* console.log('LocalStorage operations took:', stats.localStorage, 'ms');
|
|
28
|
+
* console.log('Total duration:', stats.totalDuration, 'ms');
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
6
31
|
export function useScoutRefresh(options = {}) {
|
|
7
32
|
const { autoRefresh = true, onRefreshComplete } = options;
|
|
8
33
|
const dispatch = useAppDispatch();
|
|
9
|
-
const
|
|
10
|
-
|
|
34
|
+
const refreshInProgressRef = useRef(false);
|
|
35
|
+
// Refs to store timing measurements
|
|
36
|
+
const timingRefs = useRef({
|
|
37
|
+
startTime: 0,
|
|
38
|
+
herdModulesDuration: 0,
|
|
39
|
+
userApiDuration: 0,
|
|
40
|
+
dataProcessingDuration: 0,
|
|
41
|
+
localStorageDuration: 0,
|
|
42
|
+
});
|
|
43
|
+
const handleRefresh = useCallback(async () => {
|
|
44
|
+
// Prevent concurrent refresh calls
|
|
45
|
+
if (refreshInProgressRef.current) {
|
|
46
|
+
console.warn("[useScoutRefresh] Refresh already in progress, skipping");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
refreshInProgressRef.current = true;
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
timingRefs.current.startTime = startTime;
|
|
11
52
|
try {
|
|
53
|
+
dispatch(setStatus(EnumScoutStateStatus.LOADING));
|
|
54
|
+
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.LOADING));
|
|
55
|
+
// Measure herd modules API call duration
|
|
56
|
+
const herdModulesStartTime = Date.now();
|
|
12
57
|
const compatible_new_herd_modules = await server_load_herd_modules();
|
|
58
|
+
const herdModulesDuration = Date.now() - herdModulesStartTime;
|
|
59
|
+
timingRefs.current.herdModulesDuration = herdModulesDuration;
|
|
60
|
+
dispatch(setHerdModulesApiDuration(herdModulesDuration));
|
|
61
|
+
// Measure user API call duration
|
|
62
|
+
const userApiStartTime = Date.now();
|
|
13
63
|
const res_new_user = await server_get_user();
|
|
64
|
+
const userApiDuration = Date.now() - userApiStartTime;
|
|
65
|
+
timingRefs.current.userApiDuration = userApiDuration;
|
|
66
|
+
dispatch(setUserApiDuration(userApiDuration));
|
|
67
|
+
// Validate API responses
|
|
68
|
+
if (!compatible_new_herd_modules ||
|
|
69
|
+
!Array.isArray(compatible_new_herd_modules)) {
|
|
70
|
+
throw new Error("Invalid herd modules response");
|
|
71
|
+
}
|
|
72
|
+
if (!res_new_user || !res_new_user.data) {
|
|
73
|
+
throw new Error("Invalid user response");
|
|
74
|
+
}
|
|
75
|
+
// Measure data processing duration
|
|
76
|
+
const dataProcessingStartTime = Date.now();
|
|
14
77
|
dispatch(setHerdModules(compatible_new_herd_modules));
|
|
15
78
|
dispatch(setUser(res_new_user.data));
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
79
|
+
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
|
|
80
|
+
const dataProcessingDuration = Date.now() - dataProcessingStartTime;
|
|
81
|
+
timingRefs.current.dataProcessingDuration = dataProcessingDuration;
|
|
82
|
+
dispatch(setDataProcessingDuration(dataProcessingDuration));
|
|
83
|
+
// Measure localStorage operations duration
|
|
84
|
+
const localStorageStartTime = Date.now();
|
|
85
|
+
// Safely handle localStorage operations
|
|
86
|
+
try {
|
|
87
|
+
// Check local storage for a last selected herd
|
|
88
|
+
const lastSelectedHerd = localStorage.getItem("last_selected_herd");
|
|
89
|
+
if (lastSelectedHerd) {
|
|
90
|
+
const found_herd = compatible_new_herd_modules.find((hm) => hm.herd.id.toString() === lastSelectedHerd)?.herd;
|
|
91
|
+
// If herd is found then set it
|
|
92
|
+
if (found_herd) {
|
|
93
|
+
dispatch(setActiveHerdId(found_herd.id.toString()));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// If there is no last selected herd then select the first one
|
|
97
|
+
else if (compatible_new_herd_modules.length > 0) {
|
|
98
|
+
const firstHerdId = compatible_new_herd_modules[0].herd.id.toString();
|
|
99
|
+
localStorage.setItem("last_selected_herd", firstHerdId);
|
|
100
|
+
dispatch(setActiveHerdId(firstHerdId));
|
|
22
101
|
}
|
|
23
102
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
103
|
+
catch (localStorageError) {
|
|
104
|
+
console.warn("[useScoutRefresh] localStorage not available:", localStorageError);
|
|
105
|
+
// Fallback: select first herd without localStorage
|
|
106
|
+
if (compatible_new_herd_modules.length > 0) {
|
|
107
|
+
dispatch(setActiveHerdId(compatible_new_herd_modules[0].herd.id.toString()));
|
|
108
|
+
}
|
|
28
109
|
}
|
|
110
|
+
const localStorageDuration = Date.now() - localStorageStartTime;
|
|
111
|
+
timingRefs.current.localStorageDuration = localStorageDuration;
|
|
112
|
+
dispatch(setLocalStorageDuration(localStorageDuration));
|
|
113
|
+
const loadingDuration = Date.now() - startTime;
|
|
114
|
+
dispatch(setHerdModulesLoadedInMs(loadingDuration));
|
|
29
115
|
dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
|
|
116
|
+
// Log timing statistics
|
|
117
|
+
console.log("[useScoutRefresh] Refresh completed successfully. Timing breakdown:");
|
|
118
|
+
console.log(` - Herd modules API: ${herdModulesDuration}ms`);
|
|
119
|
+
console.log(` - User API: ${userApiDuration}ms`);
|
|
120
|
+
console.log(` - Data processing: ${dataProcessingDuration}ms`);
|
|
121
|
+
console.log(` - LocalStorage operations: ${localStorageDuration}ms`);
|
|
122
|
+
console.log(` - Total duration: ${loadingDuration}ms`);
|
|
30
123
|
onRefreshComplete?.();
|
|
31
124
|
}
|
|
32
125
|
catch (error) {
|
|
126
|
+
const loadingDuration = Date.now() - startTime;
|
|
33
127
|
console.error("Error refreshing scout data:", error);
|
|
128
|
+
// Ensure consistent state updates on error
|
|
129
|
+
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.UNSUCCESSFULLY_LOADED));
|
|
130
|
+
dispatch(setHerdModulesLoadedInMs(loadingDuration));
|
|
34
131
|
dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
|
|
132
|
+
// Log timing statistics even on error
|
|
133
|
+
console.log("[useScoutRefresh] Refresh failed. Partial timing breakdown:");
|
|
134
|
+
console.log(` - Herd modules API: ${timingRefs.current.herdModulesDuration}ms`);
|
|
135
|
+
console.log(` - User API: ${timingRefs.current.userApiDuration}ms`);
|
|
136
|
+
console.log(` - Data processing: ${timingRefs.current.dataProcessingDuration}ms`);
|
|
137
|
+
console.log(` - LocalStorage operations: ${timingRefs.current.localStorageDuration}ms`);
|
|
138
|
+
console.log(` - Total duration: ${loadingDuration}ms`);
|
|
35
139
|
}
|
|
36
|
-
|
|
140
|
+
finally {
|
|
141
|
+
refreshInProgressRef.current = false;
|
|
142
|
+
}
|
|
143
|
+
}, [dispatch, onRefreshComplete]);
|
|
37
144
|
useEffect(() => {
|
|
38
145
|
if (autoRefresh) {
|
|
39
146
|
handleRefresh();
|
|
40
147
|
}
|
|
41
|
-
}, [autoRefresh]);
|
|
148
|
+
}, [autoRefresh, handleRefresh]);
|
|
149
|
+
// Utility function to get timing statistics
|
|
150
|
+
const getTimingStats = useCallback(() => {
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
const startTime = timingRefs.current.startTime;
|
|
153
|
+
return {
|
|
154
|
+
totalDuration: startTime > 0 ? now - startTime : 0,
|
|
155
|
+
herdModulesApi: timingRefs.current.herdModulesDuration,
|
|
156
|
+
userApi: timingRefs.current.userApiDuration,
|
|
157
|
+
dataProcessing: timingRefs.current.dataProcessingDuration,
|
|
158
|
+
localStorage: timingRefs.current.localStorageDuration,
|
|
159
|
+
};
|
|
160
|
+
}, []);
|
|
42
161
|
return {
|
|
43
162
|
handleRefresh,
|
|
163
|
+
getTimingStats,
|
|
44
164
|
};
|
|
45
165
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useScoutRefresh } from "../hooks/useScoutRefresh";
|
|
4
4
|
import { useScoutDbListener } from "../hooks/useScoutDbListener";
|
|
5
|
-
import { createContext, useContext,
|
|
5
|
+
import { createContext, useContext, useMemo } from "react";
|
|
6
6
|
import { createBrowserClient } from "@supabase/ssr";
|
|
7
7
|
// Create context for the Supabase client
|
|
8
8
|
const SupabaseContext = createContext(null);
|
|
@@ -26,14 +26,13 @@ export function useConnectionStatus() {
|
|
|
26
26
|
export function ScoutRefreshProvider({ children }) {
|
|
27
27
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
|
|
28
28
|
const anon_key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
|
|
29
|
-
// Create a single Supabase client instance
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
29
|
+
// Create a single Supabase client instance using useMemo to prevent recreation
|
|
30
|
+
const supabaseClient = useMemo(() => {
|
|
31
|
+
console.log("[ScoutRefreshProvider] Creating Supabase client");
|
|
32
|
+
return createBrowserClient(url, anon_key);
|
|
33
|
+
}, [url, anon_key]);
|
|
35
34
|
// Use the enhanced DB listener with connection status
|
|
36
|
-
useScoutDbListener(
|
|
35
|
+
useScoutDbListener(supabaseClient);
|
|
37
36
|
useScoutRefresh();
|
|
38
37
|
// // Log connection status changes for debugging
|
|
39
38
|
// if (connectionStatus.lastError) {
|
|
@@ -45,5 +44,5 @@ export function ScoutRefreshProvider({ children }) {
|
|
|
45
44
|
// if (connectionStatus.isConnected) {
|
|
46
45
|
// console.log("[ScoutRefreshProvider] ✅ DB Listener connected");
|
|
47
46
|
// }
|
|
48
|
-
return (_jsx(SupabaseContext.Provider, { value:
|
|
47
|
+
return (_jsx(SupabaseContext.Provider, { value: supabaseClient, children: children }));
|
|
49
48
|
}
|
package/dist/store/hooks.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
1
|
export declare const useAppDispatch: <AppDispatch extends import("redux").Dispatch<import("redux").AnyAction> = import("redux").Dispatch<import("redux").AnyAction>>() => AppDispatch;
|
|
2
|
+
export declare const useHerdModulesLoadingState: () => any;
|
|
3
|
+
export declare const useIsHerdModulesLoading: () => boolean;
|
|
4
|
+
export declare const useIsHerdModulesLoaded: () => boolean;
|
|
5
|
+
export declare const useIsHerdModulesFailed: () => boolean;
|
|
6
|
+
export declare const useHerdModulesLoadedAt: () => any;
|
|
7
|
+
export declare const useHerdModulesLoadingDuration: () => any;
|
|
8
|
+
export declare const useHerdModulesLoadingTimeAgo: () => string | null;
|
package/dist/store/hooks.js
CHANGED
|
@@ -1,3 +1,67 @@
|
|
|
1
|
-
import { useDispatch } from "react-redux";
|
|
1
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
2
|
+
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
2
3
|
// Simple wrapper for useDispatch to maintain compatibility
|
|
3
4
|
export const useAppDispatch = useDispatch;
|
|
5
|
+
// Selector hook for herd modules loading state
|
|
6
|
+
export const useHerdModulesLoadingState = () => {
|
|
7
|
+
return useSelector((state) => state.scout.herd_modules_loading_state);
|
|
8
|
+
};
|
|
9
|
+
// Selector hook for checking if herd modules are currently loading
|
|
10
|
+
export const useIsHerdModulesLoading = () => {
|
|
11
|
+
return useSelector((state) => state.scout.herd_modules_loading_state ===
|
|
12
|
+
EnumHerdModulesLoadingState.LOADING);
|
|
13
|
+
};
|
|
14
|
+
// Selector hook for checking if herd modules loaded successfully
|
|
15
|
+
export const useIsHerdModulesLoaded = () => {
|
|
16
|
+
return useSelector((state) => state.scout.herd_modules_loading_state ===
|
|
17
|
+
EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED);
|
|
18
|
+
};
|
|
19
|
+
// Selector hook for checking if herd modules failed to load
|
|
20
|
+
export const useIsHerdModulesFailed = () => {
|
|
21
|
+
return useSelector((state) => state.scout.herd_modules_loading_state ===
|
|
22
|
+
EnumHerdModulesLoadingState.UNSUCCESSFULLY_LOADED);
|
|
23
|
+
};
|
|
24
|
+
// Selector hook for getting when herd modules were last loaded
|
|
25
|
+
export const useHerdModulesLoadedAt = () => {
|
|
26
|
+
return useSelector((state) => state.scout.herd_modules_loaded_in_ms);
|
|
27
|
+
};
|
|
28
|
+
// Selector hook for getting the loading duration in milliseconds
|
|
29
|
+
export const useHerdModulesLoadingDuration = () => {
|
|
30
|
+
return useSelector((state) => {
|
|
31
|
+
return state.scout.herd_modules_loaded_in_ms;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
// Selector hook for getting formatted loading time (e.g., "2.5s ago")
|
|
35
|
+
export const useHerdModulesLoadingTimeAgo = () => {
|
|
36
|
+
return useSelector((state) => {
|
|
37
|
+
const loadingDuration = state.scout.herd_modules_loaded_in_ms;
|
|
38
|
+
if (!loadingDuration)
|
|
39
|
+
return null;
|
|
40
|
+
// Since we store the duration, we need to calculate when it was loaded
|
|
41
|
+
// We'll use the lastRefreshed timestamp from the store
|
|
42
|
+
const lastRefreshed = state.scout.lastRefreshed;
|
|
43
|
+
if (!lastRefreshed)
|
|
44
|
+
return null;
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const timeSinceLoaded = now - lastRefreshed;
|
|
47
|
+
// Handle edge case where timeSinceLoaded might be negative
|
|
48
|
+
if (timeSinceLoaded < 0) {
|
|
49
|
+
return "just now";
|
|
50
|
+
}
|
|
51
|
+
const diffSeconds = Math.floor(timeSinceLoaded / 1000);
|
|
52
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
53
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
54
|
+
if (diffHours > 0) {
|
|
55
|
+
return `${diffHours}h ago`;
|
|
56
|
+
}
|
|
57
|
+
else if (diffMinutes > 0) {
|
|
58
|
+
return `${diffMinutes}m ago`;
|
|
59
|
+
}
|
|
60
|
+
else if (diffSeconds > 0) {
|
|
61
|
+
return `${diffSeconds}s ago`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return "just now";
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
package/dist/store/scout.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IUser } from "../types/db";
|
|
2
|
-
import { IHerdModule } from "../types/herd_module";
|
|
2
|
+
import { IHerdModule, EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
3
3
|
export declare enum EnumScoutStateStatus {
|
|
4
4
|
LOADING = "LOADING",
|
|
5
5
|
DONE_LOADING = "DONE_LOADING"
|
|
@@ -7,6 +7,12 @@ export declare enum EnumScoutStateStatus {
|
|
|
7
7
|
export interface ScoutState {
|
|
8
8
|
herd_modules: IHerdModule[];
|
|
9
9
|
status: EnumScoutStateStatus;
|
|
10
|
+
herd_modules_loading_state: EnumHerdModulesLoadingState;
|
|
11
|
+
herd_modules_loaded_in_ms: number | null;
|
|
12
|
+
herd_modules_api_duration_ms: number | null;
|
|
13
|
+
user_api_duration_ms: number | null;
|
|
14
|
+
data_processing_duration_ms: number | null;
|
|
15
|
+
localStorage_duration_ms: number | null;
|
|
10
16
|
active_herd_id: string | null;
|
|
11
17
|
active_device_id: string | null;
|
|
12
18
|
lastRefreshed: number;
|
|
@@ -21,6 +27,30 @@ export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
|
|
|
21
27
|
payload: any;
|
|
22
28
|
type: string;
|
|
23
29
|
}) => void;
|
|
30
|
+
setHerdModulesLoadingState: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
31
|
+
payload: any;
|
|
32
|
+
type: string;
|
|
33
|
+
}) => void;
|
|
34
|
+
setHerdModulesLoadedInMs: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
35
|
+
payload: any;
|
|
36
|
+
type: string;
|
|
37
|
+
}) => void;
|
|
38
|
+
setHerdModulesApiDuration: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
39
|
+
payload: any;
|
|
40
|
+
type: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
setUserApiDuration: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
43
|
+
payload: any;
|
|
44
|
+
type: string;
|
|
45
|
+
}) => void;
|
|
46
|
+
setDataProcessingDuration: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
47
|
+
payload: any;
|
|
48
|
+
type: string;
|
|
49
|
+
}) => void;
|
|
50
|
+
setLocalStorageDuration: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
51
|
+
payload: any;
|
|
52
|
+
type: string;
|
|
53
|
+
}) => void;
|
|
24
54
|
setActiveHerdId: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
25
55
|
payload: any;
|
|
26
56
|
type: string;
|
|
@@ -110,6 +140,6 @@ export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
|
|
|
110
140
|
type: string;
|
|
111
141
|
}) => void;
|
|
112
142
|
}, "scout", "scout", import("@reduxjs/toolkit").SliceSelectors<ScoutState>>;
|
|
113
|
-
export declare const setHerdModules: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModules">, setStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setStatus">, setActiveHerdId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdId">, setActiveDeviceId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveDeviceId">, appendEventsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendEventsToHerdModule">, replaceEventsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceEventsForHerdModule">, updateEventValuesForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateEventValuesForHerdModule">, updatePageIndexForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePageIndexForHerdModule">, appendPlansToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendPlansToHerdModule">, setUser: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUser">, addTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addTag">, deleteTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteTag">, updateTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateTag">, addNewDeviceToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addNewDeviceToHerdModule">, updateDeviceForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDeviceForHerdModule">, addDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addDevice">, deleteDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteDevice">, updateDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDevice">, addPlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addPlan">, deletePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deletePlan">, updatePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePlan">, addSessionToStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addSessionToStore">, deleteSessionFromStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteSessionFromStore">, updateSessionInStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateSessionInStore">;
|
|
143
|
+
export declare const setHerdModules: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModules">, setStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setStatus">, setHerdModulesLoadingState: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadingState">, setHerdModulesLoadedInMs: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadedInMs">, setHerdModulesApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiDuration">, setUserApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUserApiDuration">, setDataProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataProcessingDuration">, setLocalStorageDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setLocalStorageDuration">, setActiveHerdId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdId">, setActiveDeviceId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveDeviceId">, appendEventsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendEventsToHerdModule">, replaceEventsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceEventsForHerdModule">, updateEventValuesForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateEventValuesForHerdModule">, updatePageIndexForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePageIndexForHerdModule">, appendPlansToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendPlansToHerdModule">, setUser: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUser">, addTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addTag">, deleteTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteTag">, updateTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateTag">, addNewDeviceToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addNewDeviceToHerdModule">, updateDeviceForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDeviceForHerdModule">, addDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addDevice">, deleteDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteDevice">, updateDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDevice">, addPlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addPlan">, deletePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deletePlan">, updatePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePlan">, addSessionToStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addSessionToStore">, deleteSessionFromStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteSessionFromStore">, updateSessionInStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateSessionInStore">;
|
|
114
144
|
declare const _default: import("redux").Reducer<ScoutState>;
|
|
115
145
|
export default _default;
|
package/dist/store/scout.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createSlice } from "@reduxjs/toolkit";
|
|
2
|
+
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
2
3
|
export var EnumScoutStateStatus;
|
|
3
4
|
(function (EnumScoutStateStatus) {
|
|
4
5
|
EnumScoutStateStatus["LOADING"] = "LOADING";
|
|
@@ -7,6 +8,13 @@ export var EnumScoutStateStatus;
|
|
|
7
8
|
const initialState = {
|
|
8
9
|
herd_modules: [],
|
|
9
10
|
status: EnumScoutStateStatus.LOADING,
|
|
11
|
+
herd_modules_loading_state: EnumHerdModulesLoadingState.NOT_LOADING,
|
|
12
|
+
herd_modules_loaded_in_ms: null,
|
|
13
|
+
// Initialize timing variables
|
|
14
|
+
herd_modules_api_duration_ms: null,
|
|
15
|
+
user_api_duration_ms: null,
|
|
16
|
+
data_processing_duration_ms: null,
|
|
17
|
+
localStorage_duration_ms: null,
|
|
10
18
|
lastRefreshed: 0,
|
|
11
19
|
active_herd_id: null,
|
|
12
20
|
active_device_id: null,
|
|
@@ -23,6 +31,24 @@ export const scoutSlice = createSlice({
|
|
|
23
31
|
setStatus: (state, action) => {
|
|
24
32
|
state.status = action.payload;
|
|
25
33
|
},
|
|
34
|
+
setHerdModulesLoadingState: (state, action) => {
|
|
35
|
+
state.herd_modules_loading_state = action.payload;
|
|
36
|
+
},
|
|
37
|
+
setHerdModulesLoadedInMs: (state, action) => {
|
|
38
|
+
state.herd_modules_loaded_in_ms = action.payload;
|
|
39
|
+
},
|
|
40
|
+
setHerdModulesApiDuration: (state, action) => {
|
|
41
|
+
state.herd_modules_api_duration_ms = action.payload;
|
|
42
|
+
},
|
|
43
|
+
setUserApiDuration: (state, action) => {
|
|
44
|
+
state.user_api_duration_ms = action.payload;
|
|
45
|
+
},
|
|
46
|
+
setDataProcessingDuration: (state, action) => {
|
|
47
|
+
state.data_processing_duration_ms = action.payload;
|
|
48
|
+
},
|
|
49
|
+
setLocalStorageDuration: (state, action) => {
|
|
50
|
+
state.localStorage_duration_ms = action.payload;
|
|
51
|
+
},
|
|
26
52
|
setActiveHerdId: (state, action) => {
|
|
27
53
|
state.active_herd_id = action.payload;
|
|
28
54
|
},
|
|
@@ -228,5 +254,5 @@ export const scoutSlice = createSlice({
|
|
|
228
254
|
},
|
|
229
255
|
});
|
|
230
256
|
// Action creators are generated for each case reducer function
|
|
231
|
-
export const { setHerdModules, setStatus, setActiveHerdId, setActiveDeviceId, appendEventsToHerdModule, replaceEventsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, } = scoutSlice.actions;
|
|
257
|
+
export const { setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setActiveHerdId, setActiveDeviceId, appendEventsToHerdModule, replaceEventsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, } = scoutSlice.actions;
|
|
232
258
|
export default scoutSlice.reducer;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import { IDevice, IEventWithTags, IHerd, IPlan, ILayer, IUserAndRole, IZoneWithActions, ISessionWithCoordinates } from "../types/db";
|
|
3
|
+
export declare enum EnumHerdModulesLoadingState {
|
|
4
|
+
NOT_LOADING = "NOT_LOADING",
|
|
5
|
+
LOADING = "LOADING",
|
|
6
|
+
SUCCESSFULLY_LOADED = "SUCCESSFULLY_LOADED",
|
|
7
|
+
UNSUCCESSFULLY_LOADED = "UNSUCCESSFULLY_LOADED"
|
|
8
|
+
}
|
|
3
9
|
export declare class HerdModule {
|
|
4
10
|
herd: IHerd;
|
|
5
11
|
devices: IDevice[];
|
|
@@ -10,6 +10,13 @@ import { EnumWebResponse } from "./requests";
|
|
|
10
10
|
import { server_get_more_zones_and_actions_for_herd } from "../helpers/zones";
|
|
11
11
|
import { server_list_api_keys_batch } from "../api_keys/actions";
|
|
12
12
|
import { getSessionsByHerdId } from "../helpers/sessions";
|
|
13
|
+
export var EnumHerdModulesLoadingState;
|
|
14
|
+
(function (EnumHerdModulesLoadingState) {
|
|
15
|
+
EnumHerdModulesLoadingState["NOT_LOADING"] = "NOT_LOADING";
|
|
16
|
+
EnumHerdModulesLoadingState["LOADING"] = "LOADING";
|
|
17
|
+
EnumHerdModulesLoadingState["SUCCESSFULLY_LOADED"] = "SUCCESSFULLY_LOADED";
|
|
18
|
+
EnumHerdModulesLoadingState["UNSUCCESSFULLY_LOADED"] = "UNSUCCESSFULLY_LOADED";
|
|
19
|
+
})(EnumHerdModulesLoadingState || (EnumHerdModulesLoadingState = {}));
|
|
13
20
|
export class HerdModule {
|
|
14
21
|
constructor(herd, devices, events, timestamp_last_refreshed, user_roles = null, events_page_index = 0, total_events = 0, total_events_with_filters = 0, labels = [], plans = [], zones = [], sessions = [], layers = []) {
|
|
15
22
|
this.user_roles = null;
|