@bash-app/bash-common 30.113.0 → 30.114.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 +1 -1
  33. package/prisma/schema.prisma +366 -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])
@@ -243,17 +257,53 @@ model CompetitionParticipant {
243
257
  imageUrl String? // Photo/avatar
244
258
  order Int @default(0) // Display order
245
259
  voteCount Int @default(0) // Denormalized for performance
260
+ isEliminated Boolean @default(false) // For tournament brackets
261
+ placement Int? // Final placement (1st, 2nd, 3rd, etc.)
246
262
  addedBy String @default("host") // 'host' or 'self'
247
263
  createdAt DateTime @default(now())
248
264
  competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
249
265
  user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
250
266
  votes CompetitionVote[]
251
267
 
268
+ // Tournament bracket relations
269
+ matchesAsParticipant1 CompetitionMatch[] @relation("MatchParticipant1")
270
+ matchesAsParticipant2 CompetitionMatch[] @relation("MatchParticipant2")
271
+ matchesWon CompetitionMatch[] @relation("MatchWinner")
272
+
252
273
  @@index([competitionId])
253
274
  @@index([userId])
254
275
  @@index([voteCount])
255
276
  }
256
277
 
278
+ // Tournament bracket matches
279
+ model CompetitionMatch {
280
+ id String @id @default(cuid())
281
+ competitionId String
282
+ round Int // 1 = first round, 2 = semifinals, etc.
283
+ matchNumber Int // Position in round (1, 2, 3...)
284
+
285
+ participant1Id String?
286
+ participant2Id String?
287
+ winnerId String?
288
+
289
+ scheduledTime DateTime?
290
+ completedAt DateTime?
291
+ score Json? // {participant1: 10, participant2: 8}
292
+ notes String? // Match notes
293
+
294
+ competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
295
+ participant1 CompetitionParticipant? @relation("MatchParticipant1", fields: [participant1Id], references: [id])
296
+ participant2 CompetitionParticipant? @relation("MatchParticipant2", fields: [participant2Id], references: [id])
297
+ winner CompetitionParticipant? @relation("MatchWinner", fields: [winnerId], references: [id])
298
+
299
+ createdAt DateTime @default(now())
300
+ updatedAt DateTime @updatedAt
301
+
302
+ @@unique([competitionId, round, matchNumber])
303
+ @@index([competitionId])
304
+ @@index([round])
305
+ }
306
+
257
307
  model CompetitionVote {
258
308
  id String @id @default(cuid())
259
309
  competitionId String
@@ -617,6 +667,9 @@ model BashEvent {
617
667
  bashFeedRatings BashFeedRating[]
618
668
  eventRankings EventRankings?
619
669
 
670
+ // Analytics Relations
671
+ analyticsPredictions AnalyticsPrediction[]
672
+
620
673
  @@index([templateId])
621
674
  @@index([parentEventId])
622
675
  }
@@ -630,14 +683,15 @@ model Coordinates {
630
683
  }
631
684
 
632
685
  model Checkout {
633
- id String @id @default(cuid())
686
+ id String @id @default(cuid())
634
687
  ownerId String
635
688
  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)
689
+ checkoutDateTime DateTime? @default(now())
690
+ stripeCheckoutSessionId String? @unique
691
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
692
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
640
693
  tickets Ticket[]
694
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this checkout
641
695
 
642
696
  @@index([bashEventId])
643
697
  }
@@ -784,12 +838,14 @@ model TicketTier {
784
838
  tickets Ticket[]
785
839
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
786
840
  waitList User[] @relation("TicketTierToUser")
841
+ specialOffers SpecialOffer[] // NEW - Special offers for this tier
842
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this tier
787
843
 
788
844
  @@unique([bashEventId, title])
789
845
  }
790
846
 
791
847
  model Ticket {
792
- id String @id @default(cuid())
848
+ id String @id @default(cuid())
793
849
  ownerId String
794
850
  bashEventId String
795
851
  ticketTierId String?
@@ -799,35 +855,36 @@ model Ticket {
799
855
  email String?
800
856
  paidOn DateTime?
801
857
  allowPromiseToPay Boolean?
802
- status TicketStatus @default(Pending)
858
+ status TicketStatus @default(Pending)
803
859
  isFreeGuest Boolean?
804
860
  geoFenceCheckInUnnecessary Boolean?
805
861
  checkedInAt DateTime?
806
862
  checkedOutAt DateTime?
807
863
  invitationId String?
808
864
  checkoutId String?
809
- checkedInMethod String? @default("manual")
865
+ checkedInMethod String? @default("manual")
810
866
  waitlistPosition Int?
811
867
  waitlistJoinedAt DateTime?
812
868
  waitlistNotifiedAt DateTime?
813
869
  waitlistExpiresAt DateTime?
814
870
  checkInLocation String?
815
871
  checkOutLocation String?
816
- checkOutMethod String? @default("manual")
872
+ checkOutMethod String? @default("manual")
817
873
  lastLocationUpdate DateTime?
818
- validationAttempts Int @default(0)
874
+ validationAttempts Int @default(0)
819
875
  lastValidationAttempt DateTime?
820
876
  lastValidatedBy String?
821
877
  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])
878
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
879
+ checkout Checkout? @relation(fields: [checkoutId], references: [id])
880
+ forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
881
+ invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
882
+ owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
883
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
884
+ waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
829
885
  metadata TicketMetadata[]
830
886
  transfers TicketTransfer[]
887
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this ticket
831
888
 
832
889
  @@index([bashEventId])
833
890
  @@index([waitlistUserId])
@@ -872,10 +929,14 @@ model Recurrence {
872
929
  interval Int @default(1)
873
930
  bashEventId String @unique
874
931
  frequency RecurringFrequency @default(Never)
875
- ends DateTime @default(now())
932
+ ends DateTime? // Nullable - used when endsType = ON_DATE
933
+ repeatCount Int? // Used when endsType = AFTER_COUNT
934
+ endsType RecurrenceEndsType @default(ON_DATE)
876
935
  repeatOnDays DayOfWeek[]
877
936
  repeatOnDayOfMonth Int?
878
937
  repeatYearlyDate DateTime?
938
+ createdAt DateTime @default(now())
939
+ updatedAt DateTime @updatedAt
879
940
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
880
941
  }
881
942
 
@@ -1339,6 +1400,9 @@ model User {
1339
1400
  reviewedCategoryRequests ServiceCategoryRequest[] @relation("ReviewedCategoryRequests")
1340
1401
  bookingCommissionsEarned BookingCommission[] @relation("CommissionReferrer")
1341
1402
 
1403
+ // Special Offers - Discount Attribution
1404
+ discountAttributions AppliedDiscount[] @relation("DiscountAttribution")
1405
+
1342
1406
  // BashCash Referral Relations
1343
1407
  referredBy User? @relation("Referrals", fields: [referredById], references: [id])
1344
1408
  referrals User[] @relation("Referrals")
@@ -1423,40 +1487,46 @@ model UserPreferences {
1423
1487
  preferredCurrency String @default("USD")
1424
1488
  savePaymentInfo Boolean @default(false)
1425
1489
  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)
1490
+
1491
+ // BashFeed Preferences
1492
+ bashFeedDefaultTab String @default("all") // "all", "upcoming", "live", "past", "saved", "my-posts"
1493
+ bashFeedDefaultSort String @default("chronological") // "for_you", "chronological", "trending", etc.
1494
+ bashFeedFilters Json? // Saved filter preferences as JSON
1495
+
1496
+ createdAt DateTime @default(now())
1497
+ updatedAt DateTime @updatedAt
1498
+ smsNotifications Boolean @default(false)
1499
+ friendRequestNotify Boolean @default(true)
1500
+ allowTagging Boolean @default(true)
1501
+ showActivityStatus Boolean @default(true)
1502
+ hiddenBashIds String[] @default([])
1503
+ hideActivitySection Boolean @default(false)
1504
+ allowLocationSharing Boolean @default(false)
1505
+ defaultLandingPage String @default("dashboard")
1506
+ contentDensity String @default("COMFORTABLE")
1507
+ fontScale String @default("MEDIUM")
1508
+ animationsEnabled Boolean @default(true)
1509
+ useHighContrastMode Boolean @default(false)
1510
+ contentFilters String[] @default([])
1511
+ topicInterests String[] @default([])
1512
+ hideSeenContent Boolean @default(false)
1513
+ autoplayVideos Boolean @default(true)
1514
+ contentSortPreference String @default("RECENT")
1515
+ contentLanguages String[] @default(["en"])
1516
+ showSensitiveContent Boolean @default(false)
1517
+ defaultEventReminder Int @default(60)
1518
+ communicationFrequency String @default("NORMAL")
1519
+ preferredContactMethod String @default("EMAIL")
1520
+ googleCalendarSyncEnabled Boolean @default(false)
1521
+ googleCalendarEventsSync Boolean @default(false)
1522
+ googleCalendarServicesSync Boolean @default(false)
1523
+ localEventsToCalendarSync Boolean @default(false)
1524
+ localEventsCity String @default("")
1525
+ localEventsState String @default("")
1526
+ showRankings Boolean @default(true) // Show rankings on profile
1527
+ showAttendeeRank Boolean @default(true) // Show attendee rank publicly
1528
+ showHostRank Boolean @default(true) // Show host rank publicly
1529
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1460
1530
  }
1461
1531
 
1462
1532
  model UserFollowing {
@@ -3366,6 +3436,21 @@ enum JudgingType {
3366
3436
  HOST_CHOOSES // Host/organizer picks winner
3367
3437
  FIRST_COME // First X people win
3368
3438
  HIGHEST_BID // Auction-style
3439
+ BRACKET_TOURNAMENT // Winner determined through tournament bracket progression
3440
+ }
3441
+
3442
+ enum BracketType {
3443
+ NONE // No bracket (voting/judging only)
3444
+ SINGLE_ELIMINATION // Lose once, you're out
3445
+ DOUBLE_ELIMINATION // Lose twice, you're out
3446
+ ROUND_ROBIN // Everyone plays everyone
3447
+ SWISS // Pairing based on performance
3448
+ }
3449
+
3450
+ enum RecurrenceEndsType {
3451
+ NEVER // Never ends
3452
+ ON_DATE // Ends on specific date
3453
+ AFTER_COUNT // Ends after X occurrences
3369
3454
  }
3370
3455
 
3371
3456
  enum Sex {
@@ -5274,3 +5359,203 @@ enum EventFormat {
5274
5359
  Virtual
5275
5360
  Hybrid
5276
5361
  }
5362
+
5363
+ // ============================================
5364
+ // SPECIAL OFFERS & UNIFIED DISCOUNT SYSTEM
5365
+ // ============================================
5366
+
5367
+ enum DiscountSourceType {
5368
+ PROMO_CODE
5369
+ SPECIAL_OFFER
5370
+ PLATFORM_CREDIT // For future BashCash integration
5371
+ MANUAL_OVERRIDE // Admin adjustments
5372
+ }
5373
+
5374
+ enum DiscountScope {
5375
+ ORDER // Applied to entire order
5376
+ LINE_ITEM // Applied to specific line item
5377
+ TICKET_TIER // Applied to specific ticket tier
5378
+ }
5379
+
5380
+ enum SpecialOfferType {
5381
+ BOGO // Buy One Get One
5382
+ GROUP_DISCOUNT // Group rates
5383
+ EARLY_BIRD // Early bird pricing
5384
+ FLASH_SALE // Time-limited offer
5385
+ BUNDLE // Package deal
5386
+ REFERRAL // Friend referral (needs attribution)
5387
+ VOLUME_DISCOUNT // Buy more save more
5388
+ FAMILY_PACK // Family package
5389
+ LOYALTY // Repeat customer
5390
+ COUNTDOWN // Urgency timer
5391
+ }
5392
+
5393
+ enum OfferDiscountType {
5394
+ PERCENTAGE // X% off
5395
+ FIXED_AMOUNT // $X off
5396
+ FREE_ITEMS // Buy X get Y free
5397
+ UPGRADED_TIER // Free upgrade to VIP
5398
+ }
5399
+
5400
+ // Unified discount tracking - captures ALL discounts at checkout
5401
+ model AppliedDiscount {
5402
+ id String @id @default(cuid())
5403
+ createdAt DateTime @default(now())
5404
+
5405
+ // What discount was applied
5406
+ sourceType DiscountSourceType
5407
+ sourceId String // ID of PromoCode or SpecialOffer
5408
+ sourceName String // "SUMMER20" or "Early Bird Special"
5409
+
5410
+ // Scope and target
5411
+ scope DiscountScope
5412
+ checkoutId String?
5413
+ ticketId String?
5414
+ ticketTierId String?
5415
+
5416
+ // Discount details
5417
+ discountType String // "PERCENTAGE" or "FIXED_AMOUNT"
5418
+ discountValue Int // Percentage or cents
5419
+ amountDiscounted Int // Actual discount applied in cents
5420
+ originalPrice Int // Price before discount
5421
+ finalPrice Int // Price after discount
5422
+
5423
+ // Attribution (optional - for PromoCodes and Referral offers)
5424
+ hasAttribution Boolean @default(false)
5425
+ attributionUserId String? // Promoter/Influencer/Referrer
5426
+ attributionCampaignId String?
5427
+ commissionRate Float?
5428
+ commissionAmount Int? // In cents
5429
+
5430
+ // Display
5431
+ displayReason String? // "Early Bird Discount" for receipt
5432
+ internalNote String? // For support/debugging
5433
+
5434
+ // Relations
5435
+ checkout Checkout? @relation(fields: [checkoutId], references: [id])
5436
+ ticket Ticket? @relation(fields: [ticketId], references: [id])
5437
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
5438
+ attributionUser User? @relation("DiscountAttribution", fields: [attributionUserId], references: [id])
5439
+
5440
+ @@index([checkoutId])
5441
+ @@index([sourceType, sourceId])
5442
+ @@index([attributionUserId])
5443
+ @@index([createdAt])
5444
+ }
5445
+
5446
+ // Special offers - automatic, visible, conversion-oriented deals
5447
+ model SpecialOffer {
5448
+ id String @id @default(cuid())
5449
+ createdAt DateTime @default(now())
5450
+ updatedAt DateTime @updatedAt
5451
+
5452
+ // Basic Info
5453
+ ticketTierId String
5454
+ offerType SpecialOfferType
5455
+ title String // "Early Bird Special", "BOGO Deal"
5456
+ description String? @db.Text
5457
+ isActive Boolean @default(true)
5458
+ displayBadge Boolean @default(true)
5459
+ badgeText String? // "50% OFF", "BOGO"
5460
+
5461
+ // Discount Configuration
5462
+ discountType OfferDiscountType
5463
+ discountValue Int // Percentage or cents
5464
+
5465
+ // BOGO Configuration
5466
+ buyQuantity Int? // Buy X tickets
5467
+ getQuantity Int? // Get Y tickets
5468
+ getDiscountPercent Int? // Get Y at Z% off (0-100)
5469
+
5470
+ // Group Discount Configuration
5471
+ minGroupSize Int?
5472
+ maxGroupSize Int?
5473
+ tieredDiscounts Json? // [{qty: 5, discount: 10}, {qty: 10, discount: 20}]
5474
+
5475
+ // Time-Based Configuration
5476
+ startDate DateTime?
5477
+ endDate DateTime?
5478
+ availableUntil DateTime? // Early bird cutoff
5479
+
5480
+ // Quantity Limitations
5481
+ maxRedemptions Int?
5482
+ maxPerUser Int?
5483
+ limitedQuantity Int?
5484
+ currentRedemptions Int @default(0)
5485
+
5486
+ // Social Proof Tracking
5487
+ viewCount Int @default(0) // How many times this offer was viewed
5488
+ clickCount Int @default(0) // How many times users clicked "Apply"
5489
+ lastRedemptionAt DateTime? // When was the last redemption
5490
+
5491
+ // Price Increase (Countdown)
5492
+ priceIncreasesAt DateTime?
5493
+ newPriceAfterIncrease Int?
5494
+ countdownEnabled Boolean @default(false)
5495
+
5496
+ // Bundle Configuration
5497
+ bundledItems Json?
5498
+ bundleSavings Int?
5499
+
5500
+ // Attribution (ONLY for Referral type offers)
5501
+ requiresAttribution Boolean @default(false)
5502
+ referralDiscountForBuyer Int?
5503
+ referralDiscountForFriend Int?
5504
+ commissionRate Float? // For referrer
5505
+
5506
+ // Loyalty Configuration
5507
+ requiresEventAttendance Int?
5508
+ hostSpecificLoyalty Boolean @default(false)
5509
+
5510
+ // Stacking Rules
5511
+ canStackWithPromoCodes Boolean @default(true) // Host decides
5512
+ canStackWithOtherOffers Boolean @default(false)
5513
+ priority Int @default(0) // Higher = applied first
5514
+
5515
+ // Display & Marketing
5516
+ urgencyMessage String? @db.Text
5517
+ showSocialProof Boolean @default(false)
5518
+ highlightColor String?
5519
+ sortOrder Int @default(0)
5520
+
5521
+ // Guardrails
5522
+ minPriceFloor Int? // Never discount below this
5523
+ maxDiscountPercent Int? // Never discount more than X%
5524
+
5525
+ // Relations
5526
+ ticketTier TicketTier @relation(fields: [ticketTierId], references: [id], onDelete: Cascade)
5527
+
5528
+ @@index([ticketTierId])
5529
+ @@index([offerType])
5530
+ @@index([isActive])
5531
+ @@index([endDate])
5532
+ }
5533
+
5534
+ // Analytics Prediction Cache Model
5535
+ model AnalyticsPrediction {
5536
+ id String @id @default(cuid())
5537
+ bashEventId String
5538
+ type PredictionType
5539
+ source PredictionSource
5540
+ prediction Json
5541
+ confidence Float
5542
+ generatedAt DateTime @default(now())
5543
+ validUntil DateTime
5544
+
5545
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
5546
+
5547
+ @@index([bashEventId, type])
5548
+ @@index([validUntil])
5549
+ }
5550
+
5551
+ enum PredictionType {
5552
+ REVENUE_FORECAST
5553
+ PRICING_OPTIMIZATION
5554
+ SELLOUT_PROBABILITY
5555
+ ACTION_RECOMMENDATION
5556
+ }
5557
+
5558
+ enum PredictionSource {
5559
+ RULE_BASED
5560
+ AI_ENHANCED
5561
+ }
@@ -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";