@adventurelabs/scout-core 1.0.72 → 1.0.74
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/dist/helpers/sessions.d.ts +17 -17
- package/dist/helpers/tags.d.ts +1 -0
- package/dist/helpers/tags.js +24 -5
- package/dist/types/db.d.ts +2 -1
- package/package.json +5 -8
- package/helpers/README.md +0 -135
- package/hooks/README.md +0 -93
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import { Database } from "../types/supabase";
|
|
3
|
-
import { ISession, IConnectivity, IEvent, ISessionWithCoordinates, IConnectivityWithCoordinates, IEventAndTagsPrettyLocation } from "../types/db";
|
|
3
|
+
import { ISession, IConnectivity, IEvent, ISessionWithCoordinates, IConnectivityWithCoordinates, IEventAndTagsPrettyLocation, ScoutDatabaseClient } from "../types/db";
|
|
4
4
|
export type SessionInput = Omit<ISession, "id" | "inserted_at">;
|
|
5
5
|
export type SessionUpdateInput = Partial<SessionInput> & {
|
|
6
6
|
id: number;
|
|
@@ -11,28 +11,28 @@ export type ConnectivityUpdateInput = Partial<ConnectivityInput> & {
|
|
|
11
11
|
id: number;
|
|
12
12
|
};
|
|
13
13
|
export type ConnectivityUpsertInput = ConnectivityInput | ConnectivityUpdateInput;
|
|
14
|
-
export declare function getSessionsByHerdId(supabase: SupabaseClient<Database>, herdId: number): Promise<ISessionWithCoordinates[]>;
|
|
15
|
-
export declare function getConnectivityBySessionId(supabase: SupabaseClient<Database>, sessionId: number): Promise<IConnectivityWithCoordinates[]>;
|
|
16
|
-
export declare function getEventsBySessionId(supabase: SupabaseClient<Database>, sessionId: number): Promise<IEvent[]>;
|
|
17
|
-
export declare function getEventsAndTagsBySessionId(supabase: SupabaseClient<Database>, sessionId: number, limit?: number, offset?: number): Promise<IEventAndTagsPrettyLocation[]>;
|
|
18
|
-
export declare function getTotalEventsForSession(supabase: SupabaseClient<Database>, sessionId: number): Promise<number>;
|
|
19
|
-
export declare function upsertSession(supabase: SupabaseClient<Database>, sessionData: SessionUpsertInput): Promise<ISession>;
|
|
20
|
-
export declare function upsertSessions(supabase: SupabaseClient<Database>, sessionsData: SessionUpsertInput[]): Promise<ISession[]>;
|
|
21
|
-
export declare function upsertConnectivity(supabase: SupabaseClient<Database>, connectivityData: ConnectivityUpsertInput): Promise<IConnectivity>;
|
|
22
|
-
export declare function upsertConnectivityBatch(supabase: SupabaseClient<Database>, connectivityDataArray: ConnectivityUpsertInput[]): Promise<IConnectivity[]>;
|
|
23
|
-
export declare function getSessionWithConnectivityAndEvents(supabase:
|
|
14
|
+
export declare function getSessionsByHerdId(supabase: SupabaseClient<Database, "public">, herdId: number): Promise<ISessionWithCoordinates[]>;
|
|
15
|
+
export declare function getConnectivityBySessionId(supabase: SupabaseClient<Database, "public">, sessionId: number): Promise<IConnectivityWithCoordinates[]>;
|
|
16
|
+
export declare function getEventsBySessionId(supabase: SupabaseClient<Database, "public">, sessionId: number): Promise<IEvent[]>;
|
|
17
|
+
export declare function getEventsAndTagsBySessionId(supabase: SupabaseClient<Database, "public">, sessionId: number, limit?: number, offset?: number): Promise<IEventAndTagsPrettyLocation[]>;
|
|
18
|
+
export declare function getTotalEventsForSession(supabase: SupabaseClient<Database, "public">, sessionId: number): Promise<number>;
|
|
19
|
+
export declare function upsertSession(supabase: SupabaseClient<Database, "public">, sessionData: SessionUpsertInput): Promise<ISession>;
|
|
20
|
+
export declare function upsertSessions(supabase: SupabaseClient<Database, "public">, sessionsData: SessionUpsertInput[]): Promise<ISession[]>;
|
|
21
|
+
export declare function upsertConnectivity(supabase: SupabaseClient<Database, "public">, connectivityData: ConnectivityUpsertInput): Promise<IConnectivity>;
|
|
22
|
+
export declare function upsertConnectivityBatch(supabase: SupabaseClient<Database, "public">, connectivityDataArray: ConnectivityUpsertInput[]): Promise<IConnectivity[]>;
|
|
23
|
+
export declare function getSessionWithConnectivityAndEvents(supabase: ScoutDatabaseClient, sessionId: number, herdId?: number): Promise<{
|
|
24
24
|
session: ISessionWithCoordinates | null;
|
|
25
25
|
connectivity: IConnectivityWithCoordinates[];
|
|
26
26
|
events: IEvent[];
|
|
27
27
|
}>;
|
|
28
|
-
export declare function getSessionWithConnectivityAndEventsWithTags(supabase:
|
|
28
|
+
export declare function getSessionWithConnectivityAndEventsWithTags(supabase: ScoutDatabaseClient, sessionId: number, limit?: number, offset?: number, herdId?: number): Promise<{
|
|
29
29
|
session: ISessionWithCoordinates | null;
|
|
30
30
|
connectivity: IConnectivityWithCoordinates[];
|
|
31
31
|
eventsWithTags: IEventAndTagsPrettyLocation[];
|
|
32
32
|
totalEvents: number;
|
|
33
33
|
}>;
|
|
34
|
-
export declare function getSessionsByDeviceId(supabase:
|
|
35
|
-
export declare function deleteSession(supabase:
|
|
36
|
-
export declare function deleteSessions(supabase:
|
|
37
|
-
export declare function deleteConnectivity(supabase:
|
|
38
|
-
export declare function deleteConnectivityBatch(supabase:
|
|
34
|
+
export declare function getSessionsByDeviceId(supabase: ScoutDatabaseClient, deviceId: number): Promise<ISessionWithCoordinates[]>;
|
|
35
|
+
export declare function deleteSession(supabase: ScoutDatabaseClient, sessionId: number): Promise<void>;
|
|
36
|
+
export declare function deleteSessions(supabase: ScoutDatabaseClient, sessionIds: number[]): Promise<void>;
|
|
37
|
+
export declare function deleteConnectivity(supabase: ScoutDatabaseClient, connectivityId: number): Promise<void>;
|
|
38
|
+
export declare function deleteConnectivityBatch(supabase: ScoutDatabaseClient, connectivityIds: number[]): Promise<void>;
|
package/dist/helpers/tags.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IEventAndTagsPrettyLocation, ITag } from "../types/db";
|
|
2
2
|
import { IWebResponseCompatible } from "../types/requests";
|
|
3
|
+
export declare function test_event_loading(device_id: number): Promise<boolean>;
|
|
3
4
|
export declare function server_create_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
|
|
4
5
|
export declare function server_delete_tags_by_ids(tag_ids: number[]): Promise<IWebResponseCompatible<boolean>>;
|
|
5
6
|
export declare function server_update_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
|
package/dist/helpers/tags.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
// add tag to db
|
|
3
3
|
import { newServerClient } from "../supabase/server";
|
|
4
|
+
import { EnumWebResponse, IWebResponse, } from "../types/requests";
|
|
5
|
+
import { addSignedUrlsToEvents, addSignedUrlToEvent } from "./storage";
|
|
4
6
|
// Helper functions to extract coordinates from location field
|
|
5
7
|
function extractLatitude(location) {
|
|
6
8
|
try {
|
|
@@ -52,8 +54,25 @@ function extractLongitude(location) {
|
|
|
52
54
|
}
|
|
53
55
|
return null;
|
|
54
56
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
// Test function to verify individual event loading works
|
|
58
|
+
export async function test_event_loading(device_id) {
|
|
59
|
+
try {
|
|
60
|
+
console.log(`[Event Test] Testing individual event loading for device ${device_id}`);
|
|
61
|
+
const events_response = await server_get_events_and_tags_for_device(device_id, 1);
|
|
62
|
+
if (events_response.status === EnumWebResponse.SUCCESS) {
|
|
63
|
+
console.log(`[Event Test] Successfully loaded ${events_response.data?.length || 0} events for device ${device_id}`);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.error(`[Event Test] Failed to load events for device ${device_id}:`, events_response.msg);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(`[Event Test] Failed to load events for device ${device_id}:`, error);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
57
76
|
export async function server_create_tags(tags) {
|
|
58
77
|
const supabase = await newServerClient();
|
|
59
78
|
// remove id key from tags
|
|
@@ -128,9 +147,8 @@ export async function server_update_tags(tags) {
|
|
|
128
147
|
}
|
|
129
148
|
export async function server_get_more_events_with_tags_by_herd(herd_id, offset, page_count = 10) {
|
|
130
149
|
const from = offset * page_count;
|
|
131
|
-
const to = from + page_count - 1;
|
|
132
150
|
const supabase = await newServerClient();
|
|
133
|
-
// make rpc call to
|
|
151
|
+
// make rpc call to get_events_and_tags_for_herd(herd_id, offset, limit)
|
|
134
152
|
const { data, error } = await supabase.rpc("get_events_and_tags_for_herd", {
|
|
135
153
|
herd_id_caller: herd_id,
|
|
136
154
|
offset_caller: from,
|
|
@@ -157,7 +175,7 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
|
|
|
157
175
|
}
|
|
158
176
|
export async function server_get_events_and_tags_for_device(device_id, limit = 3) {
|
|
159
177
|
const supabase = await newServerClient();
|
|
160
|
-
// make rpc call to
|
|
178
|
+
// make rpc call to get_events_and_tags_for_device(device_id, limit)
|
|
161
179
|
const { data, error } = await supabase.rpc("get_events_and_tags_for_device", {
|
|
162
180
|
device_id_caller: device_id,
|
|
163
181
|
limit_caller: limit,
|
|
@@ -231,6 +249,7 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
|
|
|
231
249
|
timestamp_observation: row.timestamp_observation,
|
|
232
250
|
is_public: row.is_public,
|
|
233
251
|
earthranger_url: row.earthranger_url,
|
|
252
|
+
herd_id: row.herd_id,
|
|
234
253
|
tags: Array.isArray(row.tags) ? row.tags : [],
|
|
235
254
|
};
|
|
236
255
|
eventsByDevice[device_id].push(event);
|
package/dist/types/db.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { User } from "@supabase/supabase-js";
|
|
1
|
+
import { SupabaseClient, User } from "@supabase/supabase-js";
|
|
2
2
|
import { Database } from "./supabase";
|
|
3
|
+
export type ScoutDatabaseClient = SupabaseClient<Database, "public">;
|
|
3
4
|
export type Role = Database["public"]["Enums"]["role"];
|
|
4
5
|
export type DeviceType = Database["public"]["Enums"]["device_type"];
|
|
5
6
|
export type MediaType = Database["public"]["Enums"]["media_type"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adventurelabs/scout-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.74",
|
|
4
4
|
"description": "Core utilities and helpers for Adventure Labs Scout applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,17 +36,14 @@
|
|
|
36
36
|
"next": ">=15.0.0",
|
|
37
37
|
"react": ">=18.0.0",
|
|
38
38
|
"react-dom": ">=18.0.0",
|
|
39
|
-
"react-redux": ">=9.0.0"
|
|
39
|
+
"react-redux": ">=9.0.0",
|
|
40
|
+
"@supabase/supabase-js": "^2.57.4",
|
|
41
|
+
"@supabase/ssr": "^0.6.1",
|
|
42
|
+
"@reduxjs/toolkit": "^2.0.0"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
45
|
"@types/react": "^18.0.0",
|
|
43
46
|
"@types/react-dom": "^18.0.0",
|
|
44
47
|
"typescript": "^5.0.0"
|
|
45
|
-
},
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@reduxjs/toolkit": "^2.0.0",
|
|
48
|
-
"@supabase/ssr": "^0.6.1",
|
|
49
|
-
"@supabase/supabase-js": "^2.53.0",
|
|
50
|
-
"zustand": "^4.0.0"
|
|
51
48
|
}
|
|
52
49
|
}
|
package/helpers/README.md
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Scout Core Helpers
|
|
2
|
-
|
|
3
|
-
## Storage and Signed URLs
|
|
4
|
-
|
|
5
|
-
This module provides functionality for handling file storage and signed URL generation for media files.
|
|
6
|
-
|
|
7
|
-
### Key Features
|
|
8
|
-
|
|
9
|
-
- **Signed URL Generation**: Generate secure, time-limited URLs for accessing media files
|
|
10
|
-
- **Backward Compatibility**: Support for both new signed URLs and legacy public URLs
|
|
11
|
-
- **Seamless Integration**: Signed URLs are automatically set as `media_url` for easy use
|
|
12
|
-
- **Efficient Client Usage**: Accepts existing Supabase client to avoid creating multiple instances
|
|
13
|
-
|
|
14
|
-
### Files
|
|
15
|
-
|
|
16
|
-
- **`storage.ts`**: Server-side functions for generating signed URLs (requires "use server")
|
|
17
|
-
- **`eventUtils.ts`**: Client-side utility functions for working with event media URLs
|
|
18
|
-
|
|
19
|
-
### Storage Functions (Server-side)
|
|
20
|
-
|
|
21
|
-
#### `generateSignedUrl(filePath, expiresIn, supabaseClient?)`
|
|
22
|
-
|
|
23
|
-
Generates a signed URL for a file in Supabase storage.
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// With existing client
|
|
27
|
-
const signedUrl = await generateSignedUrl(
|
|
28
|
-
"events/123/image.jpg",
|
|
29
|
-
3600,
|
|
30
|
-
supabaseClient
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Without existing client (creates new one)
|
|
34
|
-
const signedUrl = await generateSignedUrl("events/123/image.jpg", 3600);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
#### `addSignedUrlsToEvents(events, supabaseClient?)`
|
|
38
|
-
|
|
39
|
-
Adds signed URLs to an array of events, setting them as `media_url`.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// With existing client
|
|
43
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events, supabaseClient);
|
|
44
|
-
|
|
45
|
-
// Without existing client (creates new one)
|
|
46
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### `addSignedUrlToEvent(event, supabaseClient?)`
|
|
50
|
-
|
|
51
|
-
Adds a signed URL to a single event, setting it as `media_url`.
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
// With existing client
|
|
55
|
-
const eventWithUrl = await addSignedUrlToEvent(event, supabaseClient);
|
|
56
|
-
|
|
57
|
-
// Without existing client (creates new one)
|
|
58
|
-
const eventWithUrl = await addSignedUrlToEvent(event);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Event Utility Functions (Client-side)
|
|
62
|
-
|
|
63
|
-
#### `getEventMediaUrl(event)`
|
|
64
|
-
|
|
65
|
-
Gets the media URL for an event (now simply returns `event.media_url`).
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
const mediaUrl = getEventMediaUrl(event);
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### `hasEventMedia(event)`
|
|
72
|
-
|
|
73
|
-
Checks if an event has any media URL available.
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
const hasMedia = hasEventMedia(event);
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Usage in Components
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { getEventMediaUrl, hasEventMedia } from "@adventurelabs/scout-core";
|
|
83
|
-
|
|
84
|
-
function EventMedia({ event }) {
|
|
85
|
-
const mediaUrl = getEventMediaUrl(event);
|
|
86
|
-
|
|
87
|
-
if (!hasEventMedia(event)) {
|
|
88
|
-
return <div>No media available</div>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return <img src={mediaUrl} alt="Event media" />;
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Database Schema
|
|
96
|
-
|
|
97
|
-
Events now have two URL-related fields:
|
|
98
|
-
|
|
99
|
-
- `file_path`: The storage path for generating signed URLs
|
|
100
|
-
- `media_url`: The URL for accessing media (signed URL when file_path exists, legacy public URL otherwise)
|
|
101
|
-
|
|
102
|
-
### How It Works
|
|
103
|
-
|
|
104
|
-
1. **Event Creation**: Files are uploaded and `file_path` is stored (no `media_url` initially)
|
|
105
|
-
2. **Event Fetching**: When events are retrieved, signed URLs are generated and set as `media_url`
|
|
106
|
-
3. **URL Access**: Components simply use `event.media_url` as before
|
|
107
|
-
4. **Fallback**: If signed URL generation fails, existing `media_url` is preserved
|
|
108
|
-
|
|
109
|
-
### Performance Optimization
|
|
110
|
-
|
|
111
|
-
The storage functions accept an optional `supabaseClient` parameter to reuse existing client instances:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// Efficient: Reuse existing client
|
|
115
|
-
const supabase = await newServerClient();
|
|
116
|
-
const events = await fetchEvents(supabase);
|
|
117
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events, supabase);
|
|
118
|
-
|
|
119
|
-
// Less efficient: Creates new client for each operation
|
|
120
|
-
const events = await fetchEvents();
|
|
121
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events); // Creates new client
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Migration Strategy
|
|
125
|
-
|
|
126
|
-
1. **Phase 1**: Store `file_path` for new events (no `media_url`)
|
|
127
|
-
2. **Phase 2**: Generate signed URLs on fetch and set as `media_url`
|
|
128
|
-
3. **Phase 3**: All events use signed URLs seamlessly
|
|
129
|
-
|
|
130
|
-
### Security Benefits
|
|
131
|
-
|
|
132
|
-
- **Time-limited access**: URLs expire after a configurable time
|
|
133
|
-
- **Secure access**: URLs are cryptographically signed
|
|
134
|
-
- **No public exposure**: Files are not publicly accessible without signed URLs
|
|
135
|
-
- **Seamless integration**: No changes needed in existing components
|
package/hooks/README.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Scout Hooks
|
|
2
|
-
|
|
3
|
-
This directory contains React hooks for the Scout application.
|
|
4
|
-
|
|
5
|
-
## useScoutRefresh
|
|
6
|
-
|
|
7
|
-
A hook for refreshing scout data with detailed timing measurements for performance monitoring and debugging.
|
|
8
|
-
|
|
9
|
-
### Features
|
|
10
|
-
|
|
11
|
-
- **Automatic refresh**: Automatically refreshes data on component mount
|
|
12
|
-
- **Manual refresh**: Provides a function to manually trigger refreshes
|
|
13
|
-
- **Detailed timing**: Measures the duration of each portion of the loading process
|
|
14
|
-
- **Concurrent protection**: Prevents multiple simultaneous refresh operations
|
|
15
|
-
- **Error handling**: Graceful error handling with state consistency
|
|
16
|
-
|
|
17
|
-
### Timing Measurements
|
|
18
|
-
|
|
19
|
-
The hook tracks the duration of several key operations:
|
|
20
|
-
|
|
21
|
-
1. **Herd Modules API Call** (`herd_modules_api_duration_ms`): Time taken to fetch herd modules from the server
|
|
22
|
-
2. **User API Call** (`user_api_duration_ms`): Time taken to fetch user data from the server
|
|
23
|
-
3. **Data Processing** (`data_processing_duration_ms`): Time taken to process and dispatch data to the store
|
|
24
|
-
4. **LocalStorage Operations** (`localStorage_duration_ms`): Time taken for localStorage read/write operations
|
|
25
|
-
5. **Total Duration** (`herd_modules_loaded_in_ms`): Overall time from start to completion
|
|
26
|
-
|
|
27
|
-
### Usage
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
import { useScoutRefresh } from "../hooks";
|
|
31
|
-
|
|
32
|
-
function MyComponent() {
|
|
33
|
-
const { handleRefresh, getTimingStats } = useScoutRefresh({
|
|
34
|
-
autoRefresh: true,
|
|
35
|
-
onRefreshComplete: () => {
|
|
36
|
-
console.log("Refresh completed!");
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const handleManualRefresh = async () => {
|
|
41
|
-
await handleRefresh();
|
|
42
|
-
|
|
43
|
-
// Get timing statistics
|
|
44
|
-
const stats = getTimingStats();
|
|
45
|
-
console.log("Performance breakdown:");
|
|
46
|
-
console.log(`- Herd modules API: ${stats.herdModulesApi}ms`);
|
|
47
|
-
console.log(`- User API: ${stats.userApi}ms`);
|
|
48
|
-
console.log(`- Data processing: ${stats.dataProcessing}ms`);
|
|
49
|
-
console.log(`- LocalStorage: ${stats.localStorage}ms`);
|
|
50
|
-
console.log(`- Total: ${stats.totalDuration}ms`);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div>
|
|
55
|
-
<button onClick={handleManualRefresh}>Refresh Data</button>
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Redux State
|
|
62
|
-
|
|
63
|
-
The hook automatically updates the Redux store with timing information:
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
// Access timing data from the store
|
|
67
|
-
const timingData = useSelector((state) => ({
|
|
68
|
-
herdModulesApi: state.scout.herd_modules_api_duration_ms,
|
|
69
|
-
userApi: state.scout.user_api_duration_ms,
|
|
70
|
-
dataProcessing: state.scout.data_processing_duration_ms,
|
|
71
|
-
localStorage: state.scout.localStorage_duration_ms,
|
|
72
|
-
total: state.scout.herd_modules_loaded_in_ms,
|
|
73
|
-
}));
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Performance Monitoring
|
|
77
|
-
|
|
78
|
-
Use these timing measurements to:
|
|
79
|
-
|
|
80
|
-
- Identify performance bottlenecks in the loading process
|
|
81
|
-
- Monitor API response times
|
|
82
|
-
- Track data processing efficiency
|
|
83
|
-
- Debug localStorage performance issues
|
|
84
|
-
- Set performance budgets and alerts
|
|
85
|
-
|
|
86
|
-
### Error Handling
|
|
87
|
-
|
|
88
|
-
The hook includes comprehensive error handling:
|
|
89
|
-
|
|
90
|
-
- API response validation
|
|
91
|
-
- Graceful fallbacks for localStorage failures
|
|
92
|
-
- Consistent state updates even on errors
|
|
93
|
-
- Detailed error logging for debugging
|