@adventurelabs/scout-core 1.0.15 → 1.0.18

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.
@@ -2,6 +2,7 @@ import { IEventWithTags, ITag } from "../types/db";
2
2
  import { IWebResponseCompatible } from "../types/requests";
3
3
  export declare function server_create_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
4
4
  export declare function server_delete_tags_by_ids(tag_ids: number[]): Promise<IWebResponseCompatible<boolean>>;
5
+ export declare function server_update_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
5
6
  export declare function server_get_more_events_with_tags_by_herd(herd_id: number, offset: number, page_count?: number): Promise<IWebResponseCompatible<IEventWithTags[]>>;
6
7
  export declare function server_get_events_and_tags_for_device(device_id: number, limit?: number): Promise<IWebResponseCompatible<IEventWithTags[]>>;
7
8
  export declare function get_event_and_tags_by_event_id(event_id: number): Promise<IWebResponseCompatible<IEventWithTags>>;
@@ -42,6 +42,39 @@ export async function server_delete_tags_by_ids(tag_ids) {
42
42
  return IWebResponse.success(true).to_compatible();
43
43
  }
44
44
  }
45
+ export async function server_update_tags(tags) {
46
+ const supabase = await newServerClient();
47
+ // Update each tag individually since we need to preserve the id
48
+ const updatedTags = [];
49
+ for (const tag of tags) {
50
+ const { id, ...updateData } = tag;
51
+ const { data, error } = await supabase
52
+ .from("tags")
53
+ .update({
54
+ x: updateData.x,
55
+ y: updateData.y,
56
+ width: updateData.width,
57
+ height: updateData.height,
58
+ class_name: updateData.class_name,
59
+ conf: updateData.conf,
60
+ observation_type: updateData.observation_type,
61
+ })
62
+ .eq("id", id)
63
+ .select("*")
64
+ .single();
65
+ if (error) {
66
+ return {
67
+ status: EnumWebResponse.ERROR,
68
+ msg: error.message,
69
+ data: null,
70
+ };
71
+ }
72
+ if (data) {
73
+ updatedTags.push(data);
74
+ }
75
+ }
76
+ return IWebResponse.success(updatedTags).to_compatible();
77
+ }
45
78
  // export async function server_get_events_with_tags_by_herd(
46
79
  // herd_id: number
47
80
  // ): Promise<IWebResponseCompatible<IEventWithTags[]>> {
@@ -131,25 +164,64 @@ export async function get_event_and_tags_by_event_id(event_id) {
131
164
  data: null,
132
165
  };
133
166
  }
134
- if (!data[0]) {
167
+ if (!data || data.length === 0) {
135
168
  return {
136
169
  status: EnumWebResponse.ERROR,
137
170
  msg: "Event not found",
138
171
  data: null,
139
172
  };
140
173
  }
141
- // Transform location to latitude/longitude
174
+ // Debug the location data structure
175
+ console.log("Raw event data from DB:", {
176
+ eventId: data[0].id,
177
+ location: data[0].location,
178
+ locationType: typeof data[0].location,
179
+ hasCoordinates: data[0].location &&
180
+ typeof data[0].location === "object" &&
181
+ "coordinates" in data[0].location,
182
+ });
183
+ // Transform location to latitude/longitude with better error handling
184
+ let latitude = null;
185
+ let longitude = null;
186
+ try {
187
+ if (data[0].location) {
188
+ if (typeof data[0].location === "object" && data[0].location !== null) {
189
+ // Handle PostGIS Point format: { coordinates: [lon, lat] }
190
+ if ("coordinates" in data[0].location &&
191
+ Array.isArray(data[0].location.coordinates)) {
192
+ longitude = data[0].location.coordinates[0];
193
+ latitude = data[0].location.coordinates[1];
194
+ }
195
+ // Handle alternative format: { x: lon, y: lat }
196
+ else if ("x" in data[0].location && "y" in data[0].location) {
197
+ longitude = data[0].location.x;
198
+ latitude = data[0].location.y;
199
+ }
200
+ }
201
+ // Handle string format: "Point(lon lat)"
202
+ else if (typeof data[0].location === "string") {
203
+ const match = data[0].location.match(/Point\(([^)]+)\)/);
204
+ if (match) {
205
+ const coords = match[1].split(" ").map(Number);
206
+ if (coords.length === 2) {
207
+ longitude = coords[0];
208
+ latitude = coords[1];
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ catch (locationError) {
215
+ console.warn("Error parsing location data:", locationError);
216
+ // Continue with null coordinates
217
+ }
142
218
  const transformedData = {
143
219
  id: data[0].id,
144
220
  inserted_at: data[0].inserted_at,
145
221
  message: data[0].message,
146
222
  media_url: data[0].media_url,
147
- latitude: data[0].location
148
- ? data[0].location.coordinates[1]
149
- : null,
150
- longitude: data[0].location
151
- ? data[0].location.coordinates[0]
152
- : null,
223
+ latitude: latitude,
224
+ longitude: longitude,
153
225
  altitude: data[0].altitude,
154
226
  heading: data[0].heading,
155
227
  media_type: data[0].media_type,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.15",
3
+ "version": "1.0.18",
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",
@@ -1,9 +0,0 @@
1
- declare function convertToSubcurrency(amount: number, factor?: number): number;
2
- export declare function roundToTwoDecimals(amount: number): number;
3
- /**
4
- * This function takes a number and returns a string with commas and a dollar sign
5
- * @param amount
6
- * @returns
7
- */
8
- export declare function formatDollarAmountforDisplay(amount: number): string;
9
- export default convertToSubcurrency;
@@ -1,16 +0,0 @@
1
- function convertToSubcurrency(amount, factor = 100) {
2
- return Math.round(amount * factor);
3
- }
4
- export function roundToTwoDecimals(amount) {
5
- return Math.round(amount * 100) / 100;
6
- }
7
- /**
8
- * This function takes a number and returns a string with commas and a dollar sign
9
- * @param amount
10
- * @returns
11
- */
12
- export function formatDollarAmountforDisplay(amount) {
13
- // add commas to the number and prefix with $
14
- return `$${amount.toLocaleString()}`;
15
- }
16
- export default convertToSubcurrency;
@@ -1,11 +0,0 @@
1
- /**
2
- * Hook for generating signed URLs on the client side
3
- * @param filePath - The file path in storage
4
- * @param expiresIn - Number of seconds until URL expires (default: 3600 = 1 hour)
5
- * @returns Object with signedUrl and loading state
6
- */
7
- export declare function useSignedUrl(filePath: string | null, expiresIn?: number): {
8
- signedUrl: string | null;
9
- loading: boolean;
10
- error: string | null;
11
- };
@@ -1,50 +0,0 @@
1
- "use client";
2
- import { useState, useEffect } from "react";
3
- import { createBrowserClient } from "@supabase/ssr";
4
- import { BUCKET_NAME_EVENTS } from "../constants/db";
5
- /**
6
- * Hook for generating signed URLs on the client side
7
- * @param filePath - The file path in storage
8
- * @param expiresIn - Number of seconds until URL expires (default: 3600 = 1 hour)
9
- * @returns Object with signedUrl and loading state
10
- */
11
- export function useSignedUrl(filePath, expiresIn = 3600) {
12
- const [signedUrl, setSignedUrl] = useState(null);
13
- const [loading, setLoading] = useState(false);
14
- const [error, setError] = useState(null);
15
- useEffect(() => {
16
- if (!filePath) {
17
- setSignedUrl(null);
18
- setError(null);
19
- return;
20
- }
21
- const generateSignedUrl = async () => {
22
- setLoading(true);
23
- setError(null);
24
- try {
25
- const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
26
- const anon_key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
27
- const supabase = createBrowserClient(url, anon_key);
28
- const { data, error } = await supabase.storage
29
- .from(BUCKET_NAME_EVENTS)
30
- .createSignedUrl(filePath, expiresIn);
31
- if (error) {
32
- setError(error.message);
33
- setSignedUrl(null);
34
- }
35
- else {
36
- setSignedUrl(data.signedUrl);
37
- }
38
- }
39
- catch (err) {
40
- setError(err instanceof Error ? err.message : "Unknown error");
41
- setSignedUrl(null);
42
- }
43
- finally {
44
- setLoading(false);
45
- }
46
- };
47
- generateSignedUrl();
48
- }, [filePath, expiresIn]);
49
- return { signedUrl, loading, error };
50
- }