@growsober/sdk 1.0.0
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 +276 -0
- package/dist/__tests__/e2e.test.d.ts +7 -0
- package/dist/__tests__/e2e.test.js +472 -0
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.js +61 -0
- package/dist/api/mutations/admin.d.ts +167 -0
- package/dist/api/mutations/admin.js +326 -0
- package/dist/api/mutations/ambassadors.d.ts +52 -0
- package/dist/api/mutations/ambassadors.js +148 -0
- package/dist/api/mutations/auth.d.ts +267 -0
- package/dist/api/mutations/auth.js +332 -0
- package/dist/api/mutations/bookings.d.ts +59 -0
- package/dist/api/mutations/bookings.js +143 -0
- package/dist/api/mutations/event-chat.d.ts +35 -0
- package/dist/api/mutations/event-chat.js +147 -0
- package/dist/api/mutations/events.d.ts +87 -0
- package/dist/api/mutations/events.js +205 -0
- package/dist/api/mutations/grow90.d.ts +36 -0
- package/dist/api/mutations/grow90.js +132 -0
- package/dist/api/mutations/hubs.d.ts +111 -0
- package/dist/api/mutations/hubs.js +240 -0
- package/dist/api/mutations/index.d.ts +22 -0
- package/dist/api/mutations/index.js +39 -0
- package/dist/api/mutations/jack.d.ts +61 -0
- package/dist/api/mutations/jack.js +104 -0
- package/dist/api/mutations/library.d.ts +67 -0
- package/dist/api/mutations/library.js +168 -0
- package/dist/api/mutations/map.d.ts +153 -0
- package/dist/api/mutations/map.js +181 -0
- package/dist/api/mutations/matching.d.ts +130 -0
- package/dist/api/mutations/matching.js +204 -0
- package/dist/api/mutations/notifications.d.ts +63 -0
- package/dist/api/mutations/notifications.js +106 -0
- package/dist/api/mutations/offers.d.ts +26 -0
- package/dist/api/mutations/offers.js +47 -0
- package/dist/api/mutations/subscriptions.d.ts +127 -0
- package/dist/api/mutations/subscriptions.js +140 -0
- package/dist/api/mutations/support.d.ts +165 -0
- package/dist/api/mutations/support.js +307 -0
- package/dist/api/mutations/users.d.ts +211 -0
- package/dist/api/mutations/users.js +261 -0
- package/dist/api/queries/admin.d.ts +257 -0
- package/dist/api/queries/admin.js +320 -0
- package/dist/api/queries/ambassadors.d.ts +53 -0
- package/dist/api/queries/ambassadors.js +98 -0
- package/dist/api/queries/auth.d.ts +16 -0
- package/dist/api/queries/auth.js +25 -0
- package/dist/api/queries/bookings.d.ts +91 -0
- package/dist/api/queries/bookings.js +102 -0
- package/dist/api/queries/businesses.d.ts +212 -0
- package/dist/api/queries/businesses.js +154 -0
- package/dist/api/queries/event-chat.d.ts +19 -0
- package/dist/api/queries/event-chat.js +75 -0
- package/dist/api/queries/events.d.ts +322 -0
- package/dist/api/queries/events.js +221 -0
- package/dist/api/queries/grow90.d.ts +26 -0
- package/dist/api/queries/grow90.js +85 -0
- package/dist/api/queries/hubs.d.ts +165 -0
- package/dist/api/queries/hubs.js +143 -0
- package/dist/api/queries/index.d.ts +23 -0
- package/dist/api/queries/index.js +40 -0
- package/dist/api/queries/jack.d.ts +63 -0
- package/dist/api/queries/jack.js +92 -0
- package/dist/api/queries/library.d.ts +132 -0
- package/dist/api/queries/library.js +120 -0
- package/dist/api/queries/map.d.ts +216 -0
- package/dist/api/queries/map.js +278 -0
- package/dist/api/queries/matching.d.ts +136 -0
- package/dist/api/queries/matching.js +161 -0
- package/dist/api/queries/notifications.d.ts +78 -0
- package/dist/api/queries/notifications.js +88 -0
- package/dist/api/queries/offers.d.ts +91 -0
- package/dist/api/queries/offers.js +103 -0
- package/dist/api/queries/subscriptions.d.ts +56 -0
- package/dist/api/queries/subscriptions.js +73 -0
- package/dist/api/queries/support.d.ts +106 -0
- package/dist/api/queries/support.js +202 -0
- package/dist/api/queries/users.d.ts +293 -0
- package/dist/api/queries/users.js +370 -0
- package/dist/api/types.d.ts +464 -0
- package/dist/api/types.js +9 -0
- package/dist/hooks/useAuth.d.ts +5 -0
- package/dist/hooks/useAuth.js +39 -0
- package/dist/hooks/useUser.d.ts +43 -0
- package/dist/hooks/useUser.js +44 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +67 -0
- package/package.json +62 -0
- package/src/__tests__/e2e.test.ts +502 -0
- package/src/api/client.ts +71 -0
- package/src/api/mutations/admin.ts +531 -0
- package/src/api/mutations/ambassadors.ts +185 -0
- package/src/api/mutations/auth.ts +350 -0
- package/src/api/mutations/bookings.ts +190 -0
- package/src/api/mutations/event-chat.ts +177 -0
- package/src/api/mutations/events.ts +273 -0
- package/src/api/mutations/grow90.ts +169 -0
- package/src/api/mutations/hubs.ts +385 -0
- package/src/api/mutations/index.ts +23 -0
- package/src/api/mutations/jack.ts +130 -0
- package/src/api/mutations/library.ts +212 -0
- package/src/api/mutations/map.ts +230 -0
- package/src/api/mutations/matching.ts +271 -0
- package/src/api/mutations/notifications.ts +114 -0
- package/src/api/mutations/offers.ts +73 -0
- package/src/api/mutations/subscriptions.ts +162 -0
- package/src/api/mutations/support.ts +390 -0
- package/src/api/mutations/users.ts +271 -0
- package/src/api/queries/admin.ts +480 -0
- package/src/api/queries/ambassadors.ts +139 -0
- package/src/api/queries/auth.ts +24 -0
- package/src/api/queries/bookings.ts +135 -0
- package/src/api/queries/businesses.ts +203 -0
- package/src/api/queries/event-chat.ts +78 -0
- package/src/api/queries/events.ts +272 -0
- package/src/api/queries/grow90.ts +98 -0
- package/src/api/queries/hubs.ts +211 -0
- package/src/api/queries/index.ts +24 -0
- package/src/api/queries/jack.ts +127 -0
- package/src/api/queries/library.ts +166 -0
- package/src/api/queries/map.ts +331 -0
- package/src/api/queries/matching.ts +238 -0
- package/src/api/queries/notifications.ts +103 -0
- package/src/api/queries/offers.ts +136 -0
- package/src/api/queries/subscriptions.ts +91 -0
- package/src/api/queries/support.ts +235 -0
- package/src/api/queries/users.ts +393 -0
- package/src/api/types.ts +596 -0
- package/src/index.ts +57 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for map-related read operations.
|
|
5
|
+
* These hooks handle fetching map data including users, events, and businesses
|
|
6
|
+
* for display on the member map feature.
|
|
7
|
+
*
|
|
8
|
+
* @module api/queries/map
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
12
|
+
import { getApiClient } from '../client';
|
|
13
|
+
import type {
|
|
14
|
+
MapUserResponse,
|
|
15
|
+
MapEventResponse,
|
|
16
|
+
MapHubResponse,
|
|
17
|
+
MapBusinessResponse,
|
|
18
|
+
} from '../types';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// QUERY KEYS
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Query key factory for map-related queries
|
|
26
|
+
*/
|
|
27
|
+
export const mapKeys = {
|
|
28
|
+
all: ['map'] as const,
|
|
29
|
+
members: (params?: MapMembersParams) => [...mapKeys.all, 'members', params] as const,
|
|
30
|
+
events: (params?: MapEventsParams) => [...mapKeys.all, 'events', params] as const,
|
|
31
|
+
hubs: (params?: MapHubsParams) => [...mapKeys.all, 'hubs', params] as const,
|
|
32
|
+
businesses: (params?: MapBusinessesParams) => [...mapKeys.all, 'businesses', params] as const,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// PARAM TYPES
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
export interface MapMembersParams {
|
|
40
|
+
lat: number;
|
|
41
|
+
lng: number;
|
|
42
|
+
radius?: number; // km
|
|
43
|
+
hubId?: string;
|
|
44
|
+
openToMeetOnly?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MapEventsParams {
|
|
48
|
+
lat: number;
|
|
49
|
+
lng: number;
|
|
50
|
+
radius?: number; // km
|
|
51
|
+
hubId?: string;
|
|
52
|
+
upcoming?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface MapHubsParams {
|
|
56
|
+
lat: number;
|
|
57
|
+
lng: number;
|
|
58
|
+
radius?: number; // km
|
|
59
|
+
city?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface MapBusinessesParams {
|
|
63
|
+
lat: number;
|
|
64
|
+
lng: number;
|
|
65
|
+
radius?: number; // km
|
|
66
|
+
type?: string;
|
|
67
|
+
afDrinksOnly?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// QUERY HOOKS
|
|
72
|
+
// ============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get members for map display
|
|
76
|
+
*
|
|
77
|
+
* @description
|
|
78
|
+
* Retrieves users who have opted to be visible on the member map.
|
|
79
|
+
* Only shows users who have enabled "open to meet" and shared their location.
|
|
80
|
+
* Premium feature.
|
|
81
|
+
*
|
|
82
|
+
* @endpoint GET /api/v1/map/members
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* import { useMapMembers } from '@growsober/sdk';
|
|
87
|
+
*
|
|
88
|
+
* function MemberMap() {
|
|
89
|
+
* const { data: members, isLoading } = useMapMembers({
|
|
90
|
+
* lat: 51.5074,
|
|
91
|
+
* lng: -0.1278,
|
|
92
|
+
* radius: 50, // 50km radius
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* if (isLoading) return <MapLoader />;
|
|
96
|
+
*
|
|
97
|
+
* return (
|
|
98
|
+
* <Map>
|
|
99
|
+
* {members?.map(member => (
|
|
100
|
+
* <Marker
|
|
101
|
+
* key={member.id}
|
|
102
|
+
* position={[member.locationLat, member.locationLong]}
|
|
103
|
+
* >
|
|
104
|
+
* <Avatar src={member.avatar} />
|
|
105
|
+
* </Marker>
|
|
106
|
+
* ))}
|
|
107
|
+
* </Map>
|
|
108
|
+
* );
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @param params - Location and filter parameters
|
|
113
|
+
* @param options - TanStack Query options
|
|
114
|
+
* @returns TanStack Query result with array of map users
|
|
115
|
+
*/
|
|
116
|
+
export function useMapMembers(
|
|
117
|
+
params: MapMembersParams,
|
|
118
|
+
options?: Omit<UseQueryOptions<MapUserResponse[]>, 'queryKey' | 'queryFn'>
|
|
119
|
+
): UseQueryResult<MapUserResponse[]> {
|
|
120
|
+
return useQuery({
|
|
121
|
+
queryKey: mapKeys.members(params),
|
|
122
|
+
queryFn: async (): Promise<MapUserResponse[]> => {
|
|
123
|
+
const client = getApiClient();
|
|
124
|
+
const response = await client.get<MapUserResponse[]>('/api/v1/map/members', {
|
|
125
|
+
params: {
|
|
126
|
+
lat: params.lat,
|
|
127
|
+
lng: params.lng,
|
|
128
|
+
radius: params.radius,
|
|
129
|
+
hubId: params.hubId,
|
|
130
|
+
openToMeetOnly: params.openToMeetOnly,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return response.data;
|
|
134
|
+
},
|
|
135
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
136
|
+
...options,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get events for map display
|
|
142
|
+
*
|
|
143
|
+
* @description
|
|
144
|
+
* Retrieves events with location data for display on the map.
|
|
145
|
+
* Shows upcoming events within the specified radius.
|
|
146
|
+
*
|
|
147
|
+
* @endpoint GET /api/v1/map/events
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* import { useMapEvents } from '@growsober/sdk';
|
|
152
|
+
*
|
|
153
|
+
* function EventMap() {
|
|
154
|
+
* const { data: events, isLoading } = useMapEvents({
|
|
155
|
+
* lat: 51.5074,
|
|
156
|
+
* lng: -0.1278,
|
|
157
|
+
* radius: 25,
|
|
158
|
+
* upcoming: true,
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* return (
|
|
162
|
+
* <Map>
|
|
163
|
+
* {events?.map(event => (
|
|
164
|
+
* <Marker
|
|
165
|
+
* key={event.id}
|
|
166
|
+
* position={[event.locationLat, event.locationLong]}
|
|
167
|
+
* icon="event"
|
|
168
|
+
* >
|
|
169
|
+
* <EventPopup event={event} />
|
|
170
|
+
* </Marker>
|
|
171
|
+
* ))}
|
|
172
|
+
* </Map>
|
|
173
|
+
* );
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* @param params - Location and filter parameters
|
|
178
|
+
* @param options - TanStack Query options
|
|
179
|
+
* @returns TanStack Query result with array of map events
|
|
180
|
+
*/
|
|
181
|
+
export function useMapEvents(
|
|
182
|
+
params: MapEventsParams,
|
|
183
|
+
options?: Omit<UseQueryOptions<MapEventResponse[]>, 'queryKey' | 'queryFn'>
|
|
184
|
+
): UseQueryResult<MapEventResponse[]> {
|
|
185
|
+
return useQuery({
|
|
186
|
+
queryKey: mapKeys.events(params),
|
|
187
|
+
queryFn: async (): Promise<MapEventResponse[]> => {
|
|
188
|
+
const client = getApiClient();
|
|
189
|
+
const response = await client.get<MapEventResponse[]>('/api/v1/map/events', {
|
|
190
|
+
params: {
|
|
191
|
+
lat: params.lat,
|
|
192
|
+
lng: params.lng,
|
|
193
|
+
radius: params.radius,
|
|
194
|
+
hubId: params.hubId,
|
|
195
|
+
upcoming: params.upcoming,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
return response.data;
|
|
199
|
+
},
|
|
200
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
201
|
+
...options,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get hubs for map display
|
|
207
|
+
*
|
|
208
|
+
* @description
|
|
209
|
+
* Retrieves hubs with location data for display on the map.
|
|
210
|
+
* Shows active hubs within the specified radius.
|
|
211
|
+
*
|
|
212
|
+
* @endpoint GET /api/v1/map/hubs
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```tsx
|
|
216
|
+
* import { useMapHubs } from '@growsober/sdk';
|
|
217
|
+
*
|
|
218
|
+
* function HubMap() {
|
|
219
|
+
* const { data: hubs, isLoading } = useMapHubs({
|
|
220
|
+
* lat: 51.5074,
|
|
221
|
+
* lng: -0.1278,
|
|
222
|
+
* radius: 50,
|
|
223
|
+
* });
|
|
224
|
+
*
|
|
225
|
+
* return (
|
|
226
|
+
* <Map>
|
|
227
|
+
* {hubs?.map(hub => (
|
|
228
|
+
* <Marker
|
|
229
|
+
* key={hub.id}
|
|
230
|
+
* position={[hub.locationLat, hub.locationLong]}
|
|
231
|
+
* icon="hub"
|
|
232
|
+
* >
|
|
233
|
+
* <HubPopup hub={hub} />
|
|
234
|
+
* </Marker>
|
|
235
|
+
* ))}
|
|
236
|
+
* </Map>
|
|
237
|
+
* );
|
|
238
|
+
* }
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @param params - Location and filter parameters
|
|
242
|
+
* @param options - TanStack Query options
|
|
243
|
+
* @returns TanStack Query result with array of map hubs
|
|
244
|
+
*/
|
|
245
|
+
export function useMapHubs(
|
|
246
|
+
params: MapHubsParams,
|
|
247
|
+
options?: Omit<UseQueryOptions<MapHubResponse[]>, 'queryKey' | 'queryFn'>
|
|
248
|
+
): UseQueryResult<MapHubResponse[]> {
|
|
249
|
+
return useQuery({
|
|
250
|
+
queryKey: mapKeys.hubs(params),
|
|
251
|
+
queryFn: async (): Promise<MapHubResponse[]> => {
|
|
252
|
+
const client = getApiClient();
|
|
253
|
+
const response = await client.get<MapHubResponse[]>('/api/v1/map/hubs', {
|
|
254
|
+
params: {
|
|
255
|
+
lat: params.lat,
|
|
256
|
+
lng: params.lng,
|
|
257
|
+
radius: params.radius,
|
|
258
|
+
city: params.city,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
return response.data;
|
|
262
|
+
},
|
|
263
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
264
|
+
...options,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get businesses for map display
|
|
270
|
+
*
|
|
271
|
+
* @description
|
|
272
|
+
* Retrieves partner businesses with location data for display on the map.
|
|
273
|
+
* Can filter by type and whether they serve alcohol-free drinks.
|
|
274
|
+
*
|
|
275
|
+
* @endpoint GET /api/v1/map/businesses
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* ```tsx
|
|
279
|
+
* import { useMapBusinesses } from '@growsober/sdk';
|
|
280
|
+
*
|
|
281
|
+
* function BusinessMap() {
|
|
282
|
+
* const { data: businesses, isLoading } = useMapBusinesses({
|
|
283
|
+
* lat: 51.5074,
|
|
284
|
+
* lng: -0.1278,
|
|
285
|
+
* radius: 10,
|
|
286
|
+
* afDrinksOnly: true, // Only show AF-friendly venues
|
|
287
|
+
* });
|
|
288
|
+
*
|
|
289
|
+
* return (
|
|
290
|
+
* <Map>
|
|
291
|
+
* {businesses?.map(business => (
|
|
292
|
+
* <Marker
|
|
293
|
+
* key={business.id}
|
|
294
|
+
* position={[business.locationLat, business.locationLong]}
|
|
295
|
+
* icon={business.hasAfDrinks ? 'af-venue' : 'venue'}
|
|
296
|
+
* >
|
|
297
|
+
* <BusinessPopup business={business} />
|
|
298
|
+
* </Marker>
|
|
299
|
+
* ))}
|
|
300
|
+
* </Map>
|
|
301
|
+
* );
|
|
302
|
+
* }
|
|
303
|
+
* ```
|
|
304
|
+
*
|
|
305
|
+
* @param params - Location and filter parameters
|
|
306
|
+
* @param options - TanStack Query options
|
|
307
|
+
* @returns TanStack Query result with array of map businesses
|
|
308
|
+
*/
|
|
309
|
+
export function useMapBusinesses(
|
|
310
|
+
params: MapBusinessesParams,
|
|
311
|
+
options?: Omit<UseQueryOptions<MapBusinessResponse[]>, 'queryKey' | 'queryFn'>
|
|
312
|
+
): UseQueryResult<MapBusinessResponse[]> {
|
|
313
|
+
return useQuery({
|
|
314
|
+
queryKey: mapKeys.businesses(params),
|
|
315
|
+
queryFn: async (): Promise<MapBusinessResponse[]> => {
|
|
316
|
+
const client = getApiClient();
|
|
317
|
+
const response = await client.get<MapBusinessResponse[]>('/api/v1/map/businesses', {
|
|
318
|
+
params: {
|
|
319
|
+
lat: params.lat,
|
|
320
|
+
lng: params.lng,
|
|
321
|
+
radius: params.radius,
|
|
322
|
+
type: params.type,
|
|
323
|
+
afDrinksOnly: params.afDrinksOnly,
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
return response.data;
|
|
327
|
+
},
|
|
328
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
329
|
+
...options,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// QUERY KEY FACTORY
|
|
6
|
+
// ============================================================================
|
|
7
|
+
|
|
8
|
+
export const matchingKeys = {
|
|
9
|
+
all: ['matching'] as const,
|
|
10
|
+
discover: (filters?: MatchQueryFilters) => [...matchingKeys.all, 'discover', filters] as const,
|
|
11
|
+
matches: (filters?: MatchQueryFilters) => [...matchingKeys.all, 'matches', filters] as const,
|
|
12
|
+
match: (id: string) => [...matchingKeys.all, 'match', id] as const,
|
|
13
|
+
buddies: () => [...matchingKeys.all, 'buddies'] as const,
|
|
14
|
+
buddyRequests: () => [...matchingKeys.all, 'buddy-requests'] as const,
|
|
15
|
+
stats: () => [...matchingKeys.all, 'stats'] as const,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// TYPES
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface MatchQueryFilters {
|
|
23
|
+
status?: 'PENDING' | 'ACCEPTED' | 'DECLINED' | 'BLOCKED';
|
|
24
|
+
limit?: number;
|
|
25
|
+
offset?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MatchResponse {
|
|
29
|
+
id: string;
|
|
30
|
+
userId: string;
|
|
31
|
+
matchedUserId: string;
|
|
32
|
+
status: 'PENDING' | 'ACCEPTED' | 'DECLINED' | 'BLOCKED';
|
|
33
|
+
matchScore?: number;
|
|
34
|
+
sharedInterests?: string[];
|
|
35
|
+
createdAt: string;
|
|
36
|
+
updatedAt: string;
|
|
37
|
+
matchedUser?: {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
avatarUrl?: string;
|
|
41
|
+
bio?: string;
|
|
42
|
+
city?: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DiscoverMatchResponse {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
avatarUrl?: string;
|
|
50
|
+
bio?: string;
|
|
51
|
+
city?: string;
|
|
52
|
+
matchScore: number;
|
|
53
|
+
sharedInterests: string[];
|
|
54
|
+
distance?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface BuddyResponse {
|
|
58
|
+
id: string;
|
|
59
|
+
userId: string;
|
|
60
|
+
buddyId: string;
|
|
61
|
+
status: 'PENDING' | 'ACTIVE' | 'ENDED';
|
|
62
|
+
startDate?: string;
|
|
63
|
+
endDate?: string;
|
|
64
|
+
lastActivity?: string;
|
|
65
|
+
buddy?: {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
avatarUrl?: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface MatchingStatsResponse {
|
|
73
|
+
totalMatches: number;
|
|
74
|
+
pendingMatches: number;
|
|
75
|
+
acceptedMatches: number;
|
|
76
|
+
activeBuddies: number;
|
|
77
|
+
matchRate: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// QUERY HOOKS
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Discover potential matches based on shared interests and location
|
|
86
|
+
*
|
|
87
|
+
* @endpoint GET /api/v1/matching/discover
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* const { data: matches, isLoading } = useDiscoverMatches();
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function useDiscoverMatches(
|
|
95
|
+
filters?: MatchQueryFilters,
|
|
96
|
+
options?: Omit<UseQueryOptions<DiscoverMatchResponse[]>, 'queryKey' | 'queryFn'>
|
|
97
|
+
) {
|
|
98
|
+
return useQuery({
|
|
99
|
+
queryKey: matchingKeys.discover(filters),
|
|
100
|
+
queryFn: async (): Promise<DiscoverMatchResponse[]> => {
|
|
101
|
+
const client = getApiClient();
|
|
102
|
+
const response = await client.get('/api/v1/matching/discover', {
|
|
103
|
+
params: filters,
|
|
104
|
+
});
|
|
105
|
+
return response.data?.data || response.data;
|
|
106
|
+
},
|
|
107
|
+
...options,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get all my matches
|
|
113
|
+
*
|
|
114
|
+
* @endpoint GET /api/v1/matching/matches
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```tsx
|
|
118
|
+
* const { data: matches, isLoading } = useMyMatches({ status: 'PENDING' });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function useMyMatches(
|
|
122
|
+
filters?: MatchQueryFilters,
|
|
123
|
+
options?: Omit<UseQueryOptions<MatchResponse[]>, 'queryKey' | 'queryFn'>
|
|
124
|
+
) {
|
|
125
|
+
return useQuery({
|
|
126
|
+
queryKey: matchingKeys.matches(filters),
|
|
127
|
+
queryFn: async (): Promise<MatchResponse[]> => {
|
|
128
|
+
const client = getApiClient();
|
|
129
|
+
const response = await client.get('/api/v1/matching/matches', {
|
|
130
|
+
params: filters,
|
|
131
|
+
});
|
|
132
|
+
return response.data?.data || response.data;
|
|
133
|
+
},
|
|
134
|
+
staleTime: 30 * 1000,
|
|
135
|
+
...options,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get pending matches (convenience hook)
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```tsx
|
|
144
|
+
* const { data: pendingMatches } = usePendingMatches();
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export function usePendingMatches(
|
|
148
|
+
options?: Omit<UseQueryOptions<MatchResponse[]>, 'queryKey' | 'queryFn'>
|
|
149
|
+
) {
|
|
150
|
+
return useMyMatches({ status: 'PENDING' }, options);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get accepted matches (convenience hook)
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```tsx
|
|
158
|
+
* const { data: acceptedMatches } = useAcceptedMatches();
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export function useAcceptedMatches(
|
|
162
|
+
options?: Omit<UseQueryOptions<MatchResponse[]>, 'queryKey' | 'queryFn'>
|
|
163
|
+
) {
|
|
164
|
+
return useMyMatches({ status: 'ACCEPTED' }, options);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get my accountability buddies
|
|
169
|
+
*
|
|
170
|
+
* @endpoint GET /api/v1/matching/buddies
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```tsx
|
|
174
|
+
* const { data: buddies, isLoading } = useMyBuddies();
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function useMyBuddies(
|
|
178
|
+
options?: Omit<UseQueryOptions<BuddyResponse[]>, 'queryKey' | 'queryFn'>
|
|
179
|
+
) {
|
|
180
|
+
return useQuery({
|
|
181
|
+
queryKey: matchingKeys.buddies(),
|
|
182
|
+
queryFn: async (): Promise<BuddyResponse[]> => {
|
|
183
|
+
const client = getApiClient();
|
|
184
|
+
const response = await client.get('/api/v1/matching/buddies');
|
|
185
|
+
return response.data?.data || response.data;
|
|
186
|
+
},
|
|
187
|
+
...options,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get pending buddy requests
|
|
193
|
+
*
|
|
194
|
+
* @endpoint GET /api/v1/matching/buddies/requests
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```tsx
|
|
198
|
+
* const { data: requests } = useBuddyRequests();
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export function useBuddyRequests(
|
|
202
|
+
options?: Omit<UseQueryOptions<BuddyResponse[]>, 'queryKey' | 'queryFn'>
|
|
203
|
+
) {
|
|
204
|
+
return useQuery({
|
|
205
|
+
queryKey: matchingKeys.buddyRequests(),
|
|
206
|
+
queryFn: async (): Promise<BuddyResponse[]> => {
|
|
207
|
+
const client = getApiClient();
|
|
208
|
+
const response = await client.get('/api/v1/matching/buddies/requests');
|
|
209
|
+
return response.data?.data || response.data;
|
|
210
|
+
},
|
|
211
|
+
...options,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get matching statistics
|
|
217
|
+
*
|
|
218
|
+
* @endpoint GET /api/v1/matching/stats
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```tsx
|
|
222
|
+
* const { data: stats } = useMatchingStats();
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export function useMatchingStats(
|
|
226
|
+
options?: Omit<UseQueryOptions<MatchingStatsResponse>, 'queryKey' | 'queryFn'>
|
|
227
|
+
) {
|
|
228
|
+
return useQuery({
|
|
229
|
+
queryKey: matchingKeys.stats(),
|
|
230
|
+
queryFn: async (): Promise<MatchingStatsResponse> => {
|
|
231
|
+
const client = getApiClient();
|
|
232
|
+
const response = await client.get('/api/v1/matching/stats');
|
|
233
|
+
return response.data?.data || response.data;
|
|
234
|
+
},
|
|
235
|
+
staleTime: 5 * 60 * 1000,
|
|
236
|
+
...options,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type { NotificationResponse } from '../types';
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// QUERY KEY FACTORY
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
export const notificationKeys = {
|
|
10
|
+
all: ['notifications'] as const,
|
|
11
|
+
list: () => [...notificationKeys.all, 'list'] as const,
|
|
12
|
+
unreadCount: () => [...notificationKeys.all, 'unread-count'] as const,
|
|
13
|
+
detail: (id: string) => [...notificationKeys.all, id] as const,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// TYPES
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export interface UnreadCount {
|
|
21
|
+
count: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// QUERY HOOKS
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get user's notifications
|
|
30
|
+
*
|
|
31
|
+
* @param options - TanStack Query options
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* const { data: notifications, isLoading } = useNotifications();
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function useNotifications(
|
|
39
|
+
options?: Omit<UseQueryOptions<NotificationResponse[]>, 'queryKey' | 'queryFn'>
|
|
40
|
+
) {
|
|
41
|
+
return useQuery({
|
|
42
|
+
queryKey: notificationKeys.list(),
|
|
43
|
+
queryFn: async (): Promise<NotificationResponse[]> => {
|
|
44
|
+
const client = getApiClient();
|
|
45
|
+
const response = await client.get('/api/v1/notifications');
|
|
46
|
+
return response.data;
|
|
47
|
+
},
|
|
48
|
+
...options,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get unread notification count
|
|
54
|
+
*
|
|
55
|
+
* @param options - TanStack Query options
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* const { data } = useUnreadNotificationCount();
|
|
60
|
+
* console.log(`You have ${data?.count} unread notifications`);
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function useUnreadNotificationCount(
|
|
64
|
+
options?: Omit<UseQueryOptions<UnreadCount>, 'queryKey' | 'queryFn'>
|
|
65
|
+
) {
|
|
66
|
+
return useQuery({
|
|
67
|
+
queryKey: notificationKeys.unreadCount(),
|
|
68
|
+
queryFn: async (): Promise<UnreadCount> => {
|
|
69
|
+
const client = getApiClient();
|
|
70
|
+
const response = await client.get('/api/v1/notifications/unread-count');
|
|
71
|
+
return response.data;
|
|
72
|
+
},
|
|
73
|
+
refetchInterval: 30000, // Refetch every 30 seconds
|
|
74
|
+
...options,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a specific notification by ID
|
|
80
|
+
*
|
|
81
|
+
* @param id - Notification ID
|
|
82
|
+
* @param options - TanStack Query options
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* const { data: notification, isLoading } = useNotification('notification-123');
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export function useNotification(
|
|
90
|
+
id: string,
|
|
91
|
+
options?: Omit<UseQueryOptions<NotificationResponse>, 'queryKey' | 'queryFn'>
|
|
92
|
+
) {
|
|
93
|
+
return useQuery({
|
|
94
|
+
queryKey: notificationKeys.detail(id),
|
|
95
|
+
queryFn: async (): Promise<NotificationResponse> => {
|
|
96
|
+
const client = getApiClient();
|
|
97
|
+
const response = await client.get(`/api/v1/notifications/${id}`);
|
|
98
|
+
return response.data;
|
|
99
|
+
},
|
|
100
|
+
enabled: !!id,
|
|
101
|
+
...options,
|
|
102
|
+
});
|
|
103
|
+
}
|