@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,390 @@
1
+ import {
2
+ useMutation,
3
+ useQueryClient,
4
+ UseMutationOptions,
5
+ UseMutationResult,
6
+ } from '@tanstack/react-query';
7
+ import { getApiClient } from '../client';
8
+ import type {
9
+ DailyCheckInResponse,
10
+ CreateCheckInRequest,
11
+ UpdateCheckInRequest,
12
+ MoodLogResponse,
13
+ CreateMoodLogRequest,
14
+ WinResponse,
15
+ CreateWinRequest,
16
+ HabitResponse,
17
+ CreateHabitRequest,
18
+ UpdateHabitRequest,
19
+ ReflectionResponse,
20
+ CreateReflectionRequest,
21
+ } from '../types';
22
+ import { supportKeys } from '../queries/support';
23
+
24
+ // ============================================================================
25
+ // MUTATION HOOKS
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Create a daily check-in
30
+ *
31
+ * @param options - TanStack Query mutation options
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * const { mutate, isPending } = useCreateCheckIn();
36
+ *
37
+ * mutate({
38
+ * mood: 8,
39
+ * notes: 'Feeling great today!',
40
+ * triggers: ['stress'],
41
+ * gratitude: ['My support group']
42
+ * });
43
+ * ```
44
+ */
45
+ export function useCreateCheckIn(
46
+ options?: Omit<
47
+ UseMutationOptions<DailyCheckInResponse, Error, CreateCheckInRequest>,
48
+ 'mutationFn'
49
+ >
50
+ ): UseMutationResult<DailyCheckInResponse, Error, CreateCheckInRequest> {
51
+ const queryClient = useQueryClient();
52
+
53
+ return useMutation({
54
+ mutationFn: async (data: CreateCheckInRequest): Promise<DailyCheckInResponse> => {
55
+ const client = getApiClient();
56
+ const response = await client.post('/api/v1/support/check-ins', data);
57
+ return response.data;
58
+ },
59
+ onSuccess: (newCheckIn, variables, context) => {
60
+ // Invalidate check-ins list and today's check-in
61
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkIns() });
62
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkInToday() });
63
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkInStreak() });
64
+
65
+ // Call user's onSuccess if provided
66
+ },
67
+ ...options,
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Update a daily check-in
73
+ *
74
+ * @param options - TanStack Query mutation options
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * const { mutate, isPending } = useUpdateCheckIn();
79
+ *
80
+ * mutate({
81
+ * id: 'checkin-123',
82
+ * data: {
83
+ * mood: 4,
84
+ * notes: 'Updated notes',
85
+ * }
86
+ * });
87
+ * ```
88
+ */
89
+ export function useUpdateCheckIn(
90
+ options?: Omit<
91
+ UseMutationOptions<DailyCheckInResponse, Error, { id: string; data: UpdateCheckInRequest }>,
92
+ 'mutationFn'
93
+ >
94
+ ): UseMutationResult<DailyCheckInResponse, Error, { id: string; data: UpdateCheckInRequest }> {
95
+ const queryClient = useQueryClient();
96
+
97
+ return useMutation({
98
+ mutationFn: async ({ id, data }: { id: string; data: UpdateCheckInRequest }): Promise<DailyCheckInResponse> => {
99
+ const client = getApiClient();
100
+ const response = await client.put(`/api/v1/support/check-ins/${id}`, data);
101
+ return response.data;
102
+ },
103
+ onSuccess: (updatedCheckIn, variables, context) => {
104
+ // Invalidate check-ins list and today's check-in
105
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkIns() });
106
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkInToday() });
107
+ queryClient.invalidateQueries({ queryKey: supportKeys.checkInStreak() });
108
+ },
109
+ ...options,
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Create a mood log
115
+ *
116
+ * @param options - TanStack Query mutation options
117
+ *
118
+ * @example
119
+ * ```tsx
120
+ * const { mutate, isPending } = useCreateMoodLog();
121
+ *
122
+ * mutate({
123
+ * mood: 7,
124
+ * notes: 'Feeling optimistic',
125
+ * activities: ['exercise', 'meditation']
126
+ * });
127
+ * ```
128
+ */
129
+ export function useCreateMoodLog(
130
+ options?: Omit<
131
+ UseMutationOptions<MoodLogResponse, Error, CreateMoodLogRequest>,
132
+ 'mutationFn'
133
+ >
134
+ ): UseMutationResult<MoodLogResponse, Error, CreateMoodLogRequest> {
135
+ const queryClient = useQueryClient();
136
+
137
+ return useMutation({
138
+ mutationFn: async (data: CreateMoodLogRequest): Promise<MoodLogResponse> => {
139
+ const client = getApiClient();
140
+ const response = await client.post('/api/v1/support/mood', data);
141
+ return response.data;
142
+ },
143
+ onSuccess: (newMoodLog, variables, context) => {
144
+ // Invalidate mood logs list
145
+ queryClient.invalidateQueries({ queryKey: supportKeys.moodLogs() });
146
+
147
+ // Call user's onSuccess if provided
148
+ },
149
+ ...options,
150
+ });
151
+ }
152
+
153
+ /**
154
+ * Create a win
155
+ *
156
+ * @param options - TanStack Query mutation options
157
+ *
158
+ * @example
159
+ * ```tsx
160
+ * const { mutate, isPending } = useCreateWin();
161
+ *
162
+ * mutate({
163
+ * title: '30 days sober!',
164
+ * description: 'Hit my first milestone',
165
+ * category: 'sobriety'
166
+ * });
167
+ * ```
168
+ */
169
+ export function useCreateWin(
170
+ options?: Omit<
171
+ UseMutationOptions<WinResponse, Error, CreateWinRequest>,
172
+ 'mutationFn'
173
+ >
174
+ ): UseMutationResult<WinResponse, Error, CreateWinRequest> {
175
+ const queryClient = useQueryClient();
176
+
177
+ return useMutation({
178
+ mutationFn: async (data: CreateWinRequest): Promise<WinResponse> => {
179
+ const client = getApiClient();
180
+ const response = await client.post('/api/v1/support/wins', data);
181
+ return response.data;
182
+ },
183
+ onSuccess: (newWin, variables, context) => {
184
+ // Invalidate wins lists
185
+ queryClient.invalidateQueries({ queryKey: supportKeys.wins() });
186
+ queryClient.invalidateQueries({ queryKey: supportKeys.winsByCategory() });
187
+
188
+ // Call user's onSuccess if provided
189
+ },
190
+ ...options,
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Create a habit
196
+ *
197
+ * @param options - TanStack Query mutation options
198
+ *
199
+ * @example
200
+ * ```tsx
201
+ * const { mutate, isPending } = useCreateHabit();
202
+ *
203
+ * mutate({
204
+ * name: 'Morning meditation',
205
+ * description: '10 minutes of mindfulness',
206
+ * frequency: 'daily',
207
+ * targetDays: 7
208
+ * });
209
+ * ```
210
+ */
211
+ export function useCreateHabit(
212
+ options?: Omit<
213
+ UseMutationOptions<HabitResponse, Error, CreateHabitRequest>,
214
+ 'mutationFn'
215
+ >
216
+ ): UseMutationResult<HabitResponse, Error, CreateHabitRequest> {
217
+ const queryClient = useQueryClient();
218
+
219
+ return useMutation({
220
+ mutationFn: async (data: CreateHabitRequest): Promise<HabitResponse> => {
221
+ const client = getApiClient();
222
+ const response = await client.post('/api/v1/support/habits', data);
223
+ return response.data;
224
+ },
225
+ onSuccess: (newHabit, variables, context) => {
226
+ // Invalidate habits list
227
+ queryClient.invalidateQueries({ queryKey: supportKeys.habits() });
228
+
229
+ // Call user's onSuccess if provided
230
+ },
231
+ ...options,
232
+ });
233
+ }
234
+
235
+ /**
236
+ * Update a habit
237
+ *
238
+ * @param options - TanStack Query mutation options
239
+ *
240
+ * @example
241
+ * ```tsx
242
+ * const { mutate, isPending } = useUpdateHabit();
243
+ *
244
+ * mutate({
245
+ * id: 'habit-123',
246
+ * data: {
247
+ * name: 'Evening meditation',
248
+ * targetDays: 14
249
+ * }
250
+ * });
251
+ * ```
252
+ */
253
+ export function useUpdateHabit(
254
+ options?: Omit<
255
+ UseMutationOptions<
256
+ HabitResponse,
257
+ Error,
258
+ { id: string; data: UpdateHabitRequest }
259
+ >,
260
+ 'mutationFn'
261
+ >
262
+ ): UseMutationResult<HabitResponse, Error, { id: string; data: UpdateHabitRequest }> {
263
+ const queryClient = useQueryClient();
264
+
265
+ return useMutation({
266
+ mutationFn: async ({
267
+ id,
268
+ data,
269
+ }: {
270
+ id: string;
271
+ data: UpdateHabitRequest;
272
+ }): Promise<HabitResponse> => {
273
+ const client = getApiClient();
274
+ const response = await client.put(`/api/v1/support/habits/${id}`, data);
275
+ return response.data;
276
+ },
277
+ onSuccess: (updatedHabit, variables, context) => {
278
+ // Invalidate habits list
279
+ queryClient.invalidateQueries({ queryKey: supportKeys.habits() });
280
+
281
+ // Call user's onSuccess if provided
282
+ },
283
+ ...options,
284
+ });
285
+ }
286
+
287
+ /**
288
+ * Delete a habit
289
+ *
290
+ * @param options - TanStack Query mutation options
291
+ *
292
+ * @example
293
+ * ```tsx
294
+ * const { mutate, isPending } = useDeleteHabit();
295
+ *
296
+ * mutate('habit-123');
297
+ * ```
298
+ */
299
+ export function useDeleteHabit(
300
+ options?: Omit<UseMutationOptions<void, Error, string>, 'mutationFn'>
301
+ ): UseMutationResult<void, Error, string> {
302
+ const queryClient = useQueryClient();
303
+
304
+ return useMutation({
305
+ mutationFn: async (id: string): Promise<void> => {
306
+ const client = getApiClient();
307
+ await client.delete(`/api/v1/support/habits/${id}`);
308
+ },
309
+ onSuccess: (data, habitId, context) => {
310
+ // Invalidate habits list
311
+ queryClient.invalidateQueries({ queryKey: supportKeys.habits() });
312
+
313
+ // Call user's onSuccess if provided
314
+ },
315
+ ...options,
316
+ });
317
+ }
318
+
319
+ /**
320
+ * Complete a habit for today
321
+ *
322
+ * @param options - TanStack Query mutation options
323
+ *
324
+ * @example
325
+ * ```tsx
326
+ * const { mutate, isPending } = useCompleteHabit();
327
+ *
328
+ * mutate('habit-123');
329
+ * ```
330
+ */
331
+ export function useCompleteHabit(
332
+ options?: Omit<UseMutationOptions<HabitResponse, Error, string>, 'mutationFn'>
333
+ ): UseMutationResult<HabitResponse, Error, string> {
334
+ const queryClient = useQueryClient();
335
+
336
+ return useMutation({
337
+ mutationFn: async (id: string): Promise<HabitResponse> => {
338
+ const client = getApiClient();
339
+ const response = await client.post(`/api/v1/support/habits/${id}/complete`);
340
+ return response.data;
341
+ },
342
+ onSuccess: (completedHabit, habitId, context) => {
343
+ // Invalidate habits list to show updated completion status
344
+ queryClient.invalidateQueries({ queryKey: supportKeys.habits() });
345
+
346
+ // Call user's onSuccess if provided
347
+ },
348
+ ...options,
349
+ });
350
+ }
351
+
352
+ /**
353
+ * Create a reflection
354
+ *
355
+ * @param options - TanStack Query mutation options
356
+ *
357
+ * @example
358
+ * ```tsx
359
+ * const { mutate, isPending } = useCreateReflection();
360
+ *
361
+ * mutate({
362
+ * content: 'Today I learned...',
363
+ * category: 'growth',
364
+ * mood: 8
365
+ * });
366
+ * ```
367
+ */
368
+ export function useCreateReflection(
369
+ options?: Omit<
370
+ UseMutationOptions<ReflectionResponse, Error, CreateReflectionRequest>,
371
+ 'mutationFn'
372
+ >
373
+ ): UseMutationResult<ReflectionResponse, Error, CreateReflectionRequest> {
374
+ const queryClient = useQueryClient();
375
+
376
+ return useMutation({
377
+ mutationFn: async (data: CreateReflectionRequest): Promise<ReflectionResponse> => {
378
+ const client = getApiClient();
379
+ const response = await client.post('/api/v1/support/reflections', data);
380
+ return response.data;
381
+ },
382
+ onSuccess: (newReflection, variables, context) => {
383
+ // Invalidate reflections list
384
+ queryClient.invalidateQueries({ queryKey: supportKeys.reflections() });
385
+
386
+ // Call user's onSuccess if provided
387
+ },
388
+ ...options,
389
+ });
390
+ }
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Users Mutation Hooks
3
+ *
4
+ * TanStack Query mutation hooks for user-related write operations.
5
+ * These hooks handle user profile updates and onboarding completion.
6
+ *
7
+ * @module api/mutations/users
8
+ */
9
+
10
+ import { useMutation, UseMutationOptions, UseMutationResult, useQueryClient } from '@tanstack/react-query';
11
+ import { getApiClient } from '../client';
12
+ import { userKeys } from '../queries/users';
13
+ import type { UpdateUserRequest, UserResponse } from '../types';
14
+
15
+ // ============================================================================
16
+ // MUTATION HOOKS
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Update current user's profile
21
+ *
22
+ * @description
23
+ * Updates the authenticated user's profile information.
24
+ * This can include name, bio, avatar, location, sober date, and other profile fields.
25
+ * Automatically invalidates relevant user queries to refresh the UI.
26
+ *
27
+ * @endpoint PUT /api/v1/users/me
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * import { useUpdateCurrentUser } from '@growsober/sdk';
32
+ *
33
+ * function EditProfileForm() {
34
+ * const { mutate: updateProfile, isPending, error } = useUpdateCurrentUser({
35
+ * onSuccess: (data) => {
36
+ * toast.success('Profile updated successfully');
37
+ * navigation.goBack();
38
+ * },
39
+ * onError: (error) => {
40
+ * toast.error('Failed to update profile: ' + error.message);
41
+ * },
42
+ * });
43
+ *
44
+ * const handleSubmit = (formData: FormData) => {
45
+ * updateProfile({
46
+ * name: formData.name,
47
+ * bio: formData.bio,
48
+ * avatar: formData.avatar,
49
+ * city: formData.city,
50
+ * soberDate: formData.soberDate,
51
+ * });
52
+ * };
53
+ *
54
+ * return (
55
+ * <form onSubmit={handleSubmit}>
56
+ * <input name="name" />
57
+ * <textarea name="bio" />
58
+ * <button type="submit" disabled={isPending}>
59
+ * {isPending ? 'Saving...' : 'Save Profile'}
60
+ * </button>
61
+ * {error && <p className="error">{error.message}</p>}
62
+ * </form>
63
+ * );
64
+ * }
65
+ * ```
66
+ *
67
+ * @example
68
+ * Update specific fields:
69
+ * ```tsx
70
+ * function AvatarUploader() {
71
+ * const { mutateAsync: updateProfile } = useUpdateCurrentUser();
72
+ *
73
+ * const handleAvatarChange = async (file: File) => {
74
+ * const uploadedUrl = await uploadImage(file);
75
+ * await updateProfile({ avatar: uploadedUrl });
76
+ * toast.success('Avatar updated!');
77
+ * };
78
+ *
79
+ * return <ImagePicker onSelect={handleAvatarChange} />;
80
+ * }
81
+ * ```
82
+ *
83
+ * @example
84
+ * Update sober date:
85
+ * ```tsx
86
+ * function SoberDatePicker() {
87
+ * const { mutate: updateProfile } = useUpdateCurrentUser();
88
+ * const { data: user } = useCurrentUser();
89
+ *
90
+ * const handleDateChange = (date: Date) => {
91
+ * updateProfile({
92
+ * soberDate: date.toISOString(),
93
+ * });
94
+ * };
95
+ *
96
+ * return (
97
+ * <DatePicker
98
+ * value={user?.soberDate}
99
+ * onChange={handleDateChange}
100
+ * />
101
+ * );
102
+ * }
103
+ * ```
104
+ *
105
+ * @param options - TanStack Query mutation options
106
+ * @returns TanStack Query mutation result
107
+ */
108
+ export function useUpdateCurrentUser(
109
+ options?: Omit<UseMutationOptions<UserResponse, Error, UpdateUserRequest>, 'mutationFn'>
110
+ ): UseMutationResult<UserResponse, Error, UpdateUserRequest> {
111
+ const queryClient = useQueryClient();
112
+
113
+ return useMutation({
114
+ mutationFn: async (data: UpdateUserRequest): Promise<UserResponse> => {
115
+ const client = getApiClient();
116
+ const response = await client.put<UserResponse>('/api/v1/users/me', data);
117
+ return response.data;
118
+ },
119
+ onSuccess: (data, variables, context) => {
120
+ // Invalidate current user query to trigger refetch with updated data
121
+ queryClient.invalidateQueries({ queryKey: userKeys.me() });
122
+
123
+ // Also invalidate the detail query for this user if they've viewed their own profile
124
+ if (data.id) {
125
+ queryClient.invalidateQueries({ queryKey: userKeys.detail(data.id) });
126
+ queryClient.invalidateQueries({ queryKey: userKeys.public(data.id) });
127
+ }
128
+
129
+ // Call user's onSuccess if provided
130
+ // User's onSuccess is handled by spreading options
131
+ },
132
+ ...options,
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Complete user onboarding
138
+ *
139
+ * @description
140
+ * Marks the user's onboarding process as complete.
141
+ * This is typically called after a user completes all required onboarding steps
142
+ * (e.g., setting up profile, selecting interests, setting sober date).
143
+ * Updates the user's onboardingCompleted flag and unlocks full app access.
144
+ *
145
+ * @endpoint POST /api/v1/users/me/complete-onboarding
146
+ *
147
+ * @example
148
+ * ```tsx
149
+ * import { useCompleteOnboarding } from '@growsober/sdk';
150
+ *
151
+ * function OnboardingFinalStep() {
152
+ * const { mutate: completeOnboarding, isPending } = useCompleteOnboarding({
153
+ * onSuccess: () => {
154
+ * toast.success('Welcome to GrowSober!');
155
+ * navigation.navigate('Home');
156
+ * },
157
+ * onError: (error) => {
158
+ * toast.error('Failed to complete onboarding: ' + error.message);
159
+ * },
160
+ * });
161
+ *
162
+ * const handleFinish = () => {
163
+ * completeOnboarding();
164
+ * };
165
+ *
166
+ * return (
167
+ * <div>
168
+ * <h1>You're All Set!</h1>
169
+ * <p>Ready to start your sober journey?</p>
170
+ * <button onClick={handleFinish} disabled={isPending}>
171
+ * {isPending ? 'Setting up...' : 'Get Started'}
172
+ * </button>
173
+ * </div>
174
+ * );
175
+ * }
176
+ * ```
177
+ *
178
+ * @example
179
+ * Multi-step onboarding with validation:
180
+ * ```tsx
181
+ * function OnboardingFlow() {
182
+ * const [currentStep, setCurrentStep] = useState(1);
183
+ * const { mutateAsync: completeOnboarding } = useCompleteOnboarding();
184
+ * const { data: user } = useCurrentUser();
185
+ *
186
+ * const handleComplete = async () => {
187
+ * // Validate all required fields are filled
188
+ * if (!user?.name || !user?.soberDate || !user?.city) {
189
+ * toast.error('Please complete all required fields');
190
+ * return;
191
+ * }
192
+ *
193
+ * try {
194
+ * await completeOnboarding();
195
+ * // User will be redirected via onSuccess callback
196
+ * } catch (error) {
197
+ * console.error('Onboarding error:', error);
198
+ * }
199
+ * };
200
+ *
201
+ * return (
202
+ * <div>
203
+ * {currentStep === 1 && <ProfileSetup onNext={() => setCurrentStep(2)} />}
204
+ * {currentStep === 2 && <InterestSelection onNext={() => setCurrentStep(3)} />}
205
+ * {currentStep === 3 && <SoberDateSetup onNext={handleComplete} />}
206
+ * </div>
207
+ * );
208
+ * }
209
+ * ```
210
+ *
211
+ * @example
212
+ * Check onboarding status:
213
+ * ```tsx
214
+ * function AppNavigator() {
215
+ * const { data: user, isLoading } = useCurrentUser();
216
+ *
217
+ * if (isLoading) return <SplashScreen />;
218
+ *
219
+ * // Redirect to onboarding if not completed
220
+ * if (user && !user.onboardingCompleted) {
221
+ * return <OnboardingFlow />;
222
+ * }
223
+ *
224
+ * return <MainApp />;
225
+ * }
226
+ * ```
227
+ *
228
+ * @example
229
+ * With analytics tracking:
230
+ * ```tsx
231
+ * const { mutate: completeOnboarding } = useCompleteOnboarding({
232
+ * onSuccess: (data) => {
233
+ * analytics.track('Onboarding Completed', {
234
+ * userId: data.id,
235
+ * completedAt: new Date().toISOString(),
236
+ * profileComplete: !!data.bio && !!data.avatar,
237
+ * });
238
+ * navigation.navigate('Home');
239
+ * },
240
+ * });
241
+ * ```
242
+ *
243
+ * @param options - TanStack Query mutation options
244
+ * @returns TanStack Query mutation result
245
+ */
246
+ export function useCompleteOnboarding(
247
+ options?: Omit<UseMutationOptions<UserResponse, Error, void>, 'mutationFn'>
248
+ ): UseMutationResult<UserResponse, Error, void> {
249
+ const queryClient = useQueryClient();
250
+
251
+ return useMutation({
252
+ mutationFn: async (): Promise<UserResponse> => {
253
+ const client = getApiClient();
254
+ const response = await client.post<UserResponse>('/api/v1/users/me/complete-onboarding');
255
+ return response.data;
256
+ },
257
+ onSuccess: (data, variables, context) => {
258
+ // Invalidate current user query to trigger refetch with updated onboarding status
259
+ queryClient.invalidateQueries({ queryKey: userKeys.me() });
260
+
261
+ // Also invalidate the detail query for this user
262
+ if (data.id) {
263
+ queryClient.invalidateQueries({ queryKey: userKeys.detail(data.id) });
264
+ }
265
+
266
+ // Call user's onSuccess if provided
267
+ // User's onSuccess is handled by spreading options
268
+ },
269
+ ...options,
270
+ });
271
+ }