@growsober/sdk 1.0.0 → 1.0.2
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/api/mutations/admin.d.ts +1 -20
- package/dist/api/mutations/admin.js +1 -1
- package/dist/api/mutations/auth.d.ts +83 -1
- package/dist/api/mutations/auth.js +102 -1
- package/dist/api/mutations/event-chat.d.ts +84 -7
- package/dist/api/mutations/event-chat.js +1 -1
- package/dist/api/mutations/matching.d.ts +2 -19
- package/dist/api/mutations/matching.js +6 -6
- package/dist/api/mutations/support.d.ts +5 -5
- package/dist/api/mutations/support.js +1 -1
- package/dist/api/queries/ambassadors.d.ts +71 -5
- package/dist/api/queries/ambassadors.js +1 -1
- package/dist/api/queries/cities.d.ts +52 -0
- package/dist/api/queries/cities.js +82 -0
- package/dist/api/queries/event-chat.d.ts +30 -4
- package/dist/api/queries/grow90.d.ts +46 -4
- package/dist/api/queries/hubs.d.ts +2 -2
- package/dist/api/queries/hubs.js +1 -1
- package/dist/api/queries/index.d.ts +1 -0
- package/dist/api/queries/index.js +2 -1
- package/dist/api/queries/map.d.ts +3 -3
- package/dist/api/queries/map.js +1 -1
- package/dist/api/queries/support.d.ts +88 -8
- package/dist/api/queries/support.js +1 -1
- package/dist/api/types.d.ts +63 -393
- package/dist/api/types.js +4 -4
- package/package.json +1 -1
- package/src/api/mutations/admin.ts +1 -20
- package/src/api/mutations/auth.ts +114 -0
- package/src/api/mutations/event-chat.ts +7 -7
- package/src/api/mutations/matching.ts +12 -32
- package/src/api/mutations/support.ts +11 -11
- package/src/api/queries/ambassadors.ts +6 -3
- package/src/api/queries/cities.ts +194 -0
- package/src/api/queries/hubs.ts +3 -3
- package/src/api/queries/index.ts +1 -0
- package/src/api/queries/map.ts +10 -10
- package/src/api/queries/support.ts +8 -8
- package/src/api/types.ts +96 -448
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
useQueryClient,
|
|
16
16
|
} from '@tanstack/react-query';
|
|
17
17
|
import { getApiClient } from '../client';
|
|
18
|
+
import type { AdminUpdateUserRequest, AdminCreateContentRequest } from '../types';
|
|
18
19
|
import { adminKeys } from '../queries/admin';
|
|
19
20
|
import type {
|
|
20
21
|
UserResponse,
|
|
@@ -31,27 +32,7 @@ import type {
|
|
|
31
32
|
// REQUEST TYPES
|
|
32
33
|
// ============================================================================
|
|
33
34
|
|
|
34
|
-
export interface AdminUpdateUserRequest {
|
|
35
|
-
displayName?: string;
|
|
36
|
-
email?: string;
|
|
37
|
-
role?: string;
|
|
38
|
-
isPremium?: boolean;
|
|
39
|
-
isSuspended?: boolean;
|
|
40
|
-
}
|
|
41
35
|
|
|
42
|
-
export interface AdminCreateContentRequest {
|
|
43
|
-
title: string;
|
|
44
|
-
type: string;
|
|
45
|
-
category: string;
|
|
46
|
-
description?: string;
|
|
47
|
-
content?: string;
|
|
48
|
-
mediaUrl?: string;
|
|
49
|
-
thumbnailUrl?: string;
|
|
50
|
-
duration?: number;
|
|
51
|
-
isPremium?: boolean;
|
|
52
|
-
isFeatured?: boolean;
|
|
53
|
-
sortOrder?: number;
|
|
54
|
-
}
|
|
55
36
|
|
|
56
37
|
export interface AdminUpdateContentRequest extends Partial<AdminCreateContentRequest> {}
|
|
57
38
|
|
|
@@ -17,6 +17,9 @@ import type {
|
|
|
17
17
|
FirebaseAuthRequest,
|
|
18
18
|
AuthResponse,
|
|
19
19
|
TokenResponse,
|
|
20
|
+
SendOtpRequest,
|
|
21
|
+
OtpSentResponse,
|
|
22
|
+
VerifyOtpRequest,
|
|
20
23
|
} from '../types';
|
|
21
24
|
|
|
22
25
|
// ============================================================================
|
|
@@ -348,3 +351,114 @@ export function useFirebaseAuth(
|
|
|
348
351
|
...options,
|
|
349
352
|
});
|
|
350
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Response type for verify OTP including isNewUser flag
|
|
357
|
+
*/
|
|
358
|
+
export type VerifyOtpResponse = AuthResponse & { isNewUser: boolean };
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Send OTP to phone number
|
|
362
|
+
*
|
|
363
|
+
* @description
|
|
364
|
+
* Sends a verification code to the specified phone number via SMS or voice call.
|
|
365
|
+
* Used for passwordless authentication.
|
|
366
|
+
*
|
|
367
|
+
* @endpoint POST /auth/phone/send-otp
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```tsx
|
|
371
|
+
* import { useSendOtp } from '@growsober/sdk';
|
|
372
|
+
*
|
|
373
|
+
* function PhoneInputScreen() {
|
|
374
|
+
* const { mutate: sendOtp, isPending, error } = useSendOtp({
|
|
375
|
+
* onSuccess: (data) => {
|
|
376
|
+
* console.log('OTP sent to:', data.phone);
|
|
377
|
+
* navigation.navigate('VerifyOtp', { phone });
|
|
378
|
+
* },
|
|
379
|
+
* onError: (error) => {
|
|
380
|
+
* Alert.alert('Error', error.message);
|
|
381
|
+
* },
|
|
382
|
+
* });
|
|
383
|
+
*
|
|
384
|
+
* const handleSend = () => {
|
|
385
|
+
* sendOtp({ phone: '+1234567890', channel: 'sms' });
|
|
386
|
+
* };
|
|
387
|
+
*
|
|
388
|
+
* return <Button onPress={handleSend} disabled={isPending} />;
|
|
389
|
+
* }
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @param options - TanStack Query mutation options
|
|
393
|
+
* @returns TanStack Query mutation result
|
|
394
|
+
*/
|
|
395
|
+
export function useSendOtp(
|
|
396
|
+
options?: Omit<UseMutationOptions<OtpSentResponse, Error, SendOtpRequest>, 'mutationFn'>
|
|
397
|
+
): UseMutationResult<OtpSentResponse, Error, SendOtpRequest> {
|
|
398
|
+
return useMutation({
|
|
399
|
+
mutationFn: async (data: SendOtpRequest): Promise<OtpSentResponse> => {
|
|
400
|
+
const client = getApiClient();
|
|
401
|
+
const response = await client.post<OtpSentResponse>('/auth/phone/send-otp', data);
|
|
402
|
+
return response.data;
|
|
403
|
+
},
|
|
404
|
+
...options,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Verify OTP and authenticate
|
|
410
|
+
*
|
|
411
|
+
* @description
|
|
412
|
+
* Verifies the OTP code sent to the phone number and authenticates the user.
|
|
413
|
+
* If the user doesn't exist, a new account is created automatically.
|
|
414
|
+
* Returns authentication tokens and user information.
|
|
415
|
+
*
|
|
416
|
+
* @endpoint POST /auth/phone/verify-otp
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```tsx
|
|
420
|
+
* import { useVerifyOtp } from '@growsober/sdk';
|
|
421
|
+
*
|
|
422
|
+
* function VerifyOtpScreen({ phone }) {
|
|
423
|
+
* const { mutate: verifyOtp, isPending, error } = useVerifyOtp({
|
|
424
|
+
* onSuccess: async (data) => {
|
|
425
|
+
* await SecureStore.setItemAsync('accessToken', data.accessToken);
|
|
426
|
+
* await SecureStore.setItemAsync('refreshToken', data.refreshToken);
|
|
427
|
+
*
|
|
428
|
+
* if (data.isNewUser) {
|
|
429
|
+
* navigation.navigate('Onboarding');
|
|
430
|
+
* } else {
|
|
431
|
+
* navigation.navigate('Home');
|
|
432
|
+
* }
|
|
433
|
+
* },
|
|
434
|
+
* });
|
|
435
|
+
*
|
|
436
|
+
* const handleVerify = (code: string) => {
|
|
437
|
+
* verifyOtp({ phone, code });
|
|
438
|
+
* };
|
|
439
|
+
*
|
|
440
|
+
* return <OtpInput onComplete={handleVerify} disabled={isPending} />;
|
|
441
|
+
* }
|
|
442
|
+
* ```
|
|
443
|
+
*
|
|
444
|
+
* @param options - TanStack Query mutation options
|
|
445
|
+
* @returns TanStack Query mutation result
|
|
446
|
+
*/
|
|
447
|
+
export function useVerifyOtp(
|
|
448
|
+
options?: Omit<UseMutationOptions<VerifyOtpResponse, Error, VerifyOtpRequest>, 'mutationFn'>
|
|
449
|
+
): UseMutationResult<VerifyOtpResponse, Error, VerifyOtpRequest> {
|
|
450
|
+
const queryClient = useQueryClient();
|
|
451
|
+
|
|
452
|
+
return useMutation({
|
|
453
|
+
mutationFn: async (data: VerifyOtpRequest): Promise<VerifyOtpResponse> => {
|
|
454
|
+
const client = getApiClient();
|
|
455
|
+
const response = await client.post<VerifyOtpResponse>('/auth/phone/verify-otp', data);
|
|
456
|
+
return response.data;
|
|
457
|
+
},
|
|
458
|
+
onSuccess: (data, variables, context) => {
|
|
459
|
+
// Invalidate current user query to trigger refetch with new token
|
|
460
|
+
queryClient.invalidateQueries({ queryKey: userKeys.me() });
|
|
461
|
+
},
|
|
462
|
+
...options,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
@@ -3,10 +3,10 @@ import { getApiClient } from '../client';
|
|
|
3
3
|
import { eventChatKeys } from '../queries/event-chat';
|
|
4
4
|
import type {
|
|
5
5
|
ChatMemberResponse,
|
|
6
|
-
|
|
6
|
+
ChatMessageResponse,
|
|
7
7
|
EventChatResponse,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
SendChatMessageRequest,
|
|
9
|
+
UpdateChatMessageRequest,
|
|
10
10
|
UpdateChatSettingsRequest,
|
|
11
11
|
UpdateMemberSettingsRequest,
|
|
12
12
|
} from '../types';
|
|
@@ -123,9 +123,9 @@ export function useSendEventChatMessage(eventId: string) {
|
|
|
123
123
|
const queryClient = useQueryClient();
|
|
124
124
|
|
|
125
125
|
return useMutation({
|
|
126
|
-
mutationFn: async (data:
|
|
126
|
+
mutationFn: async (data: SendChatMessageRequest) => {
|
|
127
127
|
const client = getApiClient();
|
|
128
|
-
const response = await client.post<
|
|
128
|
+
const response = await client.post<ChatMessageResponse>(
|
|
129
129
|
`/events/${eventId}/chat/messages`,
|
|
130
130
|
data
|
|
131
131
|
);
|
|
@@ -145,9 +145,9 @@ export function useUpdateEventChatMessage(eventId: string) {
|
|
|
145
145
|
const queryClient = useQueryClient();
|
|
146
146
|
|
|
147
147
|
return useMutation({
|
|
148
|
-
mutationFn: async ({ messageId, content }: { messageId: string } &
|
|
148
|
+
mutationFn: async ({ messageId, content }: { messageId: string } & UpdateChatMessageRequest) => {
|
|
149
149
|
const client = getApiClient();
|
|
150
|
-
const response = await client.put<
|
|
150
|
+
const response = await client.put<ChatMessageResponse>(
|
|
151
151
|
`/events/${eventId}/chat/messages/${messageId}`,
|
|
152
152
|
{ content }
|
|
153
153
|
);
|
|
@@ -6,33 +6,13 @@ import {
|
|
|
6
6
|
} from '@tanstack/react-query';
|
|
7
7
|
import { getApiClient } from '../client';
|
|
8
8
|
import { matchingKeys, MatchResponse, BuddyResponse } from '../queries/matching';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
message?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface UpdateMatchRequest {
|
|
20
|
-
status: 'ACCEPTED' | 'DECLINED' | 'BLOCKED';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface CreateBuddyRequest {
|
|
24
|
-
buddyId: string;
|
|
25
|
-
message?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface UpdateBuddyRequest {
|
|
29
|
-
status: 'ACCEPTED' | 'DECLINED' | 'ENDED';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface LogBuddyActivityRequest {
|
|
33
|
-
type: 'CHECK_IN' | 'MESSAGE' | 'SUPPORT';
|
|
34
|
-
note?: string;
|
|
35
|
-
}
|
|
9
|
+
import type {
|
|
10
|
+
CreateMatchRequest,
|
|
11
|
+
UpdateMatchRequest,
|
|
12
|
+
CreateBuddyRequest,
|
|
13
|
+
UpdateBuddyRequest,
|
|
14
|
+
LogBuddyActivityRequest,
|
|
15
|
+
} from '../types';
|
|
36
16
|
|
|
37
17
|
// ============================================================================
|
|
38
18
|
// MUTATION HOOKS
|
|
@@ -118,9 +98,9 @@ export function useAcceptMatch(
|
|
|
118
98
|
return {
|
|
119
99
|
...updateMatch,
|
|
120
100
|
mutate: (matchId: string, mutateOptions?: any) =>
|
|
121
|
-
updateMatch.mutate({ matchId, data: {
|
|
101
|
+
updateMatch.mutate({ matchId, data: { action: 'ACCEPT' } }, mutateOptions),
|
|
122
102
|
mutateAsync: (matchId: string) =>
|
|
123
|
-
updateMatch.mutateAsync({ matchId, data: {
|
|
103
|
+
updateMatch.mutateAsync({ matchId, data: { action: 'ACCEPT' } }),
|
|
124
104
|
} as UseMutationResult<MatchResponse, Error, string>;
|
|
125
105
|
}
|
|
126
106
|
|
|
@@ -141,9 +121,9 @@ export function useDeclineMatch(
|
|
|
141
121
|
return {
|
|
142
122
|
...updateMatch,
|
|
143
123
|
mutate: (matchId: string, mutateOptions?: any) =>
|
|
144
|
-
updateMatch.mutate({ matchId, data: {
|
|
124
|
+
updateMatch.mutate({ matchId, data: { action: 'DECLINE' } }, mutateOptions),
|
|
145
125
|
mutateAsync: (matchId: string) =>
|
|
146
|
-
updateMatch.mutateAsync({ matchId, data: {
|
|
126
|
+
updateMatch.mutateAsync({ matchId, data: { action: 'DECLINE' } }),
|
|
147
127
|
} as UseMutationResult<MatchResponse, Error, string>;
|
|
148
128
|
}
|
|
149
129
|
|
|
@@ -214,7 +194,7 @@ export function useRequestBuddy(
|
|
|
214
194
|
* @example
|
|
215
195
|
* ```tsx
|
|
216
196
|
* const updateBuddy = useUpdateBuddy();
|
|
217
|
-
* await updateBuddy.mutateAsync({ buddyId: 'buddy-123', data: {
|
|
197
|
+
* await updateBuddy.mutateAsync({ buddyId: 'buddy-123', data: { action: 'ACCEPT' } });
|
|
218
198
|
* ```
|
|
219
199
|
*/
|
|
220
200
|
export function useUpdateBuddy(
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
} from '@tanstack/react-query';
|
|
7
7
|
import { getApiClient } from '../client';
|
|
8
8
|
import type {
|
|
9
|
-
|
|
9
|
+
CheckInResponse,
|
|
10
10
|
CreateCheckInRequest,
|
|
11
11
|
UpdateCheckInRequest,
|
|
12
12
|
MoodLogResponse,
|
|
13
|
-
|
|
13
|
+
LogMoodRequest,
|
|
14
14
|
WinResponse,
|
|
15
15
|
CreateWinRequest,
|
|
16
16
|
HabitResponse,
|
|
@@ -44,14 +44,14 @@ import { supportKeys } from '../queries/support';
|
|
|
44
44
|
*/
|
|
45
45
|
export function useCreateCheckIn(
|
|
46
46
|
options?: Omit<
|
|
47
|
-
UseMutationOptions<
|
|
47
|
+
UseMutationOptions<CheckInResponse, Error, CreateCheckInRequest>,
|
|
48
48
|
'mutationFn'
|
|
49
49
|
>
|
|
50
|
-
): UseMutationResult<
|
|
50
|
+
): UseMutationResult<CheckInResponse, Error, CreateCheckInRequest> {
|
|
51
51
|
const queryClient = useQueryClient();
|
|
52
52
|
|
|
53
53
|
return useMutation({
|
|
54
|
-
mutationFn: async (data: CreateCheckInRequest): Promise<
|
|
54
|
+
mutationFn: async (data: CreateCheckInRequest): Promise<CheckInResponse> => {
|
|
55
55
|
const client = getApiClient();
|
|
56
56
|
const response = await client.post('/api/v1/support/check-ins', data);
|
|
57
57
|
return response.data;
|
|
@@ -88,14 +88,14 @@ export function useCreateCheckIn(
|
|
|
88
88
|
*/
|
|
89
89
|
export function useUpdateCheckIn(
|
|
90
90
|
options?: Omit<
|
|
91
|
-
UseMutationOptions<
|
|
91
|
+
UseMutationOptions<CheckInResponse, Error, { id: string; data: UpdateCheckInRequest }>,
|
|
92
92
|
'mutationFn'
|
|
93
93
|
>
|
|
94
|
-
): UseMutationResult<
|
|
94
|
+
): UseMutationResult<CheckInResponse, Error, { id: string; data: UpdateCheckInRequest }> {
|
|
95
95
|
const queryClient = useQueryClient();
|
|
96
96
|
|
|
97
97
|
return useMutation({
|
|
98
|
-
mutationFn: async ({ id, data }: { id: string; data: UpdateCheckInRequest }): Promise<
|
|
98
|
+
mutationFn: async ({ id, data }: { id: string; data: UpdateCheckInRequest }): Promise<CheckInResponse> => {
|
|
99
99
|
const client = getApiClient();
|
|
100
100
|
const response = await client.put(`/api/v1/support/check-ins/${id}`, data);
|
|
101
101
|
return response.data;
|
|
@@ -128,14 +128,14 @@ export function useUpdateCheckIn(
|
|
|
128
128
|
*/
|
|
129
129
|
export function useCreateMoodLog(
|
|
130
130
|
options?: Omit<
|
|
131
|
-
UseMutationOptions<MoodLogResponse, Error,
|
|
131
|
+
UseMutationOptions<MoodLogResponse, Error, LogMoodRequest>,
|
|
132
132
|
'mutationFn'
|
|
133
133
|
>
|
|
134
|
-
): UseMutationResult<MoodLogResponse, Error,
|
|
134
|
+
): UseMutationResult<MoodLogResponse, Error, LogMoodRequest> {
|
|
135
135
|
const queryClient = useQueryClient();
|
|
136
136
|
|
|
137
137
|
return useMutation({
|
|
138
|
-
mutationFn: async (data:
|
|
138
|
+
mutationFn: async (data: LogMoodRequest): Promise<MoodLogResponse> => {
|
|
139
139
|
const client = getApiClient();
|
|
140
140
|
const response = await client.post('/api/v1/support/mood', data);
|
|
141
141
|
return response.data;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
2
|
import { getApiClient } from '../client';
|
|
3
|
-
import type { AmbassadorResponse
|
|
3
|
+
import type { AmbassadorResponse } from '../types';
|
|
4
4
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// QUERY KEYS
|
|
@@ -21,6 +21,9 @@ export const ambassadorKeys = {
|
|
|
21
21
|
// TYPES
|
|
22
22
|
// ============================================================================
|
|
23
23
|
|
|
24
|
+
// Extract status type from AmbassadorResponse
|
|
25
|
+
type AmbassadorStatus = AmbassadorResponse['status'];
|
|
26
|
+
|
|
24
27
|
export interface AmbassadorListFilters {
|
|
25
28
|
page?: number;
|
|
26
29
|
limit?: number;
|
|
@@ -108,11 +111,11 @@ export function useMyAmbassador(
|
|
|
108
111
|
*/
|
|
109
112
|
export function useAmbassadorLeaderboard(
|
|
110
113
|
filters?: LeaderboardFilters,
|
|
111
|
-
options?: Omit<UseQueryOptions<
|
|
114
|
+
options?: Omit<UseQueryOptions<AmbassadorResponse[]>, 'queryKey' | 'queryFn'>
|
|
112
115
|
) {
|
|
113
116
|
return useQuery({
|
|
114
117
|
queryKey: ambassadorKeys.leaderboard(filters),
|
|
115
|
-
queryFn: async (): Promise<
|
|
118
|
+
queryFn: async (): Promise<AmbassadorResponse[]> => {
|
|
116
119
|
const client = getApiClient();
|
|
117
120
|
const response = await client.get('/api/v1/ambassadors/leaderboard', { params: filters });
|
|
118
121
|
return response.data;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cities Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for fetching city data.
|
|
5
|
+
* Cities are used for location-based features and user onboarding.
|
|
6
|
+
*
|
|
7
|
+
* @module api/queries/cities
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
11
|
+
import { getApiClient } from '../client';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// QUERY KEY FACTORY
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export const cityKeys = {
|
|
18
|
+
all: ['cities'] as const,
|
|
19
|
+
lists: () => [...cityKeys.all, 'list'] as const,
|
|
20
|
+
list: (filters?: CityFilters) => [...cityKeys.lists(), filters] as const,
|
|
21
|
+
details: () => [...cityKeys.all, 'detail'] as const,
|
|
22
|
+
detail: (id: string) => [...cityKeys.details(), id] as const,
|
|
23
|
+
featured: () => [...cityKeys.all, 'featured'] as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// TYPES
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
export interface CityResponse {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
country: string;
|
|
34
|
+
countryCode: string | null;
|
|
35
|
+
timezone: string | null;
|
|
36
|
+
latitude: number | null;
|
|
37
|
+
longitude: number | null;
|
|
38
|
+
memberCount: number;
|
|
39
|
+
isFeatured: boolean;
|
|
40
|
+
region: string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface CityFilters {
|
|
44
|
+
search?: string;
|
|
45
|
+
countryCode?: string;
|
|
46
|
+
isFeatured?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// QUERY HOOKS
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get list of cities with optional filters
|
|
55
|
+
*
|
|
56
|
+
* @description
|
|
57
|
+
* Fetches available cities for location selection.
|
|
58
|
+
* Can be filtered by name search, country code, or featured status.
|
|
59
|
+
* Results are ordered by featured status, member count, and name.
|
|
60
|
+
*
|
|
61
|
+
* @endpoint GET /api/v1/cities
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* import { useCities } from '@growsober/sdk';
|
|
66
|
+
*
|
|
67
|
+
* function CityPicker() {
|
|
68
|
+
* const { data: cities, isLoading } = useCities();
|
|
69
|
+
*
|
|
70
|
+
* if (isLoading) return <Spinner />;
|
|
71
|
+
*
|
|
72
|
+
* return (
|
|
73
|
+
* <select>
|
|
74
|
+
* {cities?.map(city => (
|
|
75
|
+
* <option key={city.id} value={city.id}>
|
|
76
|
+
* {city.name}, {city.country}
|
|
77
|
+
* </option>
|
|
78
|
+
* ))}
|
|
79
|
+
* </select>
|
|
80
|
+
* );
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* With search filter:
|
|
86
|
+
* ```tsx
|
|
87
|
+
* const { data: cities } = useCities({ search: 'London' });
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* Filter by country:
|
|
92
|
+
* ```tsx
|
|
93
|
+
* const { data: ukCities } = useCities({ countryCode: 'GB' });
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @param filters - Optional filters for city search
|
|
97
|
+
* @param options - TanStack Query options
|
|
98
|
+
* @returns TanStack Query result with array of cities
|
|
99
|
+
*/
|
|
100
|
+
interface CitiesApiResponse {
|
|
101
|
+
data: CityResponse[];
|
|
102
|
+
meta: {
|
|
103
|
+
timestamp: string;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function useCities(
|
|
108
|
+
filters?: CityFilters,
|
|
109
|
+
options?: Omit<UseQueryOptions<CityResponse[]>, 'queryKey' | 'queryFn'>
|
|
110
|
+
): UseQueryResult<CityResponse[]> {
|
|
111
|
+
return useQuery({
|
|
112
|
+
queryKey: cityKeys.list(filters),
|
|
113
|
+
queryFn: async (): Promise<CityResponse[]> => {
|
|
114
|
+
const client = getApiClient();
|
|
115
|
+
const params = new URLSearchParams();
|
|
116
|
+
|
|
117
|
+
if (filters?.search) params.append('search', filters.search);
|
|
118
|
+
if (filters?.countryCode) params.append('countryCode', filters.countryCode);
|
|
119
|
+
if (filters?.isFeatured !== undefined) params.append('isFeatured', String(filters.isFeatured));
|
|
120
|
+
|
|
121
|
+
const queryString = params.toString();
|
|
122
|
+
const url = `/api/v1/cities${queryString ? `?${queryString}` : ''}`;
|
|
123
|
+
|
|
124
|
+
const response = await client.get<CitiesApiResponse>(url);
|
|
125
|
+
// Handle both wrapped and unwrapped response formats
|
|
126
|
+
return Array.isArray(response.data) ? response.data : response.data.data;
|
|
127
|
+
},
|
|
128
|
+
staleTime: 5 * 60 * 1000, // Cities don't change often, cache for 5 minutes
|
|
129
|
+
...options,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get featured cities
|
|
135
|
+
*
|
|
136
|
+
* @description
|
|
137
|
+
* Fetches only cities marked as featured.
|
|
138
|
+
* Useful for showing popular/recommended cities.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* const { data: featuredCities } = useFeaturedCities();
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* @param options - TanStack Query options
|
|
146
|
+
* @returns TanStack Query result with array of featured cities
|
|
147
|
+
*/
|
|
148
|
+
export function useFeaturedCities(
|
|
149
|
+
options?: Omit<UseQueryOptions<CityResponse[]>, 'queryKey' | 'queryFn'>
|
|
150
|
+
): UseQueryResult<CityResponse[]> {
|
|
151
|
+
return useCities({ isFeatured: true }, options);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get a single city by ID
|
|
156
|
+
*
|
|
157
|
+
* @description
|
|
158
|
+
* Fetches details for a specific city.
|
|
159
|
+
*
|
|
160
|
+
* @endpoint GET /api/v1/cities/{id}
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```tsx
|
|
164
|
+
* const { data: city } = useCity('city-uuid');
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @param id - City ID
|
|
168
|
+
* @param options - TanStack Query options
|
|
169
|
+
* @returns TanStack Query result with city data
|
|
170
|
+
*/
|
|
171
|
+
interface CityApiResponse {
|
|
172
|
+
data: CityResponse;
|
|
173
|
+
meta: {
|
|
174
|
+
timestamp: string;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function useCity(
|
|
179
|
+
id: string,
|
|
180
|
+
options?: Omit<UseQueryOptions<CityResponse>, 'queryKey' | 'queryFn'>
|
|
181
|
+
): UseQueryResult<CityResponse> {
|
|
182
|
+
return useQuery({
|
|
183
|
+
queryKey: cityKeys.detail(id),
|
|
184
|
+
queryFn: async (): Promise<CityResponse> => {
|
|
185
|
+
const client = getApiClient();
|
|
186
|
+
const response = await client.get<CityApiResponse>(`/api/v1/cities/${id}`);
|
|
187
|
+
// Handle both wrapped and unwrapped response formats
|
|
188
|
+
return (response.data as any).data || response.data;
|
|
189
|
+
},
|
|
190
|
+
enabled: !!id,
|
|
191
|
+
staleTime: 5 * 60 * 1000,
|
|
192
|
+
...options,
|
|
193
|
+
});
|
|
194
|
+
}
|
package/src/api/queries/hubs.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
2
|
import { getApiClient } from '../client';
|
|
3
|
-
import type { HubResponse,
|
|
3
|
+
import type { HubResponse, ChatMemberResponse } from '../types';
|
|
4
4
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// QUERY KEYS
|
|
@@ -49,10 +49,10 @@ export interface PaginatedHubsResponse {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// ChatMemberResponse is imported from '../types'
|
|
53
53
|
|
|
54
54
|
export interface PaginatedMembersResponse {
|
|
55
|
-
data:
|
|
55
|
+
data: ChatMemberResponse[];
|
|
56
56
|
meta: {
|
|
57
57
|
total: number;
|
|
58
58
|
page: number;
|
package/src/api/queries/index.ts
CHANGED
package/src/api/queries/map.ts
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
12
12
|
import { getApiClient } from '../client';
|
|
13
13
|
import type {
|
|
14
|
-
|
|
14
|
+
MapMemberResponse,
|
|
15
15
|
MapEventResponse,
|
|
16
16
|
MapHubResponse,
|
|
17
|
-
|
|
17
|
+
BusinessResponse,
|
|
18
18
|
} from '../types';
|
|
19
19
|
|
|
20
20
|
// ============================================================================
|
|
@@ -115,13 +115,13 @@ export interface MapBusinessesParams {
|
|
|
115
115
|
*/
|
|
116
116
|
export function useMapMembers(
|
|
117
117
|
params: MapMembersParams,
|
|
118
|
-
options?: Omit<UseQueryOptions<
|
|
119
|
-
): UseQueryResult<
|
|
118
|
+
options?: Omit<UseQueryOptions<MapMemberResponse[]>, 'queryKey' | 'queryFn'>
|
|
119
|
+
): UseQueryResult<MapMemberResponse[]> {
|
|
120
120
|
return useQuery({
|
|
121
121
|
queryKey: mapKeys.members(params),
|
|
122
|
-
queryFn: async (): Promise<
|
|
122
|
+
queryFn: async (): Promise<MapMemberResponse[]> => {
|
|
123
123
|
const client = getApiClient();
|
|
124
|
-
const response = await client.get<
|
|
124
|
+
const response = await client.get<MapMemberResponse[]>('/api/v1/map/members', {
|
|
125
125
|
params: {
|
|
126
126
|
lat: params.lat,
|
|
127
127
|
lng: params.lng,
|
|
@@ -308,13 +308,13 @@ export function useMapHubs(
|
|
|
308
308
|
*/
|
|
309
309
|
export function useMapBusinesses(
|
|
310
310
|
params: MapBusinessesParams,
|
|
311
|
-
options?: Omit<UseQueryOptions<
|
|
312
|
-
): UseQueryResult<
|
|
311
|
+
options?: Omit<UseQueryOptions<BusinessResponse[]>, 'queryKey' | 'queryFn'>
|
|
312
|
+
): UseQueryResult<BusinessResponse[]> {
|
|
313
313
|
return useQuery({
|
|
314
314
|
queryKey: mapKeys.businesses(params),
|
|
315
|
-
queryFn: async (): Promise<
|
|
315
|
+
queryFn: async (): Promise<BusinessResponse[]> => {
|
|
316
316
|
const client = getApiClient();
|
|
317
|
-
const response = await client.get<
|
|
317
|
+
const response = await client.get<BusinessResponse[]>('/api/v1/map/businesses', {
|
|
318
318
|
params: {
|
|
319
319
|
lat: params.lat,
|
|
320
320
|
lng: params.lng,
|