@adventurelabs/scout-core 1.2.6 → 1.3.1

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.
@@ -225,8 +225,10 @@ export class ScoutCache {
225
225
  const result = await this.getHerdModules();
226
226
  const totalRequests = this.stats.hits + this.stats.misses;
227
227
  const hitRate = totalRequests > 0 ? this.stats.hits / totalRequests : 0;
228
+ // Calculate size based on herd modules count (no longer including events/sessions/artifacts arrays)
229
+ const size = result.data?.length || 0;
228
230
  return {
229
- size: result.data?.length || 0,
231
+ size,
230
232
  lastUpdated: result.data ? Date.now() - result.age : 0,
231
233
  isStale: result.isStale,
232
234
  hitRate: Math.round(hitRate * 100) / 100,
@@ -0,0 +1,6 @@
1
+ import { ISessionSummary } from "../types/db";
2
+ import { IWebResponseCompatible } from "../types/requests";
3
+ import { SupabaseClient } from "@supabase/supabase-js";
4
+ export declare function server_get_session_summaries_by_herd(herd_id: number, client?: SupabaseClient): Promise<IWebResponseCompatible<ISessionSummary>>;
5
+ export declare function server_get_session_summaries_by_device(device_id: number, client?: SupabaseClient): Promise<IWebResponseCompatible<ISessionSummary>>;
6
+ export declare function server_get_session_summaries_with_filters(herd_id?: number, device_id?: number, start_date?: string, end_date?: string, client?: SupabaseClient): Promise<IWebResponseCompatible<ISessionSummary>>;
@@ -0,0 +1,51 @@
1
+ "use server";
2
+ import { newServerClient } from "../supabase/server";
3
+ import { IWebResponse, } from "../types/requests";
4
+ export async function server_get_session_summaries_by_herd(herd_id, client) {
5
+ const supabase = client || (await newServerClient());
6
+ const { data, error } = await supabase.rpc("get_session_summaries", {
7
+ start_date_caller: undefined,
8
+ end_date_caller: undefined,
9
+ device_id_caller: undefined,
10
+ herd_id_caller: herd_id,
11
+ });
12
+ if (error) {
13
+ return IWebResponse.error(error.message).to_compatible();
14
+ }
15
+ if (!data) {
16
+ return IWebResponse.error("No session summary data returned").to_compatible();
17
+ }
18
+ return IWebResponse.success(data).to_compatible();
19
+ }
20
+ export async function server_get_session_summaries_by_device(device_id, client) {
21
+ const supabase = client || (await newServerClient());
22
+ const { data, error } = await supabase.rpc("get_session_summaries", {
23
+ start_date_caller: undefined,
24
+ end_date_caller: undefined,
25
+ device_id_caller: device_id,
26
+ herd_id_caller: undefined,
27
+ });
28
+ if (error) {
29
+ return IWebResponse.error(error.message).to_compatible();
30
+ }
31
+ if (!data) {
32
+ return IWebResponse.error("No session summary data returned").to_compatible();
33
+ }
34
+ return IWebResponse.success(data).to_compatible();
35
+ }
36
+ export async function server_get_session_summaries_with_filters(herd_id, device_id, start_date, end_date, client) {
37
+ const supabase = client || (await newServerClient());
38
+ const { data, error } = await supabase.rpc("get_session_summaries", {
39
+ start_date_caller: start_date || undefined,
40
+ end_date_caller: end_date || undefined,
41
+ device_id_caller: device_id || undefined,
42
+ herd_id_caller: herd_id || undefined,
43
+ });
44
+ if (error) {
45
+ return IWebResponse.error(error.message).to_compatible();
46
+ }
47
+ if (!data) {
48
+ return IWebResponse.error("No session summary data returned").to_compatible();
49
+ }
50
+ return IWebResponse.success(data).to_compatible();
51
+ }
@@ -7,3 +7,5 @@ export { useScoutRealtimeTags } from "./useScoutRealtimeTags";
7
7
  export { useScoutRealtimeSessions } from "./useScoutRealtimeSessions";
8
8
  export { useScoutRealtimePlans } from "./useScoutRealtimePlans";
9
9
  export { useScoutRealtimePins } from "./useScoutRealtimePins";
10
+ export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useIntersectionObserver, } from "./useInfiniteQuery";
11
+ export { useLoadingPerformance, useSessionSummariesByHerd, useHasSessionSummaries, } from "../store/hooks";
@@ -7,3 +7,7 @@ export { useScoutRealtimeTags } from "./useScoutRealtimeTags";
7
7
  export { useScoutRealtimeSessions } from "./useScoutRealtimeSessions";
8
8
  export { useScoutRealtimePlans } from "./useScoutRealtimePlans";
9
9
  export { useScoutRealtimePins } from "./useScoutRealtimePins";
10
+ // RTK Query infinite scroll hooks
11
+ export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useIntersectionObserver, } from "./useInfiniteQuery";
12
+ // Session summaries and performance hooks
13
+ export { useLoadingPerformance, useSessionSummariesByHerd, useHasSessionSummaries, } from "../store/hooks";
@@ -0,0 +1,167 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { useInfiniteSessionsByHerd, useInfiniteEventsByHerd, useInfiniteArtifactsByHerd } from "./useInfiniteQuery";
3
+ import { IHerdModule } from "../types/herd_module";
4
+ import { ISessionSummary } from "../types/db";
5
+ interface UseHerdDataOptions {
6
+ sessionsLimit?: number;
7
+ eventsLimit?: number;
8
+ artifactsLimit?: number;
9
+ enableSessions?: boolean;
10
+ enableEvents?: boolean;
11
+ enableArtifacts?: boolean;
12
+ supabase: SupabaseClient;
13
+ }
14
+ interface UseHerdDataReturn {
15
+ herdModule: IHerdModule | undefined;
16
+ herd: IHerdModule["herd"] | undefined;
17
+ devices: IHerdModule["devices"];
18
+ zones: IHerdModule["zones"];
19
+ plans: IHerdModule["plans"];
20
+ layers: IHerdModule["layers"];
21
+ providers: IHerdModule["providers"];
22
+ labels: IHerdModule["labels"];
23
+ userRoles: IHerdModule["user_roles"];
24
+ sessionSummaries: ISessionSummary | null;
25
+ timestampLastRefreshed: number | null;
26
+ sessions: {
27
+ items: ReturnType<typeof useInfiniteSessionsByHerd>["items"];
28
+ isLoading: boolean;
29
+ isLoadingMore: boolean;
30
+ hasMore: boolean;
31
+ loadMore: () => void;
32
+ refetch: () => void;
33
+ error: any;
34
+ };
35
+ events: {
36
+ items: ReturnType<typeof useInfiniteEventsByHerd>["items"];
37
+ isLoading: boolean;
38
+ isLoadingMore: boolean;
39
+ hasMore: boolean;
40
+ loadMore: () => void;
41
+ refetch: () => void;
42
+ error: any;
43
+ };
44
+ artifacts: {
45
+ items: ReturnType<typeof useInfiniteArtifactsByHerd>["items"];
46
+ isLoading: boolean;
47
+ isLoadingMore: boolean;
48
+ hasMore: boolean;
49
+ loadMore: () => void;
50
+ refetch: () => void;
51
+ error: any;
52
+ };
53
+ isInitialLoading: boolean;
54
+ hasAnyError: boolean;
55
+ }
56
+ /**
57
+ * Comprehensive hook for accessing herd data from both global store and RTK Query
58
+ *
59
+ * @param herdId - The herd ID to fetch data for
60
+ * @param options - Configuration options for data fetching
61
+ * @returns Complete herd data with both structural and paginated content
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const {
66
+ * herd,
67
+ * devices,
68
+ * events,
69
+ * sessions,
70
+ * artifacts,
71
+ * sessionSummaries
72
+ * } = useHerdData(123, {
73
+ * supabase,
74
+ * eventsLimit: 20,
75
+ * enableSessions: true
76
+ * });
77
+ *
78
+ * // Use structural data from global store
79
+ * console.log(herd.name, devices.length);
80
+ *
81
+ * // Use paginated data from RTK Query
82
+ * console.log(events.items.length, events.hasMore);
83
+ * if (events.hasMore) {
84
+ * events.loadMore();
85
+ * }
86
+ *
87
+ * // Use session summaries
88
+ * console.log(sessionSummaries?.total_sessions, sessionSummaries?.total_distance_meters);
89
+ * ```
90
+ */
91
+ export declare const useHerdData: (herdId: number, options: UseHerdDataOptions) => UseHerdDataReturn;
92
+ /**
93
+ * Hook for getting just the structural herd data from global store
94
+ * Useful when you only need basic herd info without paginated content
95
+ */
96
+ export declare const useHerdStructure: (herdId: number) => {
97
+ herdModule: IHerdModule | undefined;
98
+ herd: {
99
+ created_by: string;
100
+ description: string;
101
+ earthranger_domain: string | null;
102
+ earthranger_token: string | null;
103
+ id: number;
104
+ inserted_at: string;
105
+ is_public: boolean;
106
+ slug: string;
107
+ video_publisher_token: string | null;
108
+ video_server_url: string | null;
109
+ video_subscriber_token: string | null;
110
+ } | undefined;
111
+ devices: import("../types/db").IDevice[];
112
+ zones: import("../types/db").IZoneWithActions[];
113
+ plans: {
114
+ herd_id: number;
115
+ id: number;
116
+ inserted_at: string | null;
117
+ instructions: string;
118
+ name: string;
119
+ plan_type: import("..").Database["public"]["Enums"]["plan_type"];
120
+ }[];
121
+ layers: {
122
+ created_at: string;
123
+ features: import("..").Json;
124
+ herd_id: number;
125
+ id: number;
126
+ }[];
127
+ providers: {
128
+ created_at: string;
129
+ herd_id: number;
130
+ id: number;
131
+ key: string | null;
132
+ source: string;
133
+ type: string;
134
+ }[];
135
+ labels: string[];
136
+ userRoles: import("../types/db").IUserAndRole[] | null;
137
+ sessionSummaries: ISessionSummary | null;
138
+ timestampLastRefreshed: number | null;
139
+ };
140
+ /**
141
+ * Hook for getting all herd structures (without paginated data)
142
+ * Useful for dashboard/overview components
143
+ */
144
+ export declare const useAllHerds: () => {
145
+ herdModules: IHerdModule[];
146
+ herds: {
147
+ created_by: string;
148
+ description: string;
149
+ earthranger_domain: string | null;
150
+ earthranger_token: string | null;
151
+ id: number;
152
+ inserted_at: string;
153
+ is_public: boolean;
154
+ slug: string;
155
+ video_publisher_token: string | null;
156
+ video_server_url: string | null;
157
+ video_subscriber_token: string | null;
158
+ }[];
159
+ totalHerds: number;
160
+ totalDevices: number;
161
+ sessionSummaries: {
162
+ herdId: number;
163
+ herdSlug: string;
164
+ summaries: ISessionSummary | null;
165
+ }[];
166
+ };
167
+ export {};
@@ -0,0 +1,153 @@
1
+ import { useSelector } from "react-redux";
2
+ import { useInfiniteSessionsByHerd, useInfiniteEventsByHerd, useInfiniteArtifactsByHerd, } from "./useInfiniteQuery";
3
+ /**
4
+ * Comprehensive hook for accessing herd data from both global store and RTK Query
5
+ *
6
+ * @param herdId - The herd ID to fetch data for
7
+ * @param options - Configuration options for data fetching
8
+ * @returns Complete herd data with both structural and paginated content
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * const {
13
+ * herd,
14
+ * devices,
15
+ * events,
16
+ * sessions,
17
+ * artifacts,
18
+ * sessionSummaries
19
+ * } = useHerdData(123, {
20
+ * supabase,
21
+ * eventsLimit: 20,
22
+ * enableSessions: true
23
+ * });
24
+ *
25
+ * // Use structural data from global store
26
+ * console.log(herd.name, devices.length);
27
+ *
28
+ * // Use paginated data from RTK Query
29
+ * console.log(events.items.length, events.hasMore);
30
+ * if (events.hasMore) {
31
+ * events.loadMore();
32
+ * }
33
+ *
34
+ * // Use session summaries
35
+ * console.log(sessionSummaries?.total_sessions, sessionSummaries?.total_distance_meters);
36
+ * ```
37
+ */
38
+ export const useHerdData = (herdId, options) => {
39
+ const { sessionsLimit = 20, eventsLimit = 20, artifactsLimit = 20, enableSessions = true, enableEvents = true, enableArtifacts = true, supabase, } = options;
40
+ // Get structural data from global store
41
+ const herdModule = useSelector((state) => state.scout.herd_modules.find((hm) => hm.herd.id === herdId));
42
+ // RTK Query infinite data hooks
43
+ const sessionsQuery = useInfiniteSessionsByHerd(herdId, {
44
+ limit: sessionsLimit,
45
+ enabled: enableSessions,
46
+ supabase,
47
+ });
48
+ const eventsQuery = useInfiniteEventsByHerd(herdId, {
49
+ limit: eventsLimit,
50
+ enabled: enableEvents,
51
+ supabase,
52
+ });
53
+ const artifactsQuery = useInfiniteArtifactsByHerd(herdId, {
54
+ limit: artifactsLimit,
55
+ enabled: enableArtifacts,
56
+ supabase,
57
+ });
58
+ // Overall loading state - true if any enabled query is loading for the first time
59
+ const isInitialLoading = (enableSessions &&
60
+ sessionsQuery.isLoading &&
61
+ sessionsQuery.items.length === 0) ||
62
+ (enableEvents && eventsQuery.isLoading && eventsQuery.items.length === 0) ||
63
+ (enableArtifacts &&
64
+ artifactsQuery.isLoading &&
65
+ artifactsQuery.items.length === 0);
66
+ // Overall error state - true if any enabled query has an error
67
+ const hasAnyError = (enableSessions && !!sessionsQuery.error) ||
68
+ (enableEvents && !!eventsQuery.error) ||
69
+ (enableArtifacts && !!artifactsQuery.error);
70
+ return {
71
+ // Global store structural data
72
+ herdModule,
73
+ herd: herdModule?.herd,
74
+ devices: herdModule?.devices || [],
75
+ zones: herdModule?.zones || [],
76
+ plans: herdModule?.plans || [],
77
+ layers: herdModule?.layers || [],
78
+ providers: herdModule?.providers || [],
79
+ labels: herdModule?.labels || [],
80
+ userRoles: herdModule?.user_roles || null,
81
+ sessionSummaries: herdModule?.session_summaries || null,
82
+ timestampLastRefreshed: herdModule?.timestamp_last_refreshed || null,
83
+ // RTK Query paginated data
84
+ sessions: {
85
+ items: sessionsQuery.items,
86
+ isLoading: sessionsQuery.isLoading,
87
+ isLoadingMore: sessionsQuery.isLoadingMore,
88
+ hasMore: sessionsQuery.hasMore,
89
+ loadMore: sessionsQuery.loadMore,
90
+ refetch: sessionsQuery.refetch,
91
+ error: sessionsQuery.error,
92
+ },
93
+ events: {
94
+ items: eventsQuery.items,
95
+ isLoading: eventsQuery.isLoading,
96
+ isLoadingMore: eventsQuery.isLoadingMore,
97
+ hasMore: eventsQuery.hasMore,
98
+ loadMore: eventsQuery.loadMore,
99
+ refetch: eventsQuery.refetch,
100
+ error: eventsQuery.error,
101
+ },
102
+ artifacts: {
103
+ items: artifactsQuery.items,
104
+ isLoading: artifactsQuery.isLoading,
105
+ isLoadingMore: artifactsQuery.isLoadingMore,
106
+ hasMore: artifactsQuery.hasMore,
107
+ loadMore: artifactsQuery.loadMore,
108
+ refetch: artifactsQuery.refetch,
109
+ error: artifactsQuery.error,
110
+ },
111
+ // Overall states
112
+ isInitialLoading,
113
+ hasAnyError,
114
+ };
115
+ };
116
+ /**
117
+ * Hook for getting just the structural herd data from global store
118
+ * Useful when you only need basic herd info without paginated content
119
+ */
120
+ export const useHerdStructure = (herdId) => {
121
+ const herdModule = useSelector((state) => state.scout.herd_modules.find((hm) => hm.herd.id === herdId));
122
+ return {
123
+ herdModule,
124
+ herd: herdModule?.herd,
125
+ devices: herdModule?.devices || [],
126
+ zones: herdModule?.zones || [],
127
+ plans: herdModule?.plans || [],
128
+ layers: herdModule?.layers || [],
129
+ providers: herdModule?.providers || [],
130
+ labels: herdModule?.labels || [],
131
+ userRoles: herdModule?.user_roles || null,
132
+ sessionSummaries: herdModule?.session_summaries || null,
133
+ timestampLastRefreshed: herdModule?.timestamp_last_refreshed || null,
134
+ };
135
+ };
136
+ /**
137
+ * Hook for getting all herd structures (without paginated data)
138
+ * Useful for dashboard/overview components
139
+ */
140
+ export const useAllHerds = () => {
141
+ const herdModules = useSelector((state) => state.scout.herd_modules);
142
+ return {
143
+ herdModules,
144
+ herds: herdModules.map((hm) => hm.herd),
145
+ totalHerds: herdModules.length,
146
+ totalDevices: herdModules.reduce((sum, hm) => sum + hm.devices.length, 0),
147
+ sessionSummaries: herdModules.map((hm) => ({
148
+ herdId: hm.herd.id,
149
+ herdSlug: hm.herd.slug,
150
+ summaries: hm.session_summaries,
151
+ })),
152
+ };
153
+ };
@@ -0,0 +1,24 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { IArtifactWithMediaUrl, ISessionWithCoordinates, IEventAndTagsPrettyLocation } from "../types/db";
3
+ interface UseInfiniteScrollOptions {
4
+ limit?: number;
5
+ enabled?: boolean;
6
+ supabase: SupabaseClient;
7
+ }
8
+ interface InfiniteScrollData<T> {
9
+ items: T[];
10
+ isLoading: boolean;
11
+ isLoadingMore: boolean;
12
+ hasMore: boolean;
13
+ loadMore: () => void;
14
+ refetch: () => void;
15
+ error: any;
16
+ }
17
+ export declare const useInfiniteSessionsByHerd: (herdId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<ISessionWithCoordinates>;
18
+ export declare const useInfiniteSessionsByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<ISessionWithCoordinates>;
19
+ export declare const useInfiniteEventsByHerd: (herdId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IEventAndTagsPrettyLocation>;
20
+ export declare const useInfiniteEventsByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IEventAndTagsPrettyLocation>;
21
+ export declare const useInfiniteArtifactsByHerd: (herdId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IArtifactWithMediaUrl>;
22
+ export declare const useInfiniteArtifactsByDevice: (deviceId: number, options: UseInfiniteScrollOptions) => InfiniteScrollData<IArtifactWithMediaUrl>;
23
+ export declare const useIntersectionObserver: (callback: () => void, options?: IntersectionObserverInit) => import("react").Dispatch<import("react").SetStateAction<Element | null>>;
24
+ export {};