@bash-app/bash-common 30.112.0 → 30.114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/definitions.d.ts +2 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +2 -2
- package/dist/definitions.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/__tests__/recurrenceUtils.test.d.ts +2 -0
- package/dist/utils/__tests__/recurrenceUtils.test.d.ts.map +1 -0
- package/dist/utils/__tests__/recurrenceUtils.test.js +193 -0
- package/dist/utils/__tests__/recurrenceUtils.test.js.map +1 -0
- package/dist/utils/discountEngine/bestPriceResolver.d.ts +31 -0
- package/dist/utils/discountEngine/bestPriceResolver.d.ts.map +1 -0
- package/dist/utils/discountEngine/bestPriceResolver.js +147 -0
- package/dist/utils/discountEngine/bestPriceResolver.js.map +1 -0
- package/dist/utils/discountEngine/discountCalculator.d.ts +56 -0
- package/dist/utils/discountEngine/discountCalculator.d.ts.map +1 -0
- package/dist/utils/discountEngine/discountCalculator.js +219 -0
- package/dist/utils/discountEngine/discountCalculator.js.map +1 -0
- package/dist/utils/discountEngine/eligibilityValidator.d.ts +36 -0
- package/dist/utils/discountEngine/eligibilityValidator.d.ts.map +1 -0
- package/dist/utils/discountEngine/eligibilityValidator.js +189 -0
- package/dist/utils/discountEngine/eligibilityValidator.js.map +1 -0
- package/dist/utils/discountEngine/index.d.ts +4 -0
- package/dist/utils/discountEngine/index.d.ts.map +1 -0
- package/dist/utils/discountEngine/index.js +5 -0
- package/dist/utils/discountEngine/index.js.map +1 -0
- package/dist/utils/recurrenceUtils.d.ts.map +1 -1
- package/dist/utils/recurrenceUtils.js +17 -4
- package/dist/utils/recurrenceUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +448 -118
- package/src/definitions.ts +2 -1
- package/src/index.ts +2 -0
- package/src/utils/discountEngine/bestPriceResolver.ts +212 -0
- package/src/utils/discountEngine/discountCalculator.ts +281 -0
- package/src/utils/discountEngine/eligibilityValidator.ts +256 -0
- package/src/utils/discountEngine/index.ts +5 -0
- package/src/utils/recurrenceUtils.ts +20 -4
package/prisma/schema.prisma
CHANGED
|
@@ -54,15 +54,20 @@ model BashComment {
|
|
|
54
54
|
|
|
55
55
|
// BashFeed Models - Event-Centric Social Discovery Feed
|
|
56
56
|
model BashFeedPost {
|
|
57
|
-
id
|
|
58
|
-
userId
|
|
59
|
-
bashId
|
|
60
|
-
content
|
|
61
|
-
mediaIds
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
id String @id @default(cuid())
|
|
58
|
+
userId String
|
|
59
|
+
bashId String
|
|
60
|
+
content String? @db.Text
|
|
61
|
+
mediaIds String[] // Array of Media IDs
|
|
62
|
+
mentionedUserIds String[] // Array of User IDs mentioned in post
|
|
63
|
+
isAnonymous Boolean @default(false) // User can choose to post anonymously
|
|
64
|
+
viewCount Int @default(0) // Analytics only, not shown publicly
|
|
65
|
+
isFeatured Boolean @default(false) // Post is promoted/featured
|
|
66
|
+
featuredAt DateTime? // When post was featured
|
|
67
|
+
featuredBoost Float @default(0) // Current boost score (time-decayed)
|
|
68
|
+
createdAt DateTime @default(now())
|
|
69
|
+
updatedAt DateTime @updatedAt
|
|
70
|
+
editedAt DateTime?
|
|
66
71
|
|
|
67
72
|
// Relationships
|
|
68
73
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
@@ -78,6 +83,7 @@ model BashFeedPost {
|
|
|
78
83
|
@@index([userId])
|
|
79
84
|
@@index([createdAt])
|
|
80
85
|
@@index([isAnonymous])
|
|
86
|
+
@@index([isFeatured, featuredBoost])
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
model BashFeedLike {
|
|
@@ -94,13 +100,14 @@ model BashFeedLike {
|
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
model BashFeedComment {
|
|
97
|
-
id
|
|
98
|
-
userId
|
|
99
|
-
postId
|
|
100
|
-
content
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
id String @id @default(cuid())
|
|
104
|
+
userId String
|
|
105
|
+
postId String
|
|
106
|
+
content String @db.Text
|
|
107
|
+
mentionedUserIds String[] // Array of User IDs mentioned in comment
|
|
108
|
+
parentId String? // For nested replies
|
|
109
|
+
createdAt DateTime @default(now())
|
|
110
|
+
updatedAt DateTime @updatedAt
|
|
104
111
|
|
|
105
112
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
106
113
|
post BashFeedPost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
@@ -194,29 +201,36 @@ model BashFeedRepost {
|
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
model Competition {
|
|
197
|
-
id String
|
|
204
|
+
id String @id @default(cuid())
|
|
198
205
|
name String
|
|
199
|
-
description String
|
|
206
|
+
description String @db.Text
|
|
200
207
|
userId String
|
|
201
208
|
bashEventId String
|
|
202
209
|
numberOfPrizes Int
|
|
203
210
|
competitionType CompetitionType? // Main category
|
|
204
211
|
subtype String? // Subcategory
|
|
205
|
-
rules String?
|
|
212
|
+
rules String? @db.Text // Detailed rules/disclaimers
|
|
206
213
|
judgingType JudgingType? // How winner is determined
|
|
207
214
|
startTime DateTime? // Competition start time (for voting)
|
|
208
215
|
endTime DateTime? // Competition end time (for voting)
|
|
209
|
-
allowSelfRegistration Boolean
|
|
216
|
+
allowSelfRegistration Boolean @default(false)
|
|
210
217
|
maxParticipants Int? // Optional cap on participants
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
|
|
219
|
+
// Tournament bracket fields
|
|
220
|
+
bracketType BracketType? @default(NONE)
|
|
221
|
+
bracketData Json? // Store bracket structure/metadata
|
|
222
|
+
bracketVisible Boolean @default(true)
|
|
223
|
+
|
|
224
|
+
createdAt DateTime @default(now())
|
|
225
|
+
updatedAt DateTime @updatedAt
|
|
226
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
227
|
+
owner User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
228
|
+
sponsor CompetitionSponsor[]
|
|
229
|
+
prizes Prize[]
|
|
230
|
+
participants CompetitionParticipant[]
|
|
231
|
+
votes CompetitionVote[]
|
|
232
|
+
matches CompetitionMatch[]
|
|
233
|
+
notifications Notification[]
|
|
220
234
|
|
|
221
235
|
@@index([bashEventId])
|
|
222
236
|
@@index([competitionType])
|
|
@@ -243,17 +257,53 @@ model CompetitionParticipant {
|
|
|
243
257
|
imageUrl String? // Photo/avatar
|
|
244
258
|
order Int @default(0) // Display order
|
|
245
259
|
voteCount Int @default(0) // Denormalized for performance
|
|
260
|
+
isEliminated Boolean @default(false) // For tournament brackets
|
|
261
|
+
placement Int? // Final placement (1st, 2nd, 3rd, etc.)
|
|
246
262
|
addedBy String @default("host") // 'host' or 'self'
|
|
247
263
|
createdAt DateTime @default(now())
|
|
248
264
|
competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
|
|
249
265
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
250
266
|
votes CompetitionVote[]
|
|
251
267
|
|
|
268
|
+
// Tournament bracket relations
|
|
269
|
+
matchesAsParticipant1 CompetitionMatch[] @relation("MatchParticipant1")
|
|
270
|
+
matchesAsParticipant2 CompetitionMatch[] @relation("MatchParticipant2")
|
|
271
|
+
matchesWon CompetitionMatch[] @relation("MatchWinner")
|
|
272
|
+
|
|
252
273
|
@@index([competitionId])
|
|
253
274
|
@@index([userId])
|
|
254
275
|
@@index([voteCount])
|
|
255
276
|
}
|
|
256
277
|
|
|
278
|
+
// Tournament bracket matches
|
|
279
|
+
model CompetitionMatch {
|
|
280
|
+
id String @id @default(cuid())
|
|
281
|
+
competitionId String
|
|
282
|
+
round Int // 1 = first round, 2 = semifinals, etc.
|
|
283
|
+
matchNumber Int // Position in round (1, 2, 3...)
|
|
284
|
+
|
|
285
|
+
participant1Id String?
|
|
286
|
+
participant2Id String?
|
|
287
|
+
winnerId String?
|
|
288
|
+
|
|
289
|
+
scheduledTime DateTime?
|
|
290
|
+
completedAt DateTime?
|
|
291
|
+
score Json? // {participant1: 10, participant2: 8}
|
|
292
|
+
notes String? // Match notes
|
|
293
|
+
|
|
294
|
+
competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
|
|
295
|
+
participant1 CompetitionParticipant? @relation("MatchParticipant1", fields: [participant1Id], references: [id])
|
|
296
|
+
participant2 CompetitionParticipant? @relation("MatchParticipant2", fields: [participant2Id], references: [id])
|
|
297
|
+
winner CompetitionParticipant? @relation("MatchWinner", fields: [winnerId], references: [id])
|
|
298
|
+
|
|
299
|
+
createdAt DateTime @default(now())
|
|
300
|
+
updatedAt DateTime @updatedAt
|
|
301
|
+
|
|
302
|
+
@@unique([competitionId, round, matchNumber])
|
|
303
|
+
@@index([competitionId])
|
|
304
|
+
@@index([round])
|
|
305
|
+
}
|
|
306
|
+
|
|
257
307
|
model CompetitionVote {
|
|
258
308
|
id String @id @default(cuid())
|
|
259
309
|
competitionId String
|
|
@@ -331,6 +381,28 @@ model Reminder {
|
|
|
331
381
|
remindWho User? @relation("RemindersAssignedToMe", fields: [remindWhoId], references: [id], onDelete: Cascade)
|
|
332
382
|
}
|
|
333
383
|
|
|
384
|
+
model SentReminder {
|
|
385
|
+
id String @id @default(cuid())
|
|
386
|
+
userId String
|
|
387
|
+
bashEventId String?
|
|
388
|
+
serviceBookingId String?
|
|
389
|
+
invitationId String?
|
|
390
|
+
reminderType String // 'HOST_IMPROVEMENT_7D', 'HOST_IMPROVEMENT_3D', 'BOOKING_7D', 'BOOKING_24H', 'BOOKING_3H', 'RSVP_3D', 'RSVP_1D', 'PENDING_BOOKING_2D', 'PENDING_BOOKING_5D'
|
|
391
|
+
sentAt DateTime @default(now())
|
|
392
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
393
|
+
bashEvent BashEvent? @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
394
|
+
serviceBooking ServiceBooking? @relation(fields: [serviceBookingId], references: [id], onDelete: Cascade)
|
|
395
|
+
invitation Invitation? @relation(fields: [invitationId], references: [id], onDelete: Cascade)
|
|
396
|
+
|
|
397
|
+
@@unique([userId, bashEventId, serviceBookingId, invitationId, reminderType])
|
|
398
|
+
@@index([userId])
|
|
399
|
+
@@index([bashEventId])
|
|
400
|
+
@@index([serviceBookingId])
|
|
401
|
+
@@index([invitationId])
|
|
402
|
+
@@index([reminderType])
|
|
403
|
+
@@index([sentAt])
|
|
404
|
+
}
|
|
405
|
+
|
|
334
406
|
model BashEventPromoCode {
|
|
335
407
|
id String @id @default(cuid())
|
|
336
408
|
code String
|
|
@@ -462,28 +534,29 @@ model Invitation {
|
|
|
462
534
|
sentTo User? @relation("InvitationsSentToMe", fields: [sentToId], references: [id], onDelete: Cascade)
|
|
463
535
|
notifications Notification[]
|
|
464
536
|
tickets Ticket[] @relation("TicketsForInvitation")
|
|
537
|
+
sentReminders SentReminder[]
|
|
465
538
|
|
|
466
539
|
@@index([email])
|
|
467
540
|
}
|
|
468
541
|
|
|
469
542
|
model BashEvent {
|
|
470
|
-
id String
|
|
471
|
-
source BashEventSource
|
|
543
|
+
id String @id @default(cuid())
|
|
544
|
+
source BashEventSource @default(Bash)
|
|
472
545
|
title String
|
|
473
|
-
slug String?
|
|
546
|
+
slug String? @unique
|
|
474
547
|
creatorId String
|
|
475
|
-
createdAt DateTime?
|
|
476
|
-
isApproved Boolean?
|
|
548
|
+
createdAt DateTime? @default(now())
|
|
549
|
+
isApproved Boolean? @default(false)
|
|
477
550
|
description String?
|
|
478
|
-
eventType String
|
|
551
|
+
eventType String @default("Other")
|
|
479
552
|
timezone String?
|
|
480
553
|
startDateTime DateTime?
|
|
481
554
|
endDateTime DateTime?
|
|
482
555
|
waiverUrl String?
|
|
483
|
-
waiverRequired Boolean
|
|
484
|
-
waiverDisplayType String?
|
|
485
|
-
targetAudienceId String?
|
|
486
|
-
amountOfGuestsId String?
|
|
556
|
+
waiverRequired Boolean @default(false)
|
|
557
|
+
waiverDisplayType String? @default("inline")
|
|
558
|
+
targetAudienceId String? @unique
|
|
559
|
+
amountOfGuestsId String? @unique
|
|
487
560
|
vibe String?
|
|
488
561
|
occasion String?
|
|
489
562
|
dress String?
|
|
@@ -492,7 +565,7 @@ model BashEvent {
|
|
|
492
565
|
eventFormat EventFormat? // Derived from location and videoLink: In-Person, Virtual, or Hybrid
|
|
493
566
|
nonProfit Boolean?
|
|
494
567
|
nonProfitId String?
|
|
495
|
-
privacy Privacy
|
|
568
|
+
privacy Privacy @default(Public)
|
|
496
569
|
capacity Int?
|
|
497
570
|
location String?
|
|
498
571
|
street String?
|
|
@@ -500,7 +573,7 @@ model BashEvent {
|
|
|
500
573
|
state String?
|
|
501
574
|
zipCode String?
|
|
502
575
|
country String?
|
|
503
|
-
status BashStatus
|
|
576
|
+
status BashStatus @default(Draft)
|
|
504
577
|
tags String[]
|
|
505
578
|
coverPhoto String?
|
|
506
579
|
clubId String?
|
|
@@ -511,24 +584,24 @@ model BashEvent {
|
|
|
511
584
|
isFeatured Boolean?
|
|
512
585
|
isTrending Boolean?
|
|
513
586
|
venueId String?
|
|
514
|
-
averageRating Float?
|
|
515
|
-
totalRatings Int?
|
|
516
|
-
totalReviews Int?
|
|
517
|
-
totalFeedRatings Int?
|
|
518
|
-
allowDonations Boolean?
|
|
587
|
+
averageRating Float? @default(0)
|
|
588
|
+
totalRatings Int? @default(0) // Total count of all ratings (Review + BashFeedRating)
|
|
589
|
+
totalReviews Int? @default(0) // Count of Review records (anonymous quick ratings)
|
|
590
|
+
totalFeedRatings Int? @default(0) // Count of BashFeedRating records (from posts)
|
|
591
|
+
allowDonations Boolean? @default(true)
|
|
519
592
|
suggestedDonationAmount Int?
|
|
520
593
|
donationDetails String?
|
|
521
|
-
absorbDonationFees Boolean
|
|
522
|
-
absorbTicketFees Boolean
|
|
523
|
-
showAttendees Boolean
|
|
594
|
+
absorbDonationFees Boolean @default(false)
|
|
595
|
+
absorbTicketFees Boolean @default(false)
|
|
596
|
+
showAttendees Boolean @default(true)
|
|
524
597
|
servicePreferences Json? // New tiered status: { "Entertainment": "need", "Sponsors": "booked_closed", ... }
|
|
525
598
|
bookedServices Json? // Booked service details: { "Entertainment": { serviceId: "...", providerId: "...", bookedAt: "..." }, ... }
|
|
526
599
|
serviceVisibility Json? // DEPRECATED: Use servicePreferences instead. Kept for backward compatibility during migration.
|
|
527
600
|
originalCreatorId String?
|
|
528
601
|
transferredAt DateTime?
|
|
529
602
|
transferredFromId String?
|
|
530
|
-
transferCount Int
|
|
531
|
-
startTimeLocked Boolean
|
|
603
|
+
transferCount Int @default(0)
|
|
604
|
+
startTimeLocked Boolean @default(false)
|
|
532
605
|
p2pPaymentMethod String?
|
|
533
606
|
venmoUsername String?
|
|
534
607
|
venmoQRCodeUrl String?
|
|
@@ -543,48 +616,50 @@ model BashEvent {
|
|
|
543
616
|
itinerary Json? // Array of { id: string, title: string, startTime: string, endTime: string, description?: string }
|
|
544
617
|
associatedBashesReferencingMe AssociatedBash[]
|
|
545
618
|
comments BashComment[]
|
|
546
|
-
amountOfGuests AmountOfGuests?
|
|
547
|
-
club Club?
|
|
548
|
-
creator User
|
|
549
|
-
targetAudience TargetAudience?
|
|
550
|
-
transferredFrom User?
|
|
551
|
-
venue Venue?
|
|
619
|
+
amountOfGuests AmountOfGuests? @relation(fields: [amountOfGuestsId], references: [id], onDelete: Cascade)
|
|
620
|
+
club Club? @relation(fields: [clubId], references: [id])
|
|
621
|
+
creator User @relation("CreatedEvent", fields: [creatorId], references: [id], onDelete: Cascade)
|
|
622
|
+
targetAudience TargetAudience? @relation(fields: [targetAudienceId], references: [id], onDelete: Cascade)
|
|
623
|
+
transferredFrom User? @relation("TransferredFrom", fields: [transferredFromId], references: [id])
|
|
624
|
+
venue Venue? @relation(fields: [venueId], references: [id])
|
|
552
625
|
messages BashEventMessage[]
|
|
553
626
|
transferRequests BashEventTransferRequest[]
|
|
627
|
+
updateNotificationQueue EventUpdateNotificationQueue[]
|
|
554
628
|
slugHistory BashSlugHistory[]
|
|
555
629
|
checkouts Checkout[]
|
|
556
630
|
competitions Competition[]
|
|
557
631
|
coordinates Coordinates[]
|
|
558
632
|
links EventLink[]
|
|
559
633
|
eventTasks EventTask[]
|
|
560
|
-
exhibitorBookingRequests ExhibitorBookingRequest[]
|
|
634
|
+
exhibitorBookingRequests ExhibitorBookingRequest[] @relation("ExhibitorBookingEvent")
|
|
561
635
|
investments Investment[]
|
|
562
636
|
invitations Invitation[]
|
|
563
637
|
notificationsReferencingMe Notification[]
|
|
638
|
+
sentReminders SentReminder[]
|
|
564
639
|
recurrence Recurrence?
|
|
565
640
|
reviews Review[]
|
|
566
641
|
serviceBookings ServiceBooking[]
|
|
567
|
-
sponsorBookingRequests SponsorBookingRequest[]
|
|
642
|
+
sponsorBookingRequests SponsorBookingRequest[] @relation("SponsorBookingEvent")
|
|
568
643
|
sponsorships SponsoredEvent[]
|
|
569
644
|
tickets Ticket[]
|
|
570
645
|
ticketTiers TicketTier[]
|
|
571
646
|
favoritedBy UserFavorite[]
|
|
572
647
|
userPromoCodeRedemption UserPromoCodeRedemption[]
|
|
573
648
|
userReport UserReport[]
|
|
574
|
-
vendorBookingRequests VendorBookingRequest[]
|
|
575
|
-
media Media[]
|
|
576
|
-
associatedServicesReferencingMe Service[]
|
|
649
|
+
vendorBookingRequests VendorBookingRequest[] @relation("VendorBookingEvent")
|
|
650
|
+
media Media[] @relation("BashEventToMedia")
|
|
651
|
+
associatedServicesReferencingMe Service[] @relation("BashEventToService")
|
|
577
652
|
userConnections UserConnection[]
|
|
578
653
|
aiRecommendationLogs AIRecommendationLog[]
|
|
579
654
|
aiRecommendationCaches AIRecommendationCache[]
|
|
580
655
|
// Template tracking
|
|
581
656
|
templateId String?
|
|
582
|
-
template BashEventTemplate?
|
|
657
|
+
template BashEventTemplate? @relation("CreatedFromTemplate", fields: [templateId], references: [id])
|
|
583
658
|
// Recurring event fields (for child occurrences)
|
|
584
|
-
isRecurringChild Boolean
|
|
659
|
+
isRecurringChild Boolean @default(false)
|
|
585
660
|
parentEventId String?
|
|
586
|
-
parentEvent BashEvent?
|
|
587
|
-
childEvents BashEvent[]
|
|
661
|
+
parentEvent BashEvent? @relation("RecurringEventChildren", fields: [parentEventId], references: [id], onDelete: SetNull)
|
|
662
|
+
childEvents BashEvent[] @relation("RecurringEventChildren")
|
|
588
663
|
occurrenceIndex Int? // 1, 2, 3... for child events
|
|
589
664
|
|
|
590
665
|
// BashFeed Relations
|
|
@@ -592,6 +667,9 @@ model BashEvent {
|
|
|
592
667
|
bashFeedRatings BashFeedRating[]
|
|
593
668
|
eventRankings EventRankings?
|
|
594
669
|
|
|
670
|
+
// Analytics Relations
|
|
671
|
+
analyticsPredictions AnalyticsPrediction[]
|
|
672
|
+
|
|
595
673
|
@@index([templateId])
|
|
596
674
|
@@index([parentEventId])
|
|
597
675
|
}
|
|
@@ -605,14 +683,15 @@ model Coordinates {
|
|
|
605
683
|
}
|
|
606
684
|
|
|
607
685
|
model Checkout {
|
|
608
|
-
id String
|
|
686
|
+
id String @id @default(cuid())
|
|
609
687
|
ownerId String
|
|
610
688
|
bashEventId String
|
|
611
|
-
checkoutDateTime DateTime?
|
|
612
|
-
stripeCheckoutSessionId String?
|
|
613
|
-
bashEvent BashEvent
|
|
614
|
-
owner User
|
|
689
|
+
checkoutDateTime DateTime? @default(now())
|
|
690
|
+
stripeCheckoutSessionId String? @unique
|
|
691
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
692
|
+
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
615
693
|
tickets Ticket[]
|
|
694
|
+
appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this checkout
|
|
616
695
|
|
|
617
696
|
@@index([bashEventId])
|
|
618
697
|
}
|
|
@@ -759,12 +838,14 @@ model TicketTier {
|
|
|
759
838
|
tickets Ticket[]
|
|
760
839
|
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
761
840
|
waitList User[] @relation("TicketTierToUser")
|
|
841
|
+
specialOffers SpecialOffer[] // NEW - Special offers for this tier
|
|
842
|
+
appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this tier
|
|
762
843
|
|
|
763
844
|
@@unique([bashEventId, title])
|
|
764
845
|
}
|
|
765
846
|
|
|
766
847
|
model Ticket {
|
|
767
|
-
id String
|
|
848
|
+
id String @id @default(cuid())
|
|
768
849
|
ownerId String
|
|
769
850
|
bashEventId String
|
|
770
851
|
ticketTierId String?
|
|
@@ -774,35 +855,36 @@ model Ticket {
|
|
|
774
855
|
email String?
|
|
775
856
|
paidOn DateTime?
|
|
776
857
|
allowPromiseToPay Boolean?
|
|
777
|
-
status TicketStatus
|
|
858
|
+
status TicketStatus @default(Pending)
|
|
778
859
|
isFreeGuest Boolean?
|
|
779
860
|
geoFenceCheckInUnnecessary Boolean?
|
|
780
861
|
checkedInAt DateTime?
|
|
781
862
|
checkedOutAt DateTime?
|
|
782
863
|
invitationId String?
|
|
783
864
|
checkoutId String?
|
|
784
|
-
checkedInMethod String?
|
|
865
|
+
checkedInMethod String? @default("manual")
|
|
785
866
|
waitlistPosition Int?
|
|
786
867
|
waitlistJoinedAt DateTime?
|
|
787
868
|
waitlistNotifiedAt DateTime?
|
|
788
869
|
waitlistExpiresAt DateTime?
|
|
789
870
|
checkInLocation String?
|
|
790
871
|
checkOutLocation String?
|
|
791
|
-
checkOutMethod String?
|
|
872
|
+
checkOutMethod String? @default("manual")
|
|
792
873
|
lastLocationUpdate DateTime?
|
|
793
|
-
validationAttempts Int
|
|
874
|
+
validationAttempts Int @default(0)
|
|
794
875
|
lastValidationAttempt DateTime?
|
|
795
876
|
lastValidatedBy String?
|
|
796
877
|
waitlistUserId String?
|
|
797
|
-
bashEvent BashEvent
|
|
798
|
-
checkout Checkout?
|
|
799
|
-
forUser User?
|
|
800
|
-
invitation Invitation?
|
|
801
|
-
owner User
|
|
802
|
-
ticketTier TicketTier?
|
|
803
|
-
waitlistUser User?
|
|
878
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id])
|
|
879
|
+
checkout Checkout? @relation(fields: [checkoutId], references: [id])
|
|
880
|
+
forUser User? @relation("TicketsISent", fields: [forUserId], references: [id])
|
|
881
|
+
invitation Invitation? @relation("TicketsForInvitation", fields: [invitationId], references: [id])
|
|
882
|
+
owner User @relation("TicketsIOwn", fields: [ownerId], references: [id])
|
|
883
|
+
ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
|
|
884
|
+
waitlistUser User? @relation("TicketsOnWaitlist", fields: [waitlistUserId], references: [id])
|
|
804
885
|
metadata TicketMetadata[]
|
|
805
886
|
transfers TicketTransfer[]
|
|
887
|
+
appliedDiscounts AppliedDiscount[] // NEW - Discounts applied to this ticket
|
|
806
888
|
|
|
807
889
|
@@index([bashEventId])
|
|
808
890
|
@@index([waitlistUserId])
|
|
@@ -847,10 +929,14 @@ model Recurrence {
|
|
|
847
929
|
interval Int @default(1)
|
|
848
930
|
bashEventId String @unique
|
|
849
931
|
frequency RecurringFrequency @default(Never)
|
|
850
|
-
ends DateTime
|
|
932
|
+
ends DateTime? // Nullable - used when endsType = ON_DATE
|
|
933
|
+
repeatCount Int? // Used when endsType = AFTER_COUNT
|
|
934
|
+
endsType RecurrenceEndsType @default(ON_DATE)
|
|
851
935
|
repeatOnDays DayOfWeek[]
|
|
852
936
|
repeatOnDayOfMonth Int?
|
|
853
937
|
repeatYearlyDate DateTime?
|
|
938
|
+
createdAt DateTime @default(now())
|
|
939
|
+
updatedAt DateTime @updatedAt
|
|
854
940
|
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
855
941
|
}
|
|
856
942
|
|
|
@@ -1253,6 +1339,7 @@ model User {
|
|
|
1253
1339
|
promoterStats PromoterStats?
|
|
1254
1340
|
remindersCreatedByMe Reminder[] @relation("RemindersCreatedByMe")
|
|
1255
1341
|
remindersAssignedToMe Reminder[] @relation("RemindersAssignedToMe")
|
|
1342
|
+
sentReminders SentReminder[]
|
|
1256
1343
|
reviews Review[]
|
|
1257
1344
|
firstFreeListingId String?
|
|
1258
1345
|
createdServices Service[] @relation("CreatedService")
|
|
@@ -1313,6 +1400,9 @@ model User {
|
|
|
1313
1400
|
reviewedCategoryRequests ServiceCategoryRequest[] @relation("ReviewedCategoryRequests")
|
|
1314
1401
|
bookingCommissionsEarned BookingCommission[] @relation("CommissionReferrer")
|
|
1315
1402
|
|
|
1403
|
+
// Special Offers - Discount Attribution
|
|
1404
|
+
discountAttributions AppliedDiscount[] @relation("DiscountAttribution")
|
|
1405
|
+
|
|
1316
1406
|
// BashCash Referral Relations
|
|
1317
1407
|
referredBy User? @relation("Referrals", fields: [referredById], references: [id])
|
|
1318
1408
|
referrals User[] @relation("Referrals")
|
|
@@ -1397,40 +1487,46 @@ model UserPreferences {
|
|
|
1397
1487
|
preferredCurrency String @default("USD")
|
|
1398
1488
|
savePaymentInfo Boolean @default(false)
|
|
1399
1489
|
showEventPrices Boolean @default(true)
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1490
|
+
|
|
1491
|
+
// BashFeed Preferences
|
|
1492
|
+
bashFeedDefaultTab String @default("all") // "all", "upcoming", "live", "past", "saved", "my-posts"
|
|
1493
|
+
bashFeedDefaultSort String @default("chronological") // "for_you", "chronological", "trending", etc.
|
|
1494
|
+
bashFeedFilters Json? // Saved filter preferences as JSON
|
|
1495
|
+
|
|
1496
|
+
createdAt DateTime @default(now())
|
|
1497
|
+
updatedAt DateTime @updatedAt
|
|
1498
|
+
smsNotifications Boolean @default(false)
|
|
1499
|
+
friendRequestNotify Boolean @default(true)
|
|
1500
|
+
allowTagging Boolean @default(true)
|
|
1501
|
+
showActivityStatus Boolean @default(true)
|
|
1502
|
+
hiddenBashIds String[] @default([])
|
|
1503
|
+
hideActivitySection Boolean @default(false)
|
|
1504
|
+
allowLocationSharing Boolean @default(false)
|
|
1505
|
+
defaultLandingPage String @default("dashboard")
|
|
1506
|
+
contentDensity String @default("COMFORTABLE")
|
|
1507
|
+
fontScale String @default("MEDIUM")
|
|
1508
|
+
animationsEnabled Boolean @default(true)
|
|
1509
|
+
useHighContrastMode Boolean @default(false)
|
|
1510
|
+
contentFilters String[] @default([])
|
|
1511
|
+
topicInterests String[] @default([])
|
|
1512
|
+
hideSeenContent Boolean @default(false)
|
|
1513
|
+
autoplayVideos Boolean @default(true)
|
|
1514
|
+
contentSortPreference String @default("RECENT")
|
|
1515
|
+
contentLanguages String[] @default(["en"])
|
|
1516
|
+
showSensitiveContent Boolean @default(false)
|
|
1517
|
+
defaultEventReminder Int @default(60)
|
|
1518
|
+
communicationFrequency String @default("NORMAL")
|
|
1519
|
+
preferredContactMethod String @default("EMAIL")
|
|
1520
|
+
googleCalendarSyncEnabled Boolean @default(false)
|
|
1521
|
+
googleCalendarEventsSync Boolean @default(false)
|
|
1522
|
+
googleCalendarServicesSync Boolean @default(false)
|
|
1523
|
+
localEventsToCalendarSync Boolean @default(false)
|
|
1524
|
+
localEventsCity String @default("")
|
|
1525
|
+
localEventsState String @default("")
|
|
1526
|
+
showRankings Boolean @default(true) // Show rankings on profile
|
|
1527
|
+
showAttendeeRank Boolean @default(true) // Show attendee rank publicly
|
|
1528
|
+
showHostRank Boolean @default(true) // Show host rank publicly
|
|
1529
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
1434
1530
|
}
|
|
1435
1531
|
|
|
1436
1532
|
model UserFollowing {
|
|
@@ -2359,6 +2455,7 @@ model ServiceBooking {
|
|
|
2359
2455
|
messages ServiceBookingMessage[]
|
|
2360
2456
|
packages ServiceBookingPackage[]
|
|
2361
2457
|
commissions BookingCommission[] @relation("BookingCommissions")
|
|
2458
|
+
sentReminders SentReminder[]
|
|
2362
2459
|
|
|
2363
2460
|
@@index([status])
|
|
2364
2461
|
@@index([creatorId])
|
|
@@ -2717,6 +2814,24 @@ model BashEventTransferRequest {
|
|
|
2717
2814
|
@@index([fromUserId])
|
|
2718
2815
|
}
|
|
2719
2816
|
|
|
2817
|
+
model EventUpdateNotificationQueue {
|
|
2818
|
+
id String @id @default(cuid())
|
|
2819
|
+
bashEventId String
|
|
2820
|
+
scheduledFor DateTime // When to send the notification (5 minutes after last change)
|
|
2821
|
+
changes Json // Array of change objects: [{ field, oldValue, newValue, description }]
|
|
2822
|
+
oldEventSnapshot Json // Complete snapshot of event before any changes
|
|
2823
|
+
createdAt DateTime @default(now())
|
|
2824
|
+
updatedAt DateTime @updatedAt
|
|
2825
|
+
status String @default("pending") // pending, sent, cancelled
|
|
2826
|
+
sentAt DateTime?
|
|
2827
|
+
errorMessage String?
|
|
2828
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
2829
|
+
|
|
2830
|
+
@@index([bashEventId])
|
|
2831
|
+
@@index([scheduledFor, status])
|
|
2832
|
+
@@index([status])
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2720
2835
|
model BlogPost {
|
|
2721
2836
|
id String @id @default(cuid())
|
|
2722
2837
|
title String
|
|
@@ -3321,6 +3436,21 @@ enum JudgingType {
|
|
|
3321
3436
|
HOST_CHOOSES // Host/organizer picks winner
|
|
3322
3437
|
FIRST_COME // First X people win
|
|
3323
3438
|
HIGHEST_BID // Auction-style
|
|
3439
|
+
BRACKET_TOURNAMENT // Winner determined through tournament bracket progression
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
enum BracketType {
|
|
3443
|
+
NONE // No bracket (voting/judging only)
|
|
3444
|
+
SINGLE_ELIMINATION // Lose once, you're out
|
|
3445
|
+
DOUBLE_ELIMINATION // Lose twice, you're out
|
|
3446
|
+
ROUND_ROBIN // Everyone plays everyone
|
|
3447
|
+
SWISS // Pairing based on performance
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
enum RecurrenceEndsType {
|
|
3451
|
+
NEVER // Never ends
|
|
3452
|
+
ON_DATE // Ends on specific date
|
|
3453
|
+
AFTER_COUNT // Ends after X occurrences
|
|
3324
3454
|
}
|
|
3325
3455
|
|
|
3326
3456
|
enum Sex {
|
|
@@ -5229,3 +5359,203 @@ enum EventFormat {
|
|
|
5229
5359
|
Virtual
|
|
5230
5360
|
Hybrid
|
|
5231
5361
|
}
|
|
5362
|
+
|
|
5363
|
+
// ============================================
|
|
5364
|
+
// SPECIAL OFFERS & UNIFIED DISCOUNT SYSTEM
|
|
5365
|
+
// ============================================
|
|
5366
|
+
|
|
5367
|
+
enum DiscountSourceType {
|
|
5368
|
+
PROMO_CODE
|
|
5369
|
+
SPECIAL_OFFER
|
|
5370
|
+
PLATFORM_CREDIT // For future BashCash integration
|
|
5371
|
+
MANUAL_OVERRIDE // Admin adjustments
|
|
5372
|
+
}
|
|
5373
|
+
|
|
5374
|
+
enum DiscountScope {
|
|
5375
|
+
ORDER // Applied to entire order
|
|
5376
|
+
LINE_ITEM // Applied to specific line item
|
|
5377
|
+
TICKET_TIER // Applied to specific ticket tier
|
|
5378
|
+
}
|
|
5379
|
+
|
|
5380
|
+
enum SpecialOfferType {
|
|
5381
|
+
BOGO // Buy One Get One
|
|
5382
|
+
GROUP_DISCOUNT // Group rates
|
|
5383
|
+
EARLY_BIRD // Early bird pricing
|
|
5384
|
+
FLASH_SALE // Time-limited offer
|
|
5385
|
+
BUNDLE // Package deal
|
|
5386
|
+
REFERRAL // Friend referral (needs attribution)
|
|
5387
|
+
VOLUME_DISCOUNT // Buy more save more
|
|
5388
|
+
FAMILY_PACK // Family package
|
|
5389
|
+
LOYALTY // Repeat customer
|
|
5390
|
+
COUNTDOWN // Urgency timer
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
enum OfferDiscountType {
|
|
5394
|
+
PERCENTAGE // X% off
|
|
5395
|
+
FIXED_AMOUNT // $X off
|
|
5396
|
+
FREE_ITEMS // Buy X get Y free
|
|
5397
|
+
UPGRADED_TIER // Free upgrade to VIP
|
|
5398
|
+
}
|
|
5399
|
+
|
|
5400
|
+
// Unified discount tracking - captures ALL discounts at checkout
|
|
5401
|
+
model AppliedDiscount {
|
|
5402
|
+
id String @id @default(cuid())
|
|
5403
|
+
createdAt DateTime @default(now())
|
|
5404
|
+
|
|
5405
|
+
// What discount was applied
|
|
5406
|
+
sourceType DiscountSourceType
|
|
5407
|
+
sourceId String // ID of PromoCode or SpecialOffer
|
|
5408
|
+
sourceName String // "SUMMER20" or "Early Bird Special"
|
|
5409
|
+
|
|
5410
|
+
// Scope and target
|
|
5411
|
+
scope DiscountScope
|
|
5412
|
+
checkoutId String?
|
|
5413
|
+
ticketId String?
|
|
5414
|
+
ticketTierId String?
|
|
5415
|
+
|
|
5416
|
+
// Discount details
|
|
5417
|
+
discountType String // "PERCENTAGE" or "FIXED_AMOUNT"
|
|
5418
|
+
discountValue Int // Percentage or cents
|
|
5419
|
+
amountDiscounted Int // Actual discount applied in cents
|
|
5420
|
+
originalPrice Int // Price before discount
|
|
5421
|
+
finalPrice Int // Price after discount
|
|
5422
|
+
|
|
5423
|
+
// Attribution (optional - for PromoCodes and Referral offers)
|
|
5424
|
+
hasAttribution Boolean @default(false)
|
|
5425
|
+
attributionUserId String? // Promoter/Influencer/Referrer
|
|
5426
|
+
attributionCampaignId String?
|
|
5427
|
+
commissionRate Float?
|
|
5428
|
+
commissionAmount Int? // In cents
|
|
5429
|
+
|
|
5430
|
+
// Display
|
|
5431
|
+
displayReason String? // "Early Bird Discount" for receipt
|
|
5432
|
+
internalNote String? // For support/debugging
|
|
5433
|
+
|
|
5434
|
+
// Relations
|
|
5435
|
+
checkout Checkout? @relation(fields: [checkoutId], references: [id])
|
|
5436
|
+
ticket Ticket? @relation(fields: [ticketId], references: [id])
|
|
5437
|
+
ticketTier TicketTier? @relation(fields: [ticketTierId], references: [id])
|
|
5438
|
+
attributionUser User? @relation("DiscountAttribution", fields: [attributionUserId], references: [id])
|
|
5439
|
+
|
|
5440
|
+
@@index([checkoutId])
|
|
5441
|
+
@@index([sourceType, sourceId])
|
|
5442
|
+
@@index([attributionUserId])
|
|
5443
|
+
@@index([createdAt])
|
|
5444
|
+
}
|
|
5445
|
+
|
|
5446
|
+
// Special offers - automatic, visible, conversion-oriented deals
|
|
5447
|
+
model SpecialOffer {
|
|
5448
|
+
id String @id @default(cuid())
|
|
5449
|
+
createdAt DateTime @default(now())
|
|
5450
|
+
updatedAt DateTime @updatedAt
|
|
5451
|
+
|
|
5452
|
+
// Basic Info
|
|
5453
|
+
ticketTierId String
|
|
5454
|
+
offerType SpecialOfferType
|
|
5455
|
+
title String // "Early Bird Special", "BOGO Deal"
|
|
5456
|
+
description String? @db.Text
|
|
5457
|
+
isActive Boolean @default(true)
|
|
5458
|
+
displayBadge Boolean @default(true)
|
|
5459
|
+
badgeText String? // "50% OFF", "BOGO"
|
|
5460
|
+
|
|
5461
|
+
// Discount Configuration
|
|
5462
|
+
discountType OfferDiscountType
|
|
5463
|
+
discountValue Int // Percentage or cents
|
|
5464
|
+
|
|
5465
|
+
// BOGO Configuration
|
|
5466
|
+
buyQuantity Int? // Buy X tickets
|
|
5467
|
+
getQuantity Int? // Get Y tickets
|
|
5468
|
+
getDiscountPercent Int? // Get Y at Z% off (0-100)
|
|
5469
|
+
|
|
5470
|
+
// Group Discount Configuration
|
|
5471
|
+
minGroupSize Int?
|
|
5472
|
+
maxGroupSize Int?
|
|
5473
|
+
tieredDiscounts Json? // [{qty: 5, discount: 10}, {qty: 10, discount: 20}]
|
|
5474
|
+
|
|
5475
|
+
// Time-Based Configuration
|
|
5476
|
+
startDate DateTime?
|
|
5477
|
+
endDate DateTime?
|
|
5478
|
+
availableUntil DateTime? // Early bird cutoff
|
|
5479
|
+
|
|
5480
|
+
// Quantity Limitations
|
|
5481
|
+
maxRedemptions Int?
|
|
5482
|
+
maxPerUser Int?
|
|
5483
|
+
limitedQuantity Int?
|
|
5484
|
+
currentRedemptions Int @default(0)
|
|
5485
|
+
|
|
5486
|
+
// Social Proof Tracking
|
|
5487
|
+
viewCount Int @default(0) // How many times this offer was viewed
|
|
5488
|
+
clickCount Int @default(0) // How many times users clicked "Apply"
|
|
5489
|
+
lastRedemptionAt DateTime? // When was the last redemption
|
|
5490
|
+
|
|
5491
|
+
// Price Increase (Countdown)
|
|
5492
|
+
priceIncreasesAt DateTime?
|
|
5493
|
+
newPriceAfterIncrease Int?
|
|
5494
|
+
countdownEnabled Boolean @default(false)
|
|
5495
|
+
|
|
5496
|
+
// Bundle Configuration
|
|
5497
|
+
bundledItems Json?
|
|
5498
|
+
bundleSavings Int?
|
|
5499
|
+
|
|
5500
|
+
// Attribution (ONLY for Referral type offers)
|
|
5501
|
+
requiresAttribution Boolean @default(false)
|
|
5502
|
+
referralDiscountForBuyer Int?
|
|
5503
|
+
referralDiscountForFriend Int?
|
|
5504
|
+
commissionRate Float? // For referrer
|
|
5505
|
+
|
|
5506
|
+
// Loyalty Configuration
|
|
5507
|
+
requiresEventAttendance Int?
|
|
5508
|
+
hostSpecificLoyalty Boolean @default(false)
|
|
5509
|
+
|
|
5510
|
+
// Stacking Rules
|
|
5511
|
+
canStackWithPromoCodes Boolean @default(true) // Host decides
|
|
5512
|
+
canStackWithOtherOffers Boolean @default(false)
|
|
5513
|
+
priority Int @default(0) // Higher = applied first
|
|
5514
|
+
|
|
5515
|
+
// Display & Marketing
|
|
5516
|
+
urgencyMessage String? @db.Text
|
|
5517
|
+
showSocialProof Boolean @default(false)
|
|
5518
|
+
highlightColor String?
|
|
5519
|
+
sortOrder Int @default(0)
|
|
5520
|
+
|
|
5521
|
+
// Guardrails
|
|
5522
|
+
minPriceFloor Int? // Never discount below this
|
|
5523
|
+
maxDiscountPercent Int? // Never discount more than X%
|
|
5524
|
+
|
|
5525
|
+
// Relations
|
|
5526
|
+
ticketTier TicketTier @relation(fields: [ticketTierId], references: [id], onDelete: Cascade)
|
|
5527
|
+
|
|
5528
|
+
@@index([ticketTierId])
|
|
5529
|
+
@@index([offerType])
|
|
5530
|
+
@@index([isActive])
|
|
5531
|
+
@@index([endDate])
|
|
5532
|
+
}
|
|
5533
|
+
|
|
5534
|
+
// Analytics Prediction Cache Model
|
|
5535
|
+
model AnalyticsPrediction {
|
|
5536
|
+
id String @id @default(cuid())
|
|
5537
|
+
bashEventId String
|
|
5538
|
+
type PredictionType
|
|
5539
|
+
source PredictionSource
|
|
5540
|
+
prediction Json
|
|
5541
|
+
confidence Float
|
|
5542
|
+
generatedAt DateTime @default(now())
|
|
5543
|
+
validUntil DateTime
|
|
5544
|
+
|
|
5545
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
5546
|
+
|
|
5547
|
+
@@index([bashEventId, type])
|
|
5548
|
+
@@index([validUntil])
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
enum PredictionType {
|
|
5552
|
+
REVENUE_FORECAST
|
|
5553
|
+
PRICING_OPTIMIZATION
|
|
5554
|
+
SELLOUT_PROBABILITY
|
|
5555
|
+
ACTION_RECOMMENDATION
|
|
5556
|
+
}
|
|
5557
|
+
|
|
5558
|
+
enum PredictionSource {
|
|
5559
|
+
RULE_BASED
|
|
5560
|
+
AI_ENHANCED
|
|
5561
|
+
}
|