@bash-app/bash-common 30.309.0 → 30.314.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/__tests__/competitionValidation.test.d.ts +2 -0
- package/dist/__tests__/competitionValidation.test.d.ts.map +1 -0
- package/dist/__tests__/competitionValidation.test.js +28 -0
- package/dist/__tests__/competitionValidation.test.js.map +1 -0
- package/dist/__tests__/definitionsHelpers.test.js +8 -1
- package/dist/__tests__/definitionsHelpers.test.js.map +1 -1
- package/dist/__tests__/membershipDefinitions.test.js +5 -0
- package/dist/__tests__/membershipDefinitions.test.js.map +1 -1
- package/dist/__tests__/sideQuestIdeas.test.js +5 -1
- package/dist/__tests__/sideQuestIdeas.test.js.map +1 -1
- package/dist/__tests__/statusEnums.test.js +7 -0
- package/dist/__tests__/statusEnums.test.js.map +1 -1
- package/dist/competitionValidation.d.ts +11 -2
- package/dist/competitionValidation.d.ts.map +1 -1
- package/dist/competitionValidation.js +40 -2
- package/dist/competitionValidation.js.map +1 -1
- package/dist/definitions.d.ts +18 -1
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +28 -1
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +259 -46
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +76 -22
- package/dist/extendedSchemas.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/membershipDefinitions.d.ts +2 -0
- package/dist/membershipDefinitions.d.ts.map +1 -1
- package/dist/membershipDefinitions.js +3 -0
- package/dist/membershipDefinitions.js.map +1 -1
- package/dist/mirroredPrismaEnums.d.ts +10 -0
- package/dist/mirroredPrismaEnums.d.ts.map +1 -1
- package/dist/mirroredPrismaEnums.js +16 -0
- package/dist/mirroredPrismaEnums.js.map +1 -1
- package/dist/serviceTypeLabels.d.ts +15 -0
- package/dist/serviceTypeLabels.d.ts.map +1 -0
- package/dist/serviceTypeLabels.js +24 -0
- package/dist/serviceTypeLabels.js.map +1 -0
- package/dist/sideQuestIdeas.d.ts.map +1 -1
- package/dist/sideQuestIdeas.js +2 -2
- package/dist/sideQuestIdeas.js.map +1 -1
- package/dist/sideQuestTypes.d.ts +1 -0
- package/dist/sideQuestTypes.d.ts.map +1 -1
- package/dist/sideQuestTypes.js.map +1 -1
- package/dist/targetAudienceLabels.d.ts +12 -0
- package/dist/targetAudienceLabels.d.ts.map +1 -0
- package/dist/targetAudienceLabels.js +50 -0
- package/dist/targetAudienceLabels.js.map +1 -0
- package/dist/utils/service/serviceBookingTypes.d.ts +1 -1
- package/dist/utils/service/serviceBookingTypes.d.ts.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +114 -1
- package/src/__tests__/competitionValidation.test.ts +43 -0
- package/src/__tests__/definitionsHelpers.test.ts +16 -0
- package/src/__tests__/membershipDefinitions.test.ts +18 -0
- package/src/__tests__/sideQuestIdeas.test.ts +11 -1
- package/src/__tests__/statusEnums.test.ts +7 -0
- package/src/competitionValidation.ts +56 -2
- package/src/definitions.ts +40 -1
- package/src/extendedSchemas.ts +216 -63
- package/src/index.ts +2 -0
- package/src/membershipDefinitions.ts +3 -0
- package/src/mirroredPrismaEnums.ts +20 -0
- package/src/serviceTypeLabels.ts +24 -0
- package/src/sideQuestIdeas.ts +2 -2
- package/src/sideQuestTypes.ts +1 -0
- package/src/targetAudienceLabels.ts +54 -0
- package/src/utils/service/serviceBookingTypes.ts +1 -1
package/prisma/schema.prisma
CHANGED
|
@@ -266,6 +266,16 @@ model Competition {
|
|
|
266
266
|
/// How 1st/2nd/3rd place icons appear on leaderboards and profiles
|
|
267
267
|
placementAwardStyle PlacementAwardStyle @default(Trophy)
|
|
268
268
|
|
|
269
|
+
/// Optional Adventures rewards when someone becomes an ACCEPTED participant
|
|
270
|
+
participationBashPoints Int?
|
|
271
|
+
participationEventBadgeId String?
|
|
272
|
+
/// Optional Adventures rewards when a participant reaches a finalist placement (placement <= 3)
|
|
273
|
+
finalistBashPoints Int?
|
|
274
|
+
finalistEventBadgeId String?
|
|
275
|
+
/// Optional Adventures rewards when assigned as a prize winner
|
|
276
|
+
winBashPoints Int?
|
|
277
|
+
winEventBadgeId String?
|
|
278
|
+
|
|
269
279
|
createdAt DateTime @default(now())
|
|
270
280
|
updatedAt DateTime @updatedAt
|
|
271
281
|
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
@@ -279,11 +289,18 @@ model Competition {
|
|
|
279
289
|
judgeAssignments CompetitionJudgeAssignment[]
|
|
280
290
|
judgeScores CompetitionJudgeScore[]
|
|
281
291
|
notifications Notification[]
|
|
292
|
+
sideQuests SideQuest[]
|
|
293
|
+
participationEventBadge EventBadge? @relation("CompetitionParticipationBadge", fields: [participationEventBadgeId], references: [id], onDelete: SetNull)
|
|
294
|
+
finalistEventBadge EventBadge? @relation("CompetitionFinalistBadge", fields: [finalistEventBadgeId], references: [id], onDelete: SetNull)
|
|
295
|
+
winEventBadge EventBadge? @relation("CompetitionWinBadge", fields: [winEventBadgeId], references: [id], onDelete: SetNull)
|
|
282
296
|
|
|
283
297
|
@@index([bashEventId])
|
|
284
298
|
@@index([competitionType])
|
|
285
299
|
@@index([startTime])
|
|
286
300
|
@@index([endTime])
|
|
301
|
+
@@index([participationEventBadgeId])
|
|
302
|
+
@@index([finalistEventBadgeId])
|
|
303
|
+
@@index([winEventBadgeId])
|
|
287
304
|
}
|
|
288
305
|
|
|
289
306
|
model CompetitionJudgeAssignment {
|
|
@@ -373,6 +390,8 @@ model CompetitionParticipant {
|
|
|
373
390
|
name String // Display name
|
|
374
391
|
description String? @db.Text // Why they should win
|
|
375
392
|
imageUrl String? // Photo/avatar
|
|
393
|
+
/// Optional link when the entrant is a Service listing (e.g. Best Food Truck)
|
|
394
|
+
sourceServiceId String?
|
|
376
395
|
order Int @default(0) // Display order
|
|
377
396
|
voteCount Int @default(0) // Denormalized for performance
|
|
378
397
|
isEliminated Boolean @default(false) // For tournament brackets
|
|
@@ -385,6 +404,7 @@ model CompetitionParticipant {
|
|
|
385
404
|
createdAt DateTime @default(now())
|
|
386
405
|
competition Competition @relation(fields: [competitionId], references: [id], onDelete: Cascade)
|
|
387
406
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
407
|
+
sourceService Service? @relation("CompetitionParticipantSourceService", fields: [sourceServiceId], references: [id], onDelete: SetNull)
|
|
388
408
|
votes CompetitionVote[]
|
|
389
409
|
|
|
390
410
|
// Tournament bracket relations
|
|
@@ -397,6 +417,7 @@ model CompetitionParticipant {
|
|
|
397
417
|
@@index([email])
|
|
398
418
|
@@index([voteCount])
|
|
399
419
|
@@index([status])
|
|
420
|
+
@@index([sourceServiceId])
|
|
400
421
|
}
|
|
401
422
|
|
|
402
423
|
// Tournament bracket matches (1v1, heats, or battle royale via entrants join table)
|
|
@@ -994,6 +1015,7 @@ model BashEvent {
|
|
|
994
1015
|
userPromoCodeRedemption UserPromoCodeRedemption[]
|
|
995
1016
|
userReport UserReport[]
|
|
996
1017
|
vendorBookingRequests VendorBookingRequest[] @relation("VendorBookingEvent")
|
|
1018
|
+
vendorCategoryExclusivities VendorCategoryExclusivity[]
|
|
997
1019
|
vendorBids VendorBid[]
|
|
998
1020
|
media Media[] @relation("BashEventToMedia")
|
|
999
1021
|
associatedServicesReferencingMe Service[] @relation("BashEventToService")
|
|
@@ -2023,6 +2045,7 @@ enum EventBadgeAwardSource {
|
|
|
2023
2045
|
Milestone
|
|
2024
2046
|
Manual
|
|
2025
2047
|
Community
|
|
2048
|
+
Competition
|
|
2026
2049
|
}
|
|
2027
2050
|
|
|
2028
2051
|
model SideQuest {
|
|
@@ -2047,17 +2070,21 @@ model SideQuest {
|
|
|
2047
2070
|
unlockedAt DateTime?
|
|
2048
2071
|
sponsorLabel String?
|
|
2049
2072
|
sponsorServiceId String?
|
|
2073
|
+
/// When questType is Vote: the competition casting a vote completes this quest
|
|
2074
|
+
competitionId String?
|
|
2050
2075
|
createdAt DateTime @default(now())
|
|
2051
2076
|
updatedAt DateTime @updatedAt
|
|
2052
2077
|
|
|
2053
2078
|
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
2054
2079
|
eventBadge EventBadge? @relation(fields: [eventBadgeId], references: [id], onDelete: SetNull)
|
|
2080
|
+
competition Competition? @relation(fields: [competitionId], references: [id], onDelete: SetNull)
|
|
2055
2081
|
completions SideQuestCompletion[]
|
|
2056
2082
|
milestonesUnlocking SideQuestMilestone[] @relation("MilestoneUnlocksQuest")
|
|
2057
2083
|
|
|
2058
2084
|
@@index([bashEventId])
|
|
2059
2085
|
@@index([bashEventId, sortOrder])
|
|
2060
2086
|
@@index([eventBadgeId])
|
|
2087
|
+
@@index([competitionId])
|
|
2061
2088
|
}
|
|
2062
2089
|
|
|
2063
2090
|
model SideQuestCompletion {
|
|
@@ -2091,6 +2118,9 @@ model EventBadge {
|
|
|
2091
2118
|
sideQuests SideQuest[]
|
|
2092
2119
|
milestones SideQuestMilestone[]
|
|
2093
2120
|
communityGoals CommunityQuestGoal[]
|
|
2121
|
+
competitionsParticipation Competition[] @relation("CompetitionParticipationBadge")
|
|
2122
|
+
competitionsFinalist Competition[] @relation("CompetitionFinalistBadge")
|
|
2123
|
+
competitionsWin Competition[] @relation("CompetitionWinBadge")
|
|
2094
2124
|
|
|
2095
2125
|
@@index([bashEventId])
|
|
2096
2126
|
@@index([createdByUserId])
|
|
@@ -2208,6 +2238,7 @@ model EventDiscussionComment {
|
|
|
2208
2238
|
parent EventDiscussionComment? @relation("EventDiscussionReplies", fields: [parentId], references: [id], onDelete: Cascade)
|
|
2209
2239
|
replies EventDiscussionComment[] @relation("EventDiscussionReplies")
|
|
2210
2240
|
likes EventDiscussionCommentLike[]
|
|
2241
|
+
reports EventDiscussionCommentReport[]
|
|
2211
2242
|
|
|
2212
2243
|
@@index([bashEventId])
|
|
2213
2244
|
@@index([authorId])
|
|
@@ -2228,6 +2259,27 @@ model EventDiscussionCommentLike {
|
|
|
2228
2259
|
@@index([commentId])
|
|
2229
2260
|
}
|
|
2230
2261
|
|
|
2262
|
+
model EventDiscussionCommentReport {
|
|
2263
|
+
id String @id @default(cuid())
|
|
2264
|
+
userId String
|
|
2265
|
+
commentId String
|
|
2266
|
+
reason BashFeedReportReason
|
|
2267
|
+
description String? @db.Text
|
|
2268
|
+
status ReportStatus @default(Pending)
|
|
2269
|
+
reviewedBy String?
|
|
2270
|
+
reviewedAt DateTime?
|
|
2271
|
+
createdAt DateTime @default(now())
|
|
2272
|
+
|
|
2273
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
2274
|
+
comment EventDiscussionComment @relation(fields: [commentId], references: [id], onDelete: Cascade)
|
|
2275
|
+
reviewer User? @relation("ReviewedDiscussionReports", fields: [reviewedBy], references: [id])
|
|
2276
|
+
|
|
2277
|
+
@@unique([userId, commentId])
|
|
2278
|
+
@@index([commentId])
|
|
2279
|
+
@@index([status])
|
|
2280
|
+
@@index([userId])
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2231
2283
|
// === Phase 3: event experience depth ========================================
|
|
2232
2284
|
// Scalar-FK models (no Prisma relations to the large BashEvent/User models);
|
|
2233
2285
|
// DB foreign keys are added in the migration for integrity.
|
|
@@ -3766,6 +3818,8 @@ model User {
|
|
|
3766
3818
|
bashFeedComments BashFeedComment[]
|
|
3767
3819
|
bashFeedCommentLikes BashFeedCommentLike[]
|
|
3768
3820
|
eventDiscussionCommentLikes EventDiscussionCommentLike[]
|
|
3821
|
+
discussionCommentReports EventDiscussionCommentReport[]
|
|
3822
|
+
reviewedDiscussionCommentReports EventDiscussionCommentReport[] @relation("ReviewedDiscussionReports")
|
|
3769
3823
|
bashFeedRatings BashFeedRating[]
|
|
3770
3824
|
bashFeedReports BashFeedReport[]
|
|
3771
3825
|
reviewedFeedReports BashFeedReport[] @relation("ReviewedReports")
|
|
@@ -4347,6 +4401,11 @@ model Service {
|
|
|
4347
4401
|
sponsorId String? @unique
|
|
4348
4402
|
venueId String? @unique
|
|
4349
4403
|
organizationId String? @unique
|
|
4404
|
+
/// Set when this Service was created via "Also list as [complementary type]"
|
|
4405
|
+
/// (e.g. Vendors -> EventServices) as a one-time identity-field prefill copy —
|
|
4406
|
+
/// not an ongoing synced link. Used to avoid creating duplicate complementary
|
|
4407
|
+
/// drafts from repeated clicks; the two profiles are otherwise independent.
|
|
4408
|
+
complementaryOfServiceId String?
|
|
4350
4409
|
isFreeFirstListing Boolean @default(false)
|
|
4351
4410
|
monthlyPrice Decimal @default(0)
|
|
4352
4411
|
serviceListingStripeSubscriptionId String?
|
|
@@ -4377,6 +4436,8 @@ model Service {
|
|
|
4377
4436
|
exhibitorBookingRequests ExhibitorBookingRequest[] @relation("ExhibitorBookingService")
|
|
4378
4437
|
notification Notification[]
|
|
4379
4438
|
creator User? @relation("CreatedService", fields: [creatorId], references: [id])
|
|
4439
|
+
complementaryOf Service? @relation("ComplementaryService", fields: [complementaryOfServiceId], references: [id])
|
|
4440
|
+
complementaryProfiles Service[] @relation("ComplementaryService")
|
|
4380
4441
|
entertainmentService EntertainmentService? @relation(fields: [entertainmentServiceId], references: [id], onDelete: Cascade)
|
|
4381
4442
|
eventService EventService? @relation(fields: [eventServiceId], references: [id], onDelete: Cascade)
|
|
4382
4443
|
exhibitor Exhibitor? @relation(fields: [exhibitorId], references: [id], onDelete: Cascade)
|
|
@@ -4416,6 +4477,7 @@ model Service {
|
|
|
4416
4477
|
competitionPrizesSourced Prize[] @relation("PrizeSourceService")
|
|
4417
4478
|
competitionSponsorCredits CompetitionSponsor[] @relation("CompetitionSponsorSourceService")
|
|
4418
4479
|
competitionPrizeOffersSourced CompetitionPrizeOffer[] @relation("CompetitionPrizeOfferSourceService")
|
|
4480
|
+
competitionParticipantsSourced CompetitionParticipant[] @relation("CompetitionParticipantSourceService")
|
|
4419
4481
|
venueMatchCandidates VenueMatchCandidate[]
|
|
4420
4482
|
supplyLeadConversions VenueSupplyLead[] @relation("SupplyLeadConvertedService")
|
|
4421
4483
|
googleReviews GoogleReview[]
|
|
@@ -4429,6 +4491,7 @@ model Service {
|
|
|
4429
4491
|
@@index([isLicensed])
|
|
4430
4492
|
@@index([isFeaturedService, featuredReason, featuredAt])
|
|
4431
4493
|
@@index([latitude, longitude])
|
|
4494
|
+
@@index([complementaryOfServiceId])
|
|
4432
4495
|
}
|
|
4433
4496
|
|
|
4434
4497
|
model StripeAccount {
|
|
@@ -4666,6 +4729,16 @@ model Vendor {
|
|
|
4666
4729
|
tierPricing Json? // For tiered pricing: [{name: "Bronze", priceCents: 100000, description: "..."}, ...]
|
|
4667
4730
|
pricingNotes String? // Additional pricing details or terms
|
|
4668
4731
|
|
|
4732
|
+
// Guarantee & deal-terms communication (informational only — Bash does not
|
|
4733
|
+
// calculate or process any guarantee shortfall payment). Preferred/minimum
|
|
4734
|
+
// attendance for this context reuses crowdSize (crowdSizeId/AmountOfGuests)
|
|
4735
|
+
// above rather than a separate field.
|
|
4736
|
+
requiresGuarantee Boolean? @default(false)
|
|
4737
|
+
minimumRevenueGuaranteeCents Int?
|
|
4738
|
+
averageSalesGoalCents Int?
|
|
4739
|
+
openToGuaranteeNegotiation Boolean? @default(true)
|
|
4740
|
+
guaranteeNotes String? @db.Text
|
|
4741
|
+
|
|
4669
4742
|
service Service?
|
|
4670
4743
|
crowdSize AmountOfGuests? @relation(fields: [crowdSizeId], references: [id], onDelete: Cascade)
|
|
4671
4744
|
serviceRange ServiceRange? @relation(fields: [serviceRangeId], references: [id])
|
|
@@ -4740,6 +4813,9 @@ model VendorSocialMediaExpectation {
|
|
|
4740
4813
|
@@index([preferencesId])
|
|
4741
4814
|
}
|
|
4742
4815
|
|
|
4816
|
+
/// @deprecated Not written or read by any live API route — vendor-initiated offers now flow through
|
|
4817
|
+
/// the unified ServiceBookingOffer model (see ServiceBooking.isProviderInitiatedOffer). Kept only so
|
|
4818
|
+
/// existing rows aren't dropped; do not add new call sites. Slated for removal in a future migration.
|
|
4743
4819
|
model VendorBid {
|
|
4744
4820
|
id String @id @default(cuid())
|
|
4745
4821
|
vendorId String
|
|
@@ -6034,7 +6110,8 @@ model ServiceBooking {
|
|
|
6034
6110
|
totalATBCents Int
|
|
6035
6111
|
subtotalATBCents Int @default(0)
|
|
6036
6112
|
bashEventId String?
|
|
6037
|
-
isVendorBid
|
|
6113
|
+
// Renamed from isVendorBid: set whenever a ProviderToHost offer (vendor, exhibitor, OR sponsor) is accepted — not vendor-specific.
|
|
6114
|
+
isProviderInitiatedOffer Boolean @default(false) @map("isVendorBid")
|
|
6038
6115
|
vendorBidAmountCents Int?
|
|
6039
6116
|
vendorEventDetails String?
|
|
6040
6117
|
bashPointsApplied Int? // BashPoints credits applied to this booking
|
|
@@ -6204,12 +6281,22 @@ model VendorBookingRequest {
|
|
|
6204
6281
|
paidAt DateTime?
|
|
6205
6282
|
vendorResponse String?
|
|
6206
6283
|
respondedAt DateTime?
|
|
6284
|
+
/// Informational counter-offer to the vendor's stated guarantee terms — not a payment obligation.
|
|
6285
|
+
proposedGuaranteeCents Int?
|
|
6286
|
+
proposedGuaranteeNotes String? @db.Text
|
|
6287
|
+
/// Host recruiting incentive offered at request-creation time — see VendorCategoryExclusivity.
|
|
6288
|
+
/// Requires bashEventId (exclusivity is scoped to one event) and a Pro+ membership on the host.
|
|
6289
|
+
exclusiveCategoryOffered Boolean? @default(false)
|
|
6290
|
+
/// Vended product/service categories (VendedProductType/VendorServiceType values) the host
|
|
6291
|
+
/// chose to offer exclusivity for — not necessarily every category the vendor lists.
|
|
6292
|
+
exclusiveCategories String[] @default([])
|
|
6207
6293
|
serviceMessages ServiceBookingMessage[] @relation("ServiceBookingMessages")
|
|
6208
6294
|
messages VendorBookingMessage[]
|
|
6209
6295
|
bashEvent BashEvent? @relation("VendorBookingEvent", fields: [bashEventId], references: [id])
|
|
6210
6296
|
hostUser User @relation("VendorBookingHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
6211
6297
|
vendorService Service @relation("VendorBookingService", fields: [vendorServiceId], references: [id], onDelete: Cascade)
|
|
6212
6298
|
offers ServiceBookingOffer[]
|
|
6299
|
+
categoryExclusivities VendorCategoryExclusivity[]
|
|
6213
6300
|
|
|
6214
6301
|
@@index([hostUserId])
|
|
6215
6302
|
@@index([vendorServiceId])
|
|
@@ -6217,6 +6304,25 @@ model VendorBookingRequest {
|
|
|
6217
6304
|
@@index([createdAt])
|
|
6218
6305
|
}
|
|
6219
6306
|
|
|
6307
|
+
/// Records a category lock granted when a host's VendorBookingRequest with
|
|
6308
|
+
/// exclusiveCategoryOffered=true is accepted. One row per (event, category) the
|
|
6309
|
+
/// host chose — a multi-category vendor does not lock every category it lists,
|
|
6310
|
+
/// only the ones the host explicitly offered exclusivity for. Released (deleted)
|
|
6311
|
+
/// if the underlying accepted request is later cancelled.
|
|
6312
|
+
model VendorCategoryExclusivity {
|
|
6313
|
+
id String @id @default(cuid())
|
|
6314
|
+
bashEventId String
|
|
6315
|
+
/// A VendedProductType or VendorServiceType value.
|
|
6316
|
+
category String
|
|
6317
|
+
vendorBookingRequestId String
|
|
6318
|
+
createdAt DateTime @default(now())
|
|
6319
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
6320
|
+
vendorBookingRequest VendorBookingRequest @relation(fields: [vendorBookingRequestId], references: [id], onDelete: Cascade)
|
|
6321
|
+
|
|
6322
|
+
@@unique([vendorBookingRequestId, category])
|
|
6323
|
+
@@index([bashEventId, category])
|
|
6324
|
+
}
|
|
6325
|
+
|
|
6220
6326
|
model VendorBookingMessage {
|
|
6221
6327
|
id String @id @default(cuid())
|
|
6222
6328
|
createdAt DateTime @default(now())
|
|
@@ -6775,6 +6881,9 @@ enum NotificationType {
|
|
|
6775
6881
|
CompetitionParticipantRequested // Someone requested to join your competition
|
|
6776
6882
|
CompetitionParticipantApproved // Host approved your request to compete
|
|
6777
6883
|
CompetitionParticipantRejected // Host rejected your request
|
|
6884
|
+
CompetitionPrizeOfferReceived // Host: someone offered a prize contribution
|
|
6885
|
+
CompetitionPrizeOfferAccepted // Offeror: host accepted your prize offer
|
|
6886
|
+
CompetitionPrizeOfferDeclined // Offeror: host declined your prize offer
|
|
6778
6887
|
BashRating
|
|
6779
6888
|
BashRatingMilestone
|
|
6780
6889
|
BashFeedPost
|
|
@@ -9256,6 +9365,10 @@ enum CreditSourceType {
|
|
|
9256
9365
|
SideQuestCompletion // Completing a Side Quest on a bash
|
|
9257
9366
|
SideQuestMilestone // Hitting a Side Quest milestone (e.g. complete N quests)
|
|
9258
9367
|
CommunityQuestUnlock // Digital reward when a Community Quest goal unlocks
|
|
9368
|
+
CompetitionVote // Completing a Vote-type Side Quest by casting a competition vote
|
|
9369
|
+
CompetitionParticipation // Adventures reward for becoming a competition participant
|
|
9370
|
+
CompetitionFinalist // Adventures reward for reaching a finalist placement
|
|
9371
|
+
CompetitionWin // Adventures reward for winning a competition prize
|
|
9259
9372
|
}
|
|
9260
9373
|
|
|
9261
9374
|
enum ReferralTier {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertAllowedJudgingTypeForNew,
|
|
3
|
+
DEFAULT_COMPETITION_JUDGING_TYPE,
|
|
4
|
+
DISALLOWED_JUDGING_TYPE_MESSAGE,
|
|
5
|
+
getJudgingTypeDescription,
|
|
6
|
+
getJudgingTypeLabel,
|
|
7
|
+
isDisallowedJudgingTypeForNew,
|
|
8
|
+
} from "../competitionValidation";
|
|
9
|
+
import { JudgingType } from "../definitions";
|
|
10
|
+
|
|
11
|
+
describe("competitionValidation", () => {
|
|
12
|
+
it("defaults new competitions to HOST_CHOOSES", () => {
|
|
13
|
+
expect(DEFAULT_COMPETITION_JUDGING_TYPE).toBe(JudgingType.HOST_CHOOSES);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("disallows METRICS and FIRST_COME for new competitions", () => {
|
|
17
|
+
expect(isDisallowedJudgingTypeForNew(JudgingType.METRICS)).toBe(true);
|
|
18
|
+
expect(isDisallowedJudgingTypeForNew(JudgingType.FIRST_COME)).toBe(true);
|
|
19
|
+
expect(isDisallowedJudgingTypeForNew(JudgingType.RANDOM_DRAW)).toBe(true);
|
|
20
|
+
expect(isDisallowedJudgingTypeForNew(JudgingType.HIGHEST_BID)).toBe(true);
|
|
21
|
+
expect(isDisallowedJudgingTypeForNew(JudgingType.HOST_CHOOSES)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("throws with the updated disallowed judging message", () => {
|
|
25
|
+
expect(() =>
|
|
26
|
+
assertAllowedJudgingTypeForNew(JudgingType.FIRST_COME)
|
|
27
|
+
).toThrow(DISALLOWED_JUDGING_TYPE_MESSAGE);
|
|
28
|
+
expect(DISALLOWED_JUDGING_TYPE_MESSAGE).toMatch(/metrics/i);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("getJudgingTypeLabel returns TBD for null", () => {
|
|
32
|
+
expect(getJudgingTypeLabel(null)).toBe("TBD");
|
|
33
|
+
expect(getJudgingTypeLabel(undefined)).toBe("TBD");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("getJudgingTypeDescription covers AUDIENCE_VOTE and legacy types", () => {
|
|
37
|
+
expect(getJudgingTypeDescription(JudgingType.AUDIENCE_VOTE).length).toBeGreaterThan(
|
|
38
|
+
0
|
|
39
|
+
);
|
|
40
|
+
expect(getJudgingTypeDescription(JudgingType.METRICS).length).toBeGreaterThan(0);
|
|
41
|
+
expect(getJudgingTypeLabel(JudgingType.FIRST_COME)).toMatch(/first come/i);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
isUnpaidPayAtDoorTicket,
|
|
11
11
|
TICKET_PURCHASE_TYPE_COMP,
|
|
12
12
|
TICKET_PURCHASE_TYPE_PAY_AT_DOOR,
|
|
13
|
+
COMPLEMENTARY_SERVICE_TYPE,
|
|
14
|
+
ServiceTypes,
|
|
13
15
|
} from "../definitions.js";
|
|
14
16
|
|
|
15
17
|
describe("definitions helpers", () => {
|
|
@@ -133,4 +135,18 @@ describe("definitions helpers", () => {
|
|
|
133
135
|
).toBe(false);
|
|
134
136
|
});
|
|
135
137
|
});
|
|
138
|
+
|
|
139
|
+
describe("COMPLEMENTARY_SERVICE_TYPE", () => {
|
|
140
|
+
test("pairs Vendors and EventServices both ways", () => {
|
|
141
|
+
expect(COMPLEMENTARY_SERVICE_TYPE[ServiceTypes.Vendors]).toBe(
|
|
142
|
+
ServiceTypes.EventServices
|
|
143
|
+
);
|
|
144
|
+
expect(COMPLEMENTARY_SERVICE_TYPE[ServiceTypes.EventServices]).toBe(
|
|
145
|
+
ServiceTypes.Vendors
|
|
146
|
+
);
|
|
147
|
+
expect(
|
|
148
|
+
COMPLEMENTARY_SERVICE_TYPE[ServiceTypes.EntertainmentServices]
|
|
149
|
+
).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
136
152
|
});
|
|
@@ -65,6 +65,24 @@ describe("membershipDefinitions helpers", () => {
|
|
|
65
65
|
);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
test("Pro unlocks vendor category exclusivity", () => {
|
|
69
|
+
expect(
|
|
70
|
+
hasFeatureAccess(
|
|
71
|
+
MembershipTier.Pro,
|
|
72
|
+
MEMBERSHIP_FEATURES.VENDOR_CATEGORY_EXCLUSIVITY
|
|
73
|
+
)
|
|
74
|
+
).toBe(true);
|
|
75
|
+
expect(
|
|
76
|
+
hasFeatureAccess(
|
|
77
|
+
MembershipTier.Premium,
|
|
78
|
+
MEMBERSHIP_FEATURES.VENDOR_CATEGORY_EXCLUSIVITY
|
|
79
|
+
)
|
|
80
|
+
).toBe(false);
|
|
81
|
+
expect(
|
|
82
|
+
getRequiredTierForFeature(MEMBERSHIP_FEATURES.VENDOR_CATEGORY_EXCLUSIVITY)
|
|
83
|
+
).toBe(MembershipTier.Pro);
|
|
84
|
+
});
|
|
85
|
+
|
|
68
86
|
test("unknown feature returns false", () => {
|
|
69
87
|
expect(hasFeatureAccess(MembershipTier.Legend, "not_a_feature")).toBe(false);
|
|
70
88
|
});
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
filterSideQuestIdeas,
|
|
3
|
+
SIDE_QUEST_IDEAS,
|
|
4
|
+
SIDE_QUEST_PHASE1_TYPES,
|
|
5
|
+
SIDE_QUEST_TYPE_META,
|
|
6
|
+
} from "../sideQuestIdeas";
|
|
2
7
|
|
|
3
8
|
describe("sideQuestIdeas", () => {
|
|
4
9
|
it("includes festival and market templates", () => {
|
|
@@ -13,4 +18,9 @@ describe("sideQuestIdeas", () => {
|
|
|
13
18
|
expect(hits.length).toBeGreaterThan(0);
|
|
14
19
|
expect(hits.every((h) => JSON.stringify(h).toLowerCase().includes("transit") || h.category === "Transit")).toBe(true);
|
|
15
20
|
});
|
|
21
|
+
|
|
22
|
+
it("includes Vote in phase 1 types", () => {
|
|
23
|
+
expect(SIDE_QUEST_PHASE1_TYPES).toContain("Vote");
|
|
24
|
+
expect(SIDE_QUEST_TYPE_META.Vote.phase1).toBe(true);
|
|
25
|
+
});
|
|
16
26
|
});
|
|
@@ -139,6 +139,13 @@ describe("status enum value sets", () => {
|
|
|
139
139
|
"ReferralCodeUsed",
|
|
140
140
|
"VenueLoyaltyRedemption",
|
|
141
141
|
"PartnerPromoRedemption",
|
|
142
|
+
"SideQuestCompletion",
|
|
143
|
+
"SideQuestMilestone",
|
|
144
|
+
"CommunityQuestUnlock",
|
|
145
|
+
"CompetitionVote",
|
|
146
|
+
"CompetitionParticipation",
|
|
147
|
+
"CompetitionFinalist",
|
|
148
|
+
"CompetitionWin",
|
|
142
149
|
].sort(),
|
|
143
150
|
);
|
|
144
151
|
});
|
|
@@ -17,14 +17,20 @@ export function parseJudgingType(
|
|
|
17
17
|
return value as JudgingType;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Judging modes disallowed for new competitions: lottery / auction-adjacent
|
|
22
|
+
* (RANDOM_DRAW, HIGHEST_BID), plus METRICS and FIRST_COME which have no
|
|
23
|
+
* winner-resolution implementation yet (dead ends for hosts).
|
|
24
|
+
*/
|
|
21
25
|
export const DISALLOWED_JUDGING_TYPES_FOR_NEW: ReadonlySet<JudgingType> = new Set([
|
|
22
26
|
JudgingType.RANDOM_DRAW,
|
|
23
27
|
JudgingType.HIGHEST_BID,
|
|
28
|
+
JudgingType.METRICS,
|
|
29
|
+
JudgingType.FIRST_COME,
|
|
24
30
|
]);
|
|
25
31
|
|
|
26
32
|
export const DISALLOWED_JUDGING_TYPE_MESSAGE =
|
|
27
|
-
"Random draw
|
|
33
|
+
"Random draw, highest-bid, metrics, and first-come judging are not supported on Bash yet. Use host choice, skill judging, audience vote, or tournament bracket.";
|
|
28
34
|
|
|
29
35
|
export function isDisallowedJudgingTypeForNew(
|
|
30
36
|
judgingType: JudgingType | null | undefined
|
|
@@ -45,6 +51,54 @@ export function assertAllowedJudgingTypeForNew(
|
|
|
45
51
|
export const DEFAULT_COMPETITION_JUDGING_TYPE: JudgingType =
|
|
46
52
|
JudgingType.HOST_CHOOSES;
|
|
47
53
|
|
|
54
|
+
/** Single source of truth for judging-type copy — shared by the host wizard and guest-facing display. */
|
|
55
|
+
export const JUDGING_TYPE_LABELS: Record<JudgingType, string> = {
|
|
56
|
+
[JudgingType.RANDOM_DRAW]: "Drawing (legacy)",
|
|
57
|
+
[JudgingType.HIGHEST_BID]: "Highest Bid (legacy)",
|
|
58
|
+
[JudgingType.HOST_CHOOSES]: "Host Selection",
|
|
59
|
+
[JudgingType.JUDGES]: "Panel of Judges",
|
|
60
|
+
[JudgingType.AUDIENCE_VOTE]: "Audience Vote",
|
|
61
|
+
[JudgingType.METRICS]: "Performance Metrics",
|
|
62
|
+
[JudgingType.FIRST_COME]: "First Come First Serve",
|
|
63
|
+
[JudgingType.BRACKET_TOURNAMENT]: "Bracket Tournament",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const JUDGING_TYPE_DESCRIPTIONS: Record<JudgingType, string> = {
|
|
67
|
+
[JudgingType.RANDOM_DRAW]:
|
|
68
|
+
"This competition uses a legacy random drawing configuration. New competitions on Bash use host choice, skill judging, audience vote, or tournament formats.",
|
|
69
|
+
[JudgingType.HIGHEST_BID]:
|
|
70
|
+
"Legacy auction-style configuration. New competitions on Bash do not support highest-bid judging.",
|
|
71
|
+
[JudgingType.HOST_CHOOSES]:
|
|
72
|
+
"The event host will personally select the winner(s) based on their criteria.",
|
|
73
|
+
[JudgingType.JUDGES]:
|
|
74
|
+
"A panel of judges will evaluate participants based on specific criteria and select the winner(s).",
|
|
75
|
+
[JudgingType.AUDIENCE_VOTE]:
|
|
76
|
+
"Attendees will vote to determine the winner(s). The participant(s) with the most votes win!",
|
|
77
|
+
[JudgingType.METRICS]:
|
|
78
|
+
"Winners are determined by measurable performance metrics and objective criteria.",
|
|
79
|
+
[JudgingType.FIRST_COME]:
|
|
80
|
+
"Winners are determined by order of participation - first to sign up or complete wins!",
|
|
81
|
+
[JudgingType.BRACKET_TOURNAMENT]:
|
|
82
|
+
"Participants compete head-to-head in a tournament bracket. Winners advance through rounds until a champion is determined.",
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export function getJudgingTypeLabel(
|
|
86
|
+
judgingType: JudgingType | null | undefined
|
|
87
|
+
): string {
|
|
88
|
+
if (!judgingType) return "TBD";
|
|
89
|
+
return JUDGING_TYPE_LABELS[judgingType] ?? "TBD";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getJudgingTypeDescription(
|
|
93
|
+
judgingType: JudgingType | null | undefined
|
|
94
|
+
): string {
|
|
95
|
+
if (!judgingType) return "The judging method has not been determined yet.";
|
|
96
|
+
return (
|
|
97
|
+
JUDGING_TYPE_DESCRIPTIONS[judgingType] ??
|
|
98
|
+
"The judging method has not been determined yet."
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
48
102
|
export function normalizeJudgingTypeForSave(
|
|
49
103
|
judgingType: JudgingType | null | undefined,
|
|
50
104
|
isExistingLegacy: boolean
|
package/src/definitions.ts
CHANGED
|
@@ -44,6 +44,35 @@ import type { UtmFields } from "./utmAttribution.js";
|
|
|
44
44
|
* values — importing them from `@prisma/client` yields `undefined` and crashes on first
|
|
45
45
|
* property access. These mirrors match the Prisma schema / full client enum strings.
|
|
46
46
|
*/
|
|
47
|
+
/**
|
|
48
|
+
* "Also list as [complementary type]" — a one-time identity-field prefill copy
|
|
49
|
+
* (see Service.complementaryOfServiceId), not an ongoing synced link. Scoped to
|
|
50
|
+
* the Vendors <-> EventServices pairing only (e.g. a food truck that vends
|
|
51
|
+
* publicly at festivals and is also paid a flat fee at private weddings).
|
|
52
|
+
*/
|
|
53
|
+
export const COMPLEMENTARY_SERVICE_TYPE: Partial<
|
|
54
|
+
Record<ServiceTypes, ServiceTypes>
|
|
55
|
+
> = {
|
|
56
|
+
[ServiceTypes.Vendors]: ServiceTypes.EventServices,
|
|
57
|
+
[ServiceTypes.EventServices]: ServiceTypes.Vendors,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Human-readable label for a ServiceTypes value — the raw enum ("EventServices")
|
|
62
|
+
* is not fit for display copy. Only covers the values COMPLEMENTARY_SERVICE_TYPE
|
|
63
|
+
* can produce; extend as more callers need display labels for other types.
|
|
64
|
+
*/
|
|
65
|
+
export const SERVICE_TYPE_DISPLAY_LABEL: Partial<Record<ServiceTypes, string>> =
|
|
66
|
+
{
|
|
67
|
+
[ServiceTypes.Vendors]: "Vendor",
|
|
68
|
+
[ServiceTypes.EventServices]: "Event Service",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** Picks "a"/"an" for the given word, e.g. for "a Vendor" vs "an Event Service". */
|
|
72
|
+
export function articleFor(word: string): "a" | "an" {
|
|
73
|
+
return /^[aeiou]/i.test(word) ? "an" : "a";
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
export const ServiceBookingOfferDirection = {
|
|
48
77
|
HostToProvider: "HostToProvider",
|
|
49
78
|
ProviderToHost: "ProviderToHost",
|
|
@@ -1161,7 +1190,17 @@ export const YearsOfExperienceToString: { [key in YearsOfExperienceEnum]: string
|
|
|
1161
1190
|
[YearsOfExperience.FivePlusYears]: "More than five years",
|
|
1162
1191
|
};
|
|
1163
1192
|
|
|
1164
|
-
export { BracketType } from "./mirroredPrismaEnums.js";
|
|
1193
|
+
export { BracketType, ServiceBookingSettlementStatusLabel } from "./mirroredPrismaEnums.js";
|
|
1194
|
+
|
|
1195
|
+
// Re-exported for backward compatibility during migration — see serviceTypeLabels.ts/targetAudienceLabels.ts
|
|
1196
|
+
// for the canonical, consolidated source (following the BracketType precedent above).
|
|
1197
|
+
export { SERVICE_TYPE_LABELS } from "./serviceTypeLabels.js";
|
|
1198
|
+
export {
|
|
1199
|
+
AgeRangeLabel,
|
|
1200
|
+
EducationLabel,
|
|
1201
|
+
GenderLabel,
|
|
1202
|
+
OccupationLabel,
|
|
1203
|
+
} from "./targetAudienceLabels.js";
|
|
1165
1204
|
|
|
1166
1205
|
// Export Prisma enums for use in frontend
|
|
1167
1206
|
export {
|