@bash-app/bash-common 30.308.0 → 30.310.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__/sideQuestTypes.test.d.ts +2 -0
- package/dist/__tests__/sideQuestTypes.test.d.ts.map +1 -0
- package/dist/__tests__/sideQuestTypes.test.js +29 -0
- package/dist/__tests__/sideQuestTypes.test.js.map +1 -0
- 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 +7 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +10 -0
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +51 -11
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +5 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -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 +4 -0
- package/dist/mirroredPrismaEnums.d.ts.map +1 -1
- package/dist/mirroredPrismaEnums.js +4 -0
- package/dist/mirroredPrismaEnums.js.map +1 -1
- 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 +116 -0
- package/dist/sideQuestTypes.d.ts.map +1 -0
- package/dist/sideQuestTypes.js +67 -0
- package/dist/sideQuestTypes.js.map +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +111 -0
- 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__/sideQuestTypes.test.ts +44 -0
- package/src/__tests__/statusEnums.test.ts +7 -0
- package/src/competitionValidation.ts +56 -2
- package/src/definitions.ts +13 -0
- package/src/extendedSchemas.ts +23 -11
- package/src/index.ts +1 -0
- package/src/membershipDefinitions.ts +3 -0
- package/src/mirroredPrismaEnums.ts +4 -0
- package/src/sideQuestIdeas.ts +2 -2
- package/src/sideQuestTypes.ts +190 -0
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])
|
|
@@ -6204,12 +6277,22 @@ model VendorBookingRequest {
|
|
|
6204
6277
|
paidAt DateTime?
|
|
6205
6278
|
vendorResponse String?
|
|
6206
6279
|
respondedAt DateTime?
|
|
6280
|
+
/// Informational counter-offer to the vendor's stated guarantee terms — not a payment obligation.
|
|
6281
|
+
proposedGuaranteeCents Int?
|
|
6282
|
+
proposedGuaranteeNotes String? @db.Text
|
|
6283
|
+
/// Host recruiting incentive offered at request-creation time — see VendorCategoryExclusivity.
|
|
6284
|
+
/// Requires bashEventId (exclusivity is scoped to one event) and a Pro+ membership on the host.
|
|
6285
|
+
exclusiveCategoryOffered Boolean? @default(false)
|
|
6286
|
+
/// Vended product/service categories (VendedProductType/VendorServiceType values) the host
|
|
6287
|
+
/// chose to offer exclusivity for — not necessarily every category the vendor lists.
|
|
6288
|
+
exclusiveCategories String[] @default([])
|
|
6207
6289
|
serviceMessages ServiceBookingMessage[] @relation("ServiceBookingMessages")
|
|
6208
6290
|
messages VendorBookingMessage[]
|
|
6209
6291
|
bashEvent BashEvent? @relation("VendorBookingEvent", fields: [bashEventId], references: [id])
|
|
6210
6292
|
hostUser User @relation("VendorBookingHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
6211
6293
|
vendorService Service @relation("VendorBookingService", fields: [vendorServiceId], references: [id], onDelete: Cascade)
|
|
6212
6294
|
offers ServiceBookingOffer[]
|
|
6295
|
+
categoryExclusivities VendorCategoryExclusivity[]
|
|
6213
6296
|
|
|
6214
6297
|
@@index([hostUserId])
|
|
6215
6298
|
@@index([vendorServiceId])
|
|
@@ -6217,6 +6300,25 @@ model VendorBookingRequest {
|
|
|
6217
6300
|
@@index([createdAt])
|
|
6218
6301
|
}
|
|
6219
6302
|
|
|
6303
|
+
/// Records a category lock granted when a host's VendorBookingRequest with
|
|
6304
|
+
/// exclusiveCategoryOffered=true is accepted. One row per (event, category) the
|
|
6305
|
+
/// host chose — a multi-category vendor does not lock every category it lists,
|
|
6306
|
+
/// only the ones the host explicitly offered exclusivity for. Released (deleted)
|
|
6307
|
+
/// if the underlying accepted request is later cancelled.
|
|
6308
|
+
model VendorCategoryExclusivity {
|
|
6309
|
+
id String @id @default(cuid())
|
|
6310
|
+
bashEventId String
|
|
6311
|
+
/// A VendedProductType or VendorServiceType value.
|
|
6312
|
+
category String
|
|
6313
|
+
vendorBookingRequestId String
|
|
6314
|
+
createdAt DateTime @default(now())
|
|
6315
|
+
bashEvent BashEvent @relation(fields: [bashEventId], references: [id], onDelete: Cascade)
|
|
6316
|
+
vendorBookingRequest VendorBookingRequest @relation(fields: [vendorBookingRequestId], references: [id], onDelete: Cascade)
|
|
6317
|
+
|
|
6318
|
+
@@unique([vendorBookingRequestId, category])
|
|
6319
|
+
@@index([bashEventId, category])
|
|
6320
|
+
}
|
|
6321
|
+
|
|
6220
6322
|
model VendorBookingMessage {
|
|
6221
6323
|
id String @id @default(cuid())
|
|
6222
6324
|
createdAt DateTime @default(now())
|
|
@@ -6775,6 +6877,9 @@ enum NotificationType {
|
|
|
6775
6877
|
CompetitionParticipantRequested // Someone requested to join your competition
|
|
6776
6878
|
CompetitionParticipantApproved // Host approved your request to compete
|
|
6777
6879
|
CompetitionParticipantRejected // Host rejected your request
|
|
6880
|
+
CompetitionPrizeOfferReceived // Host: someone offered a prize contribution
|
|
6881
|
+
CompetitionPrizeOfferAccepted // Offeror: host accepted your prize offer
|
|
6882
|
+
CompetitionPrizeOfferDeclined // Offeror: host declined your prize offer
|
|
6778
6883
|
BashRating
|
|
6779
6884
|
BashRatingMilestone
|
|
6780
6885
|
BashFeedPost
|
|
@@ -6814,6 +6919,8 @@ enum NotificationType {
|
|
|
6814
6919
|
FeaturedGuestInviteAccepted // Host: guest accepted featured guest invite
|
|
6815
6920
|
FeaturedGuestInviteDeclined // Host: guest declined featured guest invite
|
|
6816
6921
|
PartnerPromotionBlast // Attendee: partner on-site promo deal at an event
|
|
6922
|
+
SideQuestRevealed // Attendee: host revealed a secret Side Quest
|
|
6923
|
+
CommunityQuestUnlocked // Attendee: community quest goal unlocked
|
|
6817
6924
|
}
|
|
6818
6925
|
|
|
6819
6926
|
/// Opt-in reminder for a scheduled tier release (v1.5).
|
|
@@ -9254,6 +9361,10 @@ enum CreditSourceType {
|
|
|
9254
9361
|
SideQuestCompletion // Completing a Side Quest on a bash
|
|
9255
9362
|
SideQuestMilestone // Hitting a Side Quest milestone (e.g. complete N quests)
|
|
9256
9363
|
CommunityQuestUnlock // Digital reward when a Community Quest goal unlocks
|
|
9364
|
+
CompetitionVote // Completing a Vote-type Side Quest by casting a competition vote
|
|
9365
|
+
CompetitionParticipation // Adventures reward for becoming a competition participant
|
|
9366
|
+
CompetitionFinalist // Adventures reward for reaching a finalist placement
|
|
9367
|
+
CompetitionWin // Adventures reward for winning a competition prize
|
|
9257
9368
|
}
|
|
9258
9369
|
|
|
9259
9370
|
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
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildSideQuestDeepLink,
|
|
3
|
+
buildSideQuestLegacyPayload,
|
|
4
|
+
parseSideQuestScanPayload,
|
|
5
|
+
} from "../sideQuestTypes.js";
|
|
6
|
+
|
|
7
|
+
describe("sideQuestTypes deep links", () => {
|
|
8
|
+
it("builds HTTPS ticket-details URLs", () => {
|
|
9
|
+
expect(
|
|
10
|
+
buildSideQuestDeepLink({
|
|
11
|
+
bashEventId: "evt1",
|
|
12
|
+
questId: "q1",
|
|
13
|
+
code: "ab12",
|
|
14
|
+
frontendOrigin: "https://bash.community",
|
|
15
|
+
})
|
|
16
|
+
).toBe("https://bash.community/ticket-details/evt1?quest=q1&code=AB12");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("parses HTTPS deep links", () => {
|
|
20
|
+
expect(
|
|
21
|
+
parseSideQuestScanPayload(
|
|
22
|
+
"https://bash.community/ticket-details/evt1?quest=q1&code=XY99"
|
|
23
|
+
)
|
|
24
|
+
).toEqual({
|
|
25
|
+
bashEventId: "evt1",
|
|
26
|
+
questId: "q1",
|
|
27
|
+
code: "XY99",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("parses legacy bash-side-quest payloads", () => {
|
|
32
|
+
expect(
|
|
33
|
+
parseSideQuestScanPayload(buildSideQuestLegacyPayload("evt1", "q1", "ab12"))
|
|
34
|
+
).toEqual({
|
|
35
|
+
bashEventId: "evt1",
|
|
36
|
+
questId: "q1",
|
|
37
|
+
code: "AB12",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("parses plain codes", () => {
|
|
42
|
+
expect(parseSideQuestScanPayload("ab12")).toEqual({ code: "AB12" });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -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,19 @@ 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
|
+
|
|
47
60
|
export const ServiceBookingOfferDirection = {
|
|
48
61
|
HostToProvider: "HostToProvider",
|
|
49
62
|
ProviderToHost: "ProviderToHost",
|
package/src/extendedSchemas.ts
CHANGED
|
@@ -270,14 +270,17 @@ export const PRIVATE_USER_ACCOUNT_TO_SELECT = {
|
|
|
270
270
|
|
|
271
271
|
export interface SponsoredEventExt extends SponsoredEvent {}
|
|
272
272
|
|
|
273
|
+
/** Minimal service identity used wherever a competition prize/offer just needs to point at a listing. */
|
|
274
|
+
export interface ServiceRefLite {
|
|
275
|
+
id: string;
|
|
276
|
+
serviceName: string | null;
|
|
277
|
+
coverPhoto: string | null;
|
|
278
|
+
}
|
|
279
|
+
|
|
273
280
|
export interface PrizeExt extends Prize {
|
|
274
281
|
winner?: PublicUser | null;
|
|
275
282
|
contributor?: PublicUser | null;
|
|
276
|
-
sourceService?:
|
|
277
|
-
id: string;
|
|
278
|
-
serviceName: string | null;
|
|
279
|
-
coverPhoto: string | null;
|
|
280
|
-
} | null;
|
|
283
|
+
sourceService?: ServiceRefLite | null;
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
export interface CompetitionPrizeOfferExt {
|
|
@@ -296,11 +299,7 @@ export interface CompetitionPrizeOfferExt {
|
|
|
296
299
|
createdAt: Date | string;
|
|
297
300
|
respondedAt?: Date | string | null;
|
|
298
301
|
offeredBy?: PublicUser;
|
|
299
|
-
sourceService?:
|
|
300
|
-
id: string;
|
|
301
|
-
serviceName: string | null;
|
|
302
|
-
coverPhoto: string | null;
|
|
303
|
-
} | null;
|
|
302
|
+
sourceService?: ServiceRefLite | null;
|
|
304
303
|
targetPrize?: {
|
|
305
304
|
id: string;
|
|
306
305
|
name: string;
|
|
@@ -309,6 +308,11 @@ export interface CompetitionPrizeOfferExt {
|
|
|
309
308
|
} | null;
|
|
310
309
|
}
|
|
311
310
|
|
|
311
|
+
/** One competition participant row + resolved user; shared by CompetitionExt.participants and the host roster endpoint. */
|
|
312
|
+
export interface CompetitionParticipantExt extends CompetitionParticipant {
|
|
313
|
+
user?: Pick<PublicUser, "uploadedImage" | "image"> | null;
|
|
314
|
+
}
|
|
315
|
+
|
|
312
316
|
/** Competition row + relations; Override keeps new columns visible before consumers refresh Prisma. */
|
|
313
317
|
export type CompetitionExt = Override<
|
|
314
318
|
Competition,
|
|
@@ -320,7 +324,7 @@ export type CompetitionExt = Override<
|
|
|
320
324
|
prizes: PrizeExt[];
|
|
321
325
|
}
|
|
322
326
|
> & {
|
|
323
|
-
participants?:
|
|
327
|
+
participants?: CompetitionParticipantExt[];
|
|
324
328
|
votes?: CompetitionVote[];
|
|
325
329
|
entryTicketTier?: TicketTier | null;
|
|
326
330
|
sponsor?: CompetitionSponsor[];
|
|
@@ -848,6 +852,11 @@ export const SERVICE_DATA_TO_INCLUDE = {
|
|
|
848
852
|
sportsProfile: true,
|
|
849
853
|
}
|
|
850
854
|
}, // Include organization data with member counts
|
|
855
|
+
// Lightweight — just enough for the UI to know a complementary Vendor <-> EventServices
|
|
856
|
+
// profile already exists (see Part B "also list as" flow) without a second fetch.
|
|
857
|
+
complementaryProfiles: {
|
|
858
|
+
select: { id: true, serviceType: true },
|
|
859
|
+
},
|
|
851
860
|
// bookings: {
|
|
852
861
|
// include: SERVICE_BOOKING_PRIVATE_DATA_TO_INCLUDE, //make sure only to include owned bookedDays
|
|
853
862
|
// },
|
|
@@ -1101,6 +1110,9 @@ export interface ServiceExt extends Service {
|
|
|
1101
1110
|
associatedBashesReferencingMe?: AssociatedBashExt[];
|
|
1102
1111
|
associatedServicesReferencingMe?: AssociatedServiceExt[];
|
|
1103
1112
|
|
|
1113
|
+
/** Reverse side of `complementaryOfServiceId` — the "also list as" profile(s) this service already spawned. */
|
|
1114
|
+
complementaryProfiles?: Pick<Service, "id" | "serviceType">[];
|
|
1115
|
+
|
|
1104
1116
|
// googleReviews: GoogleReview[];
|
|
1105
1117
|
|
|
1106
1118
|
// For availability filtering - matches Prisma relation name "bookings"
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./stripeListingSubscriptionMessages.js";
|
|
|
8
8
|
export * from "./extendedSchemas.js";
|
|
9
9
|
export * from "./competitionIdeas.js";
|
|
10
10
|
export * from "./sideQuestIdeas.js";
|
|
11
|
+
export * from "./sideQuestTypes.js";
|
|
11
12
|
export * from "./icebreakerPrompts.js";
|
|
12
13
|
export * from "./competitionValidation.js";
|
|
13
14
|
export * from "./groupTicketUtils.js";
|
|
@@ -813,6 +813,8 @@ export const MEMBERSHIP_FEATURES = {
|
|
|
813
813
|
SEARCH_PRIORITY: 'search_priority',
|
|
814
814
|
BASIC_ANALYTICS: 'basic_analytics',
|
|
815
815
|
VENDOR_DIRECTORY: 'vendor_directory',
|
|
816
|
+
/** Host-side perk: offer a vendor exclusive-category status for an event when sending a booking request. */
|
|
817
|
+
VENDOR_CATEGORY_EXCLUSIVITY: 'vendor_category_exclusivity',
|
|
816
818
|
TIER_BADGE: 'tier_badge',
|
|
817
819
|
|
|
818
820
|
// Elite features
|
|
@@ -854,6 +856,7 @@ export function hasFeatureAccess(userTier: MembershipTier, feature: string): boo
|
|
|
854
856
|
case MEMBERSHIP_FEATURES.SEARCH_PRIORITY:
|
|
855
857
|
case MEMBERSHIP_FEATURES.BASIC_ANALYTICS:
|
|
856
858
|
case MEMBERSHIP_FEATURES.VENDOR_DIRECTORY:
|
|
859
|
+
case MEMBERSHIP_FEATURES.VENDOR_CATEGORY_EXCLUSIVITY:
|
|
857
860
|
case MEMBERSHIP_FEATURES.TIER_BADGE:
|
|
858
861
|
return tierIndex >= 2; // Pro+
|
|
859
862
|
|
|
@@ -180,6 +180,10 @@ export const CreditSourceType = {
|
|
|
180
180
|
SideQuestCompletion: "SideQuestCompletion",
|
|
181
181
|
SideQuestMilestone: "SideQuestMilestone",
|
|
182
182
|
CommunityQuestUnlock: "CommunityQuestUnlock",
|
|
183
|
+
CompetitionVote: "CompetitionVote",
|
|
184
|
+
CompetitionParticipation: "CompetitionParticipation",
|
|
185
|
+
CompetitionFinalist: "CompetitionFinalist",
|
|
186
|
+
CompetitionWin: "CompetitionWin",
|
|
183
187
|
} as const satisfies Record<CreditSourceTypeEnum, CreditSourceTypeEnum>;
|
|
184
188
|
|
|
185
189
|
export type CreditSourceType = CreditSourceTypeEnum;
|