@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.
Files changed (129) hide show
  1. package/README.md +276 -0
  2. package/dist/__tests__/e2e.test.d.ts +7 -0
  3. package/dist/__tests__/e2e.test.js +472 -0
  4. package/dist/api/client.d.ts +11 -0
  5. package/dist/api/client.js +61 -0
  6. package/dist/api/mutations/admin.d.ts +167 -0
  7. package/dist/api/mutations/admin.js +326 -0
  8. package/dist/api/mutations/ambassadors.d.ts +52 -0
  9. package/dist/api/mutations/ambassadors.js +148 -0
  10. package/dist/api/mutations/auth.d.ts +267 -0
  11. package/dist/api/mutations/auth.js +332 -0
  12. package/dist/api/mutations/bookings.d.ts +59 -0
  13. package/dist/api/mutations/bookings.js +143 -0
  14. package/dist/api/mutations/event-chat.d.ts +35 -0
  15. package/dist/api/mutations/event-chat.js +147 -0
  16. package/dist/api/mutations/events.d.ts +87 -0
  17. package/dist/api/mutations/events.js +205 -0
  18. package/dist/api/mutations/grow90.d.ts +36 -0
  19. package/dist/api/mutations/grow90.js +132 -0
  20. package/dist/api/mutations/hubs.d.ts +111 -0
  21. package/dist/api/mutations/hubs.js +240 -0
  22. package/dist/api/mutations/index.d.ts +22 -0
  23. package/dist/api/mutations/index.js +39 -0
  24. package/dist/api/mutations/jack.d.ts +61 -0
  25. package/dist/api/mutations/jack.js +104 -0
  26. package/dist/api/mutations/library.d.ts +67 -0
  27. package/dist/api/mutations/library.js +168 -0
  28. package/dist/api/mutations/map.d.ts +153 -0
  29. package/dist/api/mutations/map.js +181 -0
  30. package/dist/api/mutations/matching.d.ts +130 -0
  31. package/dist/api/mutations/matching.js +204 -0
  32. package/dist/api/mutations/notifications.d.ts +63 -0
  33. package/dist/api/mutations/notifications.js +106 -0
  34. package/dist/api/mutations/offers.d.ts +26 -0
  35. package/dist/api/mutations/offers.js +47 -0
  36. package/dist/api/mutations/subscriptions.d.ts +127 -0
  37. package/dist/api/mutations/subscriptions.js +140 -0
  38. package/dist/api/mutations/support.d.ts +165 -0
  39. package/dist/api/mutations/support.js +307 -0
  40. package/dist/api/mutations/users.d.ts +211 -0
  41. package/dist/api/mutations/users.js +261 -0
  42. package/dist/api/queries/admin.d.ts +257 -0
  43. package/dist/api/queries/admin.js +320 -0
  44. package/dist/api/queries/ambassadors.d.ts +53 -0
  45. package/dist/api/queries/ambassadors.js +98 -0
  46. package/dist/api/queries/auth.d.ts +16 -0
  47. package/dist/api/queries/auth.js +25 -0
  48. package/dist/api/queries/bookings.d.ts +91 -0
  49. package/dist/api/queries/bookings.js +102 -0
  50. package/dist/api/queries/businesses.d.ts +212 -0
  51. package/dist/api/queries/businesses.js +154 -0
  52. package/dist/api/queries/event-chat.d.ts +19 -0
  53. package/dist/api/queries/event-chat.js +75 -0
  54. package/dist/api/queries/events.d.ts +322 -0
  55. package/dist/api/queries/events.js +221 -0
  56. package/dist/api/queries/grow90.d.ts +26 -0
  57. package/dist/api/queries/grow90.js +85 -0
  58. package/dist/api/queries/hubs.d.ts +165 -0
  59. package/dist/api/queries/hubs.js +143 -0
  60. package/dist/api/queries/index.d.ts +23 -0
  61. package/dist/api/queries/index.js +40 -0
  62. package/dist/api/queries/jack.d.ts +63 -0
  63. package/dist/api/queries/jack.js +92 -0
  64. package/dist/api/queries/library.d.ts +132 -0
  65. package/dist/api/queries/library.js +120 -0
  66. package/dist/api/queries/map.d.ts +216 -0
  67. package/dist/api/queries/map.js +278 -0
  68. package/dist/api/queries/matching.d.ts +136 -0
  69. package/dist/api/queries/matching.js +161 -0
  70. package/dist/api/queries/notifications.d.ts +78 -0
  71. package/dist/api/queries/notifications.js +88 -0
  72. package/dist/api/queries/offers.d.ts +91 -0
  73. package/dist/api/queries/offers.js +103 -0
  74. package/dist/api/queries/subscriptions.d.ts +56 -0
  75. package/dist/api/queries/subscriptions.js +73 -0
  76. package/dist/api/queries/support.d.ts +106 -0
  77. package/dist/api/queries/support.js +202 -0
  78. package/dist/api/queries/users.d.ts +293 -0
  79. package/dist/api/queries/users.js +370 -0
  80. package/dist/api/types.d.ts +464 -0
  81. package/dist/api/types.js +9 -0
  82. package/dist/hooks/useAuth.d.ts +5 -0
  83. package/dist/hooks/useAuth.js +39 -0
  84. package/dist/hooks/useUser.d.ts +43 -0
  85. package/dist/hooks/useUser.js +44 -0
  86. package/dist/index.d.ts +36 -0
  87. package/dist/index.js +67 -0
  88. package/package.json +62 -0
  89. package/src/__tests__/e2e.test.ts +502 -0
  90. package/src/api/client.ts +71 -0
  91. package/src/api/mutations/admin.ts +531 -0
  92. package/src/api/mutations/ambassadors.ts +185 -0
  93. package/src/api/mutations/auth.ts +350 -0
  94. package/src/api/mutations/bookings.ts +190 -0
  95. package/src/api/mutations/event-chat.ts +177 -0
  96. package/src/api/mutations/events.ts +273 -0
  97. package/src/api/mutations/grow90.ts +169 -0
  98. package/src/api/mutations/hubs.ts +385 -0
  99. package/src/api/mutations/index.ts +23 -0
  100. package/src/api/mutations/jack.ts +130 -0
  101. package/src/api/mutations/library.ts +212 -0
  102. package/src/api/mutations/map.ts +230 -0
  103. package/src/api/mutations/matching.ts +271 -0
  104. package/src/api/mutations/notifications.ts +114 -0
  105. package/src/api/mutations/offers.ts +73 -0
  106. package/src/api/mutations/subscriptions.ts +162 -0
  107. package/src/api/mutations/support.ts +390 -0
  108. package/src/api/mutations/users.ts +271 -0
  109. package/src/api/queries/admin.ts +480 -0
  110. package/src/api/queries/ambassadors.ts +139 -0
  111. package/src/api/queries/auth.ts +24 -0
  112. package/src/api/queries/bookings.ts +135 -0
  113. package/src/api/queries/businesses.ts +203 -0
  114. package/src/api/queries/event-chat.ts +78 -0
  115. package/src/api/queries/events.ts +272 -0
  116. package/src/api/queries/grow90.ts +98 -0
  117. package/src/api/queries/hubs.ts +211 -0
  118. package/src/api/queries/index.ts +24 -0
  119. package/src/api/queries/jack.ts +127 -0
  120. package/src/api/queries/library.ts +166 -0
  121. package/src/api/queries/map.ts +331 -0
  122. package/src/api/queries/matching.ts +238 -0
  123. package/src/api/queries/notifications.ts +103 -0
  124. package/src/api/queries/offers.ts +136 -0
  125. package/src/api/queries/subscriptions.ts +91 -0
  126. package/src/api/queries/support.ts +235 -0
  127. package/src/api/queries/users.ts +393 -0
  128. package/src/api/types.ts +596 -0
  129. 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
+ }