@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
package/src/api/types.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* All types are derived from the @growsober/types package which is auto-generated
|
|
5
5
|
* from the API OpenAPI specification. This ensures single source of truth from Prisma.
|
|
6
|
+
*
|
|
7
|
+
* To regenerate types:
|
|
8
|
+
* 1. Start the API: cd ../growsober-api && npm run start:dev
|
|
9
|
+
* 2. Generate types: cd ../growsober-types && npm run generate && npm run build
|
|
10
|
+
* 3. Rebuild SDK: npm run build
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
import type { paths, components } from '@growsober/types';
|
|
@@ -52,6 +57,40 @@ export type PathParams<
|
|
|
52
57
|
? P
|
|
53
58
|
: Record<string, never>;
|
|
54
59
|
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// ENUM TYPES (extracted from generated schemas - single source of truth)
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
// Business/Venue type (used for both Business and Venue models)
|
|
65
|
+
export type BusinessType = components['schemas']['BusinessResponseDto']['type'];
|
|
66
|
+
|
|
67
|
+
// Ownership roles
|
|
68
|
+
export type VenueOwnerRole = components['schemas']['VenueOwnerResponseDto']['role'];
|
|
69
|
+
export type BrandOwnerRole = components['schemas']['BrandOwnerResponseDto']['role'];
|
|
70
|
+
|
|
71
|
+
// Event types
|
|
72
|
+
export type GatheringType = NonNullable<components['schemas']['EventResponseDto']['gatheringType']>;
|
|
73
|
+
export type EventVisibility = components['schemas']['EventResponseDto']['visibility'];
|
|
74
|
+
export type EventStatus = components['schemas']['EventResponseDto']['status'];
|
|
75
|
+
|
|
76
|
+
// Chat roles
|
|
77
|
+
export type ChatMemberRole = components['schemas']['ChatMemberResponseDto']['role'];
|
|
78
|
+
|
|
79
|
+
// Content status
|
|
80
|
+
export type ContentStatus = components['schemas']['LibraryContentResponseDto']['contentStatus'];
|
|
81
|
+
|
|
82
|
+
// Badge types
|
|
83
|
+
export type BadgeType = components['schemas']['BadgeResponseDto']['type'];
|
|
84
|
+
|
|
85
|
+
// Notification types
|
|
86
|
+
export type NotificationType = components['schemas']['NotificationResponseDto']['type'];
|
|
87
|
+
|
|
88
|
+
// Streak types
|
|
89
|
+
export type StreakType = components['schemas']['StreakResponseDto']['type'];
|
|
90
|
+
|
|
91
|
+
// Craving triggers
|
|
92
|
+
export type CravingTrigger = NonNullable<components['schemas']['CravingLogResponseDto']['triggerType']>;
|
|
93
|
+
|
|
55
94
|
// ============================================================================
|
|
56
95
|
// AUTH TYPES
|
|
57
96
|
// ============================================================================
|
|
@@ -105,15 +144,120 @@ export type CreateBookingRequest = components['schemas']['CreateBookingDto'];
|
|
|
105
144
|
export type LibraryContentResponse = components['schemas']['LibraryContentResponseDto'];
|
|
106
145
|
export type LibraryContentDetailResponse = components['schemas']['LibraryContentDetailResponseDto'];
|
|
107
146
|
export type LibraryProgressResponse = components['schemas']['LibraryProgressResponseDto'];
|
|
147
|
+
export type ContentType = components['schemas']['LibraryContentResponseDto']['type'];
|
|
108
148
|
|
|
109
149
|
// ============================================================================
|
|
110
|
-
// BUSINESS & OFFER TYPES
|
|
150
|
+
// BUSINESS & OFFER TYPES (Legacy)
|
|
111
151
|
// ============================================================================
|
|
112
152
|
|
|
113
153
|
export type BusinessResponse = components['schemas']['BusinessResponseDto'];
|
|
114
154
|
export type OfferResponse = components['schemas']['OfferResponseDto'];
|
|
115
155
|
export type RedeemOfferRequest = components['schemas']['RedeemOfferDto'];
|
|
116
156
|
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// VENUE TYPES (from generated OpenAPI types)
|
|
159
|
+
// ============================================================================
|
|
160
|
+
|
|
161
|
+
export type VenueResponse = components['schemas']['VenueResponseDto'];
|
|
162
|
+
export type VenueOwnerResponse = components['schemas']['VenueOwnerResponseDto'];
|
|
163
|
+
|
|
164
|
+
// Request types for venue operations
|
|
165
|
+
export interface CreateVenueRequest {
|
|
166
|
+
name: string;
|
|
167
|
+
slug: string;
|
|
168
|
+
description?: string;
|
|
169
|
+
type: BusinessType;
|
|
170
|
+
hasAfDrinks?: boolean;
|
|
171
|
+
isAfVenue?: boolean;
|
|
172
|
+
afHighlights?: string[];
|
|
173
|
+
address?: string;
|
|
174
|
+
cityId?: string;
|
|
175
|
+
locationLat?: number;
|
|
176
|
+
locationLong?: number;
|
|
177
|
+
phone?: string;
|
|
178
|
+
email?: string;
|
|
179
|
+
website?: string;
|
|
180
|
+
instagram?: string;
|
|
181
|
+
profileImage?: string;
|
|
182
|
+
bannerImage?: string;
|
|
183
|
+
photos?: string[];
|
|
184
|
+
openingHours?: Record<string, string>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export interface UpdateVenueRequest extends Partial<CreateVenueRequest> {}
|
|
188
|
+
|
|
189
|
+
export interface AddVenueOwnerRequest {
|
|
190
|
+
userId: string;
|
|
191
|
+
role: VenueOwnerRole;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// CREATOR TYPES (from generated OpenAPI types)
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
export type CreatorResponse = components['schemas']['CreatorResponseDto'];
|
|
199
|
+
export type CreatorAvailabilityResponse = components['schemas']['CreatorAvailabilityResponseDto'];
|
|
200
|
+
|
|
201
|
+
export interface CreateCreatorRequest {
|
|
202
|
+
slug: string;
|
|
203
|
+
displayName: string;
|
|
204
|
+
bio?: string;
|
|
205
|
+
avatarUrl?: string;
|
|
206
|
+
cityIds?: string[];
|
|
207
|
+
canFacilitate?: boolean;
|
|
208
|
+
canCreateContent?: boolean;
|
|
209
|
+
isInfluencer?: boolean;
|
|
210
|
+
specialties?: string[];
|
|
211
|
+
certifications?: string[];
|
|
212
|
+
sessionRate?: number;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface UpdateCreatorRequest extends Partial<CreateCreatorRequest> {}
|
|
216
|
+
|
|
217
|
+
export interface CreateAvailabilityRequest {
|
|
218
|
+
dayOfWeek?: number;
|
|
219
|
+
startTime: string;
|
|
220
|
+
endTime: string;
|
|
221
|
+
timezone: string;
|
|
222
|
+
isRecurring?: boolean;
|
|
223
|
+
specificDate?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ============================================================================
|
|
227
|
+
// BRAND TYPES (from generated OpenAPI types)
|
|
228
|
+
// ============================================================================
|
|
229
|
+
|
|
230
|
+
export type BrandResponse = components['schemas']['BrandResponseDto'];
|
|
231
|
+
export type BrandOwnerResponse = components['schemas']['BrandOwnerResponseDto'];
|
|
232
|
+
export type BrandCreatorResponse = components['schemas']['BrandCreatorResponseDto'];
|
|
233
|
+
|
|
234
|
+
export interface CreateBrandRequest {
|
|
235
|
+
slug: string;
|
|
236
|
+
name: string;
|
|
237
|
+
description?: string;
|
|
238
|
+
logoUrl?: string;
|
|
239
|
+
bannerUrl?: string;
|
|
240
|
+
primaryColor?: string;
|
|
241
|
+
website?: string;
|
|
242
|
+
email?: string;
|
|
243
|
+
instagram?: string;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface UpdateBrandRequest extends Partial<CreateBrandRequest> {}
|
|
247
|
+
|
|
248
|
+
export interface AddBrandOwnerRequest {
|
|
249
|
+
userId: string;
|
|
250
|
+
role: BrandOwnerRole;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface AddBrandCreatorRequest {
|
|
254
|
+
creatorId: string;
|
|
255
|
+
commissionRate?: number;
|
|
256
|
+
isActive?: boolean;
|
|
257
|
+
startDate?: string;
|
|
258
|
+
endDate?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
117
261
|
// ============================================================================
|
|
118
262
|
// SUBSCRIPTION TYPES
|
|
119
263
|
// ============================================================================
|
|
@@ -172,6 +316,11 @@ export type ReflectionResponse = components['schemas']['ReflectionResponseDto'];
|
|
|
172
316
|
export type CreateReflectionRequest = components['schemas']['CreateReflectionDto'];
|
|
173
317
|
export type UpdateReflectionRequest = components['schemas']['UpdateReflectionDto'];
|
|
174
318
|
|
|
319
|
+
// Cravings
|
|
320
|
+
export type CravingLogResponse = components['schemas']['CravingLogResponseDto'];
|
|
321
|
+
export type LogCravingRequest = components['schemas']['LogCravingDto'];
|
|
322
|
+
export type CravingStatsResponse = components['schemas']['CravingStatsDto'];
|
|
323
|
+
|
|
175
324
|
// ============================================================================
|
|
176
325
|
// MAP TYPES
|
|
177
326
|
// ============================================================================
|
|
@@ -236,6 +385,173 @@ export type AdminCreateContentRequest = components['schemas']['AdminCreateConten
|
|
|
236
385
|
export type AdminCreateBusinessRequest = components['schemas']['AdminCreateBusinessDto'];
|
|
237
386
|
export type AdminOverviewStatsResponse = components['schemas']['AdminOverviewStatsDto'];
|
|
238
387
|
|
|
388
|
+
// ============================================================================
|
|
389
|
+
// BADGE TYPES (from generated OpenAPI types)
|
|
390
|
+
// ============================================================================
|
|
391
|
+
|
|
392
|
+
export type BadgeResponse = components['schemas']['BadgeResponseDto'];
|
|
393
|
+
export type UserBadgeResponse = components['schemas']['UserBadgeResponseDto'];
|
|
394
|
+
export type BadgeProgressResponse = components['schemas']['BadgeProgressDto'];
|
|
395
|
+
|
|
396
|
+
// Alias for SDK consistency
|
|
397
|
+
export interface BadgeWithProgress {
|
|
398
|
+
badge: BadgeResponse;
|
|
399
|
+
currentProgress: number;
|
|
400
|
+
requiredProgress: number;
|
|
401
|
+
percentComplete: number;
|
|
402
|
+
isEarned: boolean;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// UNIFIED REWARD TYPES
|
|
407
|
+
// ============================================================================
|
|
408
|
+
|
|
409
|
+
// RedeemType enum - extracted from Prisma via API
|
|
410
|
+
export type RedeemType = 'IN_PERSON' | 'ONLINE' | 'BOTH';
|
|
411
|
+
|
|
412
|
+
// Reward response with relations
|
|
413
|
+
export interface RewardResponse {
|
|
414
|
+
id: string;
|
|
415
|
+
venueId: string | null;
|
|
416
|
+
brandId: string | null;
|
|
417
|
+
badgeId: string;
|
|
418
|
+
title: string;
|
|
419
|
+
description: string | null;
|
|
420
|
+
code: string | null;
|
|
421
|
+
redeemType: RedeemType;
|
|
422
|
+
maxRedemptions: number | null;
|
|
423
|
+
redemptionsUsed: number;
|
|
424
|
+
perUserLimit: number;
|
|
425
|
+
validFrom: string | null;
|
|
426
|
+
validUntil: string | null;
|
|
427
|
+
isActive: boolean;
|
|
428
|
+
createdAt: string;
|
|
429
|
+
badge: {
|
|
430
|
+
id: string;
|
|
431
|
+
name: string;
|
|
432
|
+
slug: string;
|
|
433
|
+
icon: string | null;
|
|
434
|
+
};
|
|
435
|
+
venue?: {
|
|
436
|
+
id: string;
|
|
437
|
+
name: string;
|
|
438
|
+
slug: string;
|
|
439
|
+
profileImage: string | null;
|
|
440
|
+
address: string | null;
|
|
441
|
+
} | null;
|
|
442
|
+
brand?: {
|
|
443
|
+
id: string;
|
|
444
|
+
name: string;
|
|
445
|
+
slug: string;
|
|
446
|
+
logoUrl: string | null;
|
|
447
|
+
} | null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export interface RewardRedemptionResponse {
|
|
451
|
+
id: string;
|
|
452
|
+
userId: string;
|
|
453
|
+
rewardId: string;
|
|
454
|
+
redeemedAt: string;
|
|
455
|
+
verifiedAt: string | null;
|
|
456
|
+
verifiedBy: string | null;
|
|
457
|
+
codeUsed: string | null;
|
|
458
|
+
reward: RewardResponse;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export interface CreateRewardRequest {
|
|
462
|
+
badgeId: string;
|
|
463
|
+
title: string;
|
|
464
|
+
description?: string;
|
|
465
|
+
code?: string;
|
|
466
|
+
redeemType?: RedeemType;
|
|
467
|
+
maxRedemptions?: number;
|
|
468
|
+
perUserLimit?: number;
|
|
469
|
+
validFrom?: string;
|
|
470
|
+
validUntil?: string;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export interface UpdateRewardRequest {
|
|
474
|
+
title?: string;
|
|
475
|
+
description?: string;
|
|
476
|
+
code?: string;
|
|
477
|
+
redeemType?: RedeemType;
|
|
478
|
+
maxRedemptions?: number;
|
|
479
|
+
perUserLimit?: number;
|
|
480
|
+
validFrom?: string;
|
|
481
|
+
validUntil?: string;
|
|
482
|
+
isActive?: boolean;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export interface UserWalletResponse {
|
|
486
|
+
badges: Array<{
|
|
487
|
+
id: string;
|
|
488
|
+
badgeId: string;
|
|
489
|
+
userId: string;
|
|
490
|
+
awardedAt: string;
|
|
491
|
+
badge: BadgeResponse;
|
|
492
|
+
availableRewards: RewardResponse[];
|
|
493
|
+
redeemedRewards: Array<RewardRedemptionResponse & { qrToken: string }>;
|
|
494
|
+
}>;
|
|
495
|
+
stats: {
|
|
496
|
+
totalBadges: number;
|
|
497
|
+
totalAvailableRewards: number;
|
|
498
|
+
totalRedeemedRewards: number;
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// ============================================================================
|
|
503
|
+
// LEGACY TYPES (deprecated - use unified types)
|
|
504
|
+
// ============================================================================
|
|
505
|
+
|
|
506
|
+
/** @deprecated Use RewardResponse instead */
|
|
507
|
+
export interface PartnerRewardResponse {
|
|
508
|
+
id: string;
|
|
509
|
+
businessId: string;
|
|
510
|
+
badgeId: string;
|
|
511
|
+
title: string;
|
|
512
|
+
description: string | null;
|
|
513
|
+
code: string | null;
|
|
514
|
+
maxRedemptions: number | null;
|
|
515
|
+
redemptionsUsed: number;
|
|
516
|
+
perUserLimit: number;
|
|
517
|
+
validFrom: string;
|
|
518
|
+
validUntil: string | null;
|
|
519
|
+
isActive: boolean;
|
|
520
|
+
createdAt: string;
|
|
521
|
+
badge: {
|
|
522
|
+
id: string;
|
|
523
|
+
name: string;
|
|
524
|
+
slug: string;
|
|
525
|
+
icon: string | null;
|
|
526
|
+
};
|
|
527
|
+
business: {
|
|
528
|
+
id: string;
|
|
529
|
+
name: string;
|
|
530
|
+
profileImage: string | null;
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** @deprecated Use RewardRedemptionResponse instead */
|
|
535
|
+
export interface PartnerRewardRedemptionResponse {
|
|
536
|
+
id: string;
|
|
537
|
+
userId: string;
|
|
538
|
+
partnerRewardId: string;
|
|
539
|
+
redeemedAt: string;
|
|
540
|
+
verifiedAt: string | null;
|
|
541
|
+
partnerReward: PartnerRewardResponse;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/** @deprecated Use CreateRewardRequest instead */
|
|
545
|
+
export interface CreatePartnerRewardRequest {
|
|
546
|
+
badgeId: string;
|
|
547
|
+
title: string;
|
|
548
|
+
description?: string;
|
|
549
|
+
code?: string;
|
|
550
|
+
maxRedemptions?: number;
|
|
551
|
+
perUserLimit?: number;
|
|
552
|
+
validUntil?: string;
|
|
553
|
+
}
|
|
554
|
+
|
|
239
555
|
// ============================================================================
|
|
240
556
|
// RE-EXPORTS
|
|
241
557
|
// ============================================================================
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Grouping Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utilities for organizing events by time of day.
|
|
5
|
+
* Used in the Gatherings screen to show events grouped by
|
|
6
|
+
* Morning, Afternoon, and Evening.
|
|
7
|
+
*
|
|
8
|
+
* @module api/utils/eventGrouping
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { EventResponse } from '../types';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// TYPES
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
export type TimeOfDay = 'MORNING' | 'AFTERNOON' | 'EVENING';
|
|
18
|
+
|
|
19
|
+
export interface GroupedEvents {
|
|
20
|
+
morning: EventResponse[];
|
|
21
|
+
afternoon: EventResponse[];
|
|
22
|
+
evening: EventResponse[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TimeOfDayInfo {
|
|
26
|
+
key: TimeOfDay;
|
|
27
|
+
label: string;
|
|
28
|
+
emoji: string;
|
|
29
|
+
hours: { start: number; end: number };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// CONSTANTS
|
|
34
|
+
// ============================================================================
|
|
35
|
+
|
|
36
|
+
export const TIME_OF_DAY_CONFIG: Record<TimeOfDay, TimeOfDayInfo> = {
|
|
37
|
+
MORNING: {
|
|
38
|
+
key: 'MORNING',
|
|
39
|
+
label: 'Morning',
|
|
40
|
+
emoji: '',
|
|
41
|
+
hours: { start: 5, end: 11 }, // 5:00 AM - 11:59 AM
|
|
42
|
+
},
|
|
43
|
+
AFTERNOON: {
|
|
44
|
+
key: 'AFTERNOON',
|
|
45
|
+
label: 'Afternoon',
|
|
46
|
+
emoji: '',
|
|
47
|
+
hours: { start: 12, end: 16 }, // 12:00 PM - 4:59 PM
|
|
48
|
+
},
|
|
49
|
+
EVENING: {
|
|
50
|
+
key: 'EVENING',
|
|
51
|
+
label: 'Evening',
|
|
52
|
+
emoji: '',
|
|
53
|
+
hours: { start: 17, end: 4 }, // 5:00 PM - 4:59 AM (next day)
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// FUNCTIONS
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get time of day for a given date
|
|
63
|
+
*
|
|
64
|
+
* @param date - Date string or Date object
|
|
65
|
+
* @returns TimeOfDay - MORNING, AFTERNOON, or EVENING
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const timeOfDay = getTimeOfDay('2024-03-15T10:00:00Z');
|
|
70
|
+
* console.log(timeOfDay); // 'MORNING'
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function getTimeOfDay(date: Date | string): TimeOfDay {
|
|
74
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
75
|
+
const hour = d.getHours();
|
|
76
|
+
|
|
77
|
+
if (hour >= 5 && hour < 12) return 'MORNING';
|
|
78
|
+
if (hour >= 12 && hour < 17) return 'AFTERNOON';
|
|
79
|
+
return 'EVENING';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get label for time of day
|
|
84
|
+
*
|
|
85
|
+
* @param timeOfDay - TimeOfDay enum value
|
|
86
|
+
* @returns Human-readable label
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* const label = getTimeOfDayLabel('MORNING');
|
|
91
|
+
* console.log(label); // 'Morning'
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function getTimeOfDayLabel(timeOfDay: TimeOfDay): string {
|
|
95
|
+
return TIME_OF_DAY_CONFIG[timeOfDay].label;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Group events by time of day
|
|
100
|
+
*
|
|
101
|
+
* @param events - Array of events to group
|
|
102
|
+
* @returns GroupedEvents object with morning, afternoon, and evening arrays
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```tsx
|
|
106
|
+
* import { useUpcomingEvents, groupEventsByTimeOfDay } from '@growsober/sdk';
|
|
107
|
+
*
|
|
108
|
+
* function GatheringsList() {
|
|
109
|
+
* const { data: events } = useUpcomingEvents(30);
|
|
110
|
+
* const grouped = useMemo(() => {
|
|
111
|
+
* if (!events) return null;
|
|
112
|
+
* return groupEventsByTimeOfDay(events);
|
|
113
|
+
* }, [events]);
|
|
114
|
+
*
|
|
115
|
+
* return (
|
|
116
|
+
* <SectionList
|
|
117
|
+
* sections={[
|
|
118
|
+
* { title: 'Morning', data: grouped?.morning || [] },
|
|
119
|
+
* { title: 'Afternoon', data: grouped?.afternoon || [] },
|
|
120
|
+
* { title: 'Evening', data: grouped?.evening || [] },
|
|
121
|
+
* ]}
|
|
122
|
+
* ...
|
|
123
|
+
* />
|
|
124
|
+
* );
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function groupEventsByTimeOfDay(events: EventResponse[]): GroupedEvents {
|
|
129
|
+
const grouped: GroupedEvents = {
|
|
130
|
+
morning: [],
|
|
131
|
+
afternoon: [],
|
|
132
|
+
evening: [],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
for (const event of events) {
|
|
136
|
+
const timeOfDay = getTimeOfDay(event.startDate);
|
|
137
|
+
switch (timeOfDay) {
|
|
138
|
+
case 'MORNING':
|
|
139
|
+
grouped.morning.push(event);
|
|
140
|
+
break;
|
|
141
|
+
case 'AFTERNOON':
|
|
142
|
+
grouped.afternoon.push(event);
|
|
143
|
+
break;
|
|
144
|
+
case 'EVENING':
|
|
145
|
+
grouped.evening.push(event);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return grouped;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Convert grouped events to section list data format
|
|
155
|
+
*
|
|
156
|
+
* @param events - Array of events to group
|
|
157
|
+
* @returns Array of sections with title and data
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```tsx
|
|
161
|
+
* const sections = getEventSections(events);
|
|
162
|
+
* // [
|
|
163
|
+
* // { title: 'Morning', data: [...] },
|
|
164
|
+
* // { title: 'Afternoon', data: [...] },
|
|
165
|
+
* // { title: 'Evening', data: [...] },
|
|
166
|
+
* // ]
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export function getEventSections(events: EventResponse[]): Array<{
|
|
170
|
+
title: string;
|
|
171
|
+
timeOfDay: TimeOfDay;
|
|
172
|
+
data: EventResponse[];
|
|
173
|
+
}> {
|
|
174
|
+
const grouped = groupEventsByTimeOfDay(events);
|
|
175
|
+
|
|
176
|
+
return [
|
|
177
|
+
{ title: 'Morning', timeOfDay: 'MORNING' as TimeOfDay, data: grouped.morning },
|
|
178
|
+
{ title: 'Afternoon', timeOfDay: 'AFTERNOON' as TimeOfDay, data: grouped.afternoon },
|
|
179
|
+
{ title: 'Evening', timeOfDay: 'EVENING' as TimeOfDay, data: grouped.evening },
|
|
180
|
+
].filter(section => section.data.length > 0);
|
|
181
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -55,3 +55,9 @@ export * from './api/queries';
|
|
|
55
55
|
|
|
56
56
|
export * from './api/mutations';
|
|
57
57
|
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// UTILITIES
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
export * from './api/utils/eventGrouping';
|
|
63
|
+
|