@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,212 @@
1
+ import {
2
+ useMutation,
3
+ useQueryClient,
4
+ UseMutationOptions,
5
+ UseMutationResult,
6
+ } from '@tanstack/react-query';
7
+ import { getApiClient } from '../client';
8
+ import type { LibraryProgressResponse } from '../types';
9
+ import { libraryKeys } from '../queries/library';
10
+
11
+ // ============================================================================
12
+ // MUTATION HOOKS
13
+ // ============================================================================
14
+
15
+ /**
16
+ * Mark a library item as viewed
17
+ *
18
+ * @param options - TanStack Query mutation options
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * const { mutate, isPending } = useMarkViewed();
23
+ *
24
+ * mutate('lib-123');
25
+ * ```
26
+ */
27
+ export function useMarkViewed(
28
+ options?: Omit<
29
+ UseMutationOptions<LibraryProgressResponse, Error, string>,
30
+ 'mutationFn'
31
+ >
32
+ ): UseMutationResult<LibraryProgressResponse, Error, string> {
33
+ const queryClient = useQueryClient();
34
+
35
+ return useMutation({
36
+ mutationFn: async (id: string): Promise<LibraryProgressResponse> => {
37
+ const client = getApiClient();
38
+ const response = await client.post(`/api/v1/library/${id}/view`);
39
+ return response.data;
40
+ },
41
+ onSuccess: (data, id, context) => {
42
+ // Invalidate the specific item to reflect updated view status
43
+ queryClient.invalidateQueries({ queryKey: libraryKeys.detail(id) });
44
+
45
+ // Invalidate lists to update view counts
46
+ queryClient.invalidateQueries({ queryKey: libraryKeys.lists() });
47
+
48
+ // Call user's onSuccess if provided
49
+ // User's onSuccess is handled by spreading options
50
+ },
51
+ ...options,
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Mark a library item as completed
57
+ *
58
+ * @param options - TanStack Query mutation options
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * const { mutate, isPending } = useMarkCompleted();
63
+ *
64
+ * mutate('lib-123');
65
+ * ```
66
+ */
67
+ export function useMarkCompleted(
68
+ options?: Omit<
69
+ UseMutationOptions<LibraryProgressResponse, Error, string>,
70
+ 'mutationFn'
71
+ >
72
+ ): UseMutationResult<LibraryProgressResponse, Error, string> {
73
+ const queryClient = useQueryClient();
74
+
75
+ return useMutation({
76
+ mutationFn: async (id: string): Promise<LibraryProgressResponse> => {
77
+ const client = getApiClient();
78
+ const response = await client.post(`/api/v1/library/${id}/complete`);
79
+ return response.data;
80
+ },
81
+ onSuccess: (data, id, context) => {
82
+ // Invalidate the specific item to reflect completion status
83
+ queryClient.invalidateQueries({ queryKey: libraryKeys.detail(id) });
84
+
85
+ // Invalidate lists
86
+ queryClient.invalidateQueries({ queryKey: libraryKeys.lists() });
87
+
88
+ // Call user's onSuccess if provided
89
+ // User's onSuccess is handled by spreading options
90
+ },
91
+ ...options,
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Toggle like status for a library item
97
+ *
98
+ * @param options - TanStack Query mutation options
99
+ *
100
+ * @example
101
+ * ```tsx
102
+ * const { mutate, isPending } = useToggleLike();
103
+ *
104
+ * mutate('lib-123');
105
+ * ```
106
+ */
107
+ export function useToggleLike(
108
+ options?: Omit<
109
+ UseMutationOptions<LibraryProgressResponse, Error, string>,
110
+ 'mutationFn'
111
+ >
112
+ ): UseMutationResult<LibraryProgressResponse, Error, string> {
113
+ const queryClient = useQueryClient();
114
+
115
+ return useMutation({
116
+ mutationFn: async (id: string): Promise<LibraryProgressResponse> => {
117
+ const client = getApiClient();
118
+ const response = await client.post(`/api/v1/library/${id}/like`);
119
+ return response.data;
120
+ },
121
+ onSuccess: (data, id, context) => {
122
+ // Invalidate the specific item to reflect like status
123
+ queryClient.invalidateQueries({ queryKey: libraryKeys.detail(id) });
124
+
125
+ // Invalidate lists to update like counts
126
+ queryClient.invalidateQueries({ queryKey: libraryKeys.lists() });
127
+
128
+ // Call user's onSuccess if provided
129
+ // User's onSuccess is handled by spreading options
130
+ },
131
+ ...options,
132
+ });
133
+ }
134
+
135
+ /**
136
+ * Add a bookmark to a library item
137
+ *
138
+ * @param options - TanStack Query mutation options
139
+ *
140
+ * @example
141
+ * ```tsx
142
+ * const { mutate, isPending } = useAddBookmark();
143
+ *
144
+ * mutate('lib-123');
145
+ * ```
146
+ */
147
+ export function useAddBookmark(
148
+ options?: Omit<
149
+ UseMutationOptions<LibraryProgressResponse, Error, string>,
150
+ 'mutationFn'
151
+ >
152
+ ): UseMutationResult<LibraryProgressResponse, Error, string> {
153
+ const queryClient = useQueryClient();
154
+
155
+ return useMutation({
156
+ mutationFn: async (id: string): Promise<LibraryProgressResponse> => {
157
+ const client = getApiClient();
158
+ const response = await client.post(`/api/v1/library/${id}/bookmark`);
159
+ return response.data;
160
+ },
161
+ onSuccess: (data, id, context) => {
162
+ // Invalidate the specific item to reflect bookmark status
163
+ queryClient.invalidateQueries({ queryKey: libraryKeys.detail(id) });
164
+
165
+ // Invalidate lists
166
+ queryClient.invalidateQueries({ queryKey: libraryKeys.lists() });
167
+
168
+ // Call user's onSuccess if provided
169
+ // User's onSuccess is handled by spreading options
170
+ },
171
+ ...options,
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Remove a bookmark from a library item
177
+ *
178
+ * @param options - TanStack Query mutation options
179
+ *
180
+ * @example
181
+ * ```tsx
182
+ * const { mutate, isPending } = useRemoveBookmark();
183
+ *
184
+ * mutate('lib-123');
185
+ * ```
186
+ */
187
+ export function useRemoveBookmark(
188
+ options?: Omit<
189
+ UseMutationOptions<void, Error, string>,
190
+ 'mutationFn'
191
+ >
192
+ ): UseMutationResult<void, Error, string> {
193
+ const queryClient = useQueryClient();
194
+
195
+ return useMutation({
196
+ mutationFn: async (id: string): Promise<void> => {
197
+ const client = getApiClient();
198
+ await client.delete(`/api/v1/library/${id}/bookmark`);
199
+ },
200
+ onSuccess: (data, id, context) => {
201
+ // Invalidate the specific item to reflect bookmark removal
202
+ queryClient.invalidateQueries({ queryKey: libraryKeys.detail(id) });
203
+
204
+ // Invalidate lists
205
+ queryClient.invalidateQueries({ queryKey: libraryKeys.lists() });
206
+
207
+ // Call user's onSuccess if provided
208
+ // User's onSuccess is handled by spreading options
209
+ },
210
+ ...options,
211
+ });
212
+ }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Map Mutation Hooks
3
+ *
4
+ * TanStack Query mutation hooks for map-related write operations.
5
+ * These hooks handle updating user location and visibility settings.
6
+ *
7
+ * @module api/mutations/map
8
+ */
9
+
10
+ import {
11
+ useMutation,
12
+ UseMutationOptions,
13
+ UseMutationResult,
14
+ useQueryClient,
15
+ } from '@tanstack/react-query';
16
+ import { getApiClient } from '../client';
17
+ import { mapKeys } from '../queries/map';
18
+ import { userKeys } from '../queries/users';
19
+
20
+ // ============================================================================
21
+ // REQUEST TYPES
22
+ // ============================================================================
23
+
24
+ export interface UpdateLocationRequest {
25
+ lat: number;
26
+ lng: number;
27
+ }
28
+
29
+ export interface UpdateOpenToMeetRequest {
30
+ openToMeet: boolean;
31
+ }
32
+
33
+ // ============================================================================
34
+ // RESPONSE TYPES
35
+ // ============================================================================
36
+
37
+ export interface LocationUpdateResponse {
38
+ success: boolean;
39
+ message: string;
40
+ }
41
+
42
+ export interface OpenToMeetResponse {
43
+ openToMeet: boolean;
44
+ message: string;
45
+ }
46
+
47
+ // ============================================================================
48
+ // MUTATION HOOKS
49
+ // ============================================================================
50
+
51
+ /**
52
+ * Update current user's location
53
+ *
54
+ * @description
55
+ * Updates the authenticated user's location for display on the member map.
56
+ * Location is only visible to other users if the user has enabled "open to meet".
57
+ *
58
+ * @endpoint PUT /api/v1/map/location
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * import { useUpdateLocation } from '@growsober/sdk';
63
+ *
64
+ * function LocationUpdater() {
65
+ * const updateLocation = useUpdateLocation();
66
+ *
67
+ * const handleLocationUpdate = async () => {
68
+ * // Get current position from device
69
+ * navigator.geolocation.getCurrentPosition(
70
+ * async (position) => {
71
+ * await updateLocation.mutateAsync({
72
+ * lat: position.coords.latitude,
73
+ * lng: position.coords.longitude,
74
+ * });
75
+ * },
76
+ * (error) => console.error('Location error:', error)
77
+ * );
78
+ * };
79
+ *
80
+ * return (
81
+ * <button onClick={handleLocationUpdate} disabled={updateLocation.isPending}>
82
+ * Update My Location
83
+ * </button>
84
+ * );
85
+ * }
86
+ * ```
87
+ *
88
+ * @example
89
+ * With Expo Location:
90
+ * ```tsx
91
+ * import * as Location from 'expo-location';
92
+ * import { useUpdateLocation } from '@growsober/sdk';
93
+ *
94
+ * function App() {
95
+ * const updateLocation = useUpdateLocation();
96
+ *
97
+ * useEffect(() => {
98
+ * (async () => {
99
+ * const { status } = await Location.requestForegroundPermissionsAsync();
100
+ * if (status === 'granted') {
101
+ * const location = await Location.getCurrentPositionAsync({});
102
+ * updateLocation.mutate({
103
+ * lat: location.coords.latitude,
104
+ * lng: location.coords.longitude,
105
+ * });
106
+ * }
107
+ * })();
108
+ * }, []);
109
+ * }
110
+ * ```
111
+ *
112
+ * @param options - TanStack Query mutation options
113
+ * @returns TanStack Query mutation result
114
+ */
115
+ export function useUpdateLocation(
116
+ options?: Omit<
117
+ UseMutationOptions<LocationUpdateResponse, Error, UpdateLocationRequest>,
118
+ 'mutationFn'
119
+ >
120
+ ): UseMutationResult<LocationUpdateResponse, Error, UpdateLocationRequest> {
121
+ const queryClient = useQueryClient();
122
+
123
+ return useMutation({
124
+ mutationFn: async (data: UpdateLocationRequest): Promise<LocationUpdateResponse> => {
125
+ const client = getApiClient();
126
+ const response = await client.put<LocationUpdateResponse>(
127
+ '/api/v1/map/location',
128
+ data
129
+ );
130
+ return response.data;
131
+ },
132
+ onSuccess: () => {
133
+ // Invalidate map queries to reflect new location
134
+ queryClient.invalidateQueries({ queryKey: mapKeys.members() });
135
+ // Also invalidate user data as location is stored on user
136
+ queryClient.invalidateQueries({ queryKey: userKeys.me() });
137
+ },
138
+ ...options,
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Toggle "open to meet" status
144
+ *
145
+ * @description
146
+ * Updates the user's "open to meet" visibility setting.
147
+ * When enabled, the user's location will be visible to other premium members on the map.
148
+ * Premium feature.
149
+ *
150
+ * @endpoint PUT /api/v1/map/open-to-meet
151
+ *
152
+ * @example
153
+ * ```tsx
154
+ * import { useUpdateOpenToMeet } from '@growsober/sdk';
155
+ *
156
+ * function OpenToMeetToggle() {
157
+ * const updateOpenToMeet = useUpdateOpenToMeet();
158
+ * const [isOpen, setIsOpen] = useState(false);
159
+ *
160
+ * const handleToggle = async () => {
161
+ * const newValue = !isOpen;
162
+ * await updateOpenToMeet.mutateAsync({ openToMeet: newValue });
163
+ * setIsOpen(newValue);
164
+ * };
165
+ *
166
+ * return (
167
+ * <Switch
168
+ * checked={isOpen}
169
+ * onChange={handleToggle}
170
+ * disabled={updateOpenToMeet.isPending}
171
+ * />
172
+ * );
173
+ * }
174
+ * ```
175
+ *
176
+ * @example
177
+ * With user state:
178
+ * ```tsx
179
+ * import { useCurrentUser, useUpdateOpenToMeet } from '@growsober/sdk';
180
+ *
181
+ * function OpenToMeetSetting() {
182
+ * const { data: user } = useCurrentUser();
183
+ * const updateOpenToMeet = useUpdateOpenToMeet({
184
+ * onSuccess: (data) => {
185
+ * console.log(data.openToMeet ? 'Now visible on map!' : 'Hidden from map');
186
+ * },
187
+ * });
188
+ *
189
+ * return (
190
+ * <div>
191
+ * <label>Open to Meet</label>
192
+ * <Switch
193
+ * checked={user?.openToMeet}
194
+ * onChange={(checked) => updateOpenToMeet.mutate({ openToMeet: checked })}
195
+ * />
196
+ * <p>Show your location to other members</p>
197
+ * </div>
198
+ * );
199
+ * }
200
+ * ```
201
+ *
202
+ * @param options - TanStack Query mutation options
203
+ * @returns TanStack Query mutation result
204
+ */
205
+ export function useUpdateOpenToMeet(
206
+ options?: Omit<
207
+ UseMutationOptions<OpenToMeetResponse, Error, UpdateOpenToMeetRequest>,
208
+ 'mutationFn'
209
+ >
210
+ ): UseMutationResult<OpenToMeetResponse, Error, UpdateOpenToMeetRequest> {
211
+ const queryClient = useQueryClient();
212
+
213
+ return useMutation({
214
+ mutationFn: async (data: UpdateOpenToMeetRequest): Promise<OpenToMeetResponse> => {
215
+ const client = getApiClient();
216
+ const response = await client.put<OpenToMeetResponse>(
217
+ '/api/v1/map/open-to-meet',
218
+ data
219
+ );
220
+ return response.data;
221
+ },
222
+ onSuccess: () => {
223
+ // Invalidate user data to reflect new setting
224
+ queryClient.invalidateQueries({ queryKey: userKeys.me() });
225
+ // Invalidate map queries as visibility has changed
226
+ queryClient.invalidateQueries({ queryKey: mapKeys.members() });
227
+ },
228
+ ...options,
229
+ });
230
+ }
@@ -0,0 +1,271 @@
1
+ import {
2
+ useMutation,
3
+ useQueryClient,
4
+ UseMutationOptions,
5
+ UseMutationResult,
6
+ } from '@tanstack/react-query';
7
+ import { getApiClient } from '../client';
8
+ import { matchingKeys, MatchResponse, BuddyResponse } from '../queries/matching';
9
+
10
+ // ============================================================================
11
+ // REQUEST TYPES
12
+ // ============================================================================
13
+
14
+ export interface CreateMatchRequest {
15
+ matchedUserId: string;
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
+ }
36
+
37
+ // ============================================================================
38
+ // MUTATION HOOKS
39
+ // ============================================================================
40
+
41
+ /**
42
+ * Create a new match request
43
+ *
44
+ * @endpoint POST /api/v1/matching/matches
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * const createMatch = useCreateMatch();
49
+ * await createMatch.mutateAsync({ matchedUserId: 'user-123' });
50
+ * ```
51
+ */
52
+ export function useCreateMatch(
53
+ options?: Omit<UseMutationOptions<MatchResponse, Error, CreateMatchRequest>, 'mutationFn'>
54
+ ): UseMutationResult<MatchResponse, Error, CreateMatchRequest> {
55
+ const queryClient = useQueryClient();
56
+
57
+ return useMutation({
58
+ mutationFn: async (data: CreateMatchRequest): Promise<MatchResponse> => {
59
+ const client = getApiClient();
60
+ const response = await client.post('/api/v1/matching/matches', data);
61
+ return response.data?.data || response.data;
62
+ },
63
+ onSuccess: () => {
64
+ queryClient.invalidateQueries({ queryKey: matchingKeys.matches() });
65
+ queryClient.invalidateQueries({ queryKey: matchingKeys.stats() });
66
+ },
67
+ ...options,
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Update match status (accept/decline/block)
73
+ *
74
+ * @endpoint PUT /api/v1/matching/matches/:matchId
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * const updateMatch = useUpdateMatch();
79
+ * await updateMatch.mutateAsync({ matchId: 'match-123', status: 'ACCEPTED' });
80
+ * ```
81
+ */
82
+ export function useUpdateMatch(
83
+ options?: Omit<
84
+ UseMutationOptions<MatchResponse, Error, { matchId: string; data: UpdateMatchRequest }>,
85
+ 'mutationFn'
86
+ >
87
+ ): UseMutationResult<MatchResponse, Error, { matchId: string; data: UpdateMatchRequest }> {
88
+ const queryClient = useQueryClient();
89
+
90
+ return useMutation({
91
+ mutationFn: async ({ matchId, data }): Promise<MatchResponse> => {
92
+ const client = getApiClient();
93
+ const response = await client.put(`/api/v1/matching/matches/${matchId}`, data);
94
+ return response.data?.data || response.data;
95
+ },
96
+ onSuccess: () => {
97
+ queryClient.invalidateQueries({ queryKey: matchingKeys.matches() });
98
+ queryClient.invalidateQueries({ queryKey: matchingKeys.stats() });
99
+ },
100
+ ...options,
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Accept a match (convenience hook)
106
+ *
107
+ * @example
108
+ * ```tsx
109
+ * const acceptMatch = useAcceptMatch();
110
+ * await acceptMatch.mutateAsync('match-123');
111
+ * ```
112
+ */
113
+ export function useAcceptMatch(
114
+ options?: Omit<UseMutationOptions<MatchResponse, Error, string>, 'mutationFn'>
115
+ ): UseMutationResult<MatchResponse, Error, string> {
116
+ const updateMatch = useUpdateMatch();
117
+
118
+ return {
119
+ ...updateMatch,
120
+ mutate: (matchId: string, mutateOptions?: any) =>
121
+ updateMatch.mutate({ matchId, data: { status: 'ACCEPTED' } }, mutateOptions),
122
+ mutateAsync: (matchId: string) =>
123
+ updateMatch.mutateAsync({ matchId, data: { status: 'ACCEPTED' } }),
124
+ } as UseMutationResult<MatchResponse, Error, string>;
125
+ }
126
+
127
+ /**
128
+ * Decline a match (convenience hook)
129
+ *
130
+ * @example
131
+ * ```tsx
132
+ * const declineMatch = useDeclineMatch();
133
+ * await declineMatch.mutateAsync('match-123');
134
+ * ```
135
+ */
136
+ export function useDeclineMatch(
137
+ options?: Omit<UseMutationOptions<MatchResponse, Error, string>, 'mutationFn'>
138
+ ): UseMutationResult<MatchResponse, Error, string> {
139
+ const updateMatch = useUpdateMatch();
140
+
141
+ return {
142
+ ...updateMatch,
143
+ mutate: (matchId: string, mutateOptions?: any) =>
144
+ updateMatch.mutate({ matchId, data: { status: 'DECLINED' } }, mutateOptions),
145
+ mutateAsync: (matchId: string) =>
146
+ updateMatch.mutateAsync({ matchId, data: { status: 'DECLINED' } }),
147
+ } as UseMutationResult<MatchResponse, Error, string>;
148
+ }
149
+
150
+ /**
151
+ * Delete a match
152
+ *
153
+ * @endpoint DELETE /api/v1/matching/matches/:matchId
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * const deleteMatch = useDeleteMatch();
158
+ * await deleteMatch.mutateAsync('match-123');
159
+ * ```
160
+ */
161
+ export function useDeleteMatch(
162
+ options?: Omit<UseMutationOptions<void, Error, string>, 'mutationFn'>
163
+ ): UseMutationResult<void, Error, string> {
164
+ const queryClient = useQueryClient();
165
+
166
+ return useMutation({
167
+ mutationFn: async (matchId: string): Promise<void> => {
168
+ const client = getApiClient();
169
+ await client.delete(`/api/v1/matching/matches/${matchId}`);
170
+ },
171
+ onSuccess: () => {
172
+ queryClient.invalidateQueries({ queryKey: matchingKeys.matches() });
173
+ queryClient.invalidateQueries({ queryKey: matchingKeys.stats() });
174
+ },
175
+ ...options,
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Request someone as accountability buddy
181
+ *
182
+ * @endpoint POST /api/v1/matching/buddies
183
+ *
184
+ * @example
185
+ * ```tsx
186
+ * const requestBuddy = useRequestBuddy();
187
+ * await requestBuddy.mutateAsync({ buddyId: 'user-123' });
188
+ * ```
189
+ */
190
+ export function useRequestBuddy(
191
+ options?: Omit<UseMutationOptions<BuddyResponse, Error, CreateBuddyRequest>, 'mutationFn'>
192
+ ): UseMutationResult<BuddyResponse, Error, CreateBuddyRequest> {
193
+ const queryClient = useQueryClient();
194
+
195
+ return useMutation({
196
+ mutationFn: async (data: CreateBuddyRequest): Promise<BuddyResponse> => {
197
+ const client = getApiClient();
198
+ const response = await client.post('/api/v1/matching/buddies', data);
199
+ return response.data?.data || response.data;
200
+ },
201
+ onSuccess: () => {
202
+ queryClient.invalidateQueries({ queryKey: matchingKeys.buddies() });
203
+ queryClient.invalidateQueries({ queryKey: matchingKeys.buddyRequests() });
204
+ },
205
+ ...options,
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Update buddy request (accept/decline/end)
211
+ *
212
+ * @endpoint PUT /api/v1/matching/buddies/:buddyId
213
+ *
214
+ * @example
215
+ * ```tsx
216
+ * const updateBuddy = useUpdateBuddy();
217
+ * await updateBuddy.mutateAsync({ buddyId: 'buddy-123', data: { status: 'ACCEPTED' } });
218
+ * ```
219
+ */
220
+ export function useUpdateBuddy(
221
+ options?: Omit<
222
+ UseMutationOptions<BuddyResponse, Error, { buddyId: string; data: UpdateBuddyRequest }>,
223
+ 'mutationFn'
224
+ >
225
+ ): UseMutationResult<BuddyResponse, Error, { buddyId: string; data: UpdateBuddyRequest }> {
226
+ const queryClient = useQueryClient();
227
+
228
+ return useMutation({
229
+ mutationFn: async ({ buddyId, data }): Promise<BuddyResponse> => {
230
+ const client = getApiClient();
231
+ const response = await client.put(`/api/v1/matching/buddies/${buddyId}`, data);
232
+ return response.data?.data || response.data;
233
+ },
234
+ onSuccess: () => {
235
+ queryClient.invalidateQueries({ queryKey: matchingKeys.buddies() });
236
+ queryClient.invalidateQueries({ queryKey: matchingKeys.buddyRequests() });
237
+ },
238
+ ...options,
239
+ });
240
+ }
241
+
242
+ /**
243
+ * Log activity with buddy (check-in, message)
244
+ *
245
+ * @endpoint POST /api/v1/matching/buddies/:buddyId/activity
246
+ *
247
+ * @example
248
+ * ```tsx
249
+ * const logActivity = useLogBuddyActivity();
250
+ * await logActivity.mutateAsync({ buddyId: 'buddy-123', data: { type: 'CHECK_IN' } });
251
+ * ```
252
+ */
253
+ export function useLogBuddyActivity(
254
+ options?: Omit<
255
+ UseMutationOptions<void, Error, { buddyId: string; data: LogBuddyActivityRequest }>,
256
+ 'mutationFn'
257
+ >
258
+ ): UseMutationResult<void, Error, { buddyId: string; data: LogBuddyActivityRequest }> {
259
+ const queryClient = useQueryClient();
260
+
261
+ return useMutation({
262
+ mutationFn: async ({ buddyId, data }): Promise<void> => {
263
+ const client = getApiClient();
264
+ await client.post(`/api/v1/matching/buddies/${buddyId}/activity`, data);
265
+ },
266
+ onSuccess: () => {
267
+ queryClient.invalidateQueries({ queryKey: matchingKeys.buddies() });
268
+ },
269
+ ...options,
270
+ });
271
+ }