@bash-app/bash-common 30.225.0 → 30.227.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 (142) hide show
  1. package/dist/__tests__/agePolicy.test.d.ts +2 -0
  2. package/dist/__tests__/agePolicy.test.d.ts.map +1 -0
  3. package/dist/__tests__/agePolicy.test.js +203 -0
  4. package/dist/__tests__/agePolicy.test.js.map +1 -0
  5. package/dist/__tests__/getDisplayGender.test.d.ts +2 -0
  6. package/dist/__tests__/getDisplayGender.test.d.ts.map +1 -0
  7. package/dist/__tests__/getDisplayGender.test.js +34 -0
  8. package/dist/__tests__/getDisplayGender.test.js.map +1 -0
  9. package/dist/__tests__/statusEnums.test.d.ts +2 -0
  10. package/dist/__tests__/statusEnums.test.d.ts.map +1 -0
  11. package/dist/__tests__/statusEnums.test.js +24 -0
  12. package/dist/__tests__/statusEnums.test.js.map +1 -0
  13. package/dist/aiApproval.d.ts +89 -0
  14. package/dist/aiApproval.d.ts.map +1 -0
  15. package/dist/aiApproval.js +82 -0
  16. package/dist/aiApproval.js.map +1 -0
  17. package/dist/definitions.d.ts +44 -2
  18. package/dist/definitions.d.ts.map +1 -1
  19. package/dist/definitions.js +106 -2
  20. package/dist/definitions.js.map +1 -1
  21. package/dist/extendedSchemas.d.ts +280 -105
  22. package/dist/extendedSchemas.d.ts.map +1 -1
  23. package/dist/extendedSchemas.js +34 -2
  24. package/dist/extendedSchemas.js.map +1 -1
  25. package/dist/index.d.ts +5 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +4 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/legalTemplates.d.ts +1 -1
  30. package/dist/legalTemplates.js +1 -1
  31. package/dist/utils/__tests__/badgeUtils.test.d.ts +2 -0
  32. package/dist/utils/__tests__/badgeUtils.test.d.ts.map +1 -0
  33. package/dist/utils/__tests__/badgeUtils.test.js +157 -0
  34. package/dist/utils/__tests__/badgeUtils.test.js.map +1 -0
  35. package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts +2 -0
  36. package/dist/utils/__tests__/bashPointsPaymentUtils.test.d.ts.map +1 -0
  37. package/dist/utils/__tests__/bashPointsPaymentUtils.test.js +114 -0
  38. package/dist/utils/__tests__/bashPointsPaymentUtils.test.js.map +1 -0
  39. package/dist/utils/__tests__/contentFilterUtils.test.d.ts +2 -0
  40. package/dist/utils/__tests__/contentFilterUtils.test.d.ts.map +1 -0
  41. package/dist/utils/__tests__/contentFilterUtils.test.js +443 -0
  42. package/dist/utils/__tests__/contentFilterUtils.test.js.map +1 -0
  43. package/dist/utils/__tests__/displayDateUtils.test.d.ts +2 -0
  44. package/dist/utils/__tests__/displayDateUtils.test.d.ts.map +1 -0
  45. package/dist/utils/__tests__/displayDateUtils.test.js +105 -0
  46. package/dist/utils/__tests__/displayDateUtils.test.js.map +1 -0
  47. package/dist/utils/__tests__/flyerUtils.test.d.ts +2 -0
  48. package/dist/utils/__tests__/flyerUtils.test.d.ts.map +1 -0
  49. package/dist/utils/__tests__/flyerUtils.test.js +49 -0
  50. package/dist/utils/__tests__/flyerUtils.test.js.map +1 -0
  51. package/dist/utils/__tests__/getBackendHost.test.d.ts +2 -0
  52. package/dist/utils/__tests__/getBackendHost.test.d.ts.map +1 -0
  53. package/dist/utils/__tests__/getBackendHost.test.js +104 -0
  54. package/dist/utils/__tests__/getBackendHost.test.js.map +1 -0
  55. package/dist/utils/__tests__/getFrontendHost.test.d.ts +2 -0
  56. package/dist/utils/__tests__/getFrontendHost.test.d.ts.map +1 -0
  57. package/dist/utils/__tests__/getFrontendHost.test.js +207 -0
  58. package/dist/utils/__tests__/getFrontendHost.test.js.map +1 -0
  59. package/dist/utils/__tests__/promoCodesUtils.test.d.ts +2 -0
  60. package/dist/utils/__tests__/promoCodesUtils.test.d.ts.map +1 -0
  61. package/dist/utils/__tests__/promoCodesUtils.test.js +183 -0
  62. package/dist/utils/__tests__/promoCodesUtils.test.js.map +1 -0
  63. package/dist/utils/__tests__/qrCodeUtils.test.d.ts +2 -0
  64. package/dist/utils/__tests__/qrCodeUtils.test.d.ts.map +1 -0
  65. package/dist/utils/__tests__/qrCodeUtils.test.js +99 -0
  66. package/dist/utils/__tests__/qrCodeUtils.test.js.map +1 -0
  67. package/dist/utils/__tests__/stripeAccountUtils.test.d.ts +2 -0
  68. package/dist/utils/__tests__/stripeAccountUtils.test.d.ts.map +1 -0
  69. package/dist/utils/__tests__/stripeAccountUtils.test.js +75 -0
  70. package/dist/utils/__tests__/stripeAccountUtils.test.js.map +1 -0
  71. package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts +2 -0
  72. package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.d.ts.map +1 -0
  73. package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js +233 -0
  74. package/dist/utils/blog/__tests__/getRelatedBlogPosts.test.js.map +1 -0
  75. package/dist/utils/blog/blogCommentDbUtils.d.ts +161 -0
  76. package/dist/utils/blog/blogCommentDbUtils.d.ts.map +1 -0
  77. package/dist/utils/blog/blogCommentDbUtils.js +111 -0
  78. package/dist/utils/blog/blogCommentDbUtils.js.map +1 -0
  79. package/dist/utils/blog/blogDbUtils.d.ts +10 -0
  80. package/dist/utils/blog/blogDbUtils.d.ts.map +1 -1
  81. package/dist/utils/blog/blogDbUtils.js +71 -0
  82. package/dist/utils/blog/blogDbUtils.js.map +1 -1
  83. package/dist/utils/flyerUtils.d.ts +42 -0
  84. package/dist/utils/flyerUtils.d.ts.map +1 -0
  85. package/dist/utils/flyerUtils.js +104 -0
  86. package/dist/utils/flyerUtils.js.map +1 -0
  87. package/dist/utils/urlUtils.d.ts +10 -0
  88. package/dist/utils/urlUtils.d.ts.map +1 -1
  89. package/dist/utils/urlUtils.js +24 -5
  90. package/dist/utils/urlUtils.js.map +1 -1
  91. package/dist/utils/userUtils.d.ts +32 -0
  92. package/dist/utils/userUtils.d.ts.map +1 -1
  93. package/dist/utils/userUtils.js +27 -0
  94. package/dist/utils/userUtils.js.map +1 -1
  95. package/package.json +1 -1
  96. package/prisma/schema.prisma +1196 -845
  97. package/src/__tests__/agePolicy.test.ts +257 -0
  98. package/src/__tests__/getDisplayGender.test.ts +50 -0
  99. package/src/__tests__/statusEnums.test.ts +36 -0
  100. package/src/aiApproval.ts +118 -0
  101. package/src/definitions.ts +138 -1
  102. package/src/extendedSchemas.ts +86 -3
  103. package/src/index.ts +15 -0
  104. package/src/legalTemplates.ts +1 -1
  105. package/src/utils/__tests__/badgeUtils.test.ts +191 -0
  106. package/src/utils/__tests__/bashPointsPaymentUtils.test.ts +153 -0
  107. package/src/utils/__tests__/contentFilterUtils.test.ts +547 -0
  108. package/src/utils/__tests__/displayDateUtils.test.ts +113 -0
  109. package/src/utils/__tests__/flyerUtils.test.ts +70 -0
  110. package/src/utils/__tests__/getBackendHost.test.ts +128 -0
  111. package/src/utils/__tests__/getFrontendHost.test.ts +232 -0
  112. package/src/utils/__tests__/promoCodesUtils.test.ts +237 -0
  113. package/src/utils/__tests__/qrCodeUtils.test.ts +143 -0
  114. package/src/utils/__tests__/stripeAccountUtils.test.ts +93 -0
  115. package/src/utils/blog/__tests__/getRelatedBlogPosts.test.ts +271 -0
  116. package/src/utils/blog/blogCommentDbUtils.ts +161 -0
  117. package/src/utils/blog/blogDbUtils.ts +95 -1
  118. package/src/utils/flyerUtils.ts +137 -0
  119. package/src/utils/urlUtils.ts +27 -5
  120. package/src/utils/userUtils.ts +40 -0
  121. package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts +0 -6
  122. package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts.map +0 -1
  123. package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js +0 -104
  124. package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js.map +0 -1
  125. package/dist/utils/service/serviceRateDBUtils.d.ts +0 -1
  126. package/dist/utils/service/serviceRateDBUtils.d.ts.map +0 -1
  127. package/dist/utils/service/serviceRateDBUtils.js +0 -159
  128. package/dist/utils/service/serviceRateDBUtils.js.map +0 -1
  129. package/prisma/migrations/add_bash_availability.sql +0 -53
  130. package/prisma/migrations/add_bashcash_pricing_to_ticket_tier.sql +0 -15
  131. package/prisma/migrations/add_bashpoints_purchase_tracking.sql +0 -33
  132. package/prisma/migrations/add_event_group_allow_auto_join.sql +0 -17
  133. package/prisma/migrations/add_group_member_status_not_going_maybe.sql +0 -5
  134. package/prisma/migrations/add_groups_and_momentum.sql +0 -135
  135. package/prisma/migrations/add_groups_momentum_phase0_4.sql +0 -200
  136. package/prisma/migrations/add_new_feature_tables.sql +0 -140
  137. package/prisma/migrations/add_pricing_type_enum.sql +0 -88
  138. package/prisma/migrations/add_tier_privacy_and_fee_handling.sql +0 -29
  139. package/prisma/migrations/diagnostic_bashcash_columns.sql +0 -157
  140. package/prisma/migrations/fix_bashcash_referral_code_schema_mismatch.sql +0 -81
  141. package/prisma/migrations/rename_bashcash_to_bashpoints.sql +0 -183
  142. package/prisma/migrations/rename_bashcredits_to_bashpoints.sql +0 -96
@@ -55,38 +55,38 @@ model BashComment {
55
55
 
56
56
  // BashFeed Models - Event-Centric Social Discovery Feed
57
57
  model BashFeedPost {
58
- id String @id @default(cuid())
59
- userId String
60
- bashId String?
61
- serviceId String?
62
- bashAssociationType BashAssociationType? // How user is associated with the bash (for post display)
63
- content String? @db.Text
64
- mediaIds String[] // Array of Media IDs
65
- mentionedUserIds String[] // Array of User IDs mentioned in post
66
- isAnonymous Boolean @default(false) // User can choose to post anonymously
67
- viewCount Int @default(0) // Analytics only, not shown publicly
68
- isFeatured Boolean @default(false) // Post is promoted/featured
69
- featuredAt DateTime? // When post was featured
70
- featuredBoost Float @default(0) // Current boost score (time-decayed)
71
- featuredUntil DateTime? // When the feature boost expires
72
- createdAt DateTime @default(now())
73
- updatedAt DateTime @updatedAt
74
- editedAt DateTime?
58
+ id String @id @default(cuid())
59
+ userId String
60
+ bashId String?
61
+ serviceId String?
62
+ bashAssociationType BashAssociationType? // How user is associated with the bash (for post display)
63
+ content String? @db.Text
64
+ mediaIds String[] // Array of Media IDs
65
+ mentionedUserIds String[] // Array of User IDs mentioned in post
66
+ isAnonymous Boolean @default(false) // User can choose to post anonymously
67
+ viewCount Int @default(0) // Analytics only, not shown publicly
68
+ isFeatured Boolean @default(false) // Post is promoted/featured
69
+ featuredAt DateTime? // When post was featured
70
+ featuredBoost Float @default(0) // Current boost score (time-decayed)
71
+ featuredUntil DateTime? // When the feature boost expires
72
+ createdAt DateTime @default(now())
73
+ updatedAt DateTime @updatedAt
74
+ editedAt DateTime?
75
75
  /// When set, this row is a feed "repost shell": `user` is the reposter; content lives on `repostOf`.
76
- repostOfPostId String?
77
- repostOf BashFeedPost? @relation("BashFeedPostRepost", fields: [repostOfPostId], references: [id], onDelete: Cascade)
78
- repostShells BashFeedPost[] @relation("BashFeedPostRepost")
76
+ repostOfPostId String?
77
+ repostOf BashFeedPost? @relation("BashFeedPostRepost", fields: [repostOfPostId], references: [id], onDelete: Cascade)
78
+ repostShells BashFeedPost[] @relation("BashFeedPostRepost")
79
79
 
80
80
  // Relationships
81
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
82
- bash BashEvent? @relation(fields: [bashId], references: [id], onDelete: Cascade)
83
- service Service? @relation(fields: [serviceId], references: [id], onDelete: Cascade)
84
- likes BashFeedLike[]
85
- comments BashFeedComment[]
86
- ratings BashFeedRating[]
87
- reports BashFeedReport[]
88
- saves BashFeedSave[]
89
- reposts BashFeedRepost[]
81
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
82
+ bash BashEvent? @relation(fields: [bashId], references: [id], onDelete: Cascade)
83
+ service Service? @relation(fields: [serviceId], references: [id], onDelete: Cascade)
84
+ likes BashFeedLike[]
85
+ comments BashFeedComment[]
86
+ ratings BashFeedRating[]
87
+ reports BashFeedReport[]
88
+ saves BashFeedSave[]
89
+ reposts BashFeedRepost[]
90
90
  helpfulVotes ReviewHelpfulVote[]
91
91
 
92
92
  @@unique([userId, repostOfPostId])
@@ -236,18 +236,18 @@ model Competition {
236
236
  bracketData Json? // Store bracket structure/metadata
237
237
  bracketVisible Boolean @default(true)
238
238
 
239
- createdAt DateTime @default(now())
240
- updatedAt DateTime @updatedAt
241
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
242
- owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
243
- sponsor CompetitionSponsor[]
244
- prizes Prize[]
245
- participants CompetitionParticipant[]
246
- votes CompetitionVote[]
247
- matches CompetitionMatch[]
248
- judgeAssignments CompetitionJudgeAssignment[]
249
- judgeScores CompetitionJudgeScore[]
250
- notifications Notification[]
239
+ createdAt DateTime @default(now())
240
+ updatedAt DateTime @updatedAt
241
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
242
+ owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
243
+ sponsor CompetitionSponsor[]
244
+ prizes Prize[]
245
+ participants CompetitionParticipant[]
246
+ votes CompetitionVote[]
247
+ matches CompetitionMatch[]
248
+ judgeAssignments CompetitionJudgeAssignment[]
249
+ judgeScores CompetitionJudgeScore[]
250
+ notifications Notification[]
251
251
 
252
252
  @@index([bashEventId])
253
253
  @@index([competitionType])
@@ -318,9 +318,9 @@ model CompetitionParticipant {
318
318
  votes CompetitionVote[]
319
319
 
320
320
  // Tournament bracket relations
321
- matchesAsParticipant1 CompetitionMatch[] @relation("MatchParticipant1")
322
- matchesAsParticipant2 CompetitionMatch[] @relation("MatchParticipant2")
323
- matchesWon CompetitionMatch[] @relation("MatchWinner")
321
+ matchesAsParticipant1 CompetitionMatch[] @relation("MatchParticipant1")
322
+ matchesAsParticipant2 CompetitionMatch[] @relation("MatchParticipant2")
323
+ matchesWon CompetitionMatch[] @relation("MatchWinner")
324
324
  judgeScores CompetitionJudgeScore[]
325
325
 
326
326
  @@index([competitionId])
@@ -376,7 +376,7 @@ model CompetitionVote {
376
376
  }
377
377
 
378
378
  model EventTask {
379
- id String @id @default(cuid())
379
+ id String @id @default(cuid())
380
380
  creatorId String
381
381
  bashEventId String
382
382
  title String
@@ -385,15 +385,16 @@ model EventTask {
385
385
  status TaskStatus?
386
386
  neededCount Int? // Number of volunteers needed
387
387
  estimatedDuration Int? // Estimated duration in minutes
388
- createdAt DateTime? @default(now())
388
+ createdAt DateTime? @default(now())
389
389
  organizationId String? // Organization association
390
- assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
391
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
392
- creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
390
+ assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
391
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
392
+ creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
393
393
  notificationsReferencingMe Notification[]
394
394
  comments TaskComment[]
395
395
  invitations TaskInvitation[]
396
- potluckItem PotluckItem? @relation("PotluckItemTask")
396
+ expenses Expense[]
397
+ potluckItem PotluckItem? @relation("PotluckItemTask")
397
398
  bashEventSuggestion BashEventSuggestion? @relation("SuggestionTask")
398
399
  }
399
400
 
@@ -462,20 +463,20 @@ model SentReminder {
462
463
  }
463
464
 
464
465
  model BashEventPromoCode {
465
- id String @id @default(cuid())
466
- code String
467
- ticketTierId String
468
- stripeCouponId String?
469
- discountAmountInCents Int?
470
- discountAmountPercentage Int?
471
- maxRedemptions Int?
472
- redeemBy DateTime?
473
- promoterId String?
474
- meta String?
475
- bashPointsRewardPerTicket Int? // Host-defined BashPoints per ticket for promoter rewards
476
- promoter Promoter? @relation(fields: [promoterId], references: [id])
477
- ticketTier TicketTier @relation(fields: [ticketTierId], references: [id])
478
- usedBy User[] @relation("BashEventPromoCodeToUser")
466
+ id String @id @default(cuid())
467
+ code String
468
+ ticketTierId String
469
+ stripeCouponId String?
470
+ discountAmountInCents Int?
471
+ discountAmountPercentage Int?
472
+ maxRedemptions Int?
473
+ redeemBy DateTime?
474
+ promoterId String?
475
+ meta String?
476
+ bashPointsRewardPerTicket Int? // Host-defined BashPoints per ticket for promoter rewards
477
+ promoter Promoter? @relation(fields: [promoterId], references: [id])
478
+ ticketTier TicketTier @relation(fields: [ticketTierId], references: [id])
479
+ usedBy User[] @relation("BashEventPromoCodeToUser")
479
480
 
480
481
  @@unique([ticketTierId, code])
481
482
  }
@@ -588,6 +589,7 @@ model Invitation {
588
589
  rejectedDate DateTime?
589
590
  maybeDate DateTime?
590
591
  isFreeGuest Boolean @default(false)
592
+ sentCount Int @default(1) // Number of times this invitation email has been sent (1 on initial send; incremented on each host-confirmed re-invite for the same email + bash)
591
593
  associatedBash AssociatedBash?
592
594
  bashEvent BashEvent? @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
593
595
  creator User @relation("InvitationsCreatedByMe", fields: [creatorId], references: [id], onDelete: Cascade)
@@ -600,124 +602,135 @@ model Invitation {
600
602
  }
601
603
 
602
604
  model BashEvent {
603
- id String @id @default(cuid())
604
- source BashEventSource @default(Bash)
605
- sourcePartnerId String? // When source=Partner, e.g. 'jamwith', 'mixlog'
606
- title String
607
- slug String? @unique
608
- creatorId String
609
- createdAt DateTime? @default(now())
610
- isApproved Boolean? @default(false)
611
- description String?
612
- eventType String @default("Other")
613
- timezone String?
614
- startDateTime DateTime?
615
- endDateTime DateTime?
616
- waiverUrl String?
617
- waiverRequired Boolean @default(false)
618
- waiverDisplayType String? @default("inline")
605
+ id String @id @default(cuid())
606
+ source BashEventSource @default(Bash)
607
+ sourcePartnerId String? // When source=Partner, e.g. 'jamwith', 'mixlog'
608
+ title String
609
+ slug String? @unique
610
+ creatorId String
611
+ createdAt DateTime? @default(now())
612
+ isApproved Boolean? @default(false)
613
+ description String?
614
+ eventType String @default("Other")
615
+ timezone String?
616
+ startDateTime DateTime?
617
+ endDateTime DateTime?
618
+ waiverUrl String?
619
+ waiverRequired Boolean @default(false)
620
+ waiverDisplayType String? @default("inline")
619
621
  /// Host opt-in: show photo/video consent at checkout; null photoReleaseText = platform default template
620
- photoReleaseEnabled Boolean @default(false)
621
- photoReleaseRequired Boolean @default(true)
622
- photoReleaseText String? @db.Text
623
- targetAudienceId String? @unique
624
- amountOfGuestsId String? @unique
625
- vibe String?
626
- occasion String?
627
- dress String?
628
- allowed String?
629
- notAllowed String?
630
- eventFormat EventFormat? // Derived from location and videoLink: In-Person, Virtual, or Hybrid
631
- nonProfit Boolean?
632
- nonProfitId String?
633
- privacy Privacy @default(Public)
634
- capacity Int?
635
- location String?
636
- street String?
637
- city String?
638
- state String?
639
- zipCode String?
640
- country String?
641
- status BashStatus @default(Draft)
642
- tags String[]
643
- coverPhoto String?
644
- clubId String?
645
- dateTimePublished DateTime?
646
- includedItems String[]
647
- videoLink String?
648
- subtitle String?
649
- isFeatured Boolean?
650
- featuredUntil DateTime? // When paid feature expires (null = no expiry / super-user pinned)
651
- isTrending Boolean?
652
- venueId String?
653
- averageRating Float? @default(0)
654
- totalRatings Int? @default(0) // Total count of all ratings (Review + BashFeedRating)
655
- totalReviews Int? @default(0) // Count of Review records (anonymous quick ratings)
656
- totalFeedRatings Int? @default(0) // Count of BashFeedRating records (from posts)
657
- allowDonations Boolean? @default(true)
658
- suggestedDonationAmount Int?
659
- donationDetails String?
660
- absorbDonationFees Boolean @default(false)
661
- absorbTicketFees Boolean @default(false)
622
+ photoReleaseEnabled Boolean @default(false)
623
+ photoReleaseRequired Boolean @default(true)
624
+ photoReleaseText String? @db.Text
625
+ /// Host-required IDs for ticket checkout
626
+ requireGovernmentId Boolean @default(false)
627
+ requireStudentId Boolean @default(false)
628
+ /// Minimum-age policy surfaced on the BashCard and used as a discover filter.
629
+ /// When set to EighteenPlus / TwentyOnePlus the wizard auto-toggles requireGovernmentId
630
+ /// so we can verify age via the user's stored ID at checkout.
631
+ agePolicy AgePolicy @default(AllAges)
632
+ targetAudienceId String? @unique
633
+ amountOfGuestsId String? @unique
634
+ vibe String?
635
+ occasion String?
636
+ dress String?
637
+ allowed String?
638
+ notAllowed String?
639
+ eventFormat EventFormat? // Derived from location and videoLink: In-Person, Virtual, or Hybrid
640
+ nonProfit Boolean?
641
+ nonProfitId String?
642
+ privacy Privacy @default(Public)
643
+ capacity Int?
644
+ location String?
645
+ street String?
646
+ city String?
647
+ state String?
648
+ zipCode String?
649
+ country String?
650
+ status BashStatus @default(Draft)
651
+ tags String[]
652
+ coverPhoto String?
653
+ clubId String?
654
+ dateTimePublished DateTime?
655
+ includedItems String[]
656
+ videoLink String?
657
+ subtitle String?
658
+ isFeatured Boolean?
659
+ featuredUntil DateTime? // When paid feature expires (null = no expiry / super-user pinned)
660
+ isTrending Boolean?
661
+ venueId String?
662
+ averageRating Float? @default(0)
663
+ totalRatings Int? @default(0) // Total count of all ratings (Review + BashFeedRating)
664
+ totalReviews Int? @default(0) // Count of Review records (anonymous quick ratings)
665
+ totalFeedRatings Int? @default(0) // Count of BashFeedRating records (from posts)
666
+ allowDonations Boolean? @default(false)
667
+ suggestedDonationAmount Int?
668
+ donationDetails String?
669
+ absorbDonationFees Boolean @default(false)
670
+ absorbTicketFees Boolean @default(false)
662
671
  /// When true, attendees may request a refund from the app/email (host approves in Tickets → Refunds). When false, self-serve refund CTAs are hidden.
663
- allowAttendeeRefundRequests Boolean @default(true)
672
+ allowAttendeeRefundRequests Boolean @default(true)
664
673
  // GuestPays / Split: Stripe processing + Bash platform fee presentation (see platformFeeEnabled).
665
- feeHandling String @default("HostAbsorbs") // "GuestPays" | "HostAbsorbs" | "Split"
674
+ feeHandling String @default("HostAbsorbs") // "GuestPays" | "HostAbsorbs" | "Split"
666
675
  /// When true, Bash charges platformFeeRate on ticket gross (e.g. 3%) in addition to Stripe processing pass-through.
667
- platformFeeEnabled Boolean @default(false)
676
+ platformFeeEnabled Boolean @default(false)
668
677
  /// Bash platform take rate on ticket gross (e.g. 0.03 = 3%). Ignored when platformFeeEnabled is false.
669
- platformFeeRate Float @default(0.03)
678
+ platformFeeRate Float @default(0.03)
670
679
  /// Optional: attendees can tag which act they came to see; drives attribution reports and future payouts.
671
- artistAttributionEnabled Boolean @default(false)
680
+ artistAttributionEnabled Boolean @default(false)
672
681
  /// "percentage" | "flat" — how artistPayoutRate is interpreted for suggested splits (display/export).
673
- artistPayoutMode String?
682
+ artistPayoutMode String?
674
683
  /// Percentage of door (0–100) or flat cents per ticket depending on artistPayoutMode.
675
- artistPayoutRate Float?
676
- topPromoterPrize String? // Optional prize label: "Free table for your crew"
677
- promoterLeaderboardIsPublic Boolean @default(false) // Host opts in to public leaderboard
678
- showAttendees Boolean @default(true)
679
- /// Host opt-in: show soft "public interest" controls on the event page (signed-in viewers). Not a guest list; invite RSVP stays separate.
680
- allowPublicInterestRsvp Boolean @default(false)
681
- attendeeDisplayMode String @default("All") // "All" | "NetworkOnly" | "Hidden"
682
- servicePreferences Json? // New tiered status: { "Entertainment": "need", "Sponsors": "booked_closed", ... }
683
- bookedServices Json? // Booked service details: { "Entertainment": { serviceId: "...", providerId: "...", bookedAt: "..." }, ... }
684
- serviceVisibility Json? // DEPRECATED: Use servicePreferences instead. Kept for backward compatibility during migration.
685
- originalCreatorId String?
686
- transferredAt DateTime?
687
- transferredFromId String?
688
- transferCount Int @default(0)
689
- payoutUserId String? // When set, payouts go to this user's Stripe instead of owner's (for transfer-with-retained-payouts)
690
- startTimeLocked Boolean @default(false)
691
- p2pPaymentMethod String?
692
- venmoUsername String?
693
- venmoQRCodeUrl String?
694
- zelleEmail String?
695
- zellePhone String?
696
- zelleQRCodeUrl String?
697
- externalTicketUrl String? // URL to external ticket purchase page (for imported events)
698
- externalPriceMin Int? // Minimum ticket price in cents (for imported events)
684
+ artistPayoutRate Float?
685
+ topPromoterPrize String? // Optional prize label: "Free table for your crew"
686
+ promoterLeaderboardIsPublic Boolean @default(false) // Host opts in to public leaderboard
687
+ showAttendees Boolean @default(true)
688
+ /// Host opt-in: show soft "public interest" controls on the bash detail page (signed-in viewers). Not a guest list; invite RSVP stays separate.
689
+ allowPublicInterestRsvp Boolean @default(false)
690
+ /// When true, guests can join a waitlist (activated automatically when amountOfGuests.allowWaitlistAtMax is set and max capacity is reached)
691
+ waitlistEnabled Boolean @default(false)
692
+ attendeeDisplayMode String @default("All") // "All" | "NetworkOnly" | "Hidden"
693
+ showOrganizerTitles Boolean @default(false)
694
+ servicePreferences Json? // New tiered status: { "Entertainment": "need", "Sponsors": "booked_closed", ... }
695
+ bookedServices Json? // Booked service details: { "Entertainment": { serviceId: "...", providerId: "...", bookedAt: "..." }, ... }
696
+ serviceVisibility Json? // DEPRECATED: Use servicePreferences instead. Kept for backward compatibility during migration.
697
+ plannedServiceIds String[] @default([]) // Service IDs the host has flagged as "plan to book" in the wizard
698
+ originalCreatorId String?
699
+ transferredAt DateTime?
700
+ transferredFromId String?
701
+ transferCount Int @default(0)
702
+ payoutUserId String? // When set, payouts go to this user's Stripe instead of owner's (for transfer-with-retained-payouts)
703
+ startTimeLocked Boolean @default(false)
704
+ p2pPaymentMethod String?
705
+ venmoUsername String?
706
+ venmoQRCodeUrl String?
707
+ zelleEmail String?
708
+ zellePhone String?
709
+ zelleQRCodeUrl String?
710
+ externalTicketUrl String? // URL to external ticket purchase page (for imported events)
711
+ externalPriceMin Int? // Minimum ticket price in cents (for imported events)
699
712
  /// Structured potluck / BYOB (MVP: flags + free-text notes on detail + wizard)
700
- potluckEnabled Boolean @default(false)
701
- byobEnabled Boolean @default(false)
702
- potluckNotes String? @db.Text
713
+ potluckEnabled Boolean @default(false)
714
+ byobEnabled Boolean @default(false)
715
+ potluckNotes String? @db.Text
703
716
  /// When false, included/allowed/not-allowed chips are read-only (no upvotes or suggestions)
704
- collaborationEnabled Boolean @default(true)
705
- /// Host opt-in: show icebreaker prompts on the event page for guests
706
- icebreakerEnabled Boolean @default(false)
717
+ collaborationEnabled Boolean @default(true)
718
+ /// Host opt-in: show icebreaker prompts on the bash detail page for guests
719
+ icebreakerEnabled Boolean @default(false)
707
720
  /// Host-curated merch catalog for this event (informational; not a checkout line item for MVP)
708
- merchandiseItems Json?
709
- externalPriceMax Int? // Maximum ticket price in cents (for imported events)
710
- externalPriceSummary String? // Human-readable price summary e.g., "$50 - $150" or "$75"
711
- customTermsAndConditions String?
712
- itinerary Json? // Array of { id: string, title: string, startTime: string, endTime: string, description?: string }
713
- backgroundImage String? // Custom or curated background image URL for the event page
714
- themeColor String? // Dominant hex color extracted from the event flyer (e.g. "#7c3aed")
721
+ merchandiseItems Json?
722
+ externalPriceMax Int? // Maximum ticket price in cents (for imported events)
723
+ externalPriceSummary String? // Human-readable price summary e.g., "$50 - $150" or "$75"
724
+ customTermsAndConditions String?
725
+ itinerary Json? // Array of { id: string, title: string, startTime: string, endTime: string, description?: string }
726
+ backgroundImage String? // Custom or curated background image URL for the bash detail page
727
+ themeColor String? // Dominant hex color extracted from the event flyer (e.g. "#7c3aed")
715
728
 
716
729
  // Host broadcast fields
717
- postEventMessageSent Boolean @default(false)
718
- postEventMessageSentAt DateTime?
719
- broadcastCount Int @default(0)
720
- lastBroadcastAt DateTime?
730
+ postEventMessageSent Boolean @default(false)
731
+ postEventMessageSentAt DateTime?
732
+ broadcastCount Int @default(0)
733
+ lastBroadcastAt DateTime?
721
734
 
722
735
  // Idea-specific fields (only used when status = 'Idea')
723
736
  ideaExpiresAt DateTime?
@@ -737,6 +750,9 @@ model BashEvent {
737
750
  maxGuestsPerMember Int? // Guest limit per member (null = unlimited)
738
751
  restrictToOrgDepartments String[] @default([]) // Empty = all departments can see/join
739
752
 
753
+ /// When true, new event expenses default to Pending until host/org approves (see Expense.status).
754
+ requireExpenseApproval Boolean @default(false)
755
+
740
756
  associatedBashesReferencingMe AssociatedBash[]
741
757
  comments BashComment[]
742
758
  amountOfGuests AmountOfGuests? @relation(fields: [amountOfGuestsId], references: [id], onDelete: Cascade)
@@ -801,7 +817,9 @@ model BashEvent {
801
817
  interests IdeaInterest[]
802
818
  configurations IdeaConfiguration[]
803
819
 
804
- /** Logged-in users: soft RSVP (Going / Maybe / NotGoing) for public published events — not org EventRSVP */
820
+ /**
821
+ * Logged-in users: soft RSVP (Going / Maybe / NotGoing) for public published events — not org EventRSVP
822
+ */
805
823
  publicBashRsvps PublicBashRsvp[]
806
824
 
807
825
  // Claiming Relations
@@ -828,17 +846,33 @@ model BashEvent {
828
846
  rsvps EventRSVP[] @relation("EventRSVPs")
829
847
 
830
848
  // Groups & page views (Phases 0–4)
831
- pageViews BashEventPageView[]
832
- eventGroups EventGroup[]
849
+ pageViews BashEventPageView[]
850
+ eventGroups EventGroup[]
833
851
  groupUnlockOffers GroupUnlockOffer[]
834
852
 
835
853
  bashEventArtists BashEventArtist[]
836
854
  vanityPaths EventVanityPath[]
855
+ flyerCampaigns FlyerCampaign[]
856
+
857
+ /// Latest AI moderator verdict (see `bashApprovalAiService` + `mapAiResultToDecision`).
858
+ /// Null = never reviewed by AI. Recomputed each time the bash transitions to Pending
859
+ /// or a super-user clicks "Re-run AI review".
860
+ aiApprovalVerdict AiApprovalVerdict?
861
+ aiApprovalConfidence Float?
862
+ /// Structured `AiApprovalReasons` (criteria scores + hostFacingMessage + adminFacingNotes).
863
+ /// Schema lives in `bash-common/src/extendedSchemas.ts`.
864
+ aiApprovalReasons Json?
865
+ aiApprovalReviewedAt DateTime?
866
+ aiApprovalModel String?
867
+ aiApprovalRunCount Int @default(0)
868
+ /// SHA-256 of the snapshot fed to the AI; lets us skip a re-run when nothing changed.
869
+ aiApprovalContentHash String?
837
870
 
838
871
  @@index([templateId])
839
872
  @@index([parentEventId])
840
873
  @@index([organizationId])
841
874
  @@index([eventSeriesId])
875
+ @@index([status, aiApprovalVerdict])
842
876
  }
843
877
 
844
878
  /// Lineup / attribution roster for an event (optional act list for checkout tagging).
@@ -858,6 +892,197 @@ model BashEventArtist {
858
892
  @@index([serviceId])
859
893
  }
860
894
 
895
+ // --- Flyer Blast (USPS EDDM neighborhood mail campaigns) ---
896
+ enum FlyerTargetingMode {
897
+ RADIUS
898
+ CUSTOM
899
+ }
900
+
901
+ enum FlyerFulfillmentType {
902
+ SELF_MAIL
903
+ DONE_FOR_YOU
904
+ }
905
+
906
+ enum FlyerCampaignStatus {
907
+ DRAFT
908
+ ARTWORK_REVIEW
909
+ PAID
910
+ PRINTING
911
+ IN_TRANSIT
912
+ DELIVERED
913
+ CANCELLED
914
+ }
915
+
916
+ enum FlyerSponsorSlot {
917
+ FEATURED
918
+ SUPPORTING_1
919
+ SUPPORTING_2
920
+ }
921
+
922
+ enum FlyerConversionType {
923
+ QR_SCAN
924
+ RSVP
925
+ TICKET_PURCHASE
926
+ SPONSOR_QR_SCAN
927
+ }
928
+
929
+ model FlyerCampaign {
930
+ id String @id @default(cuid())
931
+ bashEventId String
932
+ hostUserId String
933
+
934
+ targetingMode FlyerTargetingMode @default(RADIUS)
935
+ radiusMiles Float?
936
+ centerLat Float?
937
+ centerLng Float?
938
+ customPolygon Json?
939
+ totalHouseholds Int?
940
+
941
+ fulfillmentType FlyerFulfillmentType @default(DONE_FOR_YOU)
942
+
943
+ printPostageCostCents Int?
944
+ bashFeeCents Int?
945
+ sponsorRevenueCents Int?
946
+ hostTotalCostCents Int?
947
+ stripePaymentIntentId String?
948
+ paidAt DateTime?
949
+
950
+ campaignQrSlug String @unique
951
+ campaignPromoCode String?
952
+
953
+ ctaLabel String?
954
+ templateVariant String @default("default")
955
+
956
+ status FlyerCampaignStatus @default(DRAFT)
957
+
958
+ vendorOrderRef String?
959
+ vendorStatus String?
960
+
961
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
962
+ host User @relation(fields: [hostUserId], references: [id], onDelete: Cascade)
963
+ routes FlyerMailingRoute[]
964
+ artworkVersions FlyerArtwork[]
965
+ sponsors FlyerCampaignSponsor[]
966
+ conversions FlyerConversion[]
967
+
968
+ createdAt DateTime @default(now())
969
+ updatedAt DateTime @updatedAt
970
+
971
+ @@index([bashEventId])
972
+ @@index([hostUserId])
973
+ }
974
+
975
+ model FlyerMailingRoute {
976
+ id String @id @default(cuid())
977
+ campaignId String
978
+ campaign FlyerCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
979
+
980
+ crrtCode String
981
+ zipCode String
982
+ city String
983
+ state String @default("UT")
984
+ householdCount Int
985
+
986
+ medianIncomeBand String?
987
+ avgHouseholdSize Float?
988
+ medianAge Float?
989
+
990
+ createdAt DateTime @default(now())
991
+
992
+ @@unique([campaignId, crrtCode, zipCode])
993
+ @@index([campaignId])
994
+ }
995
+
996
+ model FlyerArtwork {
997
+ id String @id @default(cuid())
998
+ campaignId String
999
+ campaign FlyerCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
1000
+
1001
+ version Int
1002
+ s3KeyPrintPdf String?
1003
+ s3KeyPreviewPng String?
1004
+
1005
+ widthIn Float @default(6.25)
1006
+ heightIn Float @default(11)
1007
+ isEddmCompliant Boolean @default(false)
1008
+
1009
+ createdAt DateTime @default(now())
1010
+
1011
+ @@index([campaignId])
1012
+ }
1013
+
1014
+ model FlyerCampaignSponsor {
1015
+ id String @id @default(cuid())
1016
+ campaignId String
1017
+ campaign FlyerCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
1018
+
1019
+ slotType FlyerSponsorSlot
1020
+ businessName String
1021
+ logoS3Key String?
1022
+ ctaText String?
1023
+ sponsorQrSlug String? @unique
1024
+
1025
+ slotPriceCents Int @default(0)
1026
+ stripePaymentIntentId String?
1027
+ paidAt DateTime?
1028
+
1029
+ hostPayoutCents Int?
1030
+ bashFeeCents Int?
1031
+
1032
+ conversions FlyerConversion[]
1033
+
1034
+ createdAt DateTime @default(now())
1035
+
1036
+ @@unique([campaignId, slotType])
1037
+ @@index([campaignId])
1038
+ }
1039
+
1040
+ model FlyerConversion {
1041
+ id String @id @default(cuid())
1042
+ campaignId String
1043
+ campaign FlyerCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
1044
+
1045
+ type FlyerConversionType @default(QR_SCAN)
1046
+ sponsorId String?
1047
+ ticketId String?
1048
+
1049
+ crrtCode String?
1050
+ userAgent String?
1051
+
1052
+ occurredAt DateTime @default(now())
1053
+
1054
+ sponsor FlyerCampaignSponsor? @relation(fields: [sponsorId], references: [id], onDelete: SetNull)
1055
+ ticket Ticket? @relation(fields: [ticketId], references: [id], onDelete: SetNull)
1056
+
1057
+ @@index([campaignId])
1058
+ @@index([occurredAt])
1059
+ @@index([sponsorId])
1060
+ @@index([ticketId])
1061
+ }
1062
+
1063
+ /// USPS carrier-route catalog (Utah MVP; expand for national rollout).
1064
+ model PostalCarrierRoute {
1065
+ id String @id @default(cuid())
1066
+ crrtCode String
1067
+ zipCode String
1068
+ city String
1069
+ state String @default("UT")
1070
+ latitude Float
1071
+ longitude Float
1072
+ householdCount Int
1073
+ residentialOnly Boolean @default(true)
1074
+ medianIncomeBand String?
1075
+ avgHouseholdSize Float?
1076
+ medianAge Float?
1077
+
1078
+ createdAt DateTime @default(now())
1079
+ updatedAt DateTime @updatedAt
1080
+
1081
+ @@unique([zipCode, crrtCode])
1082
+ @@index([latitude, longitude])
1083
+ @@index([zipCode])
1084
+ }
1085
+
861
1086
  enum BashCreativeStatus {
862
1087
  Pending
863
1088
  InReview
@@ -870,16 +1095,16 @@ enum BashCreativeStatus {
870
1095
  model BashCreativeSubmission {
871
1096
  id String @id @default(cuid())
872
1097
  serviceId String
873
- service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
1098
+ service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
874
1099
  submittedById String?
875
- submittedBy User? @relation(fields: [submittedById], references: [id], onDelete: SetNull)
876
- status BashCreativeStatus @default(Pending)
877
- scriptNotes String? @db.Text
1100
+ submittedBy User? @relation(fields: [submittedById], references: [id], onDelete: SetNull)
1101
+ status BashCreativeStatus @default(Pending)
1102
+ scriptNotes String? @db.Text
878
1103
  footageUrl String?
879
1104
  deliverableUrl String?
880
- requestedAt DateTime @default(now())
1105
+ requestedAt DateTime @default(now())
881
1106
  deliveredAt DateTime?
882
- updatedAt DateTime @updatedAt
1107
+ updatedAt DateTime @updatedAt
883
1108
 
884
1109
  @@index([serviceId])
885
1110
  @@index([status])
@@ -1045,15 +1270,15 @@ model PublicBashRsvp {
1045
1270
 
1046
1271
  // Per-event referral rewards: host defines tiers (e.g. 3 referrals = free drink)
1047
1272
  model EventReferralReward {
1048
- id String @id @default(cuid())
1049
- bashEventId String
1050
- referralCount Int // How many referrals needed for this reward
1051
- rewardType String // BashPoints, FreeTicket, FreeDrink, Custom
1052
- rewardLabel String // e.g. "Free drink at the bar"
1053
- bashPointsAmount Int? // When rewardType=BashPoints
1054
- sortOrder Int @default(0)
1055
- createdAt DateTime @default(now())
1056
- updatedAt DateTime @updatedAt
1273
+ id String @id @default(cuid())
1274
+ bashEventId String
1275
+ referralCount Int // How many referrals needed for this reward
1276
+ rewardType String // BashPoints, FreeTicket, FreeDrink, Custom
1277
+ rewardLabel String // e.g. "Free drink at the bar"
1278
+ bashPointsAmount Int? // When rewardType=BashPoints
1279
+ sortOrder Int @default(0)
1280
+ createdAt DateTime @default(now())
1281
+ updatedAt DateTime @updatedAt
1057
1282
 
1058
1283
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1059
1284
 
@@ -1062,12 +1287,12 @@ model EventReferralReward {
1062
1287
 
1063
1288
  // Tracks referrals: attendee A referred person B who bought a ticket
1064
1289
  model EventReferral {
1065
- id String @id @default(cuid())
1066
- bashEventId String
1067
- referrerId String // Attendee who shared their link
1068
- referredUserId String // Person who bought via the link
1069
- ticketId String? @unique // Ticket purchased (optional, 1:1 with Ticket)
1070
- createdAt DateTime @default(now())
1290
+ id String @id @default(cuid())
1291
+ bashEventId String
1292
+ referrerId String // Attendee who shared their link
1293
+ referredUserId String // Person who bought via the link
1294
+ ticketId String? @unique // Ticket purchased (optional, 1:1 with Ticket)
1295
+ createdAt DateTime @default(now())
1071
1296
 
1072
1297
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1073
1298
  referrer User @relation("EventReferralsMade", fields: [referrerId], references: [id], onDelete: Cascade)
@@ -1081,16 +1306,16 @@ model EventReferral {
1081
1306
 
1082
1307
  // Curated event lists for discovery (super-user editorial tool)
1083
1308
  model BashList {
1084
- id String @id @default(cuid())
1085
- title String // e.g. "Best Friday Night in Ogden"
1086
- slug String @unique // URL-friendly identifier
1087
- description String? @db.Text
1088
- filterConfig Json // { dateRange?, city?, state?, eventType?, maxPrice?, ... }
1089
- isFeatured Boolean @default(false)
1090
- sortOrder Int @default(0)
1091
- createdById String
1092
- createdAt DateTime @default(now())
1093
- updatedAt DateTime @updatedAt
1309
+ id String @id @default(cuid())
1310
+ title String // e.g. "Best Friday Night in Ogden"
1311
+ slug String @unique // URL-friendly identifier
1312
+ description String? @db.Text
1313
+ filterConfig Json // { dateRange?, city?, state?, eventType?, maxPrice?, ... }
1314
+ isFeatured Boolean @default(false)
1315
+ sortOrder Int @default(0)
1316
+ createdById String
1317
+ createdAt DateTime @default(now())
1318
+ updatedAt DateTime @updatedAt
1094
1319
 
1095
1320
  createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade)
1096
1321
 
@@ -1103,11 +1328,11 @@ model UserItinerary {
1103
1328
  id String @id @default(cuid())
1104
1329
  userId String
1105
1330
  date DateTime @db.Date // The night (e.g. 2026-03-15)
1106
- title String? // e.g. "Friday night out"
1331
+ title String? // e.g. "Friday night out"
1107
1332
  createdAt DateTime @default(now())
1108
1333
  updatedAt DateTime @updatedAt
1109
1334
 
1110
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1335
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1111
1336
  items UserItineraryItem[]
1112
1337
 
1113
1338
  @@unique([userId, date])
@@ -1138,15 +1363,15 @@ model Coordinates {
1138
1363
  }
1139
1364
 
1140
1365
  model Checkout {
1141
- id String @id @default(cuid())
1366
+ id String @id @default(cuid())
1142
1367
  ownerId String
1143
1368
  bashEventId String
1144
- checkoutDateTime DateTime? @default(now())
1145
- stripeCheckoutSessionId String? @unique
1146
- stripePaymentIntentId String? @unique
1369
+ checkoutDateTime DateTime? @default(now())
1370
+ stripeCheckoutSessionId String? @unique
1371
+ stripePaymentIntentId String? @unique
1147
1372
  refundedAt DateTime?
1148
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1149
- owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
1373
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1374
+ owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
1150
1375
  tickets Ticket[]
1151
1376
  appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this checkout
1152
1377
  waiverAcceptances WaiverAcceptance[]
@@ -1184,13 +1409,13 @@ model BashCreditTransaction {
1184
1409
  status CreditTransactionStatus @default(Pending)
1185
1410
 
1186
1411
  // Source Tracking (Legacy fields - keeping for backwards compatibility)
1187
- source String // Legacy source field
1188
- referralCode String?
1189
- referredUserId String?
1190
- referredUser User? @relation("ReferredUser", fields: [referredUserId], references: [id])
1191
- membershipTier MembershipTier?
1192
- description String?
1193
- metadata String? // JSON string for additional context
1412
+ source String // Legacy source field
1413
+ referralCode String?
1414
+ referredUserId String?
1415
+ referredUser User? @relation("ReferredUser", fields: [referredUserId], references: [id])
1416
+ membershipTier MembershipTier?
1417
+ description String?
1418
+ metadata String? // JSON string for additional context
1194
1419
 
1195
1420
  // New Source Tracking
1196
1421
  sourceType CreditSourceType?
@@ -1226,19 +1451,19 @@ model BashCreditTransaction {
1226
1451
  }
1227
1452
 
1228
1453
  model PendingBashPointsGift {
1229
- id String @id @default(cuid())
1230
- senderId String
1231
- sender User @relation("SentPendingGifts", fields: [senderId], references: [id], onDelete: Cascade)
1232
- recipientEmail String
1233
- amount Int
1234
- message String?
1235
- status PendingGiftStatus @default(Pending)
1236
- claimedAt DateTime?
1454
+ id String @id @default(cuid())
1455
+ senderId String
1456
+ sender User @relation("SentPendingGifts", fields: [senderId], references: [id], onDelete: Cascade)
1457
+ recipientEmail String
1458
+ amount Int
1459
+ message String?
1460
+ status PendingGiftStatus @default(Pending)
1461
+ claimedAt DateTime?
1237
1462
  claimedByUserId String?
1238
- claimedBy User? @relation("ClaimedPendingGifts", fields: [claimedByUserId], references: [id], onDelete: SetNull)
1239
- returnedAt DateTime?
1240
- expiresAt DateTime
1241
- createdAt DateTime @default(now())
1463
+ claimedBy User? @relation("ClaimedPendingGifts", fields: [claimedByUserId], references: [id], onDelete: SetNull)
1464
+ returnedAt DateTime?
1465
+ expiresAt DateTime
1466
+ createdAt DateTime @default(now())
1242
1467
 
1243
1468
  @@index([recipientEmail, status])
1244
1469
  @@index([senderId])
@@ -1369,23 +1594,23 @@ model TicketTier {
1369
1594
  // Availability rules
1370
1595
  availabilityType String @default("Immediate") // "Immediate" | "OnDate" | "EngagedUsersOnly" | "UnlockWhenInterestReached" | "UnlockWhenTicketsSold"
1371
1596
  availableAt DateTime? // OnDate: when tier unlocks
1372
- availableWhenInterestReached Int? // UnlockWhenInterestReached: min IdeaInterest count
1373
- unlockWhenTicketsSold Int? // UnlockWhenTicketsSold: min tickets sold across event to unlock this tier
1374
- maxTicketsPerUser Int? // Optional per-user purchase cap
1597
+ availableWhenInterestReached Int? // UnlockWhenInterestReached: min IdeaInterest count
1598
+ unlockWhenTicketsSold Int? // UnlockWhenTicketsSold: min tickets sold across event to unlock this tier
1599
+ maxTicketsPerUser Int? // Optional per-user purchase cap
1375
1600
 
1376
1601
  // Privacy: "Public" | "LinkOnly" | "InviteOnly"
1377
- tierPrivacy String @default("Public")
1602
+ tierPrivacy String @default("Public")
1378
1603
 
1379
1604
  // Checkout timer: host-configurable per-tier timeout (minutes). null = no timer.
1380
- checkoutTimeoutMinutes Int?
1605
+ checkoutTimeoutMinutes Int?
1381
1606
 
1382
1607
  /// When true, BashPass subscribers may redeem this tier (subject to caps).
1383
- bashPassEligible Boolean @default(false)
1608
+ bashPassEligible Boolean @default(false)
1384
1609
  /// Max BashPass redemptions for this tier (null = unlimited within tier capacity).
1385
- bashPassMaxRedemptions Int?
1610
+ bashPassMaxRedemptions Int?
1611
+ ticketGiftGrants TicketGiftGrant[]
1386
1612
 
1387
1613
  @@unique([bashEventId, title])
1388
- ticketGiftGrants TicketGiftGrant[]
1389
1614
  }
1390
1615
 
1391
1616
  enum TicketGiftGrantStatus {
@@ -1442,7 +1667,9 @@ model Ticket {
1442
1667
  waitlistJoinedAt DateTime?
1443
1668
  waitlistNotifiedAt DateTime?
1444
1669
  waitlistExpiresAt DateTime?
1445
- /** Set when we send the "your waitlist window expires soon" reminder (once per ticket). */
1670
+ /**
1671
+ * Set when we send the "your waitlist window expires soon" reminder (once per ticket).
1672
+ */
1446
1673
  waitlistReminderSentAt DateTime?
1447
1674
  refundRequestedAt DateTime? // Set when RefundPending; cleared once refund processes
1448
1675
  checkInLocation String?
@@ -1472,20 +1699,21 @@ model Ticket {
1472
1699
  utmMedium String?
1473
1700
  utmCampaign String?
1474
1701
 
1475
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
1702
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
1476
1703
  attributedArtistId String?
1477
- attributedArtist BashEventArtist? @relation(fields: [attributedArtistId], references: [id], onDelete: SetNull)
1478
- checkout Checkout? @relation(fields: [checkoutId], references: [id])
1479
- forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
1480
- invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
1481
- owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
1482
- ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
1483
- waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
1484
- metadata TicketMetadata[]
1485
- transfers TicketTransfer[]
1486
- transferInvite TicketTransferInvite?
1487
- appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this ticket
1488
- eventReferral EventReferral? // When this ticket was purchased via a referral link
1704
+ attributedArtist BashEventArtist? @relation(fields: [attributedArtistId], references: [id], onDelete: SetNull)
1705
+ checkout Checkout? @relation(fields: [checkoutId], references: [id])
1706
+ forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
1707
+ invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
1708
+ owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
1709
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
1710
+ waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
1711
+ metadata TicketMetadata[]
1712
+ transfers TicketTransfer[]
1713
+ transferInvite TicketTransferInvite?
1714
+ appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this ticket
1715
+ eventReferral EventReferral? // When this ticket was purchased via a referral link
1716
+ flyerConversions FlyerConversion[]
1489
1717
 
1490
1718
  @@index([bashEventId])
1491
1719
  @@index([waitlistUserId])
@@ -1526,9 +1754,9 @@ model TicketTransferInvite {
1526
1754
  expiresAt DateTime
1527
1755
  createdAt DateTime @default(now())
1528
1756
 
1529
- ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
1530
- fromUser User @relation("TicketTransferInviteFrom", fields: [fromUserId], references: [id])
1531
- invitedUser User? @relation("TicketTransferInviteToUser", fields: [toUserId], references: [id])
1757
+ ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
1758
+ fromUser User @relation("TicketTransferInviteFrom", fields: [fromUserId], references: [id])
1759
+ invitedUser User? @relation("TicketTransferInviteToUser", fields: [toUserId], references: [id])
1532
1760
 
1533
1761
  @@index([token])
1534
1762
  @@index([fromUserId])
@@ -1593,20 +1821,20 @@ model Recurrence {
1593
1821
 
1594
1822
  // Recurring event series: template that spawns many BashEvent instances (e.g. "Every Tuesday 7pm for 14 weeks")
1595
1823
  model EventSeries {
1596
- id String @id @default(cuid())
1824
+ id String @id @default(cuid())
1597
1825
  organizationId String?
1598
1826
  creatorId String
1599
1827
  name String
1600
- description String? @db.Text
1828
+ description String? @db.Text
1601
1829
  frequency RecurringFrequency
1602
- interval Int @default(1)
1830
+ interval Int @default(1)
1603
1831
  repeatOnDays DayOfWeek[]
1604
1832
  seriesStart DateTime
1605
1833
  seriesEnd DateTime
1606
- exceptions Json? // Array of ISO date strings to skip
1607
- templateConfig Json? // Event defaults: title pattern, ticketTiers, etc.
1608
- createdAt DateTime @default(now())
1609
- updatedAt DateTime @updatedAt
1834
+ exceptions Json? // Array of ISO date strings to skip
1835
+ templateConfig Json? // Event defaults: title pattern, ticketTiers, etc.
1836
+ createdAt DateTime @default(now())
1837
+ updatedAt DateTime @updatedAt
1610
1838
 
1611
1839
  organization Organization? @relation("OrganizationEventSeries", fields: [organizationId], references: [id], onDelete: SetNull)
1612
1840
  creator User @relation("EventSeriesCreator", fields: [creatorId], references: [id], onDelete: Restrict)
@@ -1655,21 +1883,28 @@ model BashEventTemplate {
1655
1883
 
1656
1884
  // Used by BashEvent (amountOfGuests) and by Services (crowdSizeId/crowdSize); same model, different relation names.
1657
1885
  model AmountOfGuests {
1658
- id String @id @default(cuid())
1659
- minimum Int? @default(10)
1660
- ideal Int? @default(35)
1661
- expected Int? @default(25)
1662
- showMinimumGuests Boolean @default(true)
1663
- showIdealGuests Boolean @default(true)
1664
- showExpectedGuests Boolean @default(true)
1665
- venueId String?
1666
- organizationId String?
1667
- bashEvent BashEvent?
1668
- entertainmentService EntertainmentService?
1669
- eventService EventService?
1670
- exhibitor Exhibitor?
1671
- sponsor Sponsor?
1672
- vendor Vendor?
1886
+ id String @id @default(cuid())
1887
+ minimum Int? @default(10)
1888
+ ideal Int? @default(35)
1889
+ expected Int? @default(25)
1890
+ showMinimumGuests Boolean @default(true)
1891
+ showIdealGuests Boolean @default(true)
1892
+ showExpectedGuests Boolean @default(true)
1893
+ maximum Int?
1894
+ showMaximumGuests Boolean @default(false)
1895
+ autoCancelIfMinNotMet Boolean @default(false)
1896
+ autoPublicWhenMinMet Boolean @default(false)
1897
+ makePrivateAtMax Boolean @default(false)
1898
+ allowWaitlistAtMax Boolean @default(false)
1899
+ aimingForMaxCapacity Boolean @default(false)
1900
+ venueId String?
1901
+ organizationId String?
1902
+ bashEvent BashEvent?
1903
+ entertainmentService EntertainmentService?
1904
+ eventService EventService?
1905
+ exhibitor Exhibitor?
1906
+ sponsor Sponsor?
1907
+ vendor Vendor?
1673
1908
  }
1674
1909
 
1675
1910
  model TargetAudience {
@@ -1795,19 +2030,19 @@ model Prize {
1795
2030
  }
1796
2031
 
1797
2032
  model Review {
1798
- id String @id @default(cuid())
1799
- rating Int
1800
- creatorId String
1801
- bashEventId String
1802
- createdAt DateTime @default(now())
1803
- updatedAt DateTime @updatedAt
2033
+ id String @id @default(cuid())
2034
+ rating Int
2035
+ creatorId String
2036
+ bashEventId String
2037
+ createdAt DateTime @default(now())
2038
+ updatedAt DateTime @updatedAt
1804
2039
  /// Public reply from the event host (service provider) to the reviewer.
1805
- hostReplyText String? @db.Text
2040
+ hostReplyText String? @db.Text
1806
2041
  hostReplyAt DateTime?
1807
- comments BashComment[]
1808
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
1809
- creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
1810
- helpfulVotes ReviewHelpfulVote[]
2042
+ comments BashComment[]
2043
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
2044
+ creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
2045
+ helpfulVotes ReviewHelpfulVote[]
1811
2046
 
1812
2047
  @@unique([creatorId, bashEventId])
1813
2048
  }
@@ -1891,7 +2126,7 @@ model User {
1891
2126
  isSuperUser Boolean @default(false)
1892
2127
  isSuspended Boolean @default(false)
1893
2128
  intent UserIntent?
1894
- signupSource String? // e.g. 'jamwith', 'mixlog', 'organic' for attribution
2129
+ signupSource String? // e.g. 'jamwith', 'mixlog', 'organic' for attribution
1895
2130
  googleCalendarAccess String?
1896
2131
 
1897
2132
  // Google OAuth tokens for API access (Contacts, Calendar, etc.)
@@ -1900,123 +2135,148 @@ model User {
1900
2135
  googleTokenExpiry DateTime?
1901
2136
 
1902
2137
  // LinkedIn OAuth tokens for UGC posting (w_member_social scope)
1903
- linkedinAccessToken String?
1904
- linkedinTokenExpiry DateTime?
1905
- linkedinPersonId String? // LinkedIn `sub` / person URN
1906
- linkedinVanityUrl String? // e.g. "stevegoodrick" → linkedin.com/in/stevegoodrick
1907
- linkedinHeadline String? // e.g. "Founder at The Bash App"
1908
-
1909
- givenName String?
1910
- familyName String?
1911
- nameChangeCount Int @default(0)
1912
- hash String?
1913
- previousPasswordHashes String[] @default([]) // Last 5 bcrypt hashes (oldest first) for password-reuse prevention
1914
- emailVerified DateTime?
1915
- image String?
1916
- imageUpdatedAt DateTime? // When Google/OAuth image was last updated
1917
- uploadedImage String?
1918
- uploadedImageUpdatedAt DateTime? // When manually uploaded image was last updated
1919
- dob DateTime?
1920
- showBirthdayOnProfile Boolean @default(false) // If true, show month/day on public profile
2138
+ linkedinAccessToken String?
2139
+ linkedinTokenExpiry DateTime?
2140
+ linkedinPersonId String? // LinkedIn `sub` / person URN
2141
+ linkedinVanityUrl String? // e.g. "stevegoodrick" → linkedin.com/in/stevegoodrick
2142
+ linkedinHeadline String? // e.g. "Founder at The Bash App"
2143
+
2144
+ givenName String?
2145
+ familyName String?
2146
+ nameChangeCount Int @default(0)
2147
+ hash String?
2148
+ previousPasswordHashes String[] @default([]) // Last 5 bcrypt hashes (oldest first) for password-reuse prevention
2149
+ emailVerified DateTime?
2150
+ image String?
2151
+ imageUpdatedAt DateTime? // When Google/OAuth image was last updated
2152
+ uploadedImage String?
2153
+ uploadedImageUpdatedAt DateTime? // When manually uploaded image was last updated
2154
+ dob DateTime?
2155
+ showBirthdayOnProfile Boolean @default(false) // If true, show month/day on public profile
1921
2156
  // Part B: Birthday Rewards full product
1922
- birthdayRewardsOptIn Boolean @default(false)
1923
- birthdayRewardsOptInAt DateTime?
1924
- birthdayLocationId String? // FK UserLocation (birthday home base)
1925
- birthdayMaxDistanceMiles Int @default(25)
1926
- birthdayNoPurchaseOnly Boolean @default(false)
2157
+ birthdayRewardsOptIn Boolean @default(false)
2158
+ birthdayRewardsOptInAt DateTime?
2159
+ birthdayLocationId String? // FK UserLocation (birthday home base)
2160
+ birthdayMaxDistanceMiles Int @default(25)
2161
+ birthdayNoPurchaseOnly Boolean @default(false)
1927
2162
  /// Email reminder N days before birthday (birthday rewards plan nudge)
1928
- birthdayReminderEnabled Boolean @default(true)
1929
- birthdayReminderDaysAdvance Int @default(7)
1930
- birthdayCategories String[] @default([]) // e.g. restaurant, coffee, dessert
2163
+ birthdayReminderEnabled Boolean @default(true)
2164
+ birthdayReminderDaysAdvance Int @default(7)
2165
+ birthdayCategories String[] @default([]) // e.g. restaurant, coffee, dessert
1931
2166
  /// Calendar year when we last sent the "your birthday month freebies" reminder (cron on 1st of month).
1932
- birthdayMonthReminderSentYear Int?
1933
- idDocumentUrl String? // Optional: secure ID upload for verification
1934
- gender Gender?
1935
- sex Sex?
1936
- organization String?
1937
- jobTitle String?
1938
- roles UserRole[] @default([User])
1939
- aboutMe String?
1940
- levelBadge String?
1941
- temporaryBadges String[] @default([])
1942
- status UserStatus
1943
- hostRating Float?
1944
- totalRatings Int?
1945
- totalEventsHosted Int? @default(0) // Total published/completed events hosted
1946
- totalAttendeesHosted Int? @default(0) // Total checked-in attendees across all events
1947
- totalBookingsAccepted Int? @default(0) // Service provider stat
1948
- totalBookingsCompleted Int? @default(0) // Service provider stat
1949
- magicLink String?
1950
- magicLinkExpiration DateTime?
1951
- hostNumber Int?
1952
- foundingHostBadgeAwarded Boolean @default(false)
1953
- firstBashWithAttendeesDate DateTime?
1954
- bashesCreatedCount Int @default(0)
1955
- bashesPublishedCount Int @default(0)
1956
- bashesApprovedCount Int @default(0)
1957
- bashesCompletedCount Int @default(0)
1958
- magicLinkUsed DateTime?
1959
- idVerified DateTime?
1960
- street String?
1961
- city String?
1962
- state String?
1963
- zipCode String?
1964
- country String? @default("US")
1965
- phone String?
1966
- documentIDId String? @unique
1967
- accepted Boolean? @default(false)
1968
- boughtTicket Boolean? @default(false)
1969
- noPay Boolean? @default(false)
1970
- supportedEvent Boolean? @default(false)
1971
- suspendedUntil DateTime?
1972
- suspendedById String?
1973
- isLightweightAccount Boolean @default(false)
1974
- notifyForTrendingBashes Boolean @default(false)
1975
- trendingBashThreshold Int @default(10)
1976
- artistsToSee String[] @default([])
1977
- googleCalendarSyncSettings String?
1978
- membershipTier MembershipTier @default(Basic)
1979
- membershipExpiresAt DateTime?
1980
- membershipAutoRenew Boolean @default(false)
1981
- membershipBillingInterval String?
1982
- membershipLastBilledAt DateTime?
1983
- membershipFeaturedEventsUsed Int @default(0)
1984
- membershipStripeSubscriptionId String? @unique
2167
+ birthdayMonthReminderSentYear Int?
2168
+ idDocumentUrl String? // Optional: secure ID upload for verification
2169
+ gender Gender?
2170
+ /// Free-text self-described gender. Takes precedence over `gender` when present.
2171
+ genderIdentity String?
2172
+ sex Sex?
2173
+ /// Public-profile visibility flags for self-reported demographics
2174
+ showAgeOnProfile Boolean @default(true)
2175
+ showGenderOnProfile Boolean @default(true)
2176
+ /// Master visibility toggle for ID/Payment/Host/Student trust badges
2177
+ showVerifiedBadgesOnProfile Boolean @default(true)
2178
+ organization String?
2179
+ jobTitle String?
2180
+ roles UserRole[] @default([User])
2181
+ aboutMe String?
2182
+ levelBadge String?
2183
+ temporaryBadges String[] @default([])
2184
+ status UserStatus
2185
+ hostRating Float?
2186
+ totalRatings Int?
2187
+ totalEventsHosted Int? @default(0) // Total published/completed events hosted
2188
+ totalAttendeesHosted Int? @default(0) // Total checked-in attendees across all events
2189
+ totalBookingsAccepted Int? @default(0) // Service provider stat
2190
+ totalBookingsCompleted Int? @default(0) // Service provider stat
2191
+ magicLink String?
2192
+ magicLinkExpiration DateTime?
2193
+ hostNumber Int?
2194
+ foundingHostBadgeAwarded Boolean @default(false)
2195
+ firstBashWithAttendeesDate DateTime?
2196
+ bashesCreatedCount Int @default(0)
2197
+ bashesPublishedCount Int @default(0)
2198
+ bashesApprovedCount Int @default(0)
2199
+ bashesCompletedCount Int @default(0)
2200
+ magicLinkUsed DateTime?
2201
+ idVerified DateTime?
2202
+ street String?
2203
+ city String?
2204
+ state String?
2205
+ zipCode String?
2206
+ country String? @default("US")
2207
+ phone String?
2208
+ documentIDId String? @unique
2209
+ /// Encrypted-S3 key for the user's stored government-ID image (deleted per retention policy / on ID expiration)
2210
+ govIdImageS3Key String?
2211
+ /// When the gov-ID image becomes deletable (null = "indefinite, until I remove it").
2212
+ /// Indexed via a Postgres PARTIAL index for the expireIdImages cron — see
2213
+ /// migrations/20260418061500_index_gov_id_image_expires_at. Prisma does
2214
+ /// not support partial indexes declaratively, so the index is SQL-only.
2215
+ govIdImageExpiresAt DateTime?
2216
+ /// Retention policy for the stored gov-ID image (drives expireIdImages cron)
2217
+ govIdRetentionPolicy GovIdRetentionPolicy?
2218
+ /// GDPR Art. 7 audit trail for retention-policy consent
2219
+ govIdRetentionConsentedAt DateTime?
2220
+ /// Encrypted-S3 key for the user's stored student-ID image (no OCR; school inferred via vision model)
2221
+ studentIdImageS3Key String?
2222
+ studentIdUploadedAt DateTime?
2223
+ /// School/university name extracted from student-ID by vision model; user-editable.
2224
+ schoolName String?
2225
+ /// Whether `schoolName` came from the AI vision model or was edited by the user
2226
+ schoolNameSource SchoolNameSource?
2227
+ accepted Boolean? @default(false)
2228
+ boughtTicket Boolean? @default(false)
2229
+ noPay Boolean? @default(false)
2230
+ supportedEvent Boolean? @default(false)
2231
+ suspendedUntil DateTime?
2232
+ suspendedById String?
2233
+ isLightweightAccount Boolean @default(false)
2234
+ notifyForTrendingBashes Boolean @default(false)
2235
+ trendingBashThreshold Int @default(10)
2236
+ artistsToSee String[] @default([])
2237
+ googleCalendarSyncSettings String?
2238
+ membershipTier MembershipTier @default(Basic)
2239
+ membershipExpiresAt DateTime?
2240
+ membershipAutoRenew Boolean @default(false)
2241
+ membershipBillingInterval String?
2242
+ membershipLastBilledAt DateTime?
2243
+ membershipFeaturedEventsUsed Int @default(0)
2244
+ membershipStripeSubscriptionId String? @unique
1985
2245
  /// Optional consumer BashPass subscription (waives guest platform fee on tickets; monthly BashPoints).
1986
- bashPassStripeSubscriptionId String? @unique
1987
- bashPassCurrentPeriodEnd DateTime?
2246
+ bashPassStripeSubscriptionId String? @unique
2247
+ bashPassCurrentPeriodEnd DateTime?
1988
2248
  /// Stripe price tier for standalone BashPass; effective tier may also come from membership bundle.
1989
- bashPassTier BashPassTier?
1990
- bashPassEventsUsedThisMonth Int @default(0)
1991
- bashPassMonthlyCapResetAt DateTime?
1992
- isVerified Boolean @default(false)
1993
- verificationMethod String?
1994
- verifiedAt DateTime?
1995
- verificationScore Int @default(0)
2249
+ bashPassTier BashPassTier?
2250
+ bashPassEventsUsedThisMonth Int @default(0)
2251
+ bashPassMonthlyCapResetAt DateTime?
2252
+ isVerified Boolean @default(false)
2253
+ verificationMethod String?
2254
+ verifiedAt DateTime?
2255
+ verificationScore Int @default(0)
1996
2256
  /// $1 card verification (Stripe) — distinct from Stripe Identity / isVerified
1997
- paymentVerified Boolean @default(false)
1998
- paymentVerifiedAt DateTime?
2257
+ paymentVerified Boolean @default(false)
2258
+ paymentVerifiedAt DateTime?
1999
2259
  /// One-time BashPoints bonus after $1 verification payment succeeded
2000
- paymentVerificationBonusAwarded Boolean @default(false)
2001
- biometricFingerprint String?
2002
- venmoUsername String?
2003
- venmoQRCodeUrl String?
2004
- zelleEmail String?
2260
+ paymentVerificationBonusAwarded Boolean @default(false)
2261
+ biometricFingerprint String?
2262
+ venmoUsername String?
2263
+ venmoQRCodeUrl String?
2264
+ zelleEmail String?
2005
2265
  /// Optional Venmo / Zelle / CashApp handle shown to hosts (promo requests, code assignment). Bash does not move money.
2006
- paymentHandle String?
2007
- zellePhone String?
2008
- zelleQRCodeUrl String?
2009
- isAmbashador Boolean @default(false)
2010
- ambashadorAwardedAt DateTime?
2011
- isBashInsider Boolean @default(false)
2012
- insiderAwardedAt DateTime?
2013
- scoutTier ScoutTier @default(None)
2014
- scoutAwardedAt DateTime?
2015
- agentAwardedAt DateTime?
2016
- totalServiceReferrals Int @default(0)
2017
- isFirstInCity Boolean @default(false)
2018
- firstInCityAt DateTime?
2019
- fcmTokens String[] @default([]) // Firebase Cloud Messaging tokens for push notifications
2266
+ paymentHandle String?
2267
+ zellePhone String?
2268
+ zelleQRCodeUrl String?
2269
+ isAmbashador Boolean @default(false)
2270
+ ambashadorAwardedAt DateTime?
2271
+ isBashInsider Boolean @default(false)
2272
+ insiderAwardedAt DateTime?
2273
+ scoutTier ScoutTier @default(None)
2274
+ scoutAwardedAt DateTime?
2275
+ agentAwardedAt DateTime?
2276
+ totalServiceReferrals Int @default(0)
2277
+ isFirstInCity Boolean @default(false)
2278
+ firstInCityAt DateTime?
2279
+ fcmTokens String[] @default([]) // Firebase Cloud Messaging tokens for push notifications
2020
2280
 
2021
2281
  // BashPoints System Fields
2022
2282
  bashPointsBalance Int @default(0)
@@ -2027,48 +2287,49 @@ model User {
2027
2287
  referralPurchaseCredited Boolean @default(false)
2028
2288
 
2029
2289
  // Security Fields
2030
- accountLockedUntil DateTime? // Account lockout timestamp for failed login protection
2031
- revokedTokens RevokedToken[] @relation("RevokedTokens") // Revoked JWT tokens
2032
- auditLogs AuditLog[] @relation("UserAuditLogs")
2033
- auditLogsPerformed AuditLog[] @relation("PerformedByUser")
2034
- consents UserConsent[]
2035
- birthdayOfferOptIns UserBirthdayOfferOptIn[]
2036
- birthdayOfferDeliveries BirthdayOfferDelivery[]
2037
- birthdayBoostGrants BirthdayBoostGrant[]
2038
- birthdayRewardRedemptions BirthdayRewardRedemption[]
2039
- birthdayRewardFavorites BirthdayRewardFavorite[]
2040
- birthdayDealSuggestions BirthdayDealSuggestion[]
2290
+ accountLockedUntil DateTime? // Account lockout timestamp for failed login protection
2291
+ revokedTokens RevokedToken[] @relation("RevokedTokens") // Revoked JWT tokens
2292
+ auditLogs AuditLog[] @relation("UserAuditLogs")
2293
+ auditLogsPerformed AuditLog[] @relation("PerformedByUser")
2294
+ consents UserConsent[]
2295
+ birthdayOfferOptIns UserBirthdayOfferOptIn[]
2296
+ birthdayOfferDeliveries BirthdayOfferDelivery[]
2297
+ birthdayBoostGrants BirthdayBoostGrant[]
2298
+ birthdayRewardRedemptions BirthdayRewardRedemption[]
2299
+ birthdayRewardFavorites BirthdayRewardFavorite[]
2300
+ birthdayDealSuggestions BirthdayDealSuggestion[]
2041
2301
  birthdayCityResourceSuggestions BirthdayCityResourceSuggestion[]
2042
- rewardReports RewardReport[]
2043
- birthdayFreebieVotes BirthdayFreebieVote[]
2044
- rewardReportsReviewed RewardReport[] @relation("RewardReportReviewedBy")
2045
- nationalChainReports NationalChainReport[]
2046
- nationalChainReportsReviewed NationalChainReport[] @relation("NationalChainReportReviewedBy")
2047
- userLocations UserLocation[]
2048
- birthdayLocation UserLocation? @relation("UserBirthdayLocation", fields: [birthdayLocationId], references: [id], onDelete: SetNull)
2049
- offerClaims OfferClaim[]
2050
- birthdayPlans BirthdayPlan[]
2051
- birthdayFreebiesSavedPlan BirthdayFreebiesSavedPlan?
2302
+ rewardReports RewardReport[]
2303
+ birthdayFreebieVotes BirthdayFreebieVote[]
2304
+ rewardReportsReviewed RewardReport[] @relation("RewardReportReviewedBy")
2305
+ nationalChainReports NationalChainReport[]
2306
+ nationalChainReportsReviewed NationalChainReport[] @relation("NationalChainReportReviewedBy")
2307
+ userLocations UserLocation[]
2308
+ birthdayLocation UserLocation? @relation("UserBirthdayLocation", fields: [birthdayLocationId], references: [id], onDelete: SetNull)
2309
+ offerClaims OfferClaim[]
2310
+ birthdayPlans BirthdayPlan[]
2311
+ birthdayFreebiesSavedPlan BirthdayFreebiesSavedPlan?
2052
2312
 
2053
2313
  associatedBashes AssociatedBash[]
2054
2314
  associatedServices AssociatedService[]
2055
2315
  comment BashComment[]
2056
- referredTransactions BashCreditTransaction[] @relation("ReferredUser")
2316
+ referredTransactions BashCreditTransaction[] @relation("ReferredUser")
2057
2317
  creditTransactions BashCreditTransaction[]
2058
- createdEvents BashEvent[] @relation("CreatedEvent")
2059
- bashEventsTransferredFrom BashEvent[] @relation("TransferredFrom")
2060
- bashEventsPayoutRecipient BashEvent[] @relation("BashEventsPayoutRecipient")
2061
- bashEventMessagesSent BashEventMessage[] @relation("BashMessageSender")
2062
- bashEventMessagesRead BashEventMessageRead[] @relation("BashMessageReader")
2063
- transferRequestsFrom BashEventTransferRequest[] @relation("TransferRequestsFrom")
2064
- transferRequestsTo BashEventTransferRequest[] @relation("TransferRequestsTo")
2065
- sentPendingGifts PendingBashPointsGift[] @relation("SentPendingGifts")
2066
- claimedPendingGifts PendingBashPointsGift[] @relation("ClaimedPendingGifts")
2318
+ createdEvents BashEvent[] @relation("CreatedEvent")
2319
+ flyerCampaigns FlyerCampaign[]
2320
+ bashEventsTransferredFrom BashEvent[] @relation("TransferredFrom")
2321
+ bashEventsPayoutRecipient BashEvent[] @relation("BashEventsPayoutRecipient")
2322
+ bashEventMessagesSent BashEventMessage[] @relation("BashMessageSender")
2323
+ bashEventMessagesRead BashEventMessageRead[] @relation("BashMessageReader")
2324
+ transferRequestsFrom BashEventTransferRequest[] @relation("TransferRequestsFrom")
2325
+ transferRequestsTo BashEventTransferRequest[] @relation("TransferRequestsTo")
2326
+ sentPendingGifts PendingBashPointsGift[] @relation("SentPendingGifts")
2327
+ claimedPendingGifts PendingBashPointsGift[] @relation("ClaimedPendingGifts")
2067
2328
  biometricCredentials BiometricCredential[]
2068
- blocksReceived BlockedUser[] @relation("UserBlocksReceived")
2069
- blocksCreated BlockedUser[] @relation("UserBlocksMade")
2070
- blogComments BlogComment[] @relation("BlogCommentAuthor")
2071
- blogPosts BlogPost[] @relation("BlogAuthor")
2329
+ blocksReceived BlockedUser[] @relation("UserBlocksReceived")
2330
+ blocksCreated BlockedUser[] @relation("UserBlocksMade")
2331
+ blogComments BlogComment[] @relation("BlogCommentAuthor")
2332
+ blogPosts BlogPost[] @relation("BlogAuthor")
2072
2333
  checkouts Checkout[]
2073
2334
  waiverAcceptances WaiverAcceptance[]
2074
2335
  clubAdmins ClubAdmin[]
@@ -2077,90 +2338,90 @@ model User {
2077
2338
  competitionSponsorships CompetitionSponsor[]
2078
2339
  competitionParticipations CompetitionParticipant[]
2079
2340
  competitionVotes CompetitionVote[]
2080
- competitionJudgeAssignments CompetitionJudgeAssignment[] @relation("CompetitionJudgeAssignments")
2081
- competitionJudgeScoresAuthored CompetitionJudgeScore[] @relation("CompetitionJudgeScoresAuthored")
2341
+ competitionJudgeAssignments CompetitionJudgeAssignment[] @relation("CompetitionJudgeAssignments")
2342
+ competitionJudgeScoresAuthored CompetitionJudgeScore[] @relation("CompetitionJudgeScoresAuthored")
2082
2343
  contacts Contact[]
2083
- contactLists ContactList[] @relation("UserContactLists")
2344
+ contactLists ContactList[] @relation("UserContactLists")
2084
2345
  contactlist ContactList[]
2085
- expungedDemerits Demerit[] @relation("ExpungedDemerits")
2086
- issuedDemerits Demerit[] @relation("IssuedDemerits")
2087
- demerits Demerit[] @relation("UserDemerits")
2346
+ expungedDemerits Demerit[] @relation("ExpungedDemerits")
2347
+ issuedDemerits Demerit[] @relation("IssuedDemerits")
2348
+ demerits Demerit[] @relation("UserDemerits")
2088
2349
  documentID DocumentID?
2089
- eventTasksAssignedToMe EventTask[] @relation("TasksAssignedToMe")
2350
+ eventTasksAssignedToMe EventTask[] @relation("TasksAssignedToMe")
2090
2351
  eventTasks EventTask[]
2091
- potluckItemsClaimed PotluckItem[] @relation("PotluckItemsClaimedBy")
2092
- bashEventSuggestionsCreated BashEventSuggestion[] @relation("BashEventSuggestionsCreated")
2093
- bashSuggestionUpvotes SuggestionUpvote[] @relation("BashSuggestionUpvotes")
2094
- itemUpvotesGiven ItemUpvote[] @relation("ItemUpvotesGiven")
2095
- taskCommentsAuthored TaskComment[] @relation("TaskCommentsAuthored")
2096
- taskInvitationsSent TaskInvitation[] @relation("TaskInvitationsSent")
2097
- taskInvitationsAccepted TaskInvitation[] @relation("TaskInvitationsAccepted")
2098
- exhibitorBookingRequestsAsHost ExhibitorBookingRequest[] @relation("ExhibitorBookingHost")
2352
+ potluckItemsClaimed PotluckItem[] @relation("PotluckItemsClaimedBy")
2353
+ bashEventSuggestionsCreated BashEventSuggestion[] @relation("BashEventSuggestionsCreated")
2354
+ bashSuggestionUpvotes SuggestionUpvote[] @relation("BashSuggestionUpvotes")
2355
+ itemUpvotesGiven ItemUpvote[] @relation("ItemUpvotesGiven")
2356
+ taskCommentsAuthored TaskComment[] @relation("TaskCommentsAuthored")
2357
+ taskInvitationsSent TaskInvitation[] @relation("TaskInvitationsSent")
2358
+ taskInvitationsAccepted TaskInvitation[] @relation("TaskInvitationsAccepted")
2359
+ exhibitorBookingRequestsAsHost ExhibitorBookingRequest[] @relation("ExhibitorBookingHost")
2099
2360
  investments Investment[]
2100
- invitationsCreatedByMe Invitation[] @relation("InvitationsCreatedByMe")
2101
- invitationsSentToMe Invitation[] @relation("InvitationsSentToMe")
2102
- notificationsCreatedByMe Notification[] @relation("NotificationsCreatedByMe")
2103
- notification Notification[] @relation("NotificationsReceivedByMe")
2361
+ invitationsCreatedByMe Invitation[] @relation("InvitationsCreatedByMe")
2362
+ invitationsSentToMe Invitation[] @relation("InvitationsSentToMe")
2363
+ notificationsCreatedByMe Notification[] @relation("NotificationsCreatedByMe")
2364
+ notification Notification[] @relation("NotificationsReceivedByMe")
2104
2365
  competitionPrizes Prize[]
2105
2366
  promoter Promoter[]
2106
2367
  promoterStats PromoterStats?
2107
- remindersCreatedByMe Reminder[] @relation("RemindersCreatedByMe")
2108
- remindersAssignedToMe Reminder[] @relation("RemindersAssignedToMe")
2368
+ remindersCreatedByMe Reminder[] @relation("RemindersCreatedByMe")
2369
+ remindersAssignedToMe Reminder[] @relation("RemindersAssignedToMe")
2109
2370
  sentReminders SentReminder[]
2110
2371
  reviews Review[]
2111
2372
  firstFreeListingId String?
2112
- createdServices Service[] @relation("CreatedService")
2113
- ownedServices Service[] @relation("OwnedService")
2114
- serviceBlocksReceived ServiceBlock[] @relation("ServiceBlocksReceived")
2115
- serviceBlocksMade ServiceBlock[] @relation("ServiceBlocksMade")
2116
- serviceBookingForCreator ServiceBooking[] @relation("BookingCreator")
2117
- serviceBookingForUser ServiceBooking[] @relation("BookingForUser")
2373
+ createdServices Service[] @relation("CreatedService")
2374
+ ownedServices Service[] @relation("OwnedService")
2375
+ serviceBlocksReceived ServiceBlock[] @relation("ServiceBlocksReceived")
2376
+ serviceBlocksMade ServiceBlock[] @relation("ServiceBlocksMade")
2377
+ serviceBookingForCreator ServiceBooking[] @relation("BookingCreator")
2378
+ serviceBookingForUser ServiceBooking[] @relation("BookingForUser")
2118
2379
  serviceBookingCheckout ServiceBookingCheckout[]
2119
- serviceBookingMessages ServiceBookingMessage[] @relation("ServiceMessageSender")
2380
+ serviceBookingMessages ServiceBookingMessage[] @relation("ServiceMessageSender")
2120
2381
  sessions Session[]
2121
2382
  socialMediaProfiles SocialMediaProfile[]
2122
- sponsorBookingRequestsAsHost SponsorBookingRequest[] @relation("SponsorBookingHost")
2383
+ sponsorBookingRequestsAsHost SponsorBookingRequest[] @relation("SponsorBookingHost")
2123
2384
  sponsorships SponsoredEvent[]
2124
2385
  stripeAccounts StripeAccount[]
2125
- ticketsISent Ticket[] @relation("TicketsISent")
2126
- ticketsIOwn Ticket[] @relation("TicketsIOwn")
2386
+ ticketsISent Ticket[] @relation("TicketsISent")
2387
+ ticketsIOwn Ticket[] @relation("TicketsIOwn")
2127
2388
  bashEventDonations BashEventDonation[]
2128
2389
  bashCreativeSubmissions BashCreativeSubmission[]
2129
- ticketsOnWaitlist Ticket[] @relation("TicketsOnWaitlist")
2130
- transfersFrom TicketTransfer[] @relation("TransfersFrom")
2131
- transfersTo TicketTransfer[] @relation("TransfersTo")
2132
- ticketTransferInvitesFrom TicketTransferInvite[] @relation("TicketTransferInviteFrom")
2133
- ticketTransferInvitesToUser TicketTransferInvite[] @relation("TicketTransferInviteToUser")
2134
- giftGrantsIssued TicketGiftGrant[] @relation("GiftGrantsIssued")
2135
- giftGrantsReceived TicketGiftGrant[] @relation("GiftGrantsReceived")
2136
- unblocksReceived UnblockedUserHistory[] @relation("UserUnblocksReceived")
2137
- unblocksCreated UnblockedUserHistory[] @relation("UserUnblocksMade")
2138
- suspendedBy User? @relation("SuspendedUsers", fields: [suspendedById], references: [id])
2139
- suspendedUsers User[] @relation("SuspendedUsers")
2390
+ ticketsOnWaitlist Ticket[] @relation("TicketsOnWaitlist")
2391
+ transfersFrom TicketTransfer[] @relation("TransfersFrom")
2392
+ transfersTo TicketTransfer[] @relation("TransfersTo")
2393
+ ticketTransferInvitesFrom TicketTransferInvite[] @relation("TicketTransferInviteFrom")
2394
+ ticketTransferInvitesToUser TicketTransferInvite[] @relation("TicketTransferInviteToUser")
2395
+ giftGrantsIssued TicketGiftGrant[] @relation("GiftGrantsIssued")
2396
+ giftGrantsReceived TicketGiftGrant[] @relation("GiftGrantsReceived")
2397
+ unblocksReceived UnblockedUserHistory[] @relation("UserUnblocksReceived")
2398
+ unblocksCreated UnblockedUserHistory[] @relation("UserUnblocksMade")
2399
+ suspendedBy User? @relation("SuspendedUsers", fields: [suspendedById], references: [id])
2400
+ suspendedUsers User[] @relation("SuspendedUsers")
2140
2401
  favorites UserFavorite[]
2141
2402
  publicBashRsvps PublicBashRsvp[]
2142
- following UserFollowing[] @relation("Follower")
2143
- followers UserFollowing[] @relation("Following")
2144
- connectionRequestsSent UserConnection[] @relation("ConnectionRequestsSent")
2145
- connectionRequestsReceived UserConnection[] @relation("ConnectionRequestsReceived")
2403
+ following UserFollowing[] @relation("Follower")
2404
+ followers UserFollowing[] @relation("Following")
2405
+ connectionRequestsSent UserConnection[] @relation("ConnectionRequestsSent")
2406
+ connectionRequestsReceived UserConnection[] @relation("ConnectionRequestsReceived")
2146
2407
  links UserLink[]
2147
2408
  preferences UserPreferences?
2148
2409
  rankings UserRankings?
2149
2410
  userPromoCodeRedemption UserPromoCodeRedemption[]
2150
2411
  venueLoyaltyRedemptions VenueLoyaltyRedemption[]
2151
2412
  referralCode UserReferralCode?
2152
- referralTokens UserReferralToken[] @relation("UserReferralTokens")
2153
- reportsReceived UserReport[] @relation("ReportsReceived")
2154
- reportsMade UserReport[] @relation("ReportsMade")
2155
- reportsReviewed UserReport[] @relation("ReportsReviewed")
2413
+ referralTokens UserReferralToken[] @relation("UserReferralTokens")
2414
+ reportsReceived UserReport[] @relation("ReportsReceived")
2415
+ reportsMade UserReport[] @relation("ReportsMade")
2416
+ reportsReviewed UserReport[] @relation("ReportsReviewed")
2156
2417
  userStats UserStats?
2157
2418
  userSubscription UserSubscription?
2158
2419
  vendorBid VendorBid[]
2159
- vendorBookingMessages VendorBookingMessage[] @relation("VendorMessageSender")
2420
+ vendorBookingMessages VendorBookingMessage[] @relation("VendorMessageSender")
2160
2421
  serviceAnalytics ServiceAnalytics[] // Track service analytics for this user
2161
- vendorBookingRequestsAsHost VendorBookingRequest[] @relation("VendorBookingHost")
2422
+ vendorBookingRequestsAsHost VendorBookingRequest[] @relation("VendorBookingHost")
2162
2423
  volunteerService VolunteerService[]
2163
- reviewedAmbashadorApps AmbashadorApplication[] @relation("AmbashadorReviewer")
2424
+ reviewedAmbashadorApps AmbashadorApplication[] @relation("AmbashadorReviewer")
2164
2425
 
2165
2426
  // Claiming Relations
2166
2427
  claimRequests ClaimRequest[] @relation("EventClaims")
@@ -2235,8 +2496,8 @@ model User {
2235
2496
  bashAvailability BashAvailability? @relation("UserAvailability")
2236
2497
 
2237
2498
  // Groups
2238
- ownedGroups EventGroup[] @relation("OwnedGroups")
2239
- groupMembers GroupMember[] @relation("GroupMemberships")
2499
+ ownedGroups EventGroup[] @relation("OwnedGroups")
2500
+ groupMembers GroupMember[] @relation("GroupMemberships")
2240
2501
 
2241
2502
  // Vanity paths
2242
2503
  requestedVanityPaths EventVanityPath[] @relation("VanityRequests")
@@ -2307,9 +2568,9 @@ model UserPreferences {
2307
2568
  showEventPrices Boolean @default(true)
2308
2569
 
2309
2570
  // BashFeed Preferences
2310
- bashFeedDefaultTab String @default("all") // "all", "upcoming", "live", "past", "saved", "my-posts"
2311
- bashFeedDefaultSort String @default("chronological") // "for_you", "chronological", "trending", etc.
2312
- bashFeedFilters Json? // Saved filter preferences as JSON
2571
+ bashFeedDefaultTab String @default("all") // "all", "upcoming", "live", "past", "saved", "my-posts"
2572
+ bashFeedDefaultSort String @default("chronological") // "for_you", "chronological", "trending", etc.
2573
+ bashFeedFilters Json? // Saved filter preferences as JSON
2313
2574
  /// Services discovery: keyed by service type tab, e.g. { "Entertainment": { ... } }
2314
2575
  serviceFilterSettings Json?
2315
2576
  /// Venue discovery filter bar (same pattern as serviceFilterSettings)
@@ -2588,17 +2849,19 @@ model ServiceBlock {
2588
2849
  }
2589
2850
 
2590
2851
  model AssociatedBash {
2591
- id String @id @default(cuid())
2592
- bashEventId String
2593
- invitationId String? @unique
2594
- ownerEmail String?
2595
- ownerUserId String?
2596
- isOrganizer Boolean?
2597
- isDoorStaff Boolean? @default(false)
2598
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
2599
- invitation Invitation? @relation(fields: [invitationId], references: [id])
2600
- owner User? @relation(fields: [ownerUserId], references: [id], onDelete: Cascade)
2601
- service Service[] @relation("AssociatedBashToService")
2852
+ id String @id @default(cuid())
2853
+ bashEventId String
2854
+ invitationId String? @unique
2855
+ ownerEmail String?
2856
+ ownerUserId String?
2857
+ isOrganizer Boolean?
2858
+ isCoHost Boolean? @default(false)
2859
+ isDoorStaff Boolean? @default(false)
2860
+ organizerTitle String?
2861
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
2862
+ invitation Invitation? @relation(fields: [invitationId], references: [id])
2863
+ owner User? @relation(fields: [ownerUserId], references: [id], onDelete: Cascade)
2864
+ service Service[] @relation("AssociatedBashToService")
2602
2865
 
2603
2866
  @@unique([ownerEmail, bashEventId])
2604
2867
  }
@@ -2708,6 +2971,9 @@ model Service {
2708
2971
  verifiedAt DateTime? // When verification was approved
2709
2972
  licenseNumber String? // License/registration number
2710
2973
  certifications String[] @default([]) // Array of certification names
2974
+ /// Custom scope pricing — admin-gated flag for providers who need variable/quote-based pricing
2975
+ customScopeStatus CustomScopeStatus @default(None)
2976
+ customScopeRequestNote String? // Provider's explanation of why they need custom scope
2711
2977
  bashFeedPosts BashFeedPost[]
2712
2978
  bashEventArtists BashEventArtist[]
2713
2979
  bashCreativeSubmissions BashCreativeSubmission[]
@@ -2745,6 +3011,7 @@ model Service {
2745
3011
  entertainerMatches VenueEntertainmentMatch[] @relation("EntertainerMatches")
2746
3012
  includes ServiceInclude[]
2747
3013
  addonRequests ServiceAddonRequest[]
3014
+ serviceBudgets Budget[] @relation("ServiceBudgets")
2748
3015
 
2749
3016
  @@index([serviceListingStripeSubscriptionId])
2750
3017
  @@index([isFreeFirstListing])
@@ -2775,7 +3042,7 @@ model StripeWebhookEvent {
2775
3042
 
2776
3043
  model StripeDispute {
2777
3044
  id String @id @default(cuid())
2778
- stripeChargeId String @unique
3045
+ stripeChargeId String @unique
2779
3046
  createdAt DateTime @default(now())
2780
3047
 
2781
3048
  @@index([stripeChargeId])
@@ -3091,9 +3358,9 @@ model Venue {
3091
3358
  lastMatchNotificationSent DateTime?
3092
3359
 
3093
3360
  /// Venue loyalty program intent (wizard + profile; redemption is future work)
3094
- loyaltyEnabled Boolean @default(false)
3095
- loyaltyTemplateIds String[] @default([])
3096
- loyaltyNotes String?
3361
+ loyaltyEnabled Boolean @default(false)
3362
+ loyaltyTemplateIds String[] @default([])
3363
+ loyaltyNotes String?
3097
3364
 
3098
3365
  bashEvents BashEvent[]
3099
3366
  service Service?
@@ -3166,7 +3433,7 @@ model Organization {
3166
3433
  secondaryVenueTypes String[] @default([])
3167
3434
  baseMembershipPriceCents Int?
3168
3435
  premiumMembershipPriceCents Int?
3169
- membershipBillingInterval String? // one_time | monthly | annual for recurring dues
3436
+ membershipBillingInterval String? // one_time | monthly | annual for recurring dues
3170
3437
  allowGuestsByDefault Boolean @default(false)
3171
3438
  defaultGuestLimit Int @default(0)
3172
3439
  visibility String @default("Public")
@@ -3180,7 +3447,7 @@ model Organization {
3180
3447
  subscriptionStatus String?
3181
3448
  stripeSubscriptionId String? @unique
3182
3449
  stripeCustomerId String?
3183
- stripeConnectedAccountId String? // Org-level Stripe account for wallet display
3450
+ stripeConnectedAccountId String? // Org-level Stripe account for wallet display
3184
3451
  inheritBrandingFromParent Boolean @default(true)
3185
3452
  latitude Float?
3186
3453
  longitude Float?
@@ -3246,24 +3513,24 @@ enum GameStatus {
3246
3513
 
3247
3514
  /// A scheduled or completed game between two team organizations within a league season.
3248
3515
  model Game {
3249
- id String @id @default(cuid())
3250
- seasonId String
3251
- season Season @relation(fields: [seasonId], references: [id], onDelete: Cascade)
3252
- homeTeamId String
3253
- awayTeamId String
3254
- homeTeam Organization @relation("HomeTeamGames", fields: [homeTeamId], references: [id], onDelete: Restrict)
3255
- awayTeam Organization @relation("AwayTeamGames", fields: [awayTeamId], references: [id], onDelete: Restrict)
3256
- scheduledAt DateTime
3257
- venueName String?
3258
- venueStreet String?
3259
- venueCity String?
3260
- venueState String?
3516
+ id String @id @default(cuid())
3517
+ seasonId String
3518
+ season Season @relation(fields: [seasonId], references: [id], onDelete: Cascade)
3519
+ homeTeamId String
3520
+ awayTeamId String
3521
+ homeTeam Organization @relation("HomeTeamGames", fields: [homeTeamId], references: [id], onDelete: Restrict)
3522
+ awayTeam Organization @relation("AwayTeamGames", fields: [awayTeamId], references: [id], onDelete: Restrict)
3523
+ scheduledAt DateTime
3524
+ venueName String?
3525
+ venueStreet String?
3526
+ venueCity String?
3527
+ venueState String?
3261
3528
  venueZipCode String?
3262
- homeScore Int?
3263
- awayScore Int?
3264
- status GameStatus @default(Scheduled)
3265
- createdAt DateTime @default(now())
3266
- updatedAt DateTime @updatedAt
3529
+ homeScore Int?
3530
+ awayScore Int?
3531
+ status GameStatus @default(Scheduled)
3532
+ createdAt DateTime @default(now())
3533
+ updatedAt DateTime @updatedAt
3267
3534
 
3268
3535
  @@index([seasonId])
3269
3536
  @@index([homeTeamId])
@@ -3272,31 +3539,31 @@ model Game {
3272
3539
  }
3273
3540
 
3274
3541
  model OrganizationMember {
3275
- id String @id @default(cuid())
3276
- organizationId String
3277
- userId String
3278
- role String @default("Member") // Owner, Admin, Coordinator, EventOrganizer, Treasurer, Member
3279
- membershipTier String @default("Base") // Base, Premium
3280
- status String @default("Pending") // Pending, Active, Suspended, Removed
3281
- joinedAt DateTime @default(now())
3282
- approvedAt DateTime?
3283
- approvedBy String?
3284
- paidUntil DateTime?
3285
- paymentStatus String? @default("Current") // Current, Overdue, Cancelled
3286
- guestLimit Int?
3287
- canInvite Boolean @default(true)
3288
- canCreateEvents Boolean @default(false)
3289
- canManageBudgets Boolean @default(false)
3290
- engagementScore Float? // Computed: weighted formula (events, recency, points, referrals)
3291
- lastActiveAt DateTime? // Last event attended
3292
- totalEventsAttended Int @default(0)
3293
- firstEventAttendedAt DateTime?
3294
- stripeSubscriptionId String? @unique // Stripe subscription ID for recurring org membership dues
3295
- jerseyNumber String?
3296
- position String?
3297
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
3298
- user User @relation("OrganizationMemberUser", fields: [userId], references: [id], onDelete: Cascade)
3299
- eventRSVPs EventRSVP[]
3542
+ id String @id @default(cuid())
3543
+ organizationId String
3544
+ userId String
3545
+ role String @default("Member") // Owner, Admin, Coordinator, EventOrganizer, Treasurer, Member
3546
+ membershipTier String @default("Base") // Base, Premium
3547
+ status String @default("Pending") // Pending, Active, Suspended, Removed
3548
+ joinedAt DateTime @default(now())
3549
+ approvedAt DateTime?
3550
+ approvedBy String?
3551
+ paidUntil DateTime?
3552
+ paymentStatus String? @default("Current") // Current, Overdue, Cancelled
3553
+ guestLimit Int?
3554
+ canInvite Boolean @default(true)
3555
+ canCreateEvents Boolean @default(false)
3556
+ canManageBudgets Boolean @default(false)
3557
+ engagementScore Float? // Computed: weighted formula (events, recency, points, referrals)
3558
+ lastActiveAt DateTime? // Last event attended
3559
+ totalEventsAttended Int @default(0)
3560
+ firstEventAttendedAt DateTime?
3561
+ stripeSubscriptionId String? @unique // Stripe subscription ID for recurring org membership dues
3562
+ jerseyNumber String?
3563
+ position String?
3564
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
3565
+ user User @relation("OrganizationMemberUser", fields: [userId], references: [id], onDelete: Cascade)
3566
+ eventRSVPs EventRSVP[]
3300
3567
  notificationPreferences OrganizationMemberPreferences?
3301
3568
 
3302
3569
  @@unique([organizationId, userId])
@@ -3308,20 +3575,20 @@ model OrganizationMember {
3308
3575
 
3309
3576
  /// Per-member notification toggles for organization channels (1:1 with OrganizationMember).
3310
3577
  model OrganizationMemberPreferences {
3311
- id String @id @default(cuid())
3312
- organizationMemberId String @unique
3313
- announcements Boolean @default(true)
3314
- eventReminders Boolean @default(true)
3315
- eventUpdates Boolean @default(true)
3316
- budgetAlerts Boolean @default(true)
3317
- proposalNotifications Boolean @default(true)
3318
- taskAssignments Boolean @default(true)
3319
- membershipUpdates Boolean @default(true)
3320
- emailNotifications Boolean @default(true)
3321
- pushNotifications Boolean @default(true)
3322
- createdAt DateTime @default(now())
3323
- updatedAt DateTime @updatedAt
3324
- organizationMember OrganizationMember @relation(fields: [organizationMemberId], references: [id], onDelete: Cascade)
3578
+ id String @id @default(cuid())
3579
+ organizationMemberId String @unique
3580
+ announcements Boolean @default(true)
3581
+ eventReminders Boolean @default(true)
3582
+ eventUpdates Boolean @default(true)
3583
+ budgetAlerts Boolean @default(true)
3584
+ proposalNotifications Boolean @default(true)
3585
+ taskAssignments Boolean @default(true)
3586
+ membershipUpdates Boolean @default(true)
3587
+ emailNotifications Boolean @default(true)
3588
+ pushNotifications Boolean @default(true)
3589
+ createdAt DateTime @default(now())
3590
+ updatedAt DateTime @updatedAt
3591
+ organizationMember OrganizationMember @relation(fields: [organizationMemberId], references: [id], onDelete: Cascade)
3325
3592
 
3326
3593
  @@index([organizationMemberId])
3327
3594
  }
@@ -3377,6 +3644,8 @@ model Budget {
3377
3644
  clubId String?
3378
3645
  departmentId String?
3379
3646
  userId String? // For personal event budgets
3647
+ /// Optional: budgets tied to a service profile (vendor/exhibitor/sponsor owner costs).
3648
+ serviceId String?
3380
3649
 
3381
3650
  // Budget details
3382
3651
  totalAllocatedCents Int
@@ -3409,6 +3678,7 @@ model Budget {
3409
3678
  club Club? @relation("ClubBudgets", fields: [clubId], references: [id], onDelete: Cascade)
3410
3679
  department Department? @relation("DepartmentBudgets", fields: [departmentId], references: [id], onDelete: Cascade)
3411
3680
  user User? @relation("UserBudgets", fields: [userId], references: [id], onDelete: Cascade)
3681
+ service Service? @relation("ServiceBudgets", fields: [serviceId], references: [id], onDelete: Cascade)
3412
3682
  submitter User? @relation("BudgetSubmitter", fields: [submittedBy], references: [id])
3413
3683
  approver User? @relation("BudgetApprover", fields: [approverId], references: [id])
3414
3684
  expenses Expense[]
@@ -3418,10 +3688,18 @@ model Budget {
3418
3688
  @@index([clubId])
3419
3689
  @@index([departmentId])
3420
3690
  @@index([userId])
3691
+ @@index([serviceId])
3421
3692
  @@index([status])
3422
3693
  @@index([fiscalYear])
3423
3694
  }
3424
3695
 
3696
+ enum ExpenseExtractionStatus {
3697
+ none
3698
+ pending
3699
+ complete
3700
+ failed
3701
+ }
3702
+
3425
3703
  model Expense {
3426
3704
  id String @id @default(cuid())
3427
3705
  budgetId String
@@ -3437,6 +3715,29 @@ model Expense {
3437
3715
  rejectionReason String?
3438
3716
  createdAt DateTime @default(now())
3439
3717
  updatedAt DateTime @updatedAt
3718
+
3719
+ /// Optional link to an event task (e.g. volunteer receipt for a specific task).
3720
+ eventTaskId String?
3721
+ eventTask EventTask? @relation(fields: [eventTaskId], references: [id], onDelete: SetNull)
3722
+
3723
+ /// User-confirmed or extracted metadata (Textract suggestions are confirmed before save).
3724
+ vendorName String?
3725
+ taxCents Int?
3726
+ subtotalCents Int?
3727
+ currency String? @default("USD")
3728
+
3729
+ extractionStatus ExpenseExtractionStatus @default(none)
3730
+ textractRaw Json?
3731
+
3732
+ /// Vendor invoice number (manual or extracted).
3733
+ invoiceNumber String?
3734
+ /// When payment is due (separate from expenseDate / receipt date).
3735
+ invoiceDueDate DateTime?
3736
+ /// Host marked invoice as paid outside the app — stops due-date reminders.
3737
+ invoicePaidAt DateTime?
3738
+ /// Tracks which invoice due reminders were sent: { dueSoon, dueDay, pastDue1, pastDue15 }.
3739
+ invoiceDueRemindersSent Json?
3740
+
3440
3741
  budget Budget @relation(fields: [budgetId], references: [id], onDelete: Cascade)
3441
3742
  submitter User @relation("ExpenseSubmitter", fields: [submittedBy], references: [id])
3442
3743
  approver User? @relation("ExpenseApprover", fields: [approvedBy], references: [id])
@@ -3444,6 +3745,8 @@ model Expense {
3444
3745
  @@index([budgetId])
3445
3746
  @@index([submittedBy])
3446
3747
  @@index([status])
3748
+ @@index([eventTaskId])
3749
+ @@index([invoiceDueDate])
3447
3750
  }
3448
3751
 
3449
3752
  model ServiceRange {
@@ -3707,47 +4010,47 @@ model ServiceBookingCheckout {
3707
4010
  }
3708
4011
 
3709
4012
  model ServiceBooking {
3710
- id String @id @default(cuid())
4013
+ id String @id @default(cuid())
3711
4014
  creatorId String
3712
4015
  forUserId String
3713
4016
  daysTotalATBCents Int
3714
4017
  serviceId String
3715
- rateOption ServiceBookingRateOption @default(TimeBased)
4018
+ rateOption ServiceBookingRateOption @default(TimeBased)
3716
4019
  flatRateCents Int?
3717
- status ServiceBookingStatus @default(Pending)
3718
- bookingType ServiceBookingType @default(Request)
4020
+ status ServiceBookingStatus @default(Pending)
4021
+ bookingType ServiceBookingType @default(Request)
3719
4022
  timezone String
3720
4023
  requestedOn DateTime?
3721
4024
  requestDecisionOn DateTime?
3722
4025
  bookedOn DateTime?
3723
4026
  canceledOn DateTime?
3724
- isFreeGuest Boolean @default(false)
3725
- allowPromiseToPay Boolean @default(false)
4027
+ isFreeGuest Boolean @default(false)
4028
+ allowPromiseToPay Boolean @default(false)
3726
4029
  totalATBCents Int
3727
- subtotalATBCents Int @default(0)
4030
+ subtotalATBCents Int @default(0)
3728
4031
  bashEventId String?
3729
- isVendorBid Boolean @default(false)
4032
+ isVendorBid Boolean @default(false)
3730
4033
  vendorBidAmountCents Int?
3731
4034
  vendorEventDetails String?
3732
4035
  bashPointsApplied Int? // BashPoints credits applied to this booking
3733
4036
  bashPointsTransactionId String? // Link to BashCreditTransaction
3734
4037
  // Settlement fields for percentage-based bookings (populated at event close)
3735
- settlementBasis ServiceBookingSettlementBasis?
3736
- settlementPercentage Decimal?
3737
- settlementAmountCents Int?
3738
- settledAt DateTime?
4038
+ settlementBasis ServiceBookingSettlementBasis?
4039
+ settlementPercentage Decimal?
4040
+ settlementAmountCents Int?
4041
+ settledAt DateTime?
3739
4042
  notification Notification[]
3740
- bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
3741
- creator User @relation("BookingCreator", fields: [creatorId], references: [id])
3742
- forUser User @relation("BookingForUser", fields: [forUserId], references: [id])
3743
- service Service @relation(fields: [serviceId], references: [id])
4043
+ bashEvent BashEvent? @relation(fields: [bashEventId], references: [id])
4044
+ creator User @relation("BookingCreator", fields: [creatorId], references: [id])
4045
+ forUser User @relation("BookingForUser", fields: [forUserId], references: [id])
4046
+ service Service @relation(fields: [serviceId], references: [id])
3744
4047
  addOns ServiceBookingAddOn[]
3745
4048
  checkout ServiceBookingCheckout?
3746
4049
  bookedDays ServiceBookingDay[]
3747
4050
  additionalFees ServiceBookingFee[]
3748
4051
  messages ServiceBookingMessage[]
3749
4052
  packages ServiceBookingPackage[]
3750
- commissions BookingCommission[] @relation("BookingCommissions")
4053
+ commissions BookingCommission[] @relation("BookingCommissions")
3751
4054
  sentReminders SentReminder[]
3752
4055
 
3753
4056
  @@index([status])
@@ -4088,21 +4391,21 @@ model BashSlugHistory {
4088
4391
  }
4089
4392
 
4090
4393
  model BashEventTransferRequest {
4091
- id String @id @default(cuid())
4092
- bashEventId String
4093
- fromUserId String
4094
- toUserId String? // null when transferring to a non-user by email
4095
- toEmail String? // set when toUserId is null (non-user invite transfer)
4096
- inviteToken String? @unique // secure token embedded in the signup/claim link
4097
- keepPayoutsWithMe Boolean @default(false) // when true, creator keeps receiving payouts after transfer
4098
- status TransferRequestStatus @default(Pending)
4099
- reason String?
4100
- requestedAt DateTime @default(now())
4101
- respondedAt DateTime?
4102
- expiresAt DateTime
4103
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
4104
- fromUser User @relation("TransferRequestsFrom", fields: [fromUserId], references: [id], onDelete: Cascade)
4105
- toUser User? @relation("TransferRequestsTo", fields: [toUserId], references: [id], onDelete: Cascade)
4394
+ id String @id @default(cuid())
4395
+ bashEventId String
4396
+ fromUserId String
4397
+ toUserId String? // null when transferring to a non-user by email
4398
+ toEmail String? // set when toUserId is null (non-user invite transfer)
4399
+ inviteToken String? @unique // secure token embedded in the signup/claim link
4400
+ keepPayoutsWithMe Boolean @default(false) // when true, creator keeps receiving payouts after transfer
4401
+ status TransferRequestStatus @default(Pending)
4402
+ reason String?
4403
+ requestedAt DateTime @default(now())
4404
+ respondedAt DateTime?
4405
+ expiresAt DateTime
4406
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
4407
+ fromUser User @relation("TransferRequestsFrom", fields: [fromUserId], references: [id], onDelete: Cascade)
4408
+ toUser User? @relation("TransferRequestsTo", fields: [toUserId], references: [id], onDelete: Cascade)
4106
4409
 
4107
4410
  @@index([bashEventId])
4108
4411
  @@index([toUserId, status])
@@ -4348,23 +4651,29 @@ enum NotificationType {
4348
4651
  BashRatingMilestone
4349
4652
  BashFeedPost
4350
4653
  EventMilestone
4351
- WaitlistRequested // Host: someone requested a spot on your waitlist
4352
- WaitlistApproved // Attendee: host approved your waitlist request
4353
- WaitlistDenied // Attendee: host denied your waitlist request
4654
+ WaitlistRequested // Host: someone requested a spot on your waitlist
4655
+ WaitlistApproved // Attendee: host approved your waitlist request
4656
+ WaitlistDenied // Attendee: host denied your waitlist request
4354
4657
  WaitlistSpotAvailable // Waitlister: a refund opened a spot — claim it now
4355
4658
  WaitlistSpotMayBeOpen // Waitlister: someone left the venue — standby at door
4356
4659
  // Group notifications
4357
- GroupMemberPurchased // Owner: "Riley just bought a ticket — 2 more to go!"
4358
- GroupEveryoneConfirmed // Owner: "All members want to attend. Buy tickets together?"
4359
- GroupEveryonePurchased // Owner: "Your group is officially attending"
4360
- GroupMemberJoined // Owner: "Alex joined your group"
4361
- FriendsAttending // User: "3 of your friends are attending this bash"
4362
- GroupRewardProgress // Member: "Only 2 more friends needed for your group reward"
4363
- GroupUnlockOfferReceived // Member: "Special offer unlocked for your group"
4364
- HostSalesAccelerating // Host: "Your event sales increased 38% today"
4365
- HostTopPromoter // Host: "Top promoter: Alex (6 tickets today)"
4366
- HostSalesSlowing // Host: "Sales slowed today. Try sharing your event."
4367
- TicketSold // Host: a ticket was purchased for your event (debounced in webhook)
4660
+ GroupMemberPurchased // Owner: "Riley just bought a ticket — 2 more to go!"
4661
+ GroupEveryoneConfirmed // Owner: "All members want to attend. Buy tickets together?"
4662
+ GroupEveryonePurchased // Owner: "Your group is officially attending"
4663
+ GroupMemberJoined // Owner: "Alex joined your group"
4664
+ FriendsAttending // User: "3 of your friends are attending this bash"
4665
+ GroupRewardProgress // Member: "Only 2 more friends needed for your group reward"
4666
+ GroupUnlockOfferReceived // Member: "Special offer unlocked for your group"
4667
+ HostSalesAccelerating // Host: "Your event sales increased 38% today"
4668
+ HostTopPromoter // Host: "Top promoter: Alex (6 tickets today)"
4669
+ HostSalesSlowing // Host: "Sales slowed today. Try sharing your event."
4670
+ TicketSold // Host: a ticket was purchased for your event (debounced in webhook)
4671
+ BudgetExpenseSubmitted // Host/organizer: assignee or member submitted an expense
4672
+ BudgetLineThreshold // Host/organizer: budget line crossed a spend threshold (e.g. 80% / 100%)
4673
+ BudgetInvoiceDueSoon // Host/organizer: invoice due in ~3 days
4674
+ BudgetInvoiceDueToday // Host/organizer: invoice due today
4675
+ BudgetInvoicePastDue // Host/organizer: invoice 1 day past due
4676
+ BudgetInvoicePastDueFinal // Host/organizer: invoice 15+ days past due
4368
4677
  }
4369
4678
 
4370
4679
  enum NotificationPriority {
@@ -4392,7 +4701,7 @@ enum BashEventSource {
4392
4701
  Meetup
4393
4702
  Facebook
4394
4703
  JamWith
4395
- Partner // Generic: use sourcePartnerId for partner key (e.g. 'mixlog', custom integrations)
4704
+ Partner // Generic: use sourcePartnerId for partner key (e.g. 'mixlog', custom integrations)
4396
4705
  }
4397
4706
 
4398
4707
  enum TicketStatus {
@@ -4403,7 +4712,7 @@ enum TicketStatus {
4403
4712
  LeftRadius
4404
4713
  Pending
4405
4714
  WaitlistPending // Requested a spot on a host-approval waitlist; awaiting decision
4406
- RefundPending // Refund requested; held while next waitlister is given a claim window
4715
+ RefundPending // Refund requested; held while next waitlister is given a claim window
4407
4716
  }
4408
4717
 
4409
4718
  enum DayOfWeek {
@@ -4662,6 +4971,44 @@ enum BashStatus {
4662
4971
  Archived // Unclaimed after expiration
4663
4972
  }
4664
4973
 
4974
+ /// Minimum-age policy for a bash. Used by the BashCard pill, discover filters,
4975
+ /// and (when 18+/21+) age verification at ticket checkout.
4976
+ enum AgePolicy {
4977
+ AllAges
4978
+ EighteenPlus
4979
+ TwentyOnePlus
4980
+ }
4981
+
4982
+ /// Retention policy for a user's stored government-ID image (drives expireIdImages cron + GDPR consent record)
4983
+ enum GovIdRetentionPolicy {
4984
+ ThirtyDays
4985
+ UntilIdExpires
4986
+ Indefinite
4987
+ }
4988
+
4989
+ /// Whether the user's `schoolName` was extracted by the AI vision model or confirmed/edited by the user
4990
+ enum SchoolNameSource {
4991
+ AiInferred
4992
+ UserConfirmed
4993
+ }
4994
+
4995
+ /// Admin-gated custom-scope pricing flag for service providers needing variable/quote-based pricing
4996
+ enum CustomScopeStatus {
4997
+ None
4998
+ Pending
4999
+ Approved
5000
+ Rejected
5001
+ }
5002
+
5003
+ /// AI bash-approval reviewer verdict (see `bashApprovalAiService`).
5004
+ /// `Approve` and `Reject` are recommendations; whether the system acts on them
5005
+ /// vs. queues for a human depends on confidence + AI_BASH_APPROVAL_MODE.
5006
+ enum AiApprovalVerdict {
5007
+ Approve
5008
+ Flag
5009
+ Reject
5010
+ }
5011
+
4665
5012
  /// How a user is associated with a bash (for BashFeedPost display: "Sarah attended...", "Mike is hosting...")
4666
5013
  enum BashAssociationType {
4667
5014
  Hosting
@@ -4944,6 +5291,7 @@ enum FoodAndBeverageSubType {
4944
5291
  Bartenders
4945
5292
  DessertBar
4946
5293
  BeverageCart
5294
+ ConcessionStand
4947
5295
  }
4948
5296
 
4949
5297
  enum CleaningSubType {
@@ -4975,6 +5323,9 @@ enum RentalEquipmentSubType {
4975
5323
  Generators
4976
5324
  DanceFloor
4977
5325
  PhotoBooth
5326
+ BounceHouse
5327
+ WaterSlide
5328
+ CarnivalGames
4978
5329
  }
4979
5330
 
4980
5331
  enum MediaProductionAndCreativeSubType {
@@ -6408,30 +6759,30 @@ enum ConsentType {
6408
6759
 
6409
6760
  // Birthday offers from partner companies: users opt in once, we send minimal PII to partner APIs
6410
6761
  model BirthdayOfferPartner {
6411
- id String @id @default(cuid())
6412
- name String
6413
- slug String @unique
6414
- description String? @db.Text
6415
- apiEndpoint String // Partner API URL; API keys live in env (BIRTHDAY_PARTNER_<SLUG>_API_KEY)
6416
- config Json? // Partner-specific: e.g. sendFullDob (bool), extra fields
6417
- requiresPhone Boolean @default(false) // Part B: when true, share User.phone with partner
6418
- isActive Boolean @default(true)
6419
- createdAt DateTime @default(now())
6420
- updatedAt DateTime @updatedAt
6421
- optIns UserBirthdayOfferOptIn[]
6422
- deliveries BirthdayOfferDelivery[]
6762
+ id String @id @default(cuid())
6763
+ name String
6764
+ slug String @unique
6765
+ description String? @db.Text
6766
+ apiEndpoint String // Partner API URL; API keys live in env (BIRTHDAY_PARTNER_<SLUG>_API_KEY)
6767
+ config Json? // Partner-specific: e.g. sendFullDob (bool), extra fields
6768
+ requiresPhone Boolean @default(false) // Part B: when true, share User.phone with partner
6769
+ isActive Boolean @default(true)
6770
+ createdAt DateTime @default(now())
6771
+ updatedAt DateTime @updatedAt
6772
+ optIns UserBirthdayOfferOptIn[]
6773
+ deliveries BirthdayOfferDelivery[]
6423
6774
  catalogEntries BirthdayFreebieCatalog[]
6424
6775
 
6425
6776
  @@index([isActive])
6426
6777
  }
6427
6778
 
6428
6779
  model UserBirthdayOfferOptIn {
6429
- id String @id @default(cuid())
6780
+ id String @id @default(cuid())
6430
6781
  userId String
6431
6782
  partnerId String
6432
- optedInAt DateTime @default(now())
6783
+ optedInAt DateTime @default(now())
6433
6784
  lastSentAt DateTime? // Last time we sent this user to this partner (for idempotency)
6434
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6785
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6435
6786
  partner BirthdayOfferPartner @relation(fields: [partnerId], references: [id], onDelete: Cascade)
6436
6787
 
6437
6788
  @@unique([userId, partnerId])
@@ -6440,12 +6791,12 @@ model UserBirthdayOfferOptIn {
6440
6791
  }
6441
6792
 
6442
6793
  model BirthdayOfferDelivery {
6443
- id String @id @default(cuid())
6794
+ id String @id @default(cuid())
6444
6795
  userId String
6445
6796
  partnerId String
6446
- birthdayYear Int // Year when the send counted (for idempotency: one send per user/partner/year)
6447
- sentAt DateTime @default(now())
6448
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6797
+ birthdayYear Int // Year when the send counted (for idempotency: one send per user/partner/year)
6798
+ sentAt DateTime @default(now())
6799
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6449
6800
  partner BirthdayOfferPartner @relation(fields: [partnerId], references: [id], onDelete: Cascade)
6450
6801
 
6451
6802
  @@unique([userId, partnerId, birthdayYear])
@@ -6455,11 +6806,11 @@ model BirthdayOfferDelivery {
6455
6806
 
6456
6807
  // Birthday Rewards focused scope (v1): annual BashPoints boost idempotency
6457
6808
  model BirthdayBoostGrant {
6458
- id String @id @default(cuid())
6459
- userId String
6460
- birthdayYear Int // Year for which boost was granted
6461
- grantedAt DateTime @default(now())
6462
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6809
+ id String @id @default(cuid())
6810
+ userId String
6811
+ birthdayYear Int // Year for which boost was granted
6812
+ grantedAt DateTime @default(now())
6813
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6463
6814
 
6464
6815
  @@unique([userId, birthdayYear])
6465
6816
  @@index([userId])
@@ -6481,21 +6832,21 @@ model BirthdayRewardFavorite {
6481
6832
 
6482
6833
  // User-recorded places where they picked up a birthday reward (with optional rating)
6483
6834
  model BirthdayRewardRedemption {
6484
- id String @id @default(cuid())
6835
+ id String @id @default(cuid())
6485
6836
  userId String
6486
- placeName String // e.g. "Freddy's", "Starbucks"
6487
- redeemedAt DateTime @default(now())
6837
+ placeName String // e.g. "Freddy's", "Starbucks"
6838
+ redeemedAt DateTime @default(now())
6488
6839
  birthdayYear Int
6489
- notes String? @db.Text
6490
- difficultyScore Int? // 1-10
6491
- qualityScore Int? // 1-10
6492
- ratingComment String? @db.Text
6840
+ notes String? @db.Text
6841
+ difficultyScore Int? // 1-10
6842
+ qualityScore Int? // 1-10
6843
+ ratingComment String? @db.Text
6493
6844
  ratedAt DateTime?
6494
- offerId String? // Part B: optional FK to structured BirthdayOffer
6845
+ offerId String? // Part B: optional FK to structured BirthdayOffer
6495
6846
  /// Catalog or national reward id when marked picked up from the freebies guide
6496
6847
  catalogId String?
6497
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6498
- offer BirthdayOffer? @relation(fields: [offerId], references: [id], onDelete: SetNull)
6848
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6849
+ offer BirthdayOffer? @relation(fields: [offerId], references: [id], onDelete: SetNull)
6499
6850
  catalog BirthdayFreebieCatalog? @relation(fields: [catalogId], references: [id], onDelete: SetNull)
6500
6851
 
6501
6852
  @@index([userId])
@@ -6506,13 +6857,13 @@ model BirthdayRewardRedemption {
6506
6857
 
6507
6858
  /// Logged-in user's saved "My Birthday Plan" offer ids + location context (separate from Part B generated BirthdayPlan).
6508
6859
  model BirthdayFreebiesSavedPlan {
6509
- id String @id @default(cuid())
6510
- userId String @unique
6511
- planIds String[] @default([])
6512
- stateSlug String?
6513
- citySlug String?
6514
- updatedAt DateTime @updatedAt
6515
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6860
+ id String @id @default(cuid())
6861
+ userId String @unique
6862
+ planIds String[] @default([])
6863
+ stateSlug String?
6864
+ citySlug String?
6865
+ updatedAt DateTime @updatedAt
6866
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6516
6867
  }
6517
6868
 
6518
6869
  enum BirthdayDealSuggestionStatus {
@@ -6523,22 +6874,22 @@ enum BirthdayDealSuggestionStatus {
6523
6874
 
6524
6875
  // User-suggested birthday deals (admin can approve and add to catalog later)
6525
6876
  model BirthdayDealSuggestion {
6526
- id String @id @default(cuid())
6527
- userId String
6528
- brandOrPlaceName String
6529
- dealDescription String @db.Text
6530
- sourceWhereHeard String? @db.Text
6531
- linkUrl String? @db.Text
6532
- suggestedQualityTier BirthdayOfferQualityTier? // Suggester's experience: Awesome, Okay, or Lame
6533
- signupDaysInAdvance Int? // When requiresAdvanceSignup: how many days before birthday
6877
+ id String @id @default(cuid())
6878
+ userId String
6879
+ brandOrPlaceName String
6880
+ dealDescription String @db.Text
6881
+ sourceWhereHeard String? @db.Text
6882
+ linkUrl String? @db.Text
6883
+ suggestedQualityTier BirthdayOfferQualityTier? // Suggester's experience: Awesome, Okay, or Lame
6884
+ signupDaysInAdvance Int? // When requiresAdvanceSignup: how many days before birthday
6534
6885
  /// User-selected expiration hint while the suggestion is pending (mirrors catalog enum)
6535
6886
  suggestedExpirationType BirthdayFreebieExpirationType?
6536
- status BirthdayDealSuggestionStatus @default(Pending)
6537
- createdAt DateTime @default(now())
6538
- reviewedAt DateTime?
6539
- reviewedById String?
6540
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6541
- catalogEntry BirthdayFreebieCatalog?
6887
+ status BirthdayDealSuggestionStatus @default(Pending)
6888
+ createdAt DateTime @default(now())
6889
+ reviewedAt DateTime?
6890
+ reviewedById String?
6891
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6892
+ catalogEntry BirthdayFreebieCatalog?
6542
6893
 
6543
6894
  @@index([userId])
6544
6895
  @@index([status])
@@ -6580,38 +6931,38 @@ enum BirthdayFreebieSector {
6580
6931
  }
6581
6932
 
6582
6933
  model BirthdayFreebieCatalog {
6583
- id String @id @default(cuid())
6934
+ id String @id @default(cuid())
6584
6935
  brandOrPlaceName String
6585
- description String? @db.Text
6586
- logoUrl String? @db.Text
6587
- sector BirthdayFreebieSector @default(Other)
6936
+ description String? @db.Text
6937
+ logoUrl String? @db.Text
6938
+ sector BirthdayFreebieSector @default(Other)
6588
6939
  category BirthdayFreebieCategory
6589
6940
  expirationType BirthdayFreebieExpirationType
6590
6941
  lastVerifiedAt DateTime
6591
- requiresAdvanceSignup Boolean? @default(false)
6942
+ requiresAdvanceSignup Boolean? @default(false)
6592
6943
  source BirthdayFreebieSource
6593
6944
  partnerId String?
6594
- suggestionId String? @unique // one-to-one: at most one catalog entry per suggestion
6945
+ suggestionId String? @unique // one-to-one: at most one catalog entry per suggestion
6595
6946
  stateSlug String
6596
6947
  citySlug String?
6597
- isActive Boolean @default(true)
6598
- signupUrl String? @db.Text // Official "how to redeem" / terms link
6599
- sourceUrl String? @db.Text // Optional: reference source (e.g. NerdWallet, Krazy Coupon Lady)
6600
- appRequired Boolean @default(false)
6601
- priority Int @default(0) // Tie-break sort
6948
+ isActive Boolean @default(true)
6949
+ signupUrl String? @db.Text // Official "how to redeem" / terms link
6950
+ sourceUrl String? @db.Text // Optional: reference source (e.g. NerdWallet, Krazy Coupon Lady)
6951
+ appRequired Boolean @default(false)
6952
+ priority Int @default(0) // Tie-break sort
6602
6953
  qualityTier BirthdayOfferQualityTier? // Awesome / Okay / Lame — set by super users on approve
6603
- address String? @db.Text // For Maps and display
6604
- redemptionNotes String? @db.Text // Conditions, limitations
6605
- expirationDays Int? // when Valid30Days with custom count
6606
- isNational Boolean @default(false)
6607
- createdAt DateTime @default(now())
6608
- updatedAt DateTime @updatedAt
6609
-
6610
- partner BirthdayOfferPartner? @relation(fields: [partnerId], references: [id], onDelete: SetNull)
6611
- suggestion BirthdayDealSuggestion? @relation(fields: [suggestionId], references: [id], onDelete: SetNull)
6612
- rewardReports RewardReport[]
6954
+ address String? @db.Text // For Maps and display
6955
+ redemptionNotes String? @db.Text // Conditions, limitations
6956
+ expirationDays Int? // when Valid30Days with custom count
6957
+ isNational Boolean @default(false)
6958
+ createdAt DateTime @default(now())
6959
+ updatedAt DateTime @updatedAt
6960
+
6961
+ partner BirthdayOfferPartner? @relation(fields: [partnerId], references: [id], onDelete: SetNull)
6962
+ suggestion BirthdayDealSuggestion? @relation(fields: [suggestionId], references: [id], onDelete: SetNull)
6963
+ rewardReports RewardReport[]
6613
6964
  /// User quality votes (Awesome / Okay / Lame); one row per user per catalog entry.
6614
- votes BirthdayFreebieVote[]
6965
+ votes BirthdayFreebieVote[]
6615
6966
  birthdayRewardRedemptions BirthdayRewardRedemption[]
6616
6967
 
6617
6968
  @@index([stateSlug, citySlug])
@@ -6622,12 +6973,12 @@ model BirthdayFreebieCatalog {
6622
6973
 
6623
6974
  /// User quality vote on a catalog entry (Awesome / Okay / Lame). Drives aggregate counts and optional qualityTier updates.
6624
6975
  model BirthdayFreebieVote {
6625
- id String @id @default(cuid())
6976
+ id String @id @default(cuid())
6626
6977
  catalogId String
6627
6978
  userId String
6628
6979
  tier BirthdayOfferQualityTier
6629
- createdAt DateTime @default(now())
6630
- updatedAt DateTime @updatedAt
6980
+ createdAt DateTime @default(now())
6981
+ updatedAt DateTime @updatedAt
6631
6982
 
6632
6983
  catalog BirthdayFreebieCatalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
6633
6984
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -6645,15 +6996,15 @@ enum BirthdayCityResourceType {
6645
6996
  }
6646
6997
 
6647
6998
  model BirthdayCityResource {
6648
- id String @id @default(cuid())
6649
- stateSlug String
6650
- citySlug String? // null = state-level resource
6651
- type BirthdayCityResourceType
6652
- label String // e.g. "Visit Salt Lake events"
6653
- url String @db.Text
6654
- sortOrder Int @default(0)
6655
- createdAt DateTime @default(now())
6656
- updatedAt DateTime @updatedAt
6999
+ id String @id @default(cuid())
7000
+ stateSlug String
7001
+ citySlug String? // null = state-level resource
7002
+ type BirthdayCityResourceType
7003
+ label String // e.g. "Visit Salt Lake events"
7004
+ url String @db.Text
7005
+ sortOrder Int @default(0)
7006
+ createdAt DateTime @default(now())
7007
+ updatedAt DateTime @updatedAt
6657
7008
  suggestionCreatedFor BirthdayCityResourceSuggestion? @relation("CityResourceFromSuggestion")
6658
7009
 
6659
7010
  @@index([stateSlug, citySlug])
@@ -6667,21 +7018,21 @@ enum BirthdayCityResourceSuggestionStatus {
6667
7018
 
6668
7019
  // User-suggested "free things to do" links — admin approves into BirthdayCityResource
6669
7020
  model BirthdayCityResourceSuggestion {
6670
- id String @id @default(cuid())
7021
+ id String @id @default(cuid())
6671
7022
  stateSlug String
6672
7023
  citySlug String
6673
- url String @db.Text
6674
- label String? @db.Text
6675
- notes String? @db.Text
6676
- contactEmail String? @db.Text
7024
+ url String @db.Text
7025
+ label String? @db.Text
7026
+ notes String? @db.Text
7027
+ contactEmail String? @db.Text
6677
7028
  userId String?
6678
7029
  status BirthdayCityResourceSuggestionStatus @default(Pending)
6679
- createdAt DateTime @default(now())
7030
+ createdAt DateTime @default(now())
6680
7031
  reviewedAt DateTime?
6681
7032
  reviewedById String?
6682
- createdResourceId String? @unique
6683
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
6684
- createdResource BirthdayCityResource? @relation("CityResourceFromSuggestion", fields: [createdResourceId], references: [id], onDelete: SetNull)
7033
+ createdResourceId String? @unique
7034
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
7035
+ createdResource BirthdayCityResource? @relation("CityResourceFromSuggestion", fields: [createdResourceId], references: [id], onDelete: SetNull)
6685
7036
 
6686
7037
  @@index([status])
6687
7038
  @@index([stateSlug, citySlug])
@@ -6694,18 +7045,18 @@ enum RewardReportStatus {
6694
7045
  }
6695
7046
 
6696
7047
  model RewardReport {
6697
- id String @id @default(cuid())
6698
- catalogId String
6699
- userId String
6700
- reportedAt DateTime @default(now())
6701
- reason String @db.Text
6702
- suggestedChanges String? @db.Text
6703
- status RewardReportStatus @default(Pending)
6704
- reviewedAt DateTime?
6705
- reviewedById String?
6706
- catalog BirthdayFreebieCatalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
6707
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6708
- reviewedBy User? @relation("RewardReportReviewedBy", fields: [reviewedById], references: [id], onDelete: SetNull)
7048
+ id String @id @default(cuid())
7049
+ catalogId String
7050
+ userId String
7051
+ reportedAt DateTime @default(now())
7052
+ reason String @db.Text
7053
+ suggestedChanges String? @db.Text
7054
+ status RewardReportStatus @default(Pending)
7055
+ reviewedAt DateTime?
7056
+ reviewedById String?
7057
+ catalog BirthdayFreebieCatalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
7058
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7059
+ reviewedBy User? @relation("RewardReportReviewedBy", fields: [reviewedById], references: [id], onDelete: SetNull)
6709
7060
 
6710
7061
  @@index([catalogId])
6711
7062
  @@index([userId])
@@ -6714,18 +7065,18 @@ model RewardReport {
6714
7065
 
6715
7066
  /// Reports on hard-coded national chains (nationalBirthdayRewards.ts). Super users review and update the file.
6716
7067
  model NationalChainReport {
6717
- id String @id @default(cuid())
6718
- nationalBrandId String // e.g. "national-starbucks" — id from NATIONAL_BIRTHDAY_REWARDS
6719
- nationalBrandName String // Denormalized for display
6720
- userId String
6721
- reportedAt DateTime @default(now())
6722
- reason String @db.Text
6723
- suggestedChanges String? @db.Text
6724
- status RewardReportStatus @default(Pending)
6725
- reviewedAt DateTime?
6726
- reviewedById String?
6727
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6728
- reviewedBy User? @relation("NationalChainReportReviewedBy", fields: [reviewedById], references: [id], onDelete: SetNull)
7068
+ id String @id @default(cuid())
7069
+ nationalBrandId String // e.g. "national-starbucks" — id from NATIONAL_BIRTHDAY_REWARDS
7070
+ nationalBrandName String // Denormalized for display
7071
+ userId String
7072
+ reportedAt DateTime @default(now())
7073
+ reason String @db.Text
7074
+ suggestedChanges String? @db.Text
7075
+ status RewardReportStatus @default(Pending)
7076
+ reviewedAt DateTime?
7077
+ reviewedById String?
7078
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7079
+ reviewedBy User? @relation("NationalChainReportReviewedBy", fields: [reviewedById], references: [id], onDelete: SetNull)
6729
7080
 
6730
7081
  @@index([nationalBrandId])
6731
7082
  @@index([userId])
@@ -6734,22 +7085,22 @@ model NationalChainReport {
6734
7085
 
6735
7086
  // Part B: User saved locations (birthday home base, etc.)
6736
7087
  model UserLocation {
6737
- id String @id @default(cuid())
6738
- userId String
6739
- label String? // e.g. "Home", "Work"
6740
- address1 String
6741
- address2 String?
6742
- city String
6743
- state String
6744
- zip String
6745
- country String? @default("US")
6746
- lat Float?
6747
- lng Float?
6748
- isDefault Boolean? @default(false)
6749
- createdAt DateTime @default(now())
6750
- updatedAt DateTime @updatedAt
6751
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6752
- usersWithBirthdayLocation User[] @relation("UserBirthdayLocation")
7088
+ id String @id @default(cuid())
7089
+ userId String
7090
+ label String? // e.g. "Home", "Work"
7091
+ address1 String
7092
+ address2 String?
7093
+ city String
7094
+ state String
7095
+ zip String
7096
+ country String? @default("US")
7097
+ lat Float?
7098
+ lng Float?
7099
+ isDefault Boolean? @default(false)
7100
+ createdAt DateTime @default(now())
7101
+ updatedAt DateTime @updatedAt
7102
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7103
+ usersWithBirthdayLocation User[] @relation("UserBirthdayLocation")
6753
7104
 
6754
7105
  @@index([userId])
6755
7106
  }
@@ -6763,7 +7114,7 @@ enum RedemptionMethod {
6763
7114
  }
6764
7115
 
6765
7116
  enum BirthdayOfferExpirationType {
6766
- DAY_OF // End of birthday day
7117
+ DAY_OF // End of birthday day
6767
7118
  DAYS_FROM_BDAY
6768
7119
  END_OF_MONTH
6769
7120
  VARIABLE
@@ -6784,14 +7135,14 @@ enum OfferClaimStatus {
6784
7135
  }
6785
7136
 
6786
7137
  model Brand {
6787
- id String @id @default(cuid())
7138
+ id String @id @default(cuid())
6788
7139
  name String
6789
- websiteUrl String? @db.Text
6790
- logoUrl String? @db.Text
6791
- categoryDefault String? // restaurant, coffee, dessert, retail, entertainment
6792
- isNational Boolean @default(true)
6793
- createdAt DateTime @default(now())
6794
- updatedAt DateTime @updatedAt
7140
+ websiteUrl String? @db.Text
7141
+ logoUrl String? @db.Text
7142
+ categoryDefault String? // restaurant, coffee, dessert, retail, entertainment
7143
+ isNational Boolean @default(true)
7144
+ createdAt DateTime @default(now())
7145
+ updatedAt DateTime @updatedAt
6795
7146
  offers BirthdayOffer[]
6796
7147
  locations BrandLocation[]
6797
7148
 
@@ -6799,28 +7150,28 @@ model Brand {
6799
7150
  }
6800
7151
 
6801
7152
  model BirthdayOffer {
6802
- id String @id @default(cuid())
7153
+ id String @id @default(cuid())
6803
7154
  brandId String
6804
- title String // e.g. "Free burger reward"
6805
- descriptionShort String? @db.Text
6806
- purchaseRequired Boolean @default(false)
6807
- signupRequired Boolean @default(false)
6808
- appRequired Boolean @default(false)
6809
- requiresPhone Boolean @default(false)
7155
+ title String // e.g. "Free burger reward"
7156
+ descriptionShort String? @db.Text
7157
+ purchaseRequired Boolean @default(false)
7158
+ signupRequired Boolean @default(false)
7159
+ appRequired Boolean @default(false)
7160
+ requiresPhone Boolean @default(false)
6810
7161
  redemptionMethod RedemptionMethod
6811
7162
  expirationType BirthdayOfferExpirationType
6812
- expirationDays Int? // when expirationType = DAYS_FROM_BDAY
6813
- windowStartOffsetDays Int @default(0)
6814
- termsUrl String? @db.Text
6815
- termsNotes String? @db.Text
7163
+ expirationDays Int? // when expirationType = DAYS_FROM_BDAY
7164
+ windowStartOffsetDays Int @default(0)
7165
+ termsUrl String? @db.Text
7166
+ termsNotes String? @db.Text
6816
7167
  sourceType BirthdayOfferSourceType
6817
7168
  lastVerifiedAt DateTime?
6818
- isActive Boolean @default(true)
6819
- priorityScore Int @default(0)
6820
- featuredUntil DateTime?
6821
- createdAt DateTime @default(now())
6822
- updatedAt DateTime @updatedAt
6823
- brand Brand @relation(fields: [brandId], references: [id], onDelete: Cascade)
7169
+ isActive Boolean @default(true)
7170
+ priorityScore Int @default(0)
7171
+ featuredUntil DateTime?
7172
+ createdAt DateTime @default(now())
7173
+ updatedAt DateTime @updatedAt
7174
+ brand Brand @relation(fields: [brandId], references: [id], onDelete: Cascade)
6824
7175
  claims OfferClaim[]
6825
7176
  redemptions BirthdayRewardRedemption[]
6826
7177
 
@@ -6849,15 +7200,15 @@ model BrandLocation {
6849
7200
  }
6850
7201
 
6851
7202
  model OfferClaim {
6852
- id String @id @default(cuid())
6853
- userId String
6854
- birthdayOfferId String
6855
- claimedAt DateTime @default(now())
6856
- birthdayYear Int
6857
- status OfferClaimStatus
6858
- meta Json?
6859
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6860
- birthdayOffer BirthdayOffer @relation(fields: [birthdayOfferId], references: [id], onDelete: Cascade)
7203
+ id String @id @default(cuid())
7204
+ userId String
7205
+ birthdayOfferId String
7206
+ claimedAt DateTime @default(now())
7207
+ birthdayYear Int
7208
+ status OfferClaimStatus
7209
+ meta Json?
7210
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7211
+ birthdayOffer BirthdayOffer @relation(fields: [birthdayOfferId], references: [id], onDelete: Cascade)
6861
7212
 
6862
7213
  @@index([userId])
6863
7214
  @@index([birthdayOfferId])
@@ -6865,15 +7216,15 @@ model OfferClaim {
6865
7216
  }
6866
7217
 
6867
7218
  model BirthdayPlan {
6868
- id String @id @default(cuid())
6869
- userId String
6870
- birthdayYear Int
6871
- generatedAt DateTime @default(now())
6872
- preferencesSnapshot Json // distance, filters, optimizeFor
6873
- items Json // ordered stops: plannedDate, brandId, offerId, locationId?, distanceFromHome, deadline, purchaseRequired
6874
- validFrom DateTime
6875
- validTo DateTime
6876
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
7219
+ id String @id @default(cuid())
7220
+ userId String
7221
+ birthdayYear Int
7222
+ generatedAt DateTime @default(now())
7223
+ preferencesSnapshot Json // distance, filters, optimizeFor
7224
+ items Json // ordered stops: plannedDate, brandId, offerId, locationId?, distanceFromHome, deadline, purchaseRequired
7225
+ validFrom DateTime
7226
+ validTo DateTime
7227
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
6877
7228
 
6878
7229
  @@index([userId])
6879
7230
  @@index([userId, birthdayYear])
@@ -6881,12 +7232,12 @@ model BirthdayPlan {
6881
7232
 
6882
7233
  // Part B: Geocoding cache to avoid repeated API calls (birthday rewards, UserLocation, etc.)
6883
7234
  model GeocodeCache {
6884
- id String @id @default(cuid())
7235
+ id String @id @default(cuid())
6885
7236
  addressQuery String @unique // Normalized query string (trimmed, collapsed whitespace)
6886
7237
  lat Float
6887
7238
  lng Float
6888
7239
  normalizedAddress String @db.Text // formatted_address from provider
6889
- createdAt DateTime @default(now())
7240
+ createdAt DateTime @default(now())
6890
7241
 
6891
7242
  @@index([addressQuery])
6892
7243
  }
@@ -7576,7 +7927,7 @@ model BashAvailability {
7576
7927
  window BashAvailabilityWindow
7577
7928
  city String
7578
7929
  state String?
7579
- customAt DateTime? // When window is Custom: specific date/time
7930
+ customAt DateTime? // When window is Custom: specific date/time
7580
7931
  expiresAt DateTime
7581
7932
  createdAt DateTime @default(now())
7582
7933
  updatedAt DateTime @updatedAt
@@ -7592,7 +7943,7 @@ model BashAvailability {
7592
7943
  model BashEventPageView {
7593
7944
  id String @id @default(cuid())
7594
7945
  bashEventId String
7595
- sessionHash String // hashed IP+UA for dedup
7946
+ sessionHash String // hashed IP+UA for dedup
7596
7947
  viewedAt DateTime @default(now())
7597
7948
 
7598
7949
  bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
@@ -7603,16 +7954,16 @@ model BashEventPageView {
7603
7954
  // ─── Phase 1: Bash Groups ────────────────────────────────────────────────────
7604
7955
 
7605
7956
  enum GroupVisibility {
7606
- Private // Default: owner curates members
7957
+ Private // Default: owner curates members
7607
7958
  OpenLink // Anyone with the group link can self-join as WantsToGo
7608
7959
  }
7609
7960
 
7610
7961
  enum GroupMemberStatus {
7611
- Going // Ticket purchased / attending
7612
- WantsToGo // Interested, no ticket yet
7613
- Maybe // Maybe attending (aligns with Invitation maybeDate)
7614
- NotGoing // Not attending (aligns with Invitation rejectedDate)
7615
- Invited // Invited by owner, not yet responded
7962
+ Going // Ticket purchased / attending
7963
+ WantsToGo // Interested, no ticket yet
7964
+ Maybe // Maybe attending (aligns with Invitation maybeDate)
7965
+ NotGoing // Not attending (aligns with Invitation rejectedDate)
7966
+ Invited // Invited by owner, not yet responded
7616
7967
  }
7617
7968
 
7618
7969
  // A group of attendees for a specific event
@@ -7620,11 +7971,11 @@ model EventGroup {
7620
7971
  id String @id @default(cuid())
7621
7972
  bashEventId String
7622
7973
  ownerId String
7623
- name String? // e.g. "Steve's Birthday Group", "Weber State Crew"
7624
- maxMembers Int? // null = unlimited
7974
+ name String? // e.g. "Steve's Birthday Group", "Weber State Crew"
7975
+ maxMembers Int? // null = unlimited
7625
7976
  visibility GroupVisibility @default(Private)
7626
7977
  allowAutoJoin Boolean @default(false) // When true, anyone can join instantly; when false, requests require approval
7627
- promoterId String? // Optional: links to event promoter for display context
7978
+ promoterId String? // Optional: links to event promoter for display context
7628
7979
  createdAt DateTime @default(now())
7629
7980
  updatedAt DateTime @updatedAt
7630
7981
 
@@ -7644,11 +7995,11 @@ model GroupMember {
7644
7995
  groupId String
7645
7996
  userId String
7646
7997
  status GroupMemberStatus @default(Invited)
7647
- ticketId String? // Set when status = Going
7998
+ ticketId String? // Set when status = Going
7648
7999
  joinedAt DateTime @default(now())
7649
8000
 
7650
- group EventGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
7651
- user User @relation("GroupMemberships", fields: [userId], references: [id], onDelete: Cascade)
8001
+ group EventGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
8002
+ user User @relation("GroupMemberships", fields: [userId], references: [id], onDelete: Cascade)
7652
8003
 
7653
8004
  @@unique([groupId, userId])
7654
8005
  @@index([groupId])
@@ -7662,18 +8013,18 @@ model GroupMember {
7662
8013
 
7663
8014
  // Host-created time-limited offer targeting a specific group
7664
8015
  model GroupUnlockOffer {
7665
- id String @id @default(cuid())
8016
+ id String @id @default(cuid())
7666
8017
  bashEventId String
7667
- ticketTierId String? // Optional: scoped to a specific tier
7668
- minGroupSize Int // Minimum members who must buy
7669
- rewardLabel String // "Free drink", "VIP upgrade", "Free table"
7670
- rewardType String // FreeDrink | VIPUpgrade | Merch | Custom
8018
+ ticketTierId String? // Optional: scoped to a specific tier
8019
+ minGroupSize Int // Minimum members who must buy
8020
+ rewardLabel String // "Free drink", "VIP upgrade", "Free table"
8021
+ rewardType String // FreeDrink | VIPUpgrade | Merch | Custom
7671
8022
  expiresAt DateTime
7672
- maxGroups Int? // null = unlimited
7673
- createdAt DateTime @default(now())
8023
+ maxGroups Int? // null = unlimited
8024
+ createdAt DateTime @default(now())
7674
8025
 
7675
- bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
7676
- ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
8026
+ bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
8027
+ ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
7677
8028
  unlocks GroupUnlock[]
7678
8029
 
7679
8030
  @@index([bashEventId])
@@ -7703,38 +8054,38 @@ enum PartnerTier {
7703
8054
  }
7704
8055
 
7705
8056
  model PartnerBrand {
7706
- id String @id @default(cuid())
7707
- name String
7708
- logoUrl String?
7709
- websiteUrl String?
7710
- description String? @db.Text
7711
- tier PartnerTier @default(Standard)
7712
- isActive Boolean @default(true)
7713
- affiliateBaseUrl String?
7714
- listingFeeMonthly Int? // cents; null = affiliate-only
7715
- createdAt DateTime @default(now())
7716
- products PartnerProduct[]
7717
- adSlots PartnerAdSlot[]
8057
+ id String @id @default(cuid())
8058
+ name String
8059
+ logoUrl String?
8060
+ websiteUrl String?
8061
+ description String? @db.Text
8062
+ tier PartnerTier @default(Standard)
8063
+ isActive Boolean @default(true)
8064
+ affiliateBaseUrl String?
8065
+ listingFeeMonthly Int? // cents; null = affiliate-only
8066
+ createdAt DateTime @default(now())
8067
+ products PartnerProduct[]
8068
+ adSlots PartnerAdSlot[]
7718
8069
 
7719
8070
  @@index([isActive, tier])
7720
8071
  }
7721
8072
 
7722
8073
  model PartnerProduct {
7723
- id String @id @default(cuid())
7724
- brandId String
7725
- brand PartnerBrand @relation(fields: [brandId], references: [id], onDelete: Cascade)
7726
- name String
7727
- description String? @db.Text
7728
- imageUrl String?
7729
- priceSummary String?
7730
- affiliateUrl String
7731
- categories String[]
7732
- isActive Boolean @default(true)
7733
- sortOrder Int @default(0)
7734
- impressions Int @default(0)
7735
- clicks Int @default(0)
7736
- createdAt DateTime @default(now())
7737
- adSlots PartnerAdSlot[]
8074
+ id String @id @default(cuid())
8075
+ brandId String
8076
+ brand PartnerBrand @relation(fields: [brandId], references: [id], onDelete: Cascade)
8077
+ name String
8078
+ description String? @db.Text
8079
+ imageUrl String?
8080
+ priceSummary String?
8081
+ affiliateUrl String
8082
+ categories String[]
8083
+ isActive Boolean @default(true)
8084
+ sortOrder Int @default(0)
8085
+ impressions Int @default(0)
8086
+ clicks Int @default(0)
8087
+ createdAt DateTime @default(now())
8088
+ adSlots PartnerAdSlot[]
7738
8089
 
7739
8090
  @@index([brandId, isActive])
7740
8091
  }