@bash-app/bash-common 30.113.0 → 30.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/definitions.d.ts +2 -2
  2. package/dist/definitions.d.ts.map +1 -1
  3. package/dist/definitions.js +2 -2
  4. package/dist/definitions.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/utils/__tests__/recurrenceUtils.test.d.ts +2 -0
  10. package/dist/utils/__tests__/recurrenceUtils.test.d.ts.map +1 -0
  11. package/dist/utils/__tests__/recurrenceUtils.test.js +193 -0
  12. package/dist/utils/__tests__/recurrenceUtils.test.js.map +1 -0
  13. package/dist/utils/discountEngine/bestPriceResolver.d.ts +31 -0
  14. package/dist/utils/discountEngine/bestPriceResolver.d.ts.map +1 -0
  15. package/dist/utils/discountEngine/bestPriceResolver.js +147 -0
  16. package/dist/utils/discountEngine/bestPriceResolver.js.map +1 -0
  17. package/dist/utils/discountEngine/discountCalculator.d.ts +56 -0
  18. package/dist/utils/discountEngine/discountCalculator.d.ts.map +1 -0
  19. package/dist/utils/discountEngine/discountCalculator.js +219 -0
  20. package/dist/utils/discountEngine/discountCalculator.js.map +1 -0
  21. package/dist/utils/discountEngine/eligibilityValidator.d.ts +36 -0
  22. package/dist/utils/discountEngine/eligibilityValidator.d.ts.map +1 -0
  23. package/dist/utils/discountEngine/eligibilityValidator.js +189 -0
  24. package/dist/utils/discountEngine/eligibilityValidator.js.map +1 -0
  25. package/dist/utils/discountEngine/index.d.ts +4 -0
  26. package/dist/utils/discountEngine/index.d.ts.map +1 -0
  27. package/dist/utils/discountEngine/index.js +5 -0
  28. package/dist/utils/discountEngine/index.js.map +1 -0
  29. package/dist/utils/recurrenceUtils.d.ts.map +1 -1
  30. package/dist/utils/recurrenceUtils.js +17 -4
  31. package/dist/utils/recurrenceUtils.js.map +1 -1
  32. package/package.json +5 -5
  33. package/prisma/schema.prisma +387 -81
  34. package/src/definitions.ts +2 -1
  35. package/src/index.ts +2 -0
  36. package/src/utils/discountEngine/bestPriceResolver.ts +212 -0
  37. package/src/utils/discountEngine/discountCalculator.ts +281 -0
  38. package/src/utils/discountEngine/eligibilityValidator.ts +256 -0
  39. package/src/utils/discountEngine/index.ts +5 -0
  40. package/src/utils/recurrenceUtils.ts +20 -4
@@ -54,15 +54,20 @@ model BashComment {
54
54
 
55
55
  // BashFeed Models - Event-Centric Social Discovery Feed
56
56
  model BashFeedPost {
57
- id String @id @default(cuid())
58
- userId String
59
- bashId String
60
- content String? @db.Text
61
- mediaIds String[] // Array of Media IDs
62
- isAnonymous Boolean @default(false) // User can choose to post anonymously
63
- createdAt DateTime @default(now())
64
- updatedAt DateTime @updatedAt
65
- editedAt DateTime?
57
+ id String @id @default(cuid())
58
+ userId String
59
+ bashId String
60
+ content String? @db.Text
61
+ mediaIds String[] // Array of Media IDs
62
+ mentionedUserIds String[] // Array of User IDs mentioned in post
63
+ isAnonymous Boolean @default(false) // User can choose to post anonymously
64
+ viewCount Int @default(0) // Analytics only, not shown publicly
65
+ isFeatured Boolean @default(false) // Post is promoted/featured
66
+ featuredAt DateTime? // When post was featured
67
+ featuredBoost Float @default(0) // Current boost score (time-decayed)
68
+ createdAt DateTime @default(now())
69
+ updatedAt DateTime @updatedAt
70
+ editedAt DateTime?
66
71
 
67
72
  // Relationships
68
73
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -78,6 +83,7 @@ model BashFeedPost {
78
83
  @@index([userId])
79
84
  @@index([createdAt])
80
85
  @@index([isAnonymous])
86
+ @@index([isFeatured, featuredBoost])
81
87
  }
82
88
 
83
89
  model BashFeedLike {
@@ -94,13 +100,14 @@ model BashFeedLike {
94
100
  }
95
101
 
96
102
  model BashFeedComment {
97
- id String @id @default(cuid())
98
- userId String
99
- postId String
100
- content String @db.Text
101
- parentId String? // For nested replies
102
- createdAt DateTime @default(now())
103
- updatedAt DateTime @updatedAt
103
+ id String @id @default(cuid())
104
+ userId String
105
+ postId String
106
+ content String @db.Text
107
+ mentionedUserIds String[] // Array of User IDs mentioned in comment
108
+ parentId String? // For nested replies
109
+ createdAt DateTime @default(now())
110
+ updatedAt DateTime @updatedAt
104
111
 
105
112
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
106
113
  post BashFeedPost @relation(fields: [postId], references: [id], onDelete: Cascade)
@@ -194,29 +201,36 @@ model BashFeedRepost {
194
201
  }
195
202
 
196
203
  model Competition {
197
- id String @id @default(cuid())
204
+ id String @id @default(cuid())
198
205
  name String
199
- description String @db.Text
206
+ description String @db.Text
200
207
  userId String
201
208
  bashEventId String
202
209
  numberOfPrizes Int
203
210
  competitionType CompetitionType? // Main category
204
211
  subtype String? // Subcategory
205
- rules String? @db.Text // Detailed rules/disclaimers
212
+ rules String? @db.Text // Detailed rules/disclaimers
206
213
  judgingType JudgingType? // How winner is determined
207
214
  startTime DateTime? // Competition start time (for voting)
208
215
  endTime DateTime? // Competition end time (for voting)
209
- allowSelfRegistration Boolean @default(false)
216
+ allowSelfRegistration Boolean @default(false)
210
217
  maxParticipants Int? // Optional cap on participants
211
- createdAt DateTime @default(now())
212
- updatedAt DateTime @updatedAt
213
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
214
- owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
215
- sponsor CompetitionSponsor[]
216
- prizes Prize[]
217
- participants CompetitionParticipant[]
218
- votes CompetitionVote[]
219
- notifications Notification[]
218
+
219
+ // Tournament bracket fields
220
+ bracketType BracketType? @default(NONE)
221
+ bracketData Json? // Store bracket structure/metadata
222
+ bracketVisible Boolean @default(true)
223
+
224
+ createdAt DateTime @default(now())
225
+ updatedAt DateTime @updatedAt
226
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
227
+ owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
228
+ sponsor CompetitionSponsor[]
229
+ prizes Prize[]
230
+ participants CompetitionParticipant[]
231
+ votes CompetitionVote[]
232
+ matches CompetitionMatch[]
233
+ notifications Notification[]
220
234
 
221
235
  @@index([bashEventId])
222
236
  @@index([competitionType])
@@ -238,20 +252,63 @@ model CompetitionParticipant {
238
252
  id String @id @default(cuid())
239
253
  competitionId String
240
254
  userId String? // Optional: links to user if registered
255
+ email String? // For non-registered participants
241
256
  name String // Display name
242
257
  description String? @db.Text // Why they should win
243
258
  imageUrl String? // Photo/avatar
244
259
  order Int @default(0) // Display order
245
260
  voteCount Int @default(0) // Denormalized for performance
261
+ isEliminated Boolean @default(false) // For tournament brackets
262
+ placement Int? // Final placement (1st, 2nd, 3rd, etc.)
246
263
  addedBy String @default("host") // 'host' or 'self'
264
+ status ParticipantStatus @default(ACCEPTED) // Invitation/request status
265
+ invitedAt DateTime? // When host invited them
266
+ acceptedAt DateTime? // When they accepted invitation
267
+ declinedAt DateTime? // When they declined invitation/request
247
268
  createdAt DateTime @default(now())
248
269
  competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
249
270
  user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
250
271
  votes CompetitionVote[]
251
272
 
273
+ // Tournament bracket relations
274
+ matchesAsParticipant1 CompetitionMatch[] @relation("MatchParticipant1")
275
+ matchesAsParticipant2 CompetitionMatch[] @relation("MatchParticipant2")
276
+ matchesWon CompetitionMatch[] @relation("MatchWinner")
277
+
252
278
  @@index([competitionId])
253
279
  @@index([userId])
280
+ @@index([email])
254
281
  @@index([voteCount])
282
+ @@index([status])
283
+ }
284
+
285
+ // Tournament bracket matches
286
+ model CompetitionMatch {
287
+ id String @id @default(cuid())
288
+ competitionId String
289
+ round Int // 1 = first round, 2 = semifinals, etc.
290
+ matchNumber Int // Position in round (1, 2, 3...)
291
+
292
+ participant1Id String?
293
+ participant2Id String?
294
+ winnerId String?
295
+
296
+ scheduledTime DateTime?
297
+ completedAt DateTime?
298
+ score Json? // {participant1: 10, participant2: 8}
299
+ notes String? // Match notes
300
+
301
+ competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
302
+ participant1 CompetitionParticipant? @relation("MatchParticipant1", fields: [participant1Id], references: [id])
303
+ participant2 CompetitionParticipant? @relation("MatchParticipant2", fields: [participant2Id], references: [id])
304
+ winner CompetitionParticipant? @relation("MatchWinner", fields: [winnerId], references: [id])
305
+
306
+ createdAt DateTime @default(now())
307
+ updatedAt DateTime @updatedAt
308
+
309
+ @@unique([competitionId, round, matchNumber])
310
+ @@index([competitionId])
311
+ @@index([round])
255
312
  }
256
313
 
257
314
  model CompetitionVote {
@@ -617,6 +674,9 @@ model BashEvent {
617
674
  bashFeedRatings BashFeedRating[]
618
675
  eventRankings EventRankings?
619
676
 
677
+ // Analytics Relations
678
+ analyticsPredictions AnalyticsPrediction[]
679
+
620
680
  @@index([templateId])
621
681
  @@index([parentEventId])
622
682
  }
@@ -630,14 +690,15 @@ model Coordinates {
630
690
  }
631
691
 
632
692
  model Checkout {
633
- id String @id @default(cuid())
693
+ id String @id @default(cuid())
634
694
  ownerId String
635
695
  bashEventId String
636
- checkoutDateTime DateTime? @default(now())
637
- stripeCheckoutSessionId String? @unique
638
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
639
- owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
696
+ checkoutDateTime DateTime? @default(now())
697
+ stripeCheckoutSessionId String? @unique
698
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
699
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
640
700
  tickets Ticket[]
701
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this checkout
641
702
 
642
703
  @@index([bashEventId])
643
704
  }
@@ -784,12 +845,14 @@ model TicketTier {
784
845
  tickets Ticket[]
785
846
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
786
847
  waitList User[] @relation("TicketTierToUser")
848
+ specialOffers SpecialOffer[] // NEW - Special offers for this tier
849
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this tier
787
850
 
788
851
  @@unique([bashEventId, title])
789
852
  }
790
853
 
791
854
  model Ticket {
792
- id String @id @default(cuid())
855
+ id String @id @default(cuid())
793
856
  ownerId String
794
857
  bashEventId String
795
858
  ticketTierId String?
@@ -799,35 +862,36 @@ model Ticket {
799
862
  email String?
800
863
  paidOn DateTime?
801
864
  allowPromiseToPay Boolean?
802
- status TicketStatus @default(Pending)
865
+ status TicketStatus @default(Pending)
803
866
  isFreeGuest Boolean?
804
867
  geoFenceCheckInUnnecessary Boolean?
805
868
  checkedInAt DateTime?
806
869
  checkedOutAt DateTime?
807
870
  invitationId String?
808
871
  checkoutId String?
809
- checkedInMethod String? @default("manual")
872
+ checkedInMethod String? @default("manual")
810
873
  waitlistPosition Int?
811
874
  waitlistJoinedAt DateTime?
812
875
  waitlistNotifiedAt DateTime?
813
876
  waitlistExpiresAt DateTime?
814
877
  checkInLocation String?
815
878
  checkOutLocation String?
816
- checkOutMethod String? @default("manual")
879
+ checkOutMethod String? @default("manual")
817
880
  lastLocationUpdate DateTime?
818
- validationAttempts Int @default(0)
881
+ validationAttempts Int @default(0)
819
882
  lastValidationAttempt DateTime?
820
883
  lastValidatedBy String?
821
884
  waitlistUserId String?
822
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
823
- checkout Checkout? @relation(fields: [checkoutId], references: [id])
824
- forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
825
- invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
826
- owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
827
- ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
828
- waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
885
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
886
+ checkout Checkout? @relation(fields: [checkoutId], references: [id])
887
+ forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
888
+ invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
889
+ owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
890
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
891
+ waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
829
892
  metadata TicketMetadata[]
830
893
  transfers TicketTransfer[]
894
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this ticket
831
895
 
832
896
  @@index([bashEventId])
833
897
  @@index([waitlistUserId])
@@ -872,10 +936,14 @@ model Recurrence {
872
936
  interval Int @default(1)
873
937
  bashEventId String @unique
874
938
  frequency RecurringFrequency @default(Never)
875
- ends DateTime @default(now())
939
+ ends DateTime? // Nullable - used when endsType = ON_DATE
940
+ repeatCount Int? // Used when endsType = AFTER_COUNT
941
+ endsType RecurrenceEndsType @default(ON_DATE)
876
942
  repeatOnDays DayOfWeek[]
877
943
  repeatOnDayOfMonth Int?
878
944
  repeatYearlyDate DateTime?
945
+ createdAt DateTime @default(now())
946
+ updatedAt DateTime @updatedAt
879
947
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
880
948
  }
881
949
 
@@ -1339,6 +1407,9 @@ model User {
1339
1407
  reviewedCategoryRequests ServiceCategoryRequest[] @relation("ReviewedCategoryRequests")
1340
1408
  bookingCommissionsEarned BookingCommission[] @relation("CommissionReferrer")
1341
1409
 
1410
+ // Special Offers - Discount Attribution
1411
+ discountAttributions AppliedDiscount[] @relation("DiscountAttribution")
1412
+
1342
1413
  // BashCash Referral Relations
1343
1414
  referredBy User? @relation("Referrals", fields: [referredById], references: [id])
1344
1415
  referrals User[] @relation("Referrals")
@@ -1423,40 +1494,46 @@ model UserPreferences {
1423
1494
  preferredCurrency String @default("USD")
1424
1495
  savePaymentInfo Boolean @default(false)
1425
1496
  showEventPrices Boolean @default(true)
1426
- createdAt DateTime @default(now())
1427
- updatedAt DateTime @updatedAt
1428
- smsNotifications Boolean @default(false)
1429
- friendRequestNotify Boolean @default(true)
1430
- allowTagging Boolean @default(true)
1431
- showActivityStatus Boolean @default(true)
1432
- hiddenBashIds String[] @default([])
1433
- hideActivitySection Boolean @default(false)
1434
- allowLocationSharing Boolean @default(false)
1435
- defaultLandingPage String @default("dashboard")
1436
- contentDensity String @default("COMFORTABLE")
1437
- fontScale String @default("MEDIUM")
1438
- animationsEnabled Boolean @default(true)
1439
- useHighContrastMode Boolean @default(false)
1440
- contentFilters String[] @default([])
1441
- topicInterests String[] @default([])
1442
- hideSeenContent Boolean @default(false)
1443
- autoplayVideos Boolean @default(true)
1444
- contentSortPreference String @default("RECENT")
1445
- contentLanguages String[] @default(["en"])
1446
- showSensitiveContent Boolean @default(false)
1447
- defaultEventReminder Int @default(60)
1448
- communicationFrequency String @default("NORMAL")
1449
- preferredContactMethod String @default("EMAIL")
1450
- googleCalendarSyncEnabled Boolean @default(false)
1451
- googleCalendarEventsSync Boolean @default(false)
1452
- googleCalendarServicesSync Boolean @default(false)
1453
- localEventsToCalendarSync Boolean @default(false)
1454
- localEventsCity String @default("")
1455
- localEventsState String @default("")
1456
- showRankings Boolean @default(true) // Show rankings on profile
1457
- showAttendeeRank Boolean @default(true) // Show attendee rank publicly
1458
- showHostRank Boolean @default(true) // Show host rank publicly
1459
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1497
+
1498
+ // BashFeed Preferences
1499
+ bashFeedDefaultTab String @default("all") // "all", "upcoming", "live", "past", "saved", "my-posts"
1500
+ bashFeedDefaultSort String @default("chronological") // "for_you", "chronological", "trending", etc.
1501
+ bashFeedFilters Json? // Saved filter preferences as JSON
1502
+
1503
+ createdAt DateTime @default(now())
1504
+ updatedAt DateTime @updatedAt
1505
+ smsNotifications Boolean @default(false)
1506
+ friendRequestNotify Boolean @default(true)
1507
+ allowTagging Boolean @default(true)
1508
+ showActivityStatus Boolean @default(true)
1509
+ hiddenBashIds String[] @default([])
1510
+ hideActivitySection Boolean @default(false)
1511
+ allowLocationSharing Boolean @default(false)
1512
+ defaultLandingPage String @default("dashboard")
1513
+ contentDensity String @default("COMFORTABLE")
1514
+ fontScale String @default("MEDIUM")
1515
+ animationsEnabled Boolean @default(true)
1516
+ useHighContrastMode Boolean @default(false)
1517
+ contentFilters String[] @default([])
1518
+ topicInterests String[] @default([])
1519
+ hideSeenContent Boolean @default(false)
1520
+ autoplayVideos Boolean @default(true)
1521
+ contentSortPreference String @default("RECENT")
1522
+ contentLanguages String[] @default(["en"])
1523
+ showSensitiveContent Boolean @default(false)
1524
+ defaultEventReminder Int @default(60)
1525
+ communicationFrequency String @default("NORMAL")
1526
+ preferredContactMethod String @default("EMAIL")
1527
+ googleCalendarSyncEnabled Boolean @default(false)
1528
+ googleCalendarEventsSync Boolean @default(false)
1529
+ googleCalendarServicesSync Boolean @default(false)
1530
+ localEventsToCalendarSync Boolean @default(false)
1531
+ localEventsCity String @default("")
1532
+ localEventsState String @default("")
1533
+ showRankings Boolean @default(true) // Show rankings on profile
1534
+ showAttendeeRank Boolean @default(true) // Show attendee rank publicly
1535
+ showHostRank Boolean @default(true) // Show host rank publicly
1536
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1460
1537
  }
1461
1538
 
1462
1539
  model UserFollowing {
@@ -2971,6 +3048,12 @@ enum NotificationType {
2971
3048
  CompetitionEnding
2972
3049
  CompetitionWon
2973
3050
  CompetitionResults
3051
+ CompetitionParticipantInvited // Host invited you to compete
3052
+ CompetitionParticipantAccepted // Participant accepted invitation
3053
+ CompetitionParticipantDeclined // Participant declined invitation
3054
+ CompetitionParticipantRequested // Someone requested to join your competition
3055
+ CompetitionParticipantApproved // Host approved your request to compete
3056
+ CompetitionParticipantRejected // Host rejected your request
2974
3057
  BashRating
2975
3058
  BashRatingMilestone
2976
3059
  BashFeedPost
@@ -3366,6 +3449,29 @@ enum JudgingType {
3366
3449
  HOST_CHOOSES // Host/organizer picks winner
3367
3450
  FIRST_COME // First X people win
3368
3451
  HIGHEST_BID // Auction-style
3452
+ BRACKET_TOURNAMENT // Winner determined through tournament bracket progression
3453
+ }
3454
+
3455
+ enum BracketType {
3456
+ NONE // No bracket (voting/judging only)
3457
+ SINGLE_ELIMINATION // Lose once, you're out
3458
+ DOUBLE_ELIMINATION // Lose twice, you're out
3459
+ ROUND_ROBIN // Everyone plays everyone
3460
+ SWISS // Pairing based on performance
3461
+ }
3462
+
3463
+ enum ParticipantStatus {
3464
+ INVITED // Host invited them, waiting for acceptance
3465
+ PENDING // User requested to join, waiting for host approval
3466
+ ACCEPTED // Confirmed participant
3467
+ DECLINED // Declined invitation or request denied
3468
+ REMOVED // Host removed them
3469
+ }
3470
+
3471
+ enum RecurrenceEndsType {
3472
+ NEVER // Never ends
3473
+ ON_DATE // Ends on specific date
3474
+ AFTER_COUNT // Ends after X occurrences
3369
3475
  }
3370
3476
 
3371
3477
  enum Sex {
@@ -5274,3 +5380,203 @@ enum EventFormat {
5274
5380
  Virtual
5275
5381
  Hybrid
5276
5382
  }
5383
+
5384
+ // ============================================
5385
+ // SPECIAL OFFERS & UNIFIED DISCOUNT SYSTEM
5386
+ // ============================================
5387
+
5388
+ enum DiscountSourceType {
5389
+ PROMO_CODE
5390
+ SPECIAL_OFFER
5391
+ PLATFORM_CREDIT // For future BashCash integration
5392
+ MANUAL_OVERRIDE // Admin adjustments
5393
+ }
5394
+
5395
+ enum DiscountScope {
5396
+ ORDER // Applied to entire order
5397
+ LINE_ITEM // Applied to specific line item
5398
+ TICKET_TIER // Applied to specific ticket tier
5399
+ }
5400
+
5401
+ enum SpecialOfferType {
5402
+ BOGO // Buy One Get One
5403
+ GROUP_DISCOUNT // Group rates
5404
+ EARLY_BIRD // Early bird pricing
5405
+ FLASH_SALE // Time-limited offer
5406
+ BUNDLE // Package deal
5407
+ REFERRAL // Friend referral (needs attribution)
5408
+ VOLUME_DISCOUNT // Buy more save more
5409
+ FAMILY_PACK // Family package
5410
+ LOYALTY // Repeat customer
5411
+ COUNTDOWN // Urgency timer
5412
+ }
5413
+
5414
+ enum OfferDiscountType {
5415
+ PERCENTAGE // X% off
5416
+ FIXED_AMOUNT // $X off
5417
+ FREE_ITEMS // Buy X get Y free
5418
+ UPGRADED_TIER // Free upgrade to VIP
5419
+ }
5420
+
5421
+ // Unified discount tracking - captures ALL discounts at checkout
5422
+ model AppliedDiscount {
5423
+ id String @id @default(cuid())
5424
+ createdAt DateTime @default(now())
5425
+
5426
+ // What discount was applied
5427
+ sourceType DiscountSourceType
5428
+ sourceId String // ID of PromoCode or SpecialOffer
5429
+ sourceName String // "SUMMER20" or "Early Bird Special"
5430
+
5431
+ // Scope and target
5432
+ scope DiscountScope
5433
+ checkoutId String?
5434
+ ticketId String?
5435
+ ticketTierId String?
5436
+
5437
+ // Discount details
5438
+ discountType String // "PERCENTAGE" or "FIXED_AMOUNT"
5439
+ discountValue Int // Percentage or cents
5440
+ amountDiscounted Int // Actual discount applied in cents
5441
+ originalPrice Int // Price before discount
5442
+ finalPrice Int // Price after discount
5443
+
5444
+ // Attribution (optional - for PromoCodes and Referral offers)
5445
+ hasAttribution Boolean @default(false)
5446
+ attributionUserId String? // Promoter/Influencer/Referrer
5447
+ attributionCampaignId String?
5448
+ commissionRate Float?
5449
+ commissionAmount Int? // In cents
5450
+
5451
+ // Display
5452
+ displayReason String? // "Early Bird Discount" for receipt
5453
+ internalNote String? // For support/debugging
5454
+
5455
+ // Relations
5456
+ checkout Checkout? @relation(fields: [checkoutId], references: [id])
5457
+ ticket Ticket? @relation(fields: [ticketId], references: [id])
5458
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
5459
+ attributionUser User? @relation("DiscountAttribution", fields: [attributionUserId], references: [id])
5460
+
5461
+ @@index([checkoutId])
5462
+ @@index([sourceType, sourceId])
5463
+ @@index([attributionUserId])
5464
+ @@index([createdAt])
5465
+ }
5466
+
5467
+ // Special offers - automatic, visible, conversion-oriented deals
5468
+ model SpecialOffer {
5469
+ id String @id @default(cuid())
5470
+ createdAt DateTime @default(now())
5471
+ updatedAt DateTime @updatedAt
5472
+
5473
+ // Basic Info
5474
+ ticketTierId String
5475
+ offerType SpecialOfferType
5476
+ title String // "Early Bird Special", "BOGO Deal"
5477
+ description String? @db.Text
5478
+ isActive Boolean @default(true)
5479
+ displayBadge Boolean @default(true)
5480
+ badgeText String? // "50% OFF", "BOGO"
5481
+
5482
+ // Discount Configuration
5483
+ discountType OfferDiscountType
5484
+ discountValue Int // Percentage or cents
5485
+
5486
+ // BOGO Configuration
5487
+ buyQuantity Int? // Buy X tickets
5488
+ getQuantity Int? // Get Y tickets
5489
+ getDiscountPercent Int? // Get Y at Z% off (0-100)
5490
+
5491
+ // Group Discount Configuration
5492
+ minGroupSize Int?
5493
+ maxGroupSize Int?
5494
+ tieredDiscounts Json? // [{qty: 5, discount: 10}, {qty: 10, discount: 20}]
5495
+
5496
+ // Time-Based Configuration
5497
+ startDate DateTime?
5498
+ endDate DateTime?
5499
+ availableUntil DateTime? // Early bird cutoff
5500
+
5501
+ // Quantity Limitations
5502
+ maxRedemptions Int?
5503
+ maxPerUser Int?
5504
+ limitedQuantity Int?
5505
+ currentRedemptions Int @default(0)
5506
+
5507
+ // Social Proof Tracking
5508
+ viewCount Int @default(0) // How many times this offer was viewed
5509
+ clickCount Int @default(0) // How many times users clicked "Apply"
5510
+ lastRedemptionAt DateTime? // When was the last redemption
5511
+
5512
+ // Price Increase (Countdown)
5513
+ priceIncreasesAt DateTime?
5514
+ newPriceAfterIncrease Int?
5515
+ countdownEnabled Boolean @default(false)
5516
+
5517
+ // Bundle Configuration
5518
+ bundledItems Json?
5519
+ bundleSavings Int?
5520
+
5521
+ // Attribution (ONLY for Referral type offers)
5522
+ requiresAttribution Boolean @default(false)
5523
+ referralDiscountForBuyer Int?
5524
+ referralDiscountForFriend Int?
5525
+ commissionRate Float? // For referrer
5526
+
5527
+ // Loyalty Configuration
5528
+ requiresEventAttendance Int?
5529
+ hostSpecificLoyalty Boolean @default(false)
5530
+
5531
+ // Stacking Rules
5532
+ canStackWithPromoCodes Boolean @default(true) // Host decides
5533
+ canStackWithOtherOffers Boolean @default(false)
5534
+ priority Int @default(0) // Higher = applied first
5535
+
5536
+ // Display & Marketing
5537
+ urgencyMessage String? @db.Text
5538
+ showSocialProof Boolean @default(false)
5539
+ highlightColor String?
5540
+ sortOrder Int @default(0)
5541
+
5542
+ // Guardrails
5543
+ minPriceFloor Int? // Never discount below this
5544
+ maxDiscountPercent Int? // Never discount more than X%
5545
+
5546
+ // Relations
5547
+ ticketTier TicketTier @relation(fields: [ticketTierId], references: [id], onDelete: Cascade)
5548
+
5549
+ @@index([ticketTierId])
5550
+ @@index([offerType])
5551
+ @@index([isActive])
5552
+ @@index([endDate])
5553
+ }
5554
+
5555
+ // Analytics Prediction Cache Model
5556
+ model AnalyticsPrediction {
5557
+ id String @id @default(cuid())
5558
+ bashEventId String
5559
+ type PredictionType
5560
+ source PredictionSource
5561
+ prediction Json
5562
+ confidence Float
5563
+ generatedAt DateTime @default(now())
5564
+ validUntil DateTime
5565
+
5566
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
5567
+
5568
+ @@index([bashEventId, type])
5569
+ @@index([validUntil])
5570
+ }
5571
+
5572
+ enum PredictionType {
5573
+ REVENUE_FORECAST
5574
+ PRICING_OPTIMIZATION
5575
+ SELLOUT_PROBABILITY
5576
+ ACTION_RECOMMENDATION
5577
+ }
5578
+
5579
+ enum PredictionSource {
5580
+ RULE_BASED
5581
+ AI_ENHANCED
5582
+ }
@@ -2,6 +2,7 @@ import {
2
2
  BashEventDressTags,
3
3
  BashEventType,
4
4
  BashEventVibeTags,
5
+ BracketType,
5
6
  CompetitionType,
6
7
  Contact,
7
8
  EntertainmentServiceType,
@@ -879,7 +880,7 @@ export const YearsOfExperienceToString: { [key in YearsOfExperience]: string } =
879
880
  };
880
881
 
881
882
  // Export Prisma enums for use in frontend
882
- export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes, CompetitionType, JudgingType };
883
+ export { YearsOfExperience, EntertainmentServiceType, MusicGenreType, ServiceTypes, CompetitionType, JudgingType, BracketType };
883
884
 
884
885
  export type BashEventTypeToStringType = {
885
886
  [key in BashEventType]: string;
package/src/index.ts CHANGED
@@ -8,6 +8,8 @@ export * from "./utils/awsS3Utils";
8
8
  export * from "./utils/bashCashPaymentUtils";
9
9
  export * from "./utils/contentFilterUtils";
10
10
  export * from "./utils/dateTimeUtils";
11
+ export * from "./utils/discountEngine/bestPriceResolver";
12
+ export * from "./utils/discountEngine/eligibilityValidator";
11
13
  export * from "./utils/objUtils";
12
14
  export * from "./utils/paymentUtils";
13
15
  export * from "./utils/promoCodesUtils";