@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,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Users Mutation Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query mutation hooks for user-related write operations.
|
|
6
|
+
* These hooks handle user profile updates and onboarding completion.
|
|
7
|
+
*
|
|
8
|
+
* @module api/mutations/users
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.useUpdateCurrentUser = useUpdateCurrentUser;
|
|
12
|
+
exports.useCompleteOnboarding = useCompleteOnboarding;
|
|
13
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
14
|
+
const client_1 = require("../client");
|
|
15
|
+
const users_1 = require("../queries/users");
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// MUTATION HOOKS
|
|
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
|
+
function useUpdateCurrentUser(options) {
|
|
109
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
110
|
+
return (0, react_query_1.useMutation)({
|
|
111
|
+
mutationFn: async (data) => {
|
|
112
|
+
const client = (0, client_1.getApiClient)();
|
|
113
|
+
const response = await client.put('/api/v1/users/me', data);
|
|
114
|
+
return response.data;
|
|
115
|
+
},
|
|
116
|
+
onSuccess: (data, variables, context) => {
|
|
117
|
+
// Invalidate current user query to trigger refetch with updated data
|
|
118
|
+
queryClient.invalidateQueries({ queryKey: users_1.userKeys.me() });
|
|
119
|
+
// Also invalidate the detail query for this user if they've viewed their own profile
|
|
120
|
+
if (data.id) {
|
|
121
|
+
queryClient.invalidateQueries({ queryKey: users_1.userKeys.detail(data.id) });
|
|
122
|
+
queryClient.invalidateQueries({ queryKey: users_1.userKeys.public(data.id) });
|
|
123
|
+
}
|
|
124
|
+
// Call user's onSuccess if provided
|
|
125
|
+
// User's onSuccess is handled by spreading options
|
|
126
|
+
},
|
|
127
|
+
...options,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Complete user onboarding
|
|
132
|
+
*
|
|
133
|
+
* @description
|
|
134
|
+
* Marks the user's onboarding process as complete.
|
|
135
|
+
* This is typically called after a user completes all required onboarding steps
|
|
136
|
+
* (e.g., setting up profile, selecting interests, setting sober date).
|
|
137
|
+
* Updates the user's onboardingCompleted flag and unlocks full app access.
|
|
138
|
+
*
|
|
139
|
+
* @endpoint POST /api/v1/users/me/complete-onboarding
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* import { useCompleteOnboarding } from '@growsober/sdk';
|
|
144
|
+
*
|
|
145
|
+
* function OnboardingFinalStep() {
|
|
146
|
+
* const { mutate: completeOnboarding, isPending } = useCompleteOnboarding({
|
|
147
|
+
* onSuccess: () => {
|
|
148
|
+
* toast.success('Welcome to GrowSober!');
|
|
149
|
+
* navigation.navigate('Home');
|
|
150
|
+
* },
|
|
151
|
+
* onError: (error) => {
|
|
152
|
+
* toast.error('Failed to complete onboarding: ' + error.message);
|
|
153
|
+
* },
|
|
154
|
+
* });
|
|
155
|
+
*
|
|
156
|
+
* const handleFinish = () => {
|
|
157
|
+
* completeOnboarding();
|
|
158
|
+
* };
|
|
159
|
+
*
|
|
160
|
+
* return (
|
|
161
|
+
* <div>
|
|
162
|
+
* <h1>You're All Set!</h1>
|
|
163
|
+
* <p>Ready to start your sober journey?</p>
|
|
164
|
+
* <button onClick={handleFinish} disabled={isPending}>
|
|
165
|
+
* {isPending ? 'Setting up...' : 'Get Started'}
|
|
166
|
+
* </button>
|
|
167
|
+
* </div>
|
|
168
|
+
* );
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* Multi-step onboarding with validation:
|
|
174
|
+
* ```tsx
|
|
175
|
+
* function OnboardingFlow() {
|
|
176
|
+
* const [currentStep, setCurrentStep] = useState(1);
|
|
177
|
+
* const { mutateAsync: completeOnboarding } = useCompleteOnboarding();
|
|
178
|
+
* const { data: user } = useCurrentUser();
|
|
179
|
+
*
|
|
180
|
+
* const handleComplete = async () => {
|
|
181
|
+
* // Validate all required fields are filled
|
|
182
|
+
* if (!user?.name || !user?.soberDate || !user?.city) {
|
|
183
|
+
* toast.error('Please complete all required fields');
|
|
184
|
+
* return;
|
|
185
|
+
* }
|
|
186
|
+
*
|
|
187
|
+
* try {
|
|
188
|
+
* await completeOnboarding();
|
|
189
|
+
* // User will be redirected via onSuccess callback
|
|
190
|
+
* } catch (error) {
|
|
191
|
+
* console.error('Onboarding error:', error);
|
|
192
|
+
* }
|
|
193
|
+
* };
|
|
194
|
+
*
|
|
195
|
+
* return (
|
|
196
|
+
* <div>
|
|
197
|
+
* {currentStep === 1 && <ProfileSetup onNext={() => setCurrentStep(2)} />}
|
|
198
|
+
* {currentStep === 2 && <InterestSelection onNext={() => setCurrentStep(3)} />}
|
|
199
|
+
* {currentStep === 3 && <SoberDateSetup onNext={handleComplete} />}
|
|
200
|
+
* </div>
|
|
201
|
+
* );
|
|
202
|
+
* }
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* Check onboarding status:
|
|
207
|
+
* ```tsx
|
|
208
|
+
* function AppNavigator() {
|
|
209
|
+
* const { data: user, isLoading } = useCurrentUser();
|
|
210
|
+
*
|
|
211
|
+
* if (isLoading) return <SplashScreen />;
|
|
212
|
+
*
|
|
213
|
+
* // Redirect to onboarding if not completed
|
|
214
|
+
* if (user && !user.onboardingCompleted) {
|
|
215
|
+
* return <OnboardingFlow />;
|
|
216
|
+
* }
|
|
217
|
+
*
|
|
218
|
+
* return <MainApp />;
|
|
219
|
+
* }
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* With analytics tracking:
|
|
224
|
+
* ```tsx
|
|
225
|
+
* const { mutate: completeOnboarding } = useCompleteOnboarding({
|
|
226
|
+
* onSuccess: (data) => {
|
|
227
|
+
* analytics.track('Onboarding Completed', {
|
|
228
|
+
* userId: data.id,
|
|
229
|
+
* completedAt: new Date().toISOString(),
|
|
230
|
+
* profileComplete: !!data.bio && !!data.avatar,
|
|
231
|
+
* });
|
|
232
|
+
* navigation.navigate('Home');
|
|
233
|
+
* },
|
|
234
|
+
* });
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* @param options - TanStack Query mutation options
|
|
238
|
+
* @returns TanStack Query mutation result
|
|
239
|
+
*/
|
|
240
|
+
function useCompleteOnboarding(options) {
|
|
241
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
242
|
+
return (0, react_query_1.useMutation)({
|
|
243
|
+
mutationFn: async () => {
|
|
244
|
+
const client = (0, client_1.getApiClient)();
|
|
245
|
+
const response = await client.post('/api/v1/users/me/complete-onboarding');
|
|
246
|
+
return response.data;
|
|
247
|
+
},
|
|
248
|
+
onSuccess: (data, variables, context) => {
|
|
249
|
+
// Invalidate current user query to trigger refetch with updated onboarding status
|
|
250
|
+
queryClient.invalidateQueries({ queryKey: users_1.userKeys.me() });
|
|
251
|
+
// Also invalidate the detail query for this user
|
|
252
|
+
if (data.id) {
|
|
253
|
+
queryClient.invalidateQueries({ queryKey: users_1.userKeys.detail(data.id) });
|
|
254
|
+
}
|
|
255
|
+
// Call user's onSuccess if provided
|
|
256
|
+
// User's onSuccess is handled by spreading options
|
|
257
|
+
},
|
|
258
|
+
...options,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL211dGF0aW9ucy91c2Vycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7R0FPRzs7QUFvR0gsb0RBMEJDO0FBZ0hELHNEQXlCQztBQXJRRCx1REFBMkc7QUFDM0csc0NBQXlDO0FBQ3pDLDRDQUE0QztBQUc1QywrRUFBK0U7QUFDL0UsaUJBQWlCO0FBQ2pCLCtFQUErRTtBQUUvRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdGRztBQUNILFNBQWdCLG9CQUFvQixDQUNsQyxPQUF3RjtJQUV4RixNQUFNLFdBQVcsR0FBRyxJQUFBLDRCQUFjLEdBQUUsQ0FBQztJQUVyQyxPQUFPLElBQUEseUJBQVcsRUFBQztRQUNqQixVQUFVLEVBQUUsS0FBSyxFQUFFLElBQXVCLEVBQXlCLEVBQUU7WUFDbkUsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUM7WUFDOUIsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsR0FBRyxDQUFlLGtCQUFrQixFQUFFLElBQUksQ0FBQyxDQUFDO1lBQzFFLE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQztRQUN2QixDQUFDO1FBQ0QsU0FBUyxFQUFFLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsRUFBRTtZQUN0QyxxRUFBcUU7WUFDckUsV0FBVyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsUUFBUSxFQUFFLGdCQUFRLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBRTNELHFGQUFxRjtZQUNyRixJQUFJLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDWixXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBRSxRQUFRLEVBQUUsZ0JBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDdEUsV0FBVyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsUUFBUSxFQUFFLGdCQUFRLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDeEUsQ0FBQztZQUVELG9DQUFvQztZQUNwQyxtREFBbUQ7UUFDckQsQ0FBQztRQUNELEdBQUcsT0FBTztLQUNYLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTZHRztBQUNILFNBQWdCLHFCQUFxQixDQUNuQyxPQUEyRTtJQUUzRSxNQUFNLFdBQVcsR0FBRyxJQUFBLDRCQUFjLEdBQUUsQ0FBQztJQUVyQyxPQUFPLElBQUEseUJBQVcsRUFBQztRQUNqQixVQUFVLEVBQUUsS0FBSyxJQUEyQixFQUFFO1lBQzVDLE1BQU0sTUFBTSxHQUFHLElBQUEscUJBQVksR0FBRSxDQUFDO1lBQzlCLE1BQU0sUUFBUSxHQUFHLE1BQU0sTUFBTSxDQUFDLElBQUksQ0FBZSxzQ0FBc0MsQ0FBQyxDQUFDO1lBQ3pGLE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQztRQUN2QixDQUFDO1FBQ0QsU0FBUyxFQUFFLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsRUFBRTtZQUN0QyxrRkFBa0Y7WUFDbEYsV0FBVyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsUUFBUSxFQUFFLGdCQUFRLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBRTNELGlEQUFpRDtZQUNqRCxJQUFJLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDWixXQUFXLENBQUMsaUJBQWlCLENBQUMsRUFBRSxRQUFRLEVBQUUsZ0JBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN4RSxDQUFDO1lBRUQsb0NBQW9DO1lBQ3BDLG1EQUFtRDtRQUNyRCxDQUFDO1FBQ0QsR0FBRyxPQUFPO0tBQ1gsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVXNlcnMgTXV0YXRpb24gSG9va3NcbiAqXG4gKiBUYW5TdGFjayBRdWVyeSBtdXRhdGlvbiBob29rcyBmb3IgdXNlci1yZWxhdGVkIHdyaXRlIG9wZXJhdGlvbnMuXG4gKiBUaGVzZSBob29rcyBoYW5kbGUgdXNlciBwcm9maWxlIHVwZGF0ZXMgYW5kIG9uYm9hcmRpbmcgY29tcGxldGlvbi5cbiAqXG4gKiBAbW9kdWxlIGFwaS9tdXRhdGlvbnMvdXNlcnNcbiAqL1xuXG5pbXBvcnQgeyB1c2VNdXRhdGlvbiwgVXNlTXV0YXRpb25PcHRpb25zLCBVc2VNdXRhdGlvblJlc3VsdCwgdXNlUXVlcnlDbGllbnQgfSBmcm9tICdAdGFuc3RhY2svcmVhY3QtcXVlcnknO1xuaW1wb3J0IHsgZ2V0QXBpQ2xpZW50IH0gZnJvbSAnLi4vY2xpZW50JztcbmltcG9ydCB7IHVzZXJLZXlzIH0gZnJvbSAnLi4vcXVlcmllcy91c2Vycyc7XG5pbXBvcnQgdHlwZSB7IFVwZGF0ZVVzZXJSZXF1ZXN0LCBVc2VyUmVzcG9uc2UgfSBmcm9tICcuLi90eXBlcyc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIE1VVEFUSU9OIEhPT0tTXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbi8qKlxuICogVXBkYXRlIGN1cnJlbnQgdXNlcidzIHByb2ZpbGVcbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqIFVwZGF0ZXMgdGhlIGF1dGhlbnRpY2F0ZWQgdXNlcidzIHByb2ZpbGUgaW5mb3JtYXRpb24uXG4gKiBUaGlzIGNhbiBpbmNsdWRlIG5hbWUsIGJpbywgYXZhdGFyLCBsb2NhdGlvbiwgc29iZXIgZGF0ZSwgYW5kIG90aGVyIHByb2ZpbGUgZmllbGRzLlxuICogQXV0b21hdGljYWxseSBpbnZhbGlkYXRlcyByZWxldmFudCB1c2VyIHF1ZXJpZXMgdG8gcmVmcmVzaCB0aGUgVUkuXG4gKlxuICogQGVuZHBvaW50IFBVVCAvYXBpL3YxL3VzZXJzL21lXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzeFxuICogaW1wb3J0IHsgdXNlVXBkYXRlQ3VycmVudFVzZXIgfSBmcm9tICdAZ3Jvd3NvYmVyL3Nkayc7XG4gKlxuICogZnVuY3Rpb24gRWRpdFByb2ZpbGVGb3JtKCkge1xuICogICBjb25zdCB7IG11dGF0ZTogdXBkYXRlUHJvZmlsZSwgaXNQZW5kaW5nLCBlcnJvciB9ID0gdXNlVXBkYXRlQ3VycmVudFVzZXIoe1xuICogICAgIG9uU3VjY2VzczogKGRhdGEpID0+IHtcbiAqICAgICAgIHRvYXN0LnN1Y2Nlc3MoJ1Byb2ZpbGUgdXBkYXRlZCBzdWNjZXNzZnVsbHknKTtcbiAqICAgICAgIG5hdmlnYXRpb24uZ29CYWNrKCk7XG4gKiAgICAgfSxcbiAqICAgICBvbkVycm9yOiAoZXJyb3IpID0+IHtcbiAqICAgICAgIHRvYXN0LmVycm9yKCdGYWlsZWQgdG8gdXBkYXRlIHByb2ZpbGU6ICcgKyBlcnJvci5tZXNzYWdlKTtcbiAqICAgICB9LFxuICogICB9KTtcbiAqXG4gKiAgIGNvbnN0IGhhbmRsZVN1Ym1pdCA9IChmb3JtRGF0YTogRm9ybURhdGEpID0+IHtcbiAqICAgICB1cGRhdGVQcm9maWxlKHtcbiAqICAgICAgIG5hbWU6IGZvcm1EYXRhLm5hbWUsXG4gKiAgICAgICBiaW86IGZvcm1EYXRhLmJpbyxcbiAqICAgICAgIGF2YXRhcjogZm9ybURhdGEuYXZhdGFyLFxuICogICAgICAgY2l0eTogZm9ybURhdGEuY2l0eSxcbiAqICAgICAgIHNvYmVyRGF0ZTogZm9ybURhdGEuc29iZXJEYXRlLFxuICogICAgIH0pO1xuICogICB9O1xuICpcbiAqICAgcmV0dXJuIChcbiAqICAgICA8Zm9ybSBvblN1Ym1pdD17aGFuZGxlU3VibWl0fT5cbiAqICAgICAgIDxpbnB1dCBuYW1lPVwibmFtZVwiIC8+XG4gKiAgICAgICA8dGV4dGFyZWEgbmFtZT1cImJpb1wiIC8+XG4gKiAgICAgICA8YnV0dG9uIHR5cGU9XCJzdWJtaXRcIiBkaXNhYmxlZD17aXNQZW5kaW5nfT5cbiAqICAgICAgICAge2lzUGVuZGluZyA/ICdTYXZpbmcuLi4nIDogJ1NhdmUgUHJvZmlsZSd9XG4gKiAgICAgICA8L2J1dHRvbj5cbiAqICAgICAgIHtlcnJvciAmJiA8cCBjbGFzc05hbWU9XCJlcnJvclwiPntlcnJvci5tZXNzYWdlfTwvcD59XG4gKiAgICAgPC9mb3JtPlxuICogICApO1xuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGVcbiAqIFVwZGF0ZSBzcGVjaWZpYyBmaWVsZHM6XG4gKiBgYGB0c3hcbiAqIGZ1bmN0aW9uIEF2YXRhclVwbG9hZGVyKCkge1xuICogICBjb25zdCB7IG11dGF0ZUFzeW5jOiB1cGRhdGVQcm9maWxlIH0gPSB1c2VVcGRhdGVDdXJyZW50VXNlcigpO1xuICpcbiAqICAgY29uc3QgaGFuZGxlQXZhdGFyQ2hhbmdlID0gYXN5bmMgKGZpbGU6IEZpbGUpID0+IHtcbiAqICAgICBjb25zdCB1cGxvYWRlZFVybCA9IGF3YWl0IHVwbG9hZEltYWdlKGZpbGUpO1xuICogICAgIGF3YWl0IHVwZGF0ZVByb2ZpbGUoeyBhdmF0YXI6IHVwbG9hZGVkVXJsIH0pO1xuICogICAgIHRvYXN0LnN1Y2Nlc3MoJ0F2YXRhciB1cGRhdGVkIScpO1xuICogICB9O1xuICpcbiAqICAgcmV0dXJuIDxJbWFnZVBpY2tlciBvblNlbGVjdD17aGFuZGxlQXZhdGFyQ2hhbmdlfSAvPjtcbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBleGFtcGxlXG4gKiBVcGRhdGUgc29iZXIgZGF0ZTpcbiAqIGBgYHRzeFxuICogZnVuY3Rpb24gU29iZXJEYXRlUGlja2VyKCkge1xuICogICBjb25zdCB7IG11dGF0ZTogdXBkYXRlUHJvZmlsZSB9ID0gdXNlVXBkYXRlQ3VycmVudFVzZXIoKTtcbiAqICAgY29uc3QgeyBkYXRhOiB1c2VyIH0gPSB1c2VDdXJyZW50VXNlcigpO1xuICpcbiAqICAgY29uc3QgaGFuZGxlRGF0ZUNoYW5nZSA9IChkYXRlOiBEYXRlKSA9PiB7XG4gKiAgICAgdXBkYXRlUHJvZmlsZSh7XG4gKiAgICAgICBzb2JlckRhdGU6IGRhdGUudG9JU09TdHJpbmcoKSxcbiAqICAgICB9KTtcbiAqICAgfTtcbiAqXG4gKiAgIHJldHVybiAoXG4gKiAgICAgPERhdGVQaWNrZXJcbiAqICAgICAgIHZhbHVlPXt1c2VyPy5zb2JlckRhdGV9XG4gKiAgICAgICBvbkNoYW5nZT17aGFuZGxlRGF0ZUNoYW5nZX1cbiAqICAgICAvPlxuICogICApO1xuICogfVxuICogYGBgXG4gKlxuICogQHBhcmFtIG9wdGlvbnMgLSBUYW5TdGFjayBRdWVyeSBtdXRhdGlvbiBvcHRpb25zXG4gKiBAcmV0dXJucyBUYW5TdGFjayBRdWVyeSBtdXRhdGlvbiByZXN1bHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVVwZGF0ZUN1cnJlbnRVc2VyKFxuICBvcHRpb25zPzogT21pdDxVc2VNdXRhdGlvbk9wdGlvbnM8VXNlclJlc3BvbnNlLCBFcnJvciwgVXBkYXRlVXNlclJlcXVlc3Q+LCAnbXV0YXRpb25Gbic+XG4pOiBVc2VNdXRhdGlvblJlc3VsdDxVc2VyUmVzcG9uc2UsIEVycm9yLCBVcGRhdGVVc2VyUmVxdWVzdD4ge1xuICBjb25zdCBxdWVyeUNsaWVudCA9IHVzZVF1ZXJ5Q2xpZW50KCk7XG5cbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcbiAgICBtdXRhdGlvbkZuOiBhc3luYyAoZGF0YTogVXBkYXRlVXNlclJlcXVlc3QpOiBQcm9taXNlPFVzZXJSZXNwb25zZT4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KCk7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5wdXQ8VXNlclJlc3BvbnNlPignL2FwaS92MS91c2Vycy9tZScsIGRhdGEpO1xuICAgICAgcmV0dXJuIHJlc3BvbnNlLmRhdGE7XG4gICAgfSxcbiAgICBvblN1Y2Nlc3M6IChkYXRhLCB2YXJpYWJsZXMsIGNvbnRleHQpID0+IHtcbiAgICAgIC8vIEludmFsaWRhdGUgY3VycmVudCB1c2VyIHF1ZXJ5IHRvIHRyaWdnZXIgcmVmZXRjaCB3aXRoIHVwZGF0ZWQgZGF0YVxuICAgICAgcXVlcnlDbGllbnQuaW52YWxpZGF0ZVF1ZXJpZXMoeyBxdWVyeUtleTogdXNlcktleXMubWUoKSB9KTtcblxuICAgICAgLy8gQWxzbyBpbnZhbGlkYXRlIHRoZSBkZXRhaWwgcXVlcnkgZm9yIHRoaXMgdXNlciBpZiB0aGV5J3ZlIHZpZXdlZCB0aGVpciBvd24gcHJvZmlsZVxuICAgICAgaWYgKGRhdGEuaWQpIHtcbiAgICAgICAgcXVlcnlDbGllbnQuaW52YWxpZGF0ZVF1ZXJpZXMoeyBxdWVyeUtleTogdXNlcktleXMuZGV0YWlsKGRhdGEuaWQpIH0pO1xuICAgICAgICBxdWVyeUNsaWVudC5pbnZhbGlkYXRlUXVlcmllcyh7IHF1ZXJ5S2V5OiB1c2VyS2V5cy5wdWJsaWMoZGF0YS5pZCkgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIENhbGwgdXNlcidzIG9uU3VjY2VzcyBpZiBwcm92aWRlZFxuICAgICAgLy8gVXNlcidzIG9uU3VjY2VzcyBpcyBoYW5kbGVkIGJ5IHNwcmVhZGluZyBvcHRpb25zXG4gICAgfSxcbiAgICAuLi5vcHRpb25zLFxuICB9KTtcbn1cblxuLyoqXG4gKiBDb21wbGV0ZSB1c2VyIG9uYm9hcmRpbmdcbiAqXG4gKiBAZGVzY3JpcHRpb25cbiAqIE1hcmtzIHRoZSB1c2VyJ3Mgb25ib2FyZGluZyBwcm9jZXNzIGFzIGNvbXBsZXRlLlxuICogVGhpcyBpcyB0eXBpY2FsbHkgY2FsbGVkIGFmdGVyIGEgdXNlciBjb21wbGV0ZXMgYWxsIHJlcXVpcmVkIG9uYm9hcmRpbmcgc3RlcHNcbiAqIChlLmcuLCBzZXR0aW5nIHVwIHByb2ZpbGUsIHNlbGVjdGluZyBpbnRlcmVzdHMsIHNldHRpbmcgc29iZXIgZGF0ZSkuXG4gKiBVcGRhdGVzIHRoZSB1c2VyJ3Mgb25ib2FyZGluZ0NvbXBsZXRlZCBmbGFnIGFuZCB1bmxvY2tzIGZ1bGwgYXBwIGFjY2Vzcy5cbiAqXG4gKiBAZW5kcG9pbnQgUE9TVCAvYXBpL3YxL3VzZXJzL21lL2NvbXBsZXRlLW9uYm9hcmRpbmdcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHN4XG4gKiBpbXBvcnQgeyB1c2VDb21wbGV0ZU9uYm9hcmRpbmcgfSBmcm9tICdAZ3Jvd3NvYmVyL3Nkayc7XG4gKlxuICogZnVuY3Rpb24gT25ib2FyZGluZ0ZpbmFsU3RlcCgpIHtcbiAqICAgY29uc3QgeyBtdXRhdGU6IGNvbXBsZXRlT25ib2FyZGluZywgaXNQZW5kaW5nIH0gPSB1c2VDb21wbGV0ZU9uYm9hcmRpbmcoe1xuICogICAgIG9uU3VjY2VzczogKCkgPT4ge1xuICogICAgICAgdG9hc3Quc3VjY2VzcygnV2VsY29tZSB0byBHcm93U29iZXIhJyk7XG4gKiAgICAgICBuYXZpZ2F0aW9uLm5hdmlnYXRlKCdIb21lJyk7XG4gKiAgICAgfSxcbiAqICAgICBvbkVycm9yOiAoZXJyb3IpID0+IHtcbiAqICAgICAgIHRvYXN0LmVycm9yKCdGYWlsZWQgdG8gY29tcGxldGUgb25ib2FyZGluZzogJyArIGVycm9yLm1lc3NhZ2UpO1xuICogICAgIH0sXG4gKiAgIH0pO1xuICpcbiAqICAgY29uc3QgaGFuZGxlRmluaXNoID0gKCkgPT4ge1xuICogICAgIGNvbXBsZXRlT25ib2FyZGluZygpO1xuICogICB9O1xuICpcbiAqICAgcmV0dXJuIChcbiAqICAgICA8ZGl2PlxuICogICAgICAgPGgxPllvdSdyZSBBbGwgU2V0ITwvaDE+XG4gKiAgICAgICA8cD5SZWFkeSB0byBzdGFydCB5b3VyIHNvYmVyIGpvdXJuZXk/PC9wPlxuICogICAgICAgPGJ1dHRvbiBvbkNsaWNrPXtoYW5kbGVGaW5pc2h9IGRpc2FibGVkPXtpc1BlbmRpbmd9PlxuICogICAgICAgICB7aXNQZW5kaW5nID8gJ1NldHRpbmcgdXAuLi4nIDogJ0dldCBTdGFydGVkJ31cbiAqICAgICAgIDwvYnV0dG9uPlxuICogICAgIDwvZGl2PlxuICogICApO1xuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGVcbiAqIE11bHRpLXN0ZXAgb25ib2FyZGluZyB3aXRoIHZhbGlkYXRpb246XG4gKiBgYGB0c3hcbiAqIGZ1bmN0aW9uIE9uYm9hcmRpbmdGbG93KCkge1xuICogICBjb25zdCBbY3VycmVudFN0ZXAsIHNldEN1cnJlbnRTdGVwXSA9IHVzZVN0YXRlKDEpO1xuICogICBjb25zdCB7IG11dGF0ZUFzeW5jOiBjb21wbGV0ZU9uYm9hcmRpbmcgfSA9IHVzZUNvbXBsZXRlT25ib2FyZGluZygpO1xuICogICBjb25zdCB7IGRhdGE6IHVzZXIgfSA9IHVzZUN1cnJlbnRVc2VyKCk7XG4gKlxuICogICBjb25zdCBoYW5kbGVDb21wbGV0ZSA9IGFzeW5jICgpID0+IHtcbiAqICAgICAvLyBWYWxpZGF0ZSBhbGwgcmVxdWlyZWQgZmllbGRzIGFyZSBmaWxsZWRcbiAqICAgICBpZiAoIXVzZXI/Lm5hbWUgfHwgIXVzZXI/LnNvYmVyRGF0ZSB8fCAhdXNlcj8uY2l0eSkge1xuICogICAgICAgdG9hc3QuZXJyb3IoJ1BsZWFzZSBjb21wbGV0ZSBhbGwgcmVxdWlyZWQgZmllbGRzJyk7XG4gKiAgICAgICByZXR1cm47XG4gKiAgICAgfVxuICpcbiAqICAgICB0cnkge1xuICogICAgICAgYXdhaXQgY29tcGxldGVPbmJvYXJkaW5nKCk7XG4gKiAgICAgICAvLyBVc2VyIHdpbGwgYmUgcmVkaXJlY3RlZCB2aWEgb25TdWNjZXNzIGNhbGxiYWNrXG4gKiAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAqICAgICAgIGNvbnNvbGUuZXJyb3IoJ09uYm9hcmRpbmcgZXJyb3I6JywgZXJyb3IpO1xuICogICAgIH1cbiAqICAgfTtcbiAqXG4gKiAgIHJldHVybiAoXG4gKiAgICAgPGRpdj5cbiAqICAgICAgIHtjdXJyZW50U3RlcCA9PT0gMSAmJiA8UHJvZmlsZVNldHVwIG9uTmV4dD17KCkgPT4gc2V0Q3VycmVudFN0ZXAoMil9IC8+fVxuICogICAgICAge2N1cnJlbnRTdGVwID09PSAyICYmIDxJbnRlcmVzdFNlbGVjdGlvbiBvbk5leHQ9eygpID0+IHNldEN1cnJlbnRTdGVwKDMpfSAvPn1cbiAqICAgICAgIHtjdXJyZW50U3RlcCA9PT0gMyAmJiA8U29iZXJEYXRlU2V0dXAgb25OZXh0PXtoYW5kbGVDb21wbGV0ZX0gLz59XG4gKiAgICAgPC9kaXY+XG4gKiAgICk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAZXhhbXBsZVxuICogQ2hlY2sgb25ib2FyZGluZyBzdGF0dXM6XG4gKiBgYGB0c3hcbiAqIGZ1bmN0aW9uIEFwcE5hdmlnYXRvcigpIHtcbiAqICAgY29uc3QgeyBkYXRhOiB1c2VyLCBpc0xvYWRpbmcgfSA9IHVzZUN1cnJlbnRVc2VyKCk7XG4gKlxuICogICBpZiAoaXNMb2FkaW5nKSByZXR1cm4gPFNwbGFzaFNjcmVlbiAvPjtcbiAqXG4gKiAgIC8vIFJlZGlyZWN0IHRvIG9uYm9hcmRpbmcgaWYgbm90IGNvbXBsZXRlZFxuICogICBpZiAodXNlciAmJiAhdXNlci5vbmJvYXJkaW5nQ29tcGxldGVkKSB7XG4gKiAgICAgcmV0dXJuIDxPbmJvYXJkaW5nRmxvdyAvPjtcbiAqICAgfVxuICpcbiAqICAgcmV0dXJuIDxNYWluQXBwIC8+O1xuICogfVxuICogYGBgXG4gKlxuICogQGV4YW1wbGVcbiAqIFdpdGggYW5hbHl0aWNzIHRyYWNraW5nOlxuICogYGBgdHN4XG4gKiBjb25zdCB7IG11dGF0ZTogY29tcGxldGVPbmJvYXJkaW5nIH0gPSB1c2VDb21wbGV0ZU9uYm9hcmRpbmcoe1xuICogICBvblN1Y2Nlc3M6IChkYXRhKSA9PiB7XG4gKiAgICAgYW5hbHl0aWNzLnRyYWNrKCdPbmJvYXJkaW5nIENvbXBsZXRlZCcsIHtcbiAqICAgICAgIHVzZXJJZDogZGF0YS5pZCxcbiAqICAgICAgIGNvbXBsZXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gKiAgICAgICBwcm9maWxlQ29tcGxldGU6ICEhZGF0YS5iaW8gJiYgISFkYXRhLmF2YXRhcixcbiAqICAgICB9KTtcbiAqICAgICBuYXZpZ2F0aW9uLm5hdmlnYXRlKCdIb21lJyk7XG4gKiAgIH0sXG4gKiB9KTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGFuU3RhY2sgUXVlcnkgbXV0YXRpb24gb3B0aW9uc1xuICogQHJldHVybnMgVGFuU3RhY2sgUXVlcnkgbXV0YXRpb24gcmVzdWx0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VDb21wbGV0ZU9uYm9hcmRpbmcoXG4gIG9wdGlvbnM/OiBPbWl0PFVzZU11dGF0aW9uT3B0aW9uczxVc2VyUmVzcG9uc2UsIEVycm9yLCB2b2lkPiwgJ211dGF0aW9uRm4nPlxuKTogVXNlTXV0YXRpb25SZXN1bHQ8VXNlclJlc3BvbnNlLCBFcnJvciwgdm9pZD4ge1xuICBjb25zdCBxdWVyeUNsaWVudCA9IHVzZVF1ZXJ5Q2xpZW50KCk7XG5cbiAgcmV0dXJuIHVzZU11dGF0aW9uKHtcbiAgICBtdXRhdGlvbkZuOiBhc3luYyAoKTogUHJvbWlzZTxVc2VyUmVzcG9uc2U+ID0+IHtcbiAgICAgIGNvbnN0IGNsaWVudCA9IGdldEFwaUNsaWVudCgpO1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQucG9zdDxVc2VyUmVzcG9uc2U+KCcvYXBpL3YxL3VzZXJzL21lL2NvbXBsZXRlLW9uYm9hcmRpbmcnKTtcbiAgICAgIHJldHVybiByZXNwb25zZS5kYXRhO1xuICAgIH0sXG4gICAgb25TdWNjZXNzOiAoZGF0YSwgdmFyaWFibGVzLCBjb250ZXh0KSA9PiB7XG4gICAgICAvLyBJbnZhbGlkYXRlIGN1cnJlbnQgdXNlciBxdWVyeSB0byB0cmlnZ2VyIHJlZmV0Y2ggd2l0aCB1cGRhdGVkIG9uYm9hcmRpbmcgc3RhdHVzXG4gICAgICBxdWVyeUNsaWVudC5pbnZhbGlkYXRlUXVlcmllcyh7IHF1ZXJ5S2V5OiB1c2VyS2V5cy5tZSgpIH0pO1xuXG4gICAgICAvLyBBbHNvIGludmFsaWRhdGUgdGhlIGRldGFpbCBxdWVyeSBmb3IgdGhpcyB1c2VyXG4gICAgICBpZiAoZGF0YS5pZCkge1xuICAgICAgICBxdWVyeUNsaWVudC5pbnZhbGlkYXRlUXVlcmllcyh7IHF1ZXJ5S2V5OiB1c2VyS2V5cy5kZXRhaWwoZGF0YS5pZCkgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIENhbGwgdXNlcidzIG9uU3VjY2VzcyBpZiBwcm92aWRlZFxuICAgICAgLy8gVXNlcidzIG9uU3VjY2VzcyBpcyBoYW5kbGVkIGJ5IHNwcmVhZGluZyBvcHRpb25zXG4gICAgfSxcbiAgICAuLi5vcHRpb25zLFxuICB9KTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for admin-related read operations.
|
|
5
|
+
* These hooks handle fetching data for the admin dashboard including
|
|
6
|
+
* users, hubs, events, businesses, content, and analytics.
|
|
7
|
+
*
|
|
8
|
+
* @module api/queries/admin
|
|
9
|
+
*/
|
|
10
|
+
import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
11
|
+
import type { UserResponse, HubResponse, EventResponse, BusinessResponse, LibraryContentResponse } from '../types';
|
|
12
|
+
export interface AdminDashboardStats {
|
|
13
|
+
totalUsers: number;
|
|
14
|
+
activeUsers: number;
|
|
15
|
+
newUsersToday: number;
|
|
16
|
+
newUsersThisWeek: number;
|
|
17
|
+
totalHubs: number;
|
|
18
|
+
totalEvents: number;
|
|
19
|
+
upcomingEvents: number;
|
|
20
|
+
totalBusinesses: number;
|
|
21
|
+
verifiedBusinesses: number;
|
|
22
|
+
premiumUsers: number;
|
|
23
|
+
revenue: {
|
|
24
|
+
today: number;
|
|
25
|
+
thisWeek: number;
|
|
26
|
+
thisMonth: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface AdminAnalyticsData {
|
|
30
|
+
period: string;
|
|
31
|
+
userGrowth: Array<{
|
|
32
|
+
date: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}>;
|
|
35
|
+
eventAttendance: Array<{
|
|
36
|
+
date: string;
|
|
37
|
+
count: number;
|
|
38
|
+
}>;
|
|
39
|
+
hubActivity: Array<{
|
|
40
|
+
date: string;
|
|
41
|
+
count: number;
|
|
42
|
+
}>;
|
|
43
|
+
revenue: Array<{
|
|
44
|
+
date: string;
|
|
45
|
+
amount: number;
|
|
46
|
+
}>;
|
|
47
|
+
topHubs: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
memberCount: number;
|
|
51
|
+
}>;
|
|
52
|
+
topEvents: Array<{
|
|
53
|
+
id: string;
|
|
54
|
+
title: string;
|
|
55
|
+
attendeeCount: number;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
export interface PaginatedResponse<T> {
|
|
59
|
+
data: T[];
|
|
60
|
+
meta: {
|
|
61
|
+
total: number;
|
|
62
|
+
page: number;
|
|
63
|
+
limit: number;
|
|
64
|
+
totalPages: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export interface AdminUserFilters {
|
|
68
|
+
page?: number;
|
|
69
|
+
limit?: number;
|
|
70
|
+
search?: string;
|
|
71
|
+
status?: 'active' | 'suspended' | 'deleted';
|
|
72
|
+
isPremium?: boolean;
|
|
73
|
+
sortBy?: string;
|
|
74
|
+
sortOrder?: 'asc' | 'desc';
|
|
75
|
+
}
|
|
76
|
+
export interface AdminHubFilters {
|
|
77
|
+
page?: number;
|
|
78
|
+
limit?: number;
|
|
79
|
+
search?: string;
|
|
80
|
+
status?: 'active' | 'inactive';
|
|
81
|
+
sortBy?: string;
|
|
82
|
+
sortOrder?: 'asc' | 'desc';
|
|
83
|
+
}
|
|
84
|
+
export interface AdminEventFilters {
|
|
85
|
+
page?: number;
|
|
86
|
+
limit?: number;
|
|
87
|
+
search?: string;
|
|
88
|
+
status?: 'draft' | 'published' | 'cancelled';
|
|
89
|
+
hubId?: string;
|
|
90
|
+
upcoming?: boolean;
|
|
91
|
+
sortBy?: string;
|
|
92
|
+
sortOrder?: 'asc' | 'desc';
|
|
93
|
+
}
|
|
94
|
+
export interface AdminBusinessFilters {
|
|
95
|
+
page?: number;
|
|
96
|
+
limit?: number;
|
|
97
|
+
search?: string;
|
|
98
|
+
isVerified?: boolean;
|
|
99
|
+
type?: string;
|
|
100
|
+
sortBy?: string;
|
|
101
|
+
sortOrder?: 'asc' | 'desc';
|
|
102
|
+
}
|
|
103
|
+
export interface AdminContentFilters {
|
|
104
|
+
page?: number;
|
|
105
|
+
limit?: number;
|
|
106
|
+
search?: string;
|
|
107
|
+
type?: string;
|
|
108
|
+
category?: string;
|
|
109
|
+
isFeatured?: boolean;
|
|
110
|
+
sortBy?: string;
|
|
111
|
+
sortOrder?: 'asc' | 'desc';
|
|
112
|
+
}
|
|
113
|
+
export declare const adminKeys: {
|
|
114
|
+
all: readonly ["admin"];
|
|
115
|
+
dashboard: () => readonly ["admin", "dashboard"];
|
|
116
|
+
analytics: (period?: string) => readonly ["admin", "analytics", string | undefined];
|
|
117
|
+
users: {
|
|
118
|
+
all: () => readonly ["admin", "users"];
|
|
119
|
+
list: (filters?: AdminUserFilters) => readonly ["admin", "users", "list", AdminUserFilters | undefined];
|
|
120
|
+
detail: (id: string) => readonly ["admin", "users", "detail", string];
|
|
121
|
+
};
|
|
122
|
+
hubs: {
|
|
123
|
+
all: () => readonly ["admin", "hubs"];
|
|
124
|
+
list: (filters?: AdminHubFilters) => readonly ["admin", "hubs", "list", AdminHubFilters | undefined];
|
|
125
|
+
detail: (id: string) => readonly ["admin", "hubs", "detail", string];
|
|
126
|
+
};
|
|
127
|
+
events: {
|
|
128
|
+
all: () => readonly ["admin", "events"];
|
|
129
|
+
list: (filters?: AdminEventFilters) => readonly ["admin", "events", "list", AdminEventFilters | undefined];
|
|
130
|
+
detail: (id: string) => readonly ["admin", "events", "detail", string];
|
|
131
|
+
};
|
|
132
|
+
businesses: {
|
|
133
|
+
all: () => readonly ["admin", "businesses"];
|
|
134
|
+
list: (filters?: AdminBusinessFilters) => readonly ["admin", "businesses", "list", AdminBusinessFilters | undefined];
|
|
135
|
+
detail: (id: string) => readonly ["admin", "businesses", "detail", string];
|
|
136
|
+
};
|
|
137
|
+
content: {
|
|
138
|
+
all: () => readonly ["admin", "content"];
|
|
139
|
+
list: (filters?: AdminContentFilters) => readonly ["admin", "content", "list", AdminContentFilters | undefined];
|
|
140
|
+
detail: (id: string) => readonly ["admin", "content", "detail", string];
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Get admin dashboard statistics
|
|
145
|
+
*
|
|
146
|
+
* @description
|
|
147
|
+
* Retrieves key metrics for the admin dashboard including
|
|
148
|
+
* user counts, hub stats, event stats, and revenue data.
|
|
149
|
+
*
|
|
150
|
+
* @endpoint GET /api/v1/admin/dashboard
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```tsx
|
|
154
|
+
* import { useAdminDashboard } from '@growsober/sdk';
|
|
155
|
+
*
|
|
156
|
+
* function AdminDashboard() {
|
|
157
|
+
* const { data: stats, isLoading } = useAdminDashboard();
|
|
158
|
+
*
|
|
159
|
+
* if (isLoading) return <Spinner />;
|
|
160
|
+
*
|
|
161
|
+
* return (
|
|
162
|
+
* <div>
|
|
163
|
+
* <StatCard label="Total Users" value={stats.totalUsers} />
|
|
164
|
+
* <StatCard label="Active Users" value={stats.activeUsers} />
|
|
165
|
+
* <StatCard label="Premium Users" value={stats.premiumUsers} />
|
|
166
|
+
* <StatCard label="Revenue This Month" value={stats.revenue.thisMonth} />
|
|
167
|
+
* </div>
|
|
168
|
+
* );
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export declare function useAdminDashboard(options?: Omit<UseQueryOptions<AdminDashboardStats>, 'queryKey' | 'queryFn'>): UseQueryResult<AdminDashboardStats>;
|
|
173
|
+
/**
|
|
174
|
+
* Get admin analytics data
|
|
175
|
+
*
|
|
176
|
+
* @description
|
|
177
|
+
* Retrieves detailed analytics data for charts and reports.
|
|
178
|
+
*
|
|
179
|
+
* @endpoint GET /api/v1/admin/analytics
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```tsx
|
|
183
|
+
* import { useAdminAnalytics } from '@growsober/sdk';
|
|
184
|
+
*
|
|
185
|
+
* function AnalyticsPage() {
|
|
186
|
+
* const { data: analytics } = useAdminAnalytics('30d');
|
|
187
|
+
*
|
|
188
|
+
* return (
|
|
189
|
+
* <div>
|
|
190
|
+
* <LineChart data={analytics?.userGrowth} />
|
|
191
|
+
* <BarChart data={analytics?.eventAttendance} />
|
|
192
|
+
* </div>
|
|
193
|
+
* );
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export declare function useAdminAnalytics(period?: string, options?: Omit<UseQueryOptions<AdminAnalyticsData>, 'queryKey' | 'queryFn'>): UseQueryResult<AdminAnalyticsData>;
|
|
198
|
+
/**
|
|
199
|
+
* List users for admin
|
|
200
|
+
*
|
|
201
|
+
* @endpoint GET /api/v1/admin/users
|
|
202
|
+
*/
|
|
203
|
+
export declare function useAdminUsers(filters?: AdminUserFilters, options?: Omit<UseQueryOptions<PaginatedResponse<UserResponse>>, 'queryKey' | 'queryFn'>): UseQueryResult<PaginatedResponse<UserResponse>>;
|
|
204
|
+
/**
|
|
205
|
+
* Get user detail for admin
|
|
206
|
+
*
|
|
207
|
+
* @endpoint GET /api/v1/admin/users/{id}
|
|
208
|
+
*/
|
|
209
|
+
export declare function useAdminUser(id: string, options?: Omit<UseQueryOptions<UserResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<UserResponse>;
|
|
210
|
+
/**
|
|
211
|
+
* List hubs for admin
|
|
212
|
+
*
|
|
213
|
+
* @endpoint GET /api/v1/admin/hubs
|
|
214
|
+
*/
|
|
215
|
+
export declare function useAdminHubs(filters?: AdminHubFilters, options?: Omit<UseQueryOptions<PaginatedResponse<HubResponse>>, 'queryKey' | 'queryFn'>): UseQueryResult<PaginatedResponse<HubResponse>>;
|
|
216
|
+
/**
|
|
217
|
+
* Get hub detail for admin
|
|
218
|
+
*
|
|
219
|
+
* @endpoint GET /api/v1/admin/hubs/{id}
|
|
220
|
+
*/
|
|
221
|
+
export declare function useAdminHub(id: string, options?: Omit<UseQueryOptions<HubResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<HubResponse>;
|
|
222
|
+
/**
|
|
223
|
+
* List events for admin
|
|
224
|
+
*
|
|
225
|
+
* @endpoint GET /api/v1/admin/events
|
|
226
|
+
*/
|
|
227
|
+
export declare function useAdminEvents(filters?: AdminEventFilters, options?: Omit<UseQueryOptions<PaginatedResponse<EventResponse>>, 'queryKey' | 'queryFn'>): UseQueryResult<PaginatedResponse<EventResponse>>;
|
|
228
|
+
/**
|
|
229
|
+
* Get event detail for admin
|
|
230
|
+
*
|
|
231
|
+
* @endpoint GET /api/v1/admin/events/{id}
|
|
232
|
+
*/
|
|
233
|
+
export declare function useAdminEvent(id: string, options?: Omit<UseQueryOptions<EventResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<EventResponse>;
|
|
234
|
+
/**
|
|
235
|
+
* List businesses for admin
|
|
236
|
+
*
|
|
237
|
+
* @endpoint GET /api/v1/admin/businesses
|
|
238
|
+
*/
|
|
239
|
+
export declare function useAdminBusinesses(filters?: AdminBusinessFilters, options?: Omit<UseQueryOptions<PaginatedResponse<BusinessResponse>>, 'queryKey' | 'queryFn'>): UseQueryResult<PaginatedResponse<BusinessResponse>>;
|
|
240
|
+
/**
|
|
241
|
+
* Get business detail for admin
|
|
242
|
+
*
|
|
243
|
+
* @endpoint GET /api/v1/admin/businesses/{id}
|
|
244
|
+
*/
|
|
245
|
+
export declare function useAdminBusiness(id: string, options?: Omit<UseQueryOptions<BusinessResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<BusinessResponse>;
|
|
246
|
+
/**
|
|
247
|
+
* List content for admin
|
|
248
|
+
*
|
|
249
|
+
* @endpoint GET /api/v1/admin/content
|
|
250
|
+
*/
|
|
251
|
+
export declare function useAdminContent(filters?: AdminContentFilters, options?: Omit<UseQueryOptions<PaginatedResponse<LibraryContentResponse>>, 'queryKey' | 'queryFn'>): UseQueryResult<PaginatedResponse<LibraryContentResponse>>;
|
|
252
|
+
/**
|
|
253
|
+
* Get content detail for admin
|
|
254
|
+
*
|
|
255
|
+
* @endpoint GET /api/v1/admin/content/{id}
|
|
256
|
+
*/
|
|
257
|
+
export declare function useAdminContentItem(id: string, options?: Omit<UseQueryOptions<LibraryContentResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<LibraryContentResponse>;
|