@growsober/sdk 1.0.5 → 1.0.8
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/dist/__tests__/e2e.test.d.ts +30 -0
- package/dist/__tests__/e2e.test.js +959 -63
- package/dist/api/mutations/badges.d.ts +116 -0
- package/dist/api/mutations/badges.js +177 -0
- package/dist/api/mutations/brands.d.ts +251 -0
- package/dist/api/mutations/brands.js +242 -0
- package/dist/api/mutations/creators.d.ts +131 -0
- package/dist/api/mutations/creators.js +129 -0
- package/dist/api/mutations/event-chat.d.ts +2 -2
- package/dist/api/mutations/event-chat.js +9 -9
- package/dist/api/mutations/index.d.ts +4 -0
- package/dist/api/mutations/index.js +5 -1
- package/dist/api/mutations/jack.d.ts +29 -0
- package/dist/api/mutations/jack.js +41 -1
- package/dist/api/mutations/products.d.ts +175 -0
- package/dist/api/mutations/products.js +226 -0
- package/dist/api/mutations/support.d.ts +20 -1
- package/dist/api/mutations/support.js +36 -1
- package/dist/api/queries/badges.d.ts +221 -0
- package/dist/api/queries/badges.js +290 -0
- package/dist/api/queries/bookings.d.ts +1 -1
- package/dist/api/queries/brands.d.ts +248 -0
- package/dist/api/queries/brands.js +226 -0
- package/dist/api/queries/businesses.d.ts +61 -1
- package/dist/api/queries/businesses.js +27 -1
- package/dist/api/queries/creators.d.ts +332 -0
- package/dist/api/queries/creators.js +249 -0
- package/dist/api/queries/event-chat.d.ts +1 -1
- package/dist/api/queries/event-chat.js +4 -4
- package/dist/api/queries/events.d.ts +45 -0
- package/dist/api/queries/index.d.ts +5 -0
- package/dist/api/queries/index.js +6 -1
- package/dist/api/queries/jack.d.ts +80 -0
- package/dist/api/queries/jack.js +98 -1
- package/dist/api/queries/library.d.ts +8 -0
- package/dist/api/queries/products.d.ts +185 -0
- package/dist/api/queries/products.js +203 -0
- package/dist/api/queries/support.d.ts +46 -1
- package/dist/api/queries/support.js +48 -1
- package/dist/api/queries/venues.d.ts +304 -0
- package/dist/api/queries/venues.js +211 -0
- package/dist/api/types.d.ts +245 -0
- package/dist/api/types.js +6 -1
- package/dist/api/utils/eventGrouping.d.ts +104 -0
- package/dist/api/utils/eventGrouping.js +155 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/package.json +5 -2
- package/src/__tests__/e2e.test.ts +996 -64
- package/src/api/mutations/badges.ts +228 -0
- package/src/api/mutations/brands.ts +376 -0
- package/src/api/mutations/creators.ts +171 -0
- package/src/api/mutations/event-chat.ts +8 -8
- package/src/api/mutations/index.ts +4 -0
- package/src/api/mutations/jack.ts +50 -1
- package/src/api/mutations/products.ts +336 -0
- package/src/api/mutations/support.ts +44 -0
- package/src/api/queries/badges.ts +385 -0
- package/src/api/queries/brands.ts +281 -0
- package/src/api/queries/businesses.ts +30 -1
- package/src/api/queries/creators.ts +308 -0
- package/src/api/queries/event-chat.ts +3 -3
- package/src/api/queries/index.ts +5 -0
- package/src/api/queries/jack.ts +139 -1
- package/src/api/queries/products.ts +312 -0
- package/src/api/queries/support.ts +54 -0
- package/src/api/queries/venues.ts +271 -0
- package/src/api/types.ts +317 -1
- package/src/api/utils/eventGrouping.ts +181 -0
- package/src/index.ts +6 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type {
|
|
4
|
+
BadgeResponse,
|
|
5
|
+
UserBadgeResponse,
|
|
6
|
+
BadgeWithProgress,
|
|
7
|
+
PartnerRewardResponse,
|
|
8
|
+
PartnerRewardRedemptionResponse,
|
|
9
|
+
RewardResponse,
|
|
10
|
+
UserWalletResponse,
|
|
11
|
+
} from '../types';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// QUERY KEY FACTORY
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export const badgeKeys = {
|
|
18
|
+
all: ['badges'] as const,
|
|
19
|
+
lists: () => [...badgeKeys.all, 'list'] as const,
|
|
20
|
+
list: (userId?: string) => [...badgeKeys.lists(), userId] as const,
|
|
21
|
+
detail: (id: string) => [...badgeKeys.all, 'detail', id] as const,
|
|
22
|
+
user: (userId: string) => [...badgeKeys.all, 'user', userId] as const,
|
|
23
|
+
next: (userId: string) => [...badgeKeys.all, 'next', userId] as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const rewardKeys = {
|
|
27
|
+
all: ['rewards'] as const,
|
|
28
|
+
available: () => [...rewardKeys.all, 'available'] as const,
|
|
29
|
+
forBadge: (badgeId: string) => [...rewardKeys.all, 'badge', badgeId] as const,
|
|
30
|
+
forBusiness: (businessId: string) => [...rewardKeys.all, 'business', businessId] as const,
|
|
31
|
+
forVenue: (venueId: string) => [...rewardKeys.all, 'venue', venueId] as const,
|
|
32
|
+
forBrand: (brandId: string) => [...rewardKeys.all, 'brand', brandId] as const,
|
|
33
|
+
redeemed: () => [...rewardKeys.all, 'redeemed'] as const,
|
|
34
|
+
wallet: () => [...rewardKeys.all, 'wallet'] as const,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// BADGE QUERY HOOKS
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get all available badges
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { data: badges } = useBadges();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function useBadges(
|
|
50
|
+
options?: Omit<UseQueryOptions<BadgeResponse[]>, 'queryKey' | 'queryFn'>
|
|
51
|
+
) {
|
|
52
|
+
return useQuery({
|
|
53
|
+
queryKey: badgeKeys.lists(),
|
|
54
|
+
queryFn: async (): Promise<BadgeResponse[]> => {
|
|
55
|
+
const client = getApiClient();
|
|
56
|
+
const response = await client.get('/api/v1/badges');
|
|
57
|
+
return response.data?.data || response.data || [];
|
|
58
|
+
},
|
|
59
|
+
...options,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get a single badge by ID
|
|
65
|
+
*
|
|
66
|
+
* @param id - Badge ID
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const { data: badge } = useBadge('badge-123');
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function useBadge(
|
|
73
|
+
id: string,
|
|
74
|
+
options?: Omit<UseQueryOptions<BadgeResponse>, 'queryKey' | 'queryFn'>
|
|
75
|
+
) {
|
|
76
|
+
return useQuery({
|
|
77
|
+
queryKey: badgeKeys.detail(id),
|
|
78
|
+
queryFn: async (): Promise<BadgeResponse> => {
|
|
79
|
+
const client = getApiClient();
|
|
80
|
+
const response = await client.get(`/api/v1/badges/${id}`);
|
|
81
|
+
return response.data?.data || response.data;
|
|
82
|
+
},
|
|
83
|
+
enabled: !!id,
|
|
84
|
+
...options,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get current user's earned badges
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```tsx
|
|
93
|
+
* const { data: myBadges } = useMyBadges();
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function useMyBadges(
|
|
97
|
+
options?: Omit<UseQueryOptions<UserBadgeResponse[]>, 'queryKey' | 'queryFn'>
|
|
98
|
+
) {
|
|
99
|
+
return useQuery({
|
|
100
|
+
queryKey: badgeKeys.user('me'),
|
|
101
|
+
queryFn: async (): Promise<UserBadgeResponse[]> => {
|
|
102
|
+
const client = getApiClient();
|
|
103
|
+
const response = await client.get('/api/v1/badges/me');
|
|
104
|
+
return response.data?.data || response.data || [];
|
|
105
|
+
},
|
|
106
|
+
...options,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get next badges user can earn (with progress)
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* const { data: nextBadges } = useNextBadges();
|
|
116
|
+
* // nextBadges = [{ badge: {...}, currentProgress: 5, requiredProgress: 10, percentComplete: 50 }, ...]
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export function useNextBadges(
|
|
120
|
+
options?: Omit<UseQueryOptions<BadgeWithProgress[]>, 'queryKey' | 'queryFn'>
|
|
121
|
+
) {
|
|
122
|
+
return useQuery({
|
|
123
|
+
queryKey: badgeKeys.next('me'),
|
|
124
|
+
queryFn: async (): Promise<BadgeWithProgress[]> => {
|
|
125
|
+
const client = getApiClient();
|
|
126
|
+
const response = await client.get('/api/v1/badges/next');
|
|
127
|
+
return response.data?.data || response.data || [];
|
|
128
|
+
},
|
|
129
|
+
...options,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// PARTNER REWARD QUERY HOOKS
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get all rewards available to the current user (based on earned badges)
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* const { data: rewards } = useAvailableRewards();
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export function useAvailableRewards(
|
|
146
|
+
options?: Omit<UseQueryOptions<PartnerRewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
147
|
+
) {
|
|
148
|
+
return useQuery({
|
|
149
|
+
queryKey: rewardKeys.available(),
|
|
150
|
+
queryFn: async (): Promise<PartnerRewardResponse[]> => {
|
|
151
|
+
const client = getApiClient();
|
|
152
|
+
const response = await client.get('/api/v1/rewards/available');
|
|
153
|
+
return response.data?.data || response.data || [];
|
|
154
|
+
},
|
|
155
|
+
...options,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get rewards for a specific badge
|
|
161
|
+
*
|
|
162
|
+
* @param badgeId - Badge ID
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* const { data: rewards } = useRewardsForBadge('badge-123');
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export function useRewardsForBadge(
|
|
169
|
+
badgeId: string,
|
|
170
|
+
options?: Omit<UseQueryOptions<PartnerRewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
171
|
+
) {
|
|
172
|
+
return useQuery({
|
|
173
|
+
queryKey: rewardKeys.forBadge(badgeId),
|
|
174
|
+
queryFn: async (): Promise<PartnerRewardResponse[]> => {
|
|
175
|
+
const client = getApiClient();
|
|
176
|
+
const response = await client.get(`/api/v1/rewards/badge/${badgeId}`);
|
|
177
|
+
return response.data?.data || response.data || [];
|
|
178
|
+
},
|
|
179
|
+
enabled: !!badgeId,
|
|
180
|
+
...options,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get rewards offered by a specific business
|
|
186
|
+
*
|
|
187
|
+
* @param businessId - Business ID
|
|
188
|
+
* @example
|
|
189
|
+
* ```tsx
|
|
190
|
+
* const { data: rewards } = useBusinessRewards('business-123');
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export function useBusinessRewards(
|
|
194
|
+
businessId: string,
|
|
195
|
+
options?: Omit<UseQueryOptions<PartnerRewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
196
|
+
) {
|
|
197
|
+
return useQuery({
|
|
198
|
+
queryKey: rewardKeys.forBusiness(businessId),
|
|
199
|
+
queryFn: async (): Promise<PartnerRewardResponse[]> => {
|
|
200
|
+
const client = getApiClient();
|
|
201
|
+
const response = await client.get(`/api/v1/rewards/business/${businessId}`);
|
|
202
|
+
return response.data?.data || response.data || [];
|
|
203
|
+
},
|
|
204
|
+
enabled: !!businessId,
|
|
205
|
+
...options,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get user's redeemed rewards
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```tsx
|
|
214
|
+
* const { data: redeemed } = useRedeemedRewards();
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export function useRedeemedRewards(
|
|
218
|
+
options?: Omit<UseQueryOptions<PartnerRewardRedemptionResponse[]>, 'queryKey' | 'queryFn'>
|
|
219
|
+
) {
|
|
220
|
+
return useQuery({
|
|
221
|
+
queryKey: rewardKeys.redeemed(),
|
|
222
|
+
queryFn: async (): Promise<PartnerRewardRedemptionResponse[]> => {
|
|
223
|
+
const client = getApiClient();
|
|
224
|
+
const response = await client.get('/api/v1/rewards/redeemed');
|
|
225
|
+
return response.data?.data || response.data || [];
|
|
226
|
+
},
|
|
227
|
+
...options,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// WALLET (Combined badges + rewards for Rewards tab)
|
|
233
|
+
// ============================================================================
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Wallet reward card with QR token for redemption
|
|
237
|
+
*/
|
|
238
|
+
export interface WalletReward {
|
|
239
|
+
id: string;
|
|
240
|
+
title: string;
|
|
241
|
+
description: string | null;
|
|
242
|
+
code: string | null;
|
|
243
|
+
validUntil: string | null;
|
|
244
|
+
badge: {
|
|
245
|
+
id: string;
|
|
246
|
+
name: string;
|
|
247
|
+
slug: string;
|
|
248
|
+
icon: string | null;
|
|
249
|
+
};
|
|
250
|
+
business: {
|
|
251
|
+
id: string;
|
|
252
|
+
name: string;
|
|
253
|
+
profileImage: string | null;
|
|
254
|
+
address: string | null;
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface WalletRedemption {
|
|
259
|
+
id: string;
|
|
260
|
+
redeemedAt: string;
|
|
261
|
+
verifiedAt: string | null;
|
|
262
|
+
qrToken: string;
|
|
263
|
+
partnerReward: WalletReward;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface WalletBadge {
|
|
267
|
+
id: string;
|
|
268
|
+
badgeId: string;
|
|
269
|
+
userId: string;
|
|
270
|
+
awardedAt: string;
|
|
271
|
+
note: string | null;
|
|
272
|
+
badge: BadgeResponse;
|
|
273
|
+
availableRewards: WalletReward[];
|
|
274
|
+
redeemedRewards: WalletRedemption[];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface WalletResponse {
|
|
278
|
+
badges: WalletBadge[];
|
|
279
|
+
stats: {
|
|
280
|
+
totalBadges: number;
|
|
281
|
+
totalAvailableRewards: number;
|
|
282
|
+
totalRedeemedRewards: number;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get user's wallet - all earned badges with available and redeemed rewards
|
|
288
|
+
* This is the main data source for the Rewards tab
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```tsx
|
|
292
|
+
* const { data: wallet } = useRewardsWallet();
|
|
293
|
+
* // wallet.badges = [{ badge: {...}, availableRewards: [...], redeemedRewards: [...] }, ...]
|
|
294
|
+
* // wallet.stats = { totalBadges: 5, totalAvailableRewards: 3, totalRedeemedRewards: 2 }
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export function useRewardsWallet(
|
|
298
|
+
options?: Omit<UseQueryOptions<WalletResponse>, 'queryKey' | 'queryFn'>
|
|
299
|
+
) {
|
|
300
|
+
return useQuery({
|
|
301
|
+
queryKey: rewardKeys.wallet(),
|
|
302
|
+
queryFn: async (): Promise<WalletResponse> => {
|
|
303
|
+
const client = getApiClient();
|
|
304
|
+
const response = await client.get('/api/v1/rewards/wallet');
|
|
305
|
+
return response.data?.data || response.data;
|
|
306
|
+
},
|
|
307
|
+
...options,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ============================================================================
|
|
312
|
+
// UNIFIED REWARD HOOKS (Venue & Brand)
|
|
313
|
+
// ============================================================================
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get rewards for a specific venue (unified reward model)
|
|
317
|
+
*
|
|
318
|
+
* @param venueId - Venue ID
|
|
319
|
+
* @example
|
|
320
|
+
* ```tsx
|
|
321
|
+
* const { data: rewards } = useVenueRewards('venue-123');
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
export function useVenueRewards(
|
|
325
|
+
venueId: string,
|
|
326
|
+
options?: Omit<UseQueryOptions<RewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
327
|
+
) {
|
|
328
|
+
return useQuery({
|
|
329
|
+
queryKey: rewardKeys.forVenue(venueId),
|
|
330
|
+
queryFn: async (): Promise<RewardResponse[]> => {
|
|
331
|
+
const client = getApiClient();
|
|
332
|
+
const response = await client.get(`/api/v1/rewards/venue/${venueId}`);
|
|
333
|
+
return response.data?.data || response.data || [];
|
|
334
|
+
},
|
|
335
|
+
enabled: !!venueId,
|
|
336
|
+
...options,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get rewards for a specific brand (unified reward model)
|
|
342
|
+
*
|
|
343
|
+
* @param brandId - Brand ID
|
|
344
|
+
* @example
|
|
345
|
+
* ```tsx
|
|
346
|
+
* const { data: rewards } = useBrandRewardsQuery('brand-123');
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
export function useBrandRewardsQuery(
|
|
350
|
+
brandId: string,
|
|
351
|
+
options?: Omit<UseQueryOptions<RewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
352
|
+
) {
|
|
353
|
+
return useQuery({
|
|
354
|
+
queryKey: rewardKeys.forBrand(brandId),
|
|
355
|
+
queryFn: async (): Promise<RewardResponse[]> => {
|
|
356
|
+
const client = getApiClient();
|
|
357
|
+
const response = await client.get(`/api/v1/rewards/brand/${brandId}`);
|
|
358
|
+
return response.data?.data || response.data || [];
|
|
359
|
+
},
|
|
360
|
+
enabled: !!brandId,
|
|
361
|
+
...options,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get unified wallet with venue and brand rewards
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```tsx
|
|
370
|
+
* const { data: wallet } = useUnifiedWallet();
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
export function useUnifiedWallet(
|
|
374
|
+
options?: Omit<UseQueryOptions<UserWalletResponse>, 'queryKey' | 'queryFn'>
|
|
375
|
+
) {
|
|
376
|
+
return useQuery({
|
|
377
|
+
queryKey: [...rewardKeys.wallet(), 'unified'],
|
|
378
|
+
queryFn: async (): Promise<UserWalletResponse> => {
|
|
379
|
+
const client = getApiClient();
|
|
380
|
+
const response = await client.get('/api/v1/rewards/wallet');
|
|
381
|
+
return response.data?.data || response.data;
|
|
382
|
+
},
|
|
383
|
+
...options,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type {
|
|
4
|
+
BrandResponse,
|
|
5
|
+
BrandOwnerResponse,
|
|
6
|
+
BrandCreatorResponse,
|
|
7
|
+
RewardResponse,
|
|
8
|
+
EventResponse,
|
|
9
|
+
} from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// QUERY KEY FACTORY
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export const brandKeys = {
|
|
16
|
+
all: ['brands'] as const,
|
|
17
|
+
lists: () => [...brandKeys.all, 'list'] as const,
|
|
18
|
+
list: (filters?: BrandFilters) => [...brandKeys.lists(), filters] as const,
|
|
19
|
+
details: () => [...brandKeys.all, 'detail'] as const,
|
|
20
|
+
detail: (id: string) => [...brandKeys.details(), id] as const,
|
|
21
|
+
slug: (slug: string) => [...brandKeys.all, 'slug', slug] as const,
|
|
22
|
+
featured: () => [...brandKeys.all, 'featured'] as const,
|
|
23
|
+
owners: (brandId: string) => [...brandKeys.detail(brandId), 'owners'] as const,
|
|
24
|
+
creators: (brandId: string) => [...brandKeys.detail(brandId), 'creators'] as const,
|
|
25
|
+
rewards: (brandId: string) => [...brandKeys.detail(brandId), 'rewards'] as const,
|
|
26
|
+
events: (brandId: string) => [...brandKeys.detail(brandId), 'events'] as const,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// TYPES
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
export interface BrandFilters {
|
|
34
|
+
page?: number;
|
|
35
|
+
limit?: number;
|
|
36
|
+
search?: string;
|
|
37
|
+
isVerified?: boolean;
|
|
38
|
+
isFeatured?: boolean;
|
|
39
|
+
sortBy?: string;
|
|
40
|
+
sortOrder?: 'asc' | 'desc';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PaginatedBrandsResponse {
|
|
44
|
+
brands: BrandResponse[];
|
|
45
|
+
meta: {
|
|
46
|
+
total: number;
|
|
47
|
+
page: number;
|
|
48
|
+
limit: number;
|
|
49
|
+
totalPages: number;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ============================================================================
|
|
54
|
+
// QUERY HOOKS
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get paginated list of brands with optional filters
|
|
59
|
+
*
|
|
60
|
+
* @param filters - Query parameters for filtering and pagination
|
|
61
|
+
* @param options - TanStack Query options
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const { data, isLoading } = useBrands({
|
|
66
|
+
* page: 1,
|
|
67
|
+
* limit: 20,
|
|
68
|
+
* isFeatured: true,
|
|
69
|
+
* });
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function useBrands(
|
|
73
|
+
filters?: BrandFilters,
|
|
74
|
+
options?: Omit<UseQueryOptions<PaginatedBrandsResponse>, 'queryKey' | 'queryFn'>
|
|
75
|
+
) {
|
|
76
|
+
return useQuery({
|
|
77
|
+
queryKey: brandKeys.list(filters),
|
|
78
|
+
queryFn: async (): Promise<PaginatedBrandsResponse> => {
|
|
79
|
+
const client = getApiClient();
|
|
80
|
+
const response = await client.get('/api/v1/brands', {
|
|
81
|
+
params: filters,
|
|
82
|
+
});
|
|
83
|
+
return response.data;
|
|
84
|
+
},
|
|
85
|
+
...options,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get a single brand by ID
|
|
91
|
+
*
|
|
92
|
+
* @param id - Brand ID
|
|
93
|
+
* @param options - TanStack Query options
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* const { data, isLoading } = useBrand('brand-123');
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export function useBrand(
|
|
101
|
+
id: string,
|
|
102
|
+
options?: Omit<UseQueryOptions<BrandResponse>, 'queryKey' | 'queryFn'>
|
|
103
|
+
) {
|
|
104
|
+
return useQuery({
|
|
105
|
+
queryKey: brandKeys.detail(id),
|
|
106
|
+
queryFn: async (): Promise<BrandResponse> => {
|
|
107
|
+
const client = getApiClient();
|
|
108
|
+
const response = await client.get(`/api/v1/brands/${id}`);
|
|
109
|
+
return response.data;
|
|
110
|
+
},
|
|
111
|
+
enabled: !!id,
|
|
112
|
+
...options,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get a brand by its slug
|
|
118
|
+
*
|
|
119
|
+
* @param slug - Brand slug
|
|
120
|
+
* @param options - TanStack Query options
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* const { data, isLoading } = useBrandBySlug('wellness-co');
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function useBrandBySlug(
|
|
128
|
+
slug: string,
|
|
129
|
+
options?: Omit<UseQueryOptions<BrandResponse>, 'queryKey' | 'queryFn'>
|
|
130
|
+
) {
|
|
131
|
+
return useQuery({
|
|
132
|
+
queryKey: brandKeys.slug(slug),
|
|
133
|
+
queryFn: async (): Promise<BrandResponse> => {
|
|
134
|
+
const client = getApiClient();
|
|
135
|
+
const response = await client.get(`/api/v1/brands/slug/${slug}`);
|
|
136
|
+
return response.data;
|
|
137
|
+
},
|
|
138
|
+
enabled: !!slug,
|
|
139
|
+
...options,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get featured brands
|
|
145
|
+
*
|
|
146
|
+
* @param limit - Maximum number of brands to return
|
|
147
|
+
* @param options - TanStack Query options
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* const { data, isLoading } = useFeaturedBrands(10);
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export function useFeaturedBrands(
|
|
155
|
+
limit?: number,
|
|
156
|
+
options?: Omit<UseQueryOptions<BrandResponse[]>, 'queryKey' | 'queryFn'>
|
|
157
|
+
) {
|
|
158
|
+
return useQuery({
|
|
159
|
+
queryKey: brandKeys.featured(),
|
|
160
|
+
queryFn: async (): Promise<BrandResponse[]> => {
|
|
161
|
+
const client = getApiClient();
|
|
162
|
+
const response = await client.get('/api/v1/brands/featured', {
|
|
163
|
+
params: { limit },
|
|
164
|
+
});
|
|
165
|
+
return response.data;
|
|
166
|
+
},
|
|
167
|
+
...options,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get brand owners
|
|
173
|
+
*
|
|
174
|
+
* @param brandId - Brand ID
|
|
175
|
+
* @param options - TanStack Query options
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```tsx
|
|
179
|
+
* const { data, isLoading } = useBrandOwners('brand-123');
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export function useBrandOwners(
|
|
183
|
+
brandId: string,
|
|
184
|
+
options?: Omit<UseQueryOptions<BrandOwnerResponse[]>, 'queryKey' | 'queryFn'>
|
|
185
|
+
) {
|
|
186
|
+
return useQuery({
|
|
187
|
+
queryKey: brandKeys.owners(brandId),
|
|
188
|
+
queryFn: async (): Promise<BrandOwnerResponse[]> => {
|
|
189
|
+
const client = getApiClient();
|
|
190
|
+
const response = await client.get(`/api/v1/brands/${brandId}/owners`);
|
|
191
|
+
return response.data;
|
|
192
|
+
},
|
|
193
|
+
enabled: !!brandId,
|
|
194
|
+
...options,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get brand creator partnerships
|
|
200
|
+
*
|
|
201
|
+
* @param brandId - Brand ID
|
|
202
|
+
* @param options - TanStack Query options
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```tsx
|
|
206
|
+
* const { data, isLoading } = useBrandCreators('brand-123');
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export function useBrandCreators(
|
|
210
|
+
brandId: string,
|
|
211
|
+
options?: Omit<UseQueryOptions<BrandCreatorResponse[]>, 'queryKey' | 'queryFn'>
|
|
212
|
+
) {
|
|
213
|
+
return useQuery({
|
|
214
|
+
queryKey: brandKeys.creators(brandId),
|
|
215
|
+
queryFn: async (): Promise<BrandCreatorResponse[]> => {
|
|
216
|
+
const client = getApiClient();
|
|
217
|
+
const response = await client.get(`/api/v1/brands/${brandId}/creators`);
|
|
218
|
+
return response.data;
|
|
219
|
+
},
|
|
220
|
+
enabled: !!brandId,
|
|
221
|
+
...options,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get brand rewards
|
|
227
|
+
*
|
|
228
|
+
* @param brandId - Brand ID
|
|
229
|
+
* @param options - TanStack Query options
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```tsx
|
|
233
|
+
* const { data, isLoading } = useBrandRewards('brand-123');
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
export function useBrandRewards(
|
|
237
|
+
brandId: string,
|
|
238
|
+
options?: Omit<UseQueryOptions<RewardResponse[]>, 'queryKey' | 'queryFn'>
|
|
239
|
+
) {
|
|
240
|
+
return useQuery({
|
|
241
|
+
queryKey: brandKeys.rewards(brandId),
|
|
242
|
+
queryFn: async (): Promise<RewardResponse[]> => {
|
|
243
|
+
const client = getApiClient();
|
|
244
|
+
const response = await client.get(`/api/v1/rewards/brand/${brandId}`);
|
|
245
|
+
return response.data;
|
|
246
|
+
},
|
|
247
|
+
enabled: !!brandId,
|
|
248
|
+
...options,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get events sponsored by a brand
|
|
254
|
+
*
|
|
255
|
+
* @param brandId - Brand ID
|
|
256
|
+
* @param upcoming - Whether to filter to upcoming events only (default: true)
|
|
257
|
+
* @param options - TanStack Query options
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* const { data, isLoading } = useBrandSponsoredEvents('brand-123');
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
export function useBrandSponsoredEvents(
|
|
265
|
+
brandId: string,
|
|
266
|
+
upcoming = true,
|
|
267
|
+
options?: Omit<UseQueryOptions<EventResponse[]>, 'queryKey' | 'queryFn'>
|
|
268
|
+
) {
|
|
269
|
+
return useQuery({
|
|
270
|
+
queryKey: brandKeys.events(brandId),
|
|
271
|
+
queryFn: async (): Promise<EventResponse[]> => {
|
|
272
|
+
const client = getApiClient();
|
|
273
|
+
const response = await client.get(`/api/v1/brands/${brandId}/events`, {
|
|
274
|
+
params: { upcoming },
|
|
275
|
+
});
|
|
276
|
+
return response.data;
|
|
277
|
+
},
|
|
278
|
+
enabled: !!brandId,
|
|
279
|
+
...options,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
2
|
import { getApiClient } from '../client';
|
|
3
|
-
import type { BusinessResponse, OfferResponse } from '../types';
|
|
3
|
+
import type { BusinessResponse, OfferResponse, EventResponse } from '../types';
|
|
4
4
|
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// QUERY KEY FACTORY
|
|
@@ -15,6 +15,7 @@ export const businessKeys = {
|
|
|
15
15
|
featured: () => [...businessKeys.all, 'featured'] as const,
|
|
16
16
|
nearby: (params?: NearbyBusinessParams) => [...businessKeys.all, 'nearby', params] as const,
|
|
17
17
|
offers: (businessId: string) => [...businessKeys.detail(businessId), 'offers'] as const,
|
|
18
|
+
events: (businessId: string) => [...businessKeys.detail(businessId), 'events'] as const,
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
// ============================================================================
|
|
@@ -201,3 +202,31 @@ export function useBusinessOffers(
|
|
|
201
202
|
...options,
|
|
202
203
|
});
|
|
203
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get all events for a specific business
|
|
208
|
+
*
|
|
209
|
+
* @param businessId - Business ID
|
|
210
|
+
* @param options - TanStack Query options
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```tsx
|
|
214
|
+
* const { data, isLoading } = useBusinessEvents('business-123');
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
export function useBusinessEvents(
|
|
218
|
+
businessId: string,
|
|
219
|
+
options?: Omit<UseQueryOptions<EventResponse[]>, 'queryKey' | 'queryFn'>
|
|
220
|
+
) {
|
|
221
|
+
return useQuery({
|
|
222
|
+
queryKey: businessKeys.events(businessId),
|
|
223
|
+
queryFn: async (): Promise<EventResponse[]> => {
|
|
224
|
+
const client = getApiClient();
|
|
225
|
+
const response = await client.get(`/api/v1/businesses/${businessId}/events`);
|
|
226
|
+
// Handle wrapped response
|
|
227
|
+
return response.data?.data || response.data || [];
|
|
228
|
+
},
|
|
229
|
+
enabled: !!businessId,
|
|
230
|
+
...options,
|
|
231
|
+
});
|
|
232
|
+
}
|