@bash-app/bash-common 30.368.0 → 30.371.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/accessScreening.js +1 -0
- package/dist/aiApproval.js +1 -0
- package/dist/aiEventPlan.js +1 -0
- package/dist/bashFeedTypes.js +1 -0
- package/dist/bookingIntent.js +1 -0
- package/dist/causeTags.js +1 -0
- package/dist/entertainmentNotableVenues.js +1 -0
- package/dist/groupTicketUtils.js +1 -0
- package/dist/guestPartySize.js +1 -0
- package/dist/hostCrmAutomation.js +1 -0
- package/dist/hostCrmConstants.js +1 -0
- package/dist/hostCrmInsights.js +1 -0
- package/dist/hostCrmSegments.js +1 -0
- package/dist/icebreakerPrompts.js +1 -0
- 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/influencerAudience.js +1 -0
- package/dist/legalTemplates.js +1 -0
- package/dist/liveActivity/liveActivityTypes.d.ts +59 -0
- package/dist/liveActivity/liveActivityTypes.d.ts.map +1 -0
- package/dist/liveActivity/liveActivityTypes.js +27 -0
- package/dist/liveActivity/liveActivityTypes.js.map +1 -0
- package/dist/membershipDefinitions.js +1 -0
- package/dist/mirroredPrismaEnums.js +1 -0
- package/dist/onSaleCapabilityRecommendations.js +1 -0
- package/dist/partnerStoreTypes.js +1 -0
- package/dist/productLaunch.js +1 -0
- package/dist/relationshipHealth.js +1 -0
- package/dist/requestAuthUser.js +1 -0
- package/dist/sideQuestIdeas.js +1 -0
- package/dist/storePrintArtworkUtils.js +1 -0
- package/dist/storeTypes.js +1 -0
- package/dist/stripeListingSubscriptionMessages.js +1 -0
- package/dist/templates/eventTemplates.js +1 -0
- package/dist/ticketBnplPaymentMethods.js +1 -0
- package/dist/userReportTypes.js +1 -0
- package/dist/utils/__tests__/organizationEntitlements.test.js +42 -9
- package/dist/utils/__tests__/organizationEntitlements.test.js.map +1 -1
- package/dist/utils/agencyBookingCheckoutCopy.js +1 -0
- package/dist/utils/arrayUtils.js +1 -0
- package/dist/utils/bashPointsPaymentUtils.js +1 -0
- package/dist/utils/birthdayExperienceQuality.js +1 -0
- package/dist/utils/contentFilterUtils.js +1 -0
- package/dist/utils/discountEngine/eligibilityValidator.js +1 -0
- package/dist/utils/displayDateUtils.js +1 -0
- package/dist/utils/entertainmentDrawFit.js +1 -0
- package/dist/utils/entityUtils.js +1 -0
- package/dist/utils/featuredDiscoveryGeo.js +1 -0
- package/dist/utils/flyerUtils.js +1 -0
- package/dist/utils/generalDateTimeUtils.js +1 -0
- package/dist/utils/lobFlyerUtils.js +1 -0
- package/dist/utils/localPartnerHours.js +1 -0
- package/dist/utils/luxonUtils.js +1 -0
- package/dist/utils/mathUtils.js +1 -0
- package/dist/utils/mediaClientDefaults.js +1 -0
- package/dist/utils/nonprofitUtils.js +1 -0
- package/dist/utils/objUtils.js +1 -0
- package/dist/utils/orgPromotionalPackageUtils.js +1 -0
- package/dist/utils/organizationEntitlements.d.ts +27 -7
- package/dist/utils/organizationEntitlements.d.ts.map +1 -1
- package/dist/utils/organizationEntitlements.js +28 -11
- package/dist/utils/organizationEntitlements.js.map +1 -1
- package/dist/utils/promotionBlastUtils.js +1 -0
- package/dist/utils/reputationUtils.js +1 -0
- package/dist/utils/reviewDimensionUtils.js +1 -0
- package/dist/utils/service/attendeeOptionUtils.js +1 -0
- package/dist/utils/service/billingCapUtils.js +1 -0
- package/dist/utils/service/eventPromoterUtils.js +1 -0
- package/dist/utils/service/offerAllowListUtils.js +1 -0
- package/dist/utils/service/offerComparisonUtils.js +1 -0
- package/dist/utils/service/regexUtils.js +1 -0
- package/dist/utils/service/serviceBookingExposureUtils.js +1 -0
- package/dist/utils/service/serviceFeaturedSortUtils.js +1 -0
- package/dist/utils/service/unifiedPricingResolver.js +1 -0
- package/dist/utils/service/venueSettlementUtils.js +1 -0
- package/dist/utils/service/volunteerCompensationUtils.js +1 -0
- package/dist/utils/serviceAvailabilityDisplay.js +1 -0
- package/dist/utils/sortUtils.js +1 -0
- package/dist/utils/stringUtils.js +1 -0
- package/dist/utils/ticketCheckInDisplay.js +1 -0
- package/dist/utils/trustScoreUtils.js +1 -0
- package/dist/utils/typeUtils.js +1 -0
- package/dist/utils/userPromoCodeUtils.js +1 -0
- package/dist/utils/userUtils.js +1 -0
- package/dist/utils/venueLoyalty/redemptionCodeUtils.js +1 -0
- package/dist/utils/venueTypeFormatter.js +1 -0
- package/dist/utmAttribution.js +1 -0
- package/dist/venueMatch.js +1 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +377 -0
- package/src/index.ts +1 -0
- package/src/liveActivity/liveActivityTypes.ts +75 -0
- package/src/utils/__tests__/organizationEntitlements.test.ts +47 -8
- package/src/utils/organizationEntitlements.ts +42 -11
package/prisma/schema.prisma
CHANGED
|
@@ -1039,6 +1039,12 @@ model BashEvent {
|
|
|
1039
1039
|
allowMemberGuests Boolean @default(false) // Members can bring +1s
|
|
1040
1040
|
maxGuestsPerMember Int? // Guest limit per member (null = unlimited)
|
|
1041
1041
|
restrictToOrgDepartments String[] @default([]) // Empty = all departments can see/join
|
|
1042
|
+
/// Discovery opt-out for portal-created events. DB default `true` preserves current
|
|
1043
|
+
/// behavior for every existing/consumer-created event. The Organization Workspace's
|
|
1044
|
+
/// event wizard sets this to `false` at creation (app-level default, not this DB
|
|
1045
|
+
/// default) so private-portal events don't leak into public Bash discovery unless
|
|
1046
|
+
/// the host flips "Publish to Bash" on. See publicDiscoveryPredicate.ts.
|
|
1047
|
+
publishToBashDiscovery Boolean @default(true)
|
|
1042
1048
|
|
|
1043
1049
|
/// When true, new event expenses default to Pending until host/org approves (see Expense.status).
|
|
1044
1050
|
requireExpenseApproval Boolean @default(false)
|
|
@@ -3380,6 +3386,14 @@ model Ticket {
|
|
|
3380
3386
|
lastReminded24hAt DateTime?
|
|
3381
3387
|
lastReminded1weekAt DateTime?
|
|
3382
3388
|
lastReminded3hAt DateTime?
|
|
3389
|
+
/// Per-activity ActivityKit update/end push token, reported by the client once its
|
|
3390
|
+
/// Live Activity actually starts (distinct from the user-level push-to-start token
|
|
3391
|
+
/// in LiveActivityPushToken, which can only start new activities, not update/end them).
|
|
3392
|
+
liveActivityPushToken String?
|
|
3393
|
+
/// Set once the liveActivityLifecycle cron has sent the start push for this ticket; avoids duplicate sends.
|
|
3394
|
+
liveActivityStartPushedAt DateTime?
|
|
3395
|
+
/// Set once the liveActivityLifecycle cron has sent the end push for this ticket; avoids duplicate sends.
|
|
3396
|
+
liveActivityEndPushedAt DateTime?
|
|
3383
3397
|
refundRequestedAt DateTime? // Set when RefundPending; cleared once refund processes
|
|
3384
3398
|
/// Set when the guest keeps or declines after a postponement; cleared on each new Postpone.
|
|
3385
3399
|
postponeRespondedAt DateTime?
|
|
@@ -3764,6 +3778,8 @@ model Media {
|
|
|
3764
3778
|
sponsoredEventsReferencingMe SponsoredEvent[] @relation("MediaToSponsoredEvent")
|
|
3765
3779
|
organizationsNonprofitDeterminationDoc Organization[] @relation("OrganizationNonprofitDeterminationDoc")
|
|
3766
3780
|
memoryCaptures BashMemoryCapture[]
|
|
3781
|
+
courseLessonsReferencingMe CourseLesson[]
|
|
3782
|
+
organizationResourcesReferencingMe OrganizationResource[]
|
|
3767
3783
|
}
|
|
3768
3784
|
|
|
3769
3785
|
model Prize {
|
|
@@ -4388,6 +4404,7 @@ model User {
|
|
|
4388
4404
|
feedbackTeamApplications FeedbackTeamApplication[]
|
|
4389
4405
|
newsletterSubscriptions NewsletterSubscriber[]
|
|
4390
4406
|
pushNotificationTokens PushNotificationToken[]
|
|
4407
|
+
liveActivityPushTokens LiveActivityPushToken[]
|
|
4391
4408
|
redeemedVouchers Voucher[]
|
|
4392
4409
|
bashEventPromoCodesIUsed BashEventPromoCode[] @relation("BashEventPromoCodeToUser")
|
|
4393
4410
|
ticketTiersWaitListsIveJoined TicketTier[] @relation("TicketTierToUser")
|
|
@@ -4487,6 +4504,16 @@ model User {
|
|
|
4487
4504
|
organizationEmailSubscriptions OrganizationSubscriber[]
|
|
4488
4505
|
eventSeriesCreated EventSeries[] @relation("EventSeriesCreator")
|
|
4489
4506
|
|
|
4507
|
+
/// Organization Workspace: courses, quizzes/certificates, and community.
|
|
4508
|
+
coursesInstructed Course[] @relation("CourseInstructor")
|
|
4509
|
+
courseEnrollments CourseEnrollment[]
|
|
4510
|
+
courseQuizAttempts CourseQuizAttempt[]
|
|
4511
|
+
courseCertificates CourseCertificate[]
|
|
4512
|
+
communityPostsAuthored OrganizationCommunityPost[] @relation("OrganizationCommunityPostAuthor")
|
|
4513
|
+
communityCommentsAuthored OrganizationCommunityComment[] @relation("OrganizationCommunityCommentAuthor")
|
|
4514
|
+
communityPostLikes OrganizationCommunityPostLike[]
|
|
4515
|
+
communityPostReports OrganizationCommunityPostReport[]
|
|
4516
|
+
|
|
4490
4517
|
// Service Includes & Add-On Requests
|
|
4491
4518
|
suggestedIncludes ServiceInclude[] @relation("SuggestedIncludes")
|
|
4492
4519
|
includeVotes ServiceIncludeVote[]
|
|
@@ -5893,6 +5920,31 @@ model Organization {
|
|
|
5893
5920
|
stripeConnectedAccountId String? // Org-level Stripe account for wallet display
|
|
5894
5921
|
accentColor String? // Hex color for org page branding (e.g. "#6366f1")
|
|
5895
5922
|
inheritBrandingFromParent Boolean @default(true)
|
|
5923
|
+
|
|
5924
|
+
/// White-label branding + Organization Workspace modules. All additive with safe
|
|
5925
|
+
/// defaults — existing orgs are unaffected until a module is explicitly enabled.
|
|
5926
|
+
faviconUrl String?
|
|
5927
|
+
secondaryBrandColor String?
|
|
5928
|
+
fontPreset String? @default("default")
|
|
5929
|
+
coursesEnabled Boolean @default(false)
|
|
5930
|
+
communityEnabled Boolean @default(false)
|
|
5931
|
+
resourcesEnabled Boolean @default(false)
|
|
5932
|
+
removeBashBranding Boolean @default(false)
|
|
5933
|
+
/// Marks the single first-party "Bash Academy" org that hosts Bash's own courses/
|
|
5934
|
+
/// certificates (e.g. the event-planning certification program). Drives a
|
|
5935
|
+
/// "Certified by Bash" trust badge; no other special-casing anywhere else in the stack.
|
|
5936
|
+
isBashOfficial Boolean @default(false)
|
|
5937
|
+
|
|
5938
|
+
/// Custom domain (white-label tier), provisioned via AWS CloudFront SaaS Manager
|
|
5939
|
+
/// Distribution Tenants + CloudFront-managed ACM certs. See organizationDomainApi.ts.
|
|
5940
|
+
customDomain String? @unique
|
|
5941
|
+
customDomainStatus CustomDomainStatus @default(Unverified)
|
|
5942
|
+
customDomainCname String?
|
|
5943
|
+
cloudfrontTenantId String?
|
|
5944
|
+
cloudfrontTenantArn String?
|
|
5945
|
+
customDomainVerifiedAt DateTime?
|
|
5946
|
+
customDomainLastCheckedAt DateTime?
|
|
5947
|
+
|
|
5896
5948
|
latitude Float?
|
|
5897
5949
|
longitude Float?
|
|
5898
5950
|
createdAt DateTime @default(now())
|
|
@@ -5914,6 +5966,11 @@ model Organization {
|
|
|
5914
5966
|
awayTeamGames Game[] @relation("AwayTeamGames")
|
|
5915
5967
|
products OrgProduct[]
|
|
5916
5968
|
|
|
5969
|
+
/// Organization Workspace modules (additive, gated by the *Enabled flags above).
|
|
5970
|
+
courses Course[]
|
|
5971
|
+
communityPosts OrganizationCommunityPost[]
|
|
5972
|
+
resources OrganizationResource[]
|
|
5973
|
+
|
|
5917
5974
|
/// Verified-nonprofit fields. Verification is a Bash-side admin action driven by
|
|
5918
5975
|
/// the host's uploaded determination letter (nonprofitDeterminationDocId). These
|
|
5919
5976
|
/// are the ONLY signals fee-math is allowed to read — `BashEvent.nonProfit` is
|
|
@@ -5968,6 +6025,15 @@ model Organization {
|
|
|
5968
6025
|
accountManager User? @relation("OrganizationAccountManager", fields: [accountManagerUserId], references: [id], onDelete: SetNull)
|
|
5969
6026
|
/// "Standard" | "Priority" | "Dedicated" — drives named-AM banner + support routing copy.
|
|
5970
6027
|
supportTier String @default("Standard")
|
|
6028
|
+
/// True only when ops set subscriptionTier="Enterprise" via the admin
|
|
6029
|
+
/// enterprise-provisioning endpoint (a signed deal). Self-serve Stripe checkout
|
|
6030
|
+
/// (organizationSubscriptionUtils.ts) can also set subscriptionTier="Enterprise"
|
|
6031
|
+
/// but never sets this flag. organizationHasEnterprisePlatformAccess() and the
|
|
6032
|
+
/// SSO/audit-log/procurement/compliance/API entitlements below require this flag
|
|
6033
|
+
/// in addition to the Enterprise tier — self-serve Enterprise unlocks white-label
|
|
6034
|
+
/// (organizationHasCustomDomainAccess/organizationHasVanitySubdomain do NOT
|
|
6035
|
+
/// require this flag) but not the sales-negotiated platform surface.
|
|
6036
|
+
enterpriseSalesProvisioned Boolean @default(false)
|
|
5971
6037
|
|
|
5972
6038
|
/// SSO / identity (Organization Enterprise only). `ssoEnforced` blocks password
|
|
5973
6039
|
/// login for members whose email matches `ssoDomain` once `ssoConnection.status`
|
|
@@ -6008,6 +6074,301 @@ model Organization {
|
|
|
6008
6074
|
@@index([accountManagerUserId])
|
|
6009
6075
|
}
|
|
6010
6076
|
|
|
6077
|
+
/// Custom-domain provisioning status (white-label tier). See Organization.customDomain*
|
|
6078
|
+
/// fields and organizationDomainApi.ts (AWS CloudFront SaaS Manager Distribution Tenants).
|
|
6079
|
+
enum CustomDomainStatus {
|
|
6080
|
+
Unverified
|
|
6081
|
+
PendingValidation
|
|
6082
|
+
Active
|
|
6083
|
+
Failed
|
|
6084
|
+
}
|
|
6085
|
+
|
|
6086
|
+
// ─── Organization Workspace: Courses, Quizzes & Certificates ─────────────────────
|
|
6087
|
+
// Generic, org-scoped course platform any Organization can enable via
|
|
6088
|
+
// `Organization.coursesEnabled`. Bash's own event-planning certification program
|
|
6089
|
+
// is not special-cased here — it is authored on a first-party "Bash Academy"
|
|
6090
|
+
// Organization (`Organization.isBashOfficial`) through this exact same model.
|
|
6091
|
+
|
|
6092
|
+
enum CourseVisibility {
|
|
6093
|
+
Public
|
|
6094
|
+
MembersOnly
|
|
6095
|
+
PremiumOnly
|
|
6096
|
+
Paid
|
|
6097
|
+
}
|
|
6098
|
+
|
|
6099
|
+
enum CourseStatus {
|
|
6100
|
+
Draft
|
|
6101
|
+
Published
|
|
6102
|
+
Archived
|
|
6103
|
+
}
|
|
6104
|
+
|
|
6105
|
+
model Course {
|
|
6106
|
+
id String @id @default(cuid())
|
|
6107
|
+
organizationId String
|
|
6108
|
+
title String
|
|
6109
|
+
slug String
|
|
6110
|
+
description String? @db.Text
|
|
6111
|
+
coverImage String?
|
|
6112
|
+
instructorUserId String
|
|
6113
|
+
visibility CourseVisibility @default(MembersOnly)
|
|
6114
|
+
priceCents Int?
|
|
6115
|
+
status CourseStatus @default(Draft)
|
|
6116
|
+
/// Requires a published CourseQuiz with >=1 question before this can be turned on.
|
|
6117
|
+
certificateEnabled Boolean @default(false)
|
|
6118
|
+
createdAt DateTime @default(now())
|
|
6119
|
+
updatedAt DateTime @updatedAt
|
|
6120
|
+
|
|
6121
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
6122
|
+
instructor User @relation("CourseInstructor", fields: [instructorUserId], references: [id], onDelete: Restrict)
|
|
6123
|
+
modules CourseModule[]
|
|
6124
|
+
lessons CourseLesson[]
|
|
6125
|
+
enrollments CourseEnrollment[]
|
|
6126
|
+
quiz CourseQuiz?
|
|
6127
|
+
certificates CourseCertificate[]
|
|
6128
|
+
|
|
6129
|
+
@@unique([organizationId, slug])
|
|
6130
|
+
@@index([organizationId])
|
|
6131
|
+
@@index([instructorUserId])
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6134
|
+
model CourseModule {
|
|
6135
|
+
id String @id @default(cuid())
|
|
6136
|
+
courseId String
|
|
6137
|
+
title String
|
|
6138
|
+
sortOrder Int @default(0)
|
|
6139
|
+
|
|
6140
|
+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
6141
|
+
lessons CourseLesson[]
|
|
6142
|
+
|
|
6143
|
+
@@index([courseId])
|
|
6144
|
+
}
|
|
6145
|
+
|
|
6146
|
+
enum CourseLessonContentType {
|
|
6147
|
+
Video
|
|
6148
|
+
Text
|
|
6149
|
+
Embed
|
|
6150
|
+
File
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6153
|
+
model CourseLesson {
|
|
6154
|
+
id String @id @default(cuid())
|
|
6155
|
+
courseId String
|
|
6156
|
+
moduleId String?
|
|
6157
|
+
title String
|
|
6158
|
+
contentType CourseLessonContentType @default(Text)
|
|
6159
|
+
videoUrl String?
|
|
6160
|
+
richTextContent String? @db.Text
|
|
6161
|
+
mediaId String?
|
|
6162
|
+
sortOrder Int @default(0)
|
|
6163
|
+
/// Viewable without enrolling — course "trailer" lesson(s).
|
|
6164
|
+
isPreviewable Boolean @default(false)
|
|
6165
|
+
|
|
6166
|
+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
6167
|
+
module CourseModule? @relation(fields: [moduleId], references: [id], onDelete: SetNull)
|
|
6168
|
+
media Media? @relation(fields: [mediaId], references: [id], onDelete: SetNull)
|
|
6169
|
+
progressEntries CourseLessonProgress[]
|
|
6170
|
+
|
|
6171
|
+
@@index([courseId])
|
|
6172
|
+
@@index([moduleId])
|
|
6173
|
+
}
|
|
6174
|
+
|
|
6175
|
+
enum CourseEnrollmentSource {
|
|
6176
|
+
Membership
|
|
6177
|
+
Purchase
|
|
6178
|
+
Manual
|
|
6179
|
+
}
|
|
6180
|
+
|
|
6181
|
+
model CourseEnrollment {
|
|
6182
|
+
id String @id @default(cuid())
|
|
6183
|
+
courseId String
|
|
6184
|
+
userId String
|
|
6185
|
+
source CourseEnrollmentSource @default(Membership)
|
|
6186
|
+
stripePaymentIntentId String?
|
|
6187
|
+
enrolledAt DateTime @default(now())
|
|
6188
|
+
|
|
6189
|
+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
6190
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
6191
|
+
lessonProgress CourseLessonProgress[]
|
|
6192
|
+
quizAttempts CourseQuizAttempt[]
|
|
6193
|
+
certificate CourseCertificate?
|
|
6194
|
+
|
|
6195
|
+
@@unique([courseId, userId])
|
|
6196
|
+
@@index([courseId])
|
|
6197
|
+
@@index([userId])
|
|
6198
|
+
}
|
|
6199
|
+
|
|
6200
|
+
model CourseLessonProgress {
|
|
6201
|
+
id String @id @default(cuid())
|
|
6202
|
+
enrollmentId String
|
|
6203
|
+
lessonId String
|
|
6204
|
+
completedAt DateTime?
|
|
6205
|
+
|
|
6206
|
+
enrollment CourseEnrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
|
|
6207
|
+
lesson CourseLesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
6208
|
+
|
|
6209
|
+
@@unique([enrollmentId, lessonId])
|
|
6210
|
+
@@index([lessonId])
|
|
6211
|
+
}
|
|
6212
|
+
|
|
6213
|
+
/// One final quiz gates the certificate (MVP). Per-module quizzes are a natural
|
|
6214
|
+
/// fast-follow with the same shape.
|
|
6215
|
+
model CourseQuiz {
|
|
6216
|
+
id String @id @default(cuid())
|
|
6217
|
+
courseId String @unique
|
|
6218
|
+
passThresholdPercent Int @default(80)
|
|
6219
|
+
|
|
6220
|
+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
6221
|
+
questions CourseQuizQuestion[]
|
|
6222
|
+
attempts CourseQuizAttempt[]
|
|
6223
|
+
}
|
|
6224
|
+
|
|
6225
|
+
model CourseQuizQuestion {
|
|
6226
|
+
id String @id @default(cuid())
|
|
6227
|
+
quizId String
|
|
6228
|
+
prompt String @db.Text
|
|
6229
|
+
/// [{ id: string, text: string }, ...]
|
|
6230
|
+
choices Json
|
|
6231
|
+
correctChoiceId String
|
|
6232
|
+
sortOrder Int @default(0)
|
|
6233
|
+
|
|
6234
|
+
quiz CourseQuiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
|
|
6235
|
+
|
|
6236
|
+
@@index([quizId])
|
|
6237
|
+
}
|
|
6238
|
+
|
|
6239
|
+
model CourseQuizAttempt {
|
|
6240
|
+
id String @id @default(cuid())
|
|
6241
|
+
quizId String
|
|
6242
|
+
enrollmentId String
|
|
6243
|
+
userId String
|
|
6244
|
+
scorePercent Int
|
|
6245
|
+
passed Boolean
|
|
6246
|
+
/// [{ questionId: string, choiceId: string }, ...]
|
|
6247
|
+
answers Json
|
|
6248
|
+
attemptedAt DateTime @default(now())
|
|
6249
|
+
|
|
6250
|
+
quiz CourseQuiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
|
|
6251
|
+
enrollment CourseEnrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
|
|
6252
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
6253
|
+
|
|
6254
|
+
@@index([quizId])
|
|
6255
|
+
@@index([enrollmentId])
|
|
6256
|
+
}
|
|
6257
|
+
|
|
6258
|
+
/// Auto-issued (server-side, never client-trusted) the moment a CourseQuizAttempt
|
|
6259
|
+
/// for the user's enrollment passes AND all non-preview lessons are complete.
|
|
6260
|
+
/// `certificateNumber` — never the DB id — is the public verification token used by
|
|
6261
|
+
/// GET /courses/certificates/verify/:certificateNumber.
|
|
6262
|
+
model CourseCertificate {
|
|
6263
|
+
id String @id @default(cuid())
|
|
6264
|
+
courseId String
|
|
6265
|
+
enrollmentId String @unique
|
|
6266
|
+
userId String
|
|
6267
|
+
certificateNumber String @unique @default(cuid())
|
|
6268
|
+
quizAttemptId String?
|
|
6269
|
+
issuedAt DateTime @default(now())
|
|
6270
|
+
revokedAt DateTime?
|
|
6271
|
+
|
|
6272
|
+
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
6273
|
+
enrollment CourseEnrollment @relation(fields: [enrollmentId], references: [id], onDelete: Cascade)
|
|
6274
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
6275
|
+
|
|
6276
|
+
@@unique([courseId, userId])
|
|
6277
|
+
@@index([certificateNumber])
|
|
6278
|
+
@@index([userId])
|
|
6279
|
+
}
|
|
6280
|
+
|
|
6281
|
+
// ─── Organization Workspace: Resources ────────────────────────────────────────────
|
|
6282
|
+
|
|
6283
|
+
enum ResourceVisibility {
|
|
6284
|
+
Public
|
|
6285
|
+
MembersOnly
|
|
6286
|
+
}
|
|
6287
|
+
|
|
6288
|
+
model OrganizationResource {
|
|
6289
|
+
id String @id @default(cuid())
|
|
6290
|
+
organizationId String
|
|
6291
|
+
title String
|
|
6292
|
+
description String? @db.Text
|
|
6293
|
+
url String?
|
|
6294
|
+
mediaId String?
|
|
6295
|
+
category String?
|
|
6296
|
+
visibility ResourceVisibility @default(MembersOnly)
|
|
6297
|
+
sortOrder Int @default(0)
|
|
6298
|
+
createdAt DateTime @default(now())
|
|
6299
|
+
|
|
6300
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
6301
|
+
media Media? @relation(fields: [mediaId], references: [id], onDelete: SetNull)
|
|
6302
|
+
|
|
6303
|
+
@@index([organizationId])
|
|
6304
|
+
}
|
|
6305
|
+
|
|
6306
|
+
// ─── Organization Workspace: Community ────────────────────────────────────────────
|
|
6307
|
+
// Mirrors the EventDiscussionComment shape, scoped to an Organization instead of a
|
|
6308
|
+
// single BashEvent. Gated by `Organization.communityEnabled` + active membership or
|
|
6309
|
+
// `Organization.visibility === "Public"`.
|
|
6310
|
+
|
|
6311
|
+
model OrganizationCommunityPost {
|
|
6312
|
+
id String @id @default(cuid())
|
|
6313
|
+
organizationId String
|
|
6314
|
+
authorUserId String
|
|
6315
|
+
content String @db.Text
|
|
6316
|
+
pinnedAt DateTime?
|
|
6317
|
+
createdAt DateTime @default(now())
|
|
6318
|
+
updatedAt DateTime @updatedAt
|
|
6319
|
+
|
|
6320
|
+
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
6321
|
+
author User @relation("OrganizationCommunityPostAuthor", fields: [authorUserId], references: [id], onDelete: Cascade)
|
|
6322
|
+
comments OrganizationCommunityComment[]
|
|
6323
|
+
likes OrganizationCommunityPostLike[]
|
|
6324
|
+
reports OrganizationCommunityPostReport[]
|
|
6325
|
+
|
|
6326
|
+
@@index([organizationId])
|
|
6327
|
+
@@index([authorUserId])
|
|
6328
|
+
}
|
|
6329
|
+
|
|
6330
|
+
model OrganizationCommunityComment {
|
|
6331
|
+
id String @id @default(cuid())
|
|
6332
|
+
postId String
|
|
6333
|
+
authorUserId String
|
|
6334
|
+
content String @db.Text
|
|
6335
|
+
createdAt DateTime @default(now())
|
|
6336
|
+
|
|
6337
|
+
post OrganizationCommunityPost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
6338
|
+
author User @relation("OrganizationCommunityCommentAuthor", fields: [authorUserId], references: [id], onDelete: Cascade)
|
|
6339
|
+
|
|
6340
|
+
@@index([postId])
|
|
6341
|
+
}
|
|
6342
|
+
|
|
6343
|
+
model OrganizationCommunityPostLike {
|
|
6344
|
+
id String @id @default(cuid())
|
|
6345
|
+
postId String
|
|
6346
|
+
userId String
|
|
6347
|
+
|
|
6348
|
+
post OrganizationCommunityPost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
6349
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
6350
|
+
|
|
6351
|
+
@@unique([postId, userId])
|
|
6352
|
+
@@index([postId])
|
|
6353
|
+
}
|
|
6354
|
+
|
|
6355
|
+
/// Moderation report on a top-level community post (mirrors EventDiscussionCommentReport).
|
|
6356
|
+
/// Scoped to posts, not comments, for MVP — the atomic unit worth a host's review.
|
|
6357
|
+
model OrganizationCommunityPostReport {
|
|
6358
|
+
id String @id @default(cuid())
|
|
6359
|
+
postId String
|
|
6360
|
+
userId String
|
|
6361
|
+
reason BashFeedReportReason
|
|
6362
|
+
description String? @db.Text
|
|
6363
|
+
createdAt DateTime @default(now())
|
|
6364
|
+
|
|
6365
|
+
post OrganizationCommunityPost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
|
6366
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
6367
|
+
|
|
6368
|
+
@@unique([postId, userId])
|
|
6369
|
+
@@index([postId])
|
|
6370
|
+
}
|
|
6371
|
+
|
|
6011
6372
|
/// Email-first followers of a public organizer (org landing). Authenticated users who
|
|
6012
6373
|
/// Follow also get a row here when we know their email. Distinct from org membership.
|
|
6013
6374
|
model OrganizationSubscriber {
|
|
@@ -7578,6 +7939,22 @@ model PushNotificationToken {
|
|
|
7578
7939
|
@@map("push_notification_tokens")
|
|
7579
7940
|
}
|
|
7580
7941
|
|
|
7942
|
+
// iOS ActivityKit "push-to-start" token — distinct from PushNotificationToken (FCM).
|
|
7943
|
+
// One per device; used to auto-start a Live Activity for an upcoming bash via raw APNs.
|
|
7944
|
+
// Android has no equivalent: it reuses the existing FCM token in PushNotificationToken.
|
|
7945
|
+
model LiveActivityPushToken {
|
|
7946
|
+
id String @id @default(cuid())
|
|
7947
|
+
userId String
|
|
7948
|
+
token String @unique
|
|
7949
|
+
platform String @default("ios")
|
|
7950
|
+
isActive Boolean @default(true)
|
|
7951
|
+
createdAt DateTime @default(now())
|
|
7952
|
+
updatedAt DateTime @updatedAt
|
|
7953
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
7954
|
+
|
|
7955
|
+
@@unique([userId, platform])
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7581
7958
|
model AmbashadorApplication {
|
|
7582
7959
|
id String @id @default(cuid())
|
|
7583
7960
|
userId String?
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export * from "./utils/featuredDiscoveryGeo.js";
|
|
|
49
49
|
export * from "./utils/organizationEntitlements.js";
|
|
50
50
|
export * from "./serviceTypeLabels.js";
|
|
51
51
|
export * from "./targetAudienceLabels.js";
|
|
52
|
+
export * from "./liveActivity/liveActivityTypes.js";
|
|
52
53
|
|
|
53
54
|
// Re-export ALL Prisma enums as values (usable as runtime constants, e.g. ServiceTypes.Entertainment)
|
|
54
55
|
// Excludes enums already re-exported by definitions.ts, mirroredPrismaEnums.ts, and membershipDefinitions.ts:
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared shapes for the bash-ticket Live Activity (iOS ActivityKit Lock Screen /
|
|
3
|
+
* Dynamic Island) and Android Live Update / sticky-notification fallback.
|
|
4
|
+
*
|
|
5
|
+
* `api` builds `LiveActivityContentState` server-side to push via APNs
|
|
6
|
+
* (push-to-start/update/end) or an FCM data message; `bash-app` uses the same
|
|
7
|
+
* shape to type the native plugin's `start`/`update` calls for the manual
|
|
8
|
+
* "Track this bash" toggle. Keeping this in bash-common avoids the two repos
|
|
9
|
+
* drifting on field names for what is ultimately one wire contract.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Static, per-activity identifiers — set once, never change during the activity. */
|
|
13
|
+
export interface LiveActivityAttributes {
|
|
14
|
+
bashEventId: string;
|
|
15
|
+
ticketId: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Dynamic content shown on the Lock Screen / Dynamic Island / Android chip.
|
|
20
|
+
* Kept intentionally small — ActivityKit caps combined attributes + state at 4KB,
|
|
21
|
+
* and every field here is re-sent in full on every update (no partial merges).
|
|
22
|
+
*/
|
|
23
|
+
export interface LiveActivityContentState {
|
|
24
|
+
/** Bash event title — primary headline. */
|
|
25
|
+
eventTitle: string;
|
|
26
|
+
/** Short venue/location line — subtitle (e.g. venue name or first line of address). */
|
|
27
|
+
venueLabel: string;
|
|
28
|
+
/** ISO 8601 — bash start time. Widget shows a countdown until this, then a "LIVE" badge. */
|
|
29
|
+
startTime: string;
|
|
30
|
+
/** ISO 8601 — bash end time. Used for the lazy client-side end fallback. */
|
|
31
|
+
endTime: string;
|
|
32
|
+
/**
|
|
33
|
+
* Absolute URL that opens the device's native Maps app for directions
|
|
34
|
+
* (Apple Maps on iOS, Google Maps on Android) — never a relative path,
|
|
35
|
+
* since the widget/notification has no app routing context.
|
|
36
|
+
*/
|
|
37
|
+
directionsUrl: string;
|
|
38
|
+
/**
|
|
39
|
+
* Absolute universal-link URL back into bash-app's ticket QR screen
|
|
40
|
+
* (`bashEventQrCodeUrl` + the app's public origin). Deliberately NOT an
|
|
41
|
+
* embedded QR image/token — keeps the payload small and the QR always fresh.
|
|
42
|
+
*/
|
|
43
|
+
ticketUrl: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Discriminates the three lifecycle pushes sent over APNs (iOS) or as an FCM data message (Android). */
|
|
47
|
+
export type LiveActivityPushEventType =
|
|
48
|
+
| "live_activity_start"
|
|
49
|
+
| "live_activity_update"
|
|
50
|
+
| "live_activity_end";
|
|
51
|
+
|
|
52
|
+
/** Platform values accepted on `LiveActivityPushToken.platform` — iOS only today (Android reuses the FCM token). */
|
|
53
|
+
export const LIVE_ACTIVITY_TOKEN_PLATFORM_IOS = "ios" as const;
|
|
54
|
+
|
|
55
|
+
/** Which native Maps app `buildLiveActivityDirectionsUrl` should target. */
|
|
56
|
+
export type LiveActivityMapsPlatform = "ios" | "android";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Builds the "Directions" deep link shared by the api (server-built push
|
|
60
|
+
* payloads) and bash-app (client-built manual-start payload) — Apple Maps on
|
|
61
|
+
* iOS, Google Maps everywhere else, preferring coordinates over the raw
|
|
62
|
+
* address/title string when available.
|
|
63
|
+
*/
|
|
64
|
+
export function buildLiveActivityDirectionsUrl(
|
|
65
|
+
destinationCoords: { lat: number; lng: number } | null | undefined,
|
|
66
|
+
fallbackLabel: string,
|
|
67
|
+
platform: LiveActivityMapsPlatform
|
|
68
|
+
): string {
|
|
69
|
+
const destination = destinationCoords
|
|
70
|
+
? `${destinationCoords.lat},${destinationCoords.lng}`
|
|
71
|
+
: encodeURIComponent(fallbackLabel);
|
|
72
|
+
return platform === "ios"
|
|
73
|
+
? `https://maps.apple.com/?daddr=${destination}`
|
|
74
|
+
: `https://www.google.com/maps/search/?api=1&query=${destination}`;
|
|
75
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
organizationHasApiAccess,
|
|
5
5
|
organizationHasAuditLogAccess,
|
|
6
6
|
organizationHasComplianceAccess,
|
|
7
|
+
organizationHasCustomDomainAccess,
|
|
7
8
|
organizationHasEnterprisePlatformAccess,
|
|
8
9
|
organizationHasProcurementAccess,
|
|
9
10
|
organizationHasSsoAccess,
|
|
@@ -67,41 +68,79 @@ describe("organizationHasVanitySubdomain", () => {
|
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
describe("enterprise platform gates (audit logs, SSO, procurement, API access)", () => {
|
|
70
|
-
|
|
71
|
+
// Sales-provisioned: ops set enterpriseSalesProvisioned via the admin
|
|
72
|
+
// enterprise-provisioning endpoint after a signed deal.
|
|
73
|
+
const salesProvisionedEnterprise = {
|
|
71
74
|
subscriptionTier: "Enterprise",
|
|
72
75
|
subscriptionStatus: "Active",
|
|
76
|
+
enterpriseSalesProvisioned: true,
|
|
77
|
+
};
|
|
78
|
+
// Self-serve: bought via Stripe checkout, never grants the platform surface.
|
|
79
|
+
const selfServeEnterprise = {
|
|
80
|
+
subscriptionTier: "Enterprise",
|
|
81
|
+
subscriptionStatus: "Active",
|
|
82
|
+
enterpriseSalesProvisioned: false,
|
|
73
83
|
};
|
|
74
84
|
const growthActive = { subscriptionTier: "Growth", subscriptionStatus: "Active" };
|
|
75
85
|
|
|
76
|
-
test("organizationHasEnterprisePlatformAccess
|
|
77
|
-
expect(organizationHasEnterprisePlatformAccess(
|
|
86
|
+
test("organizationHasEnterprisePlatformAccess requires enterpriseSalesProvisioned", () => {
|
|
87
|
+
expect(organizationHasEnterprisePlatformAccess(salesProvisionedEnterprise)).toBe(true);
|
|
88
|
+
expect(organizationHasEnterprisePlatformAccess(selfServeEnterprise)).toBe(false);
|
|
78
89
|
expect(organizationHasEnterprisePlatformAccess(growthActive)).toBe(false);
|
|
79
90
|
expect(organizationHasEnterprisePlatformAccess(null)).toBe(false);
|
|
80
91
|
expect(organizationHasEnterprisePlatformAccess(undefined)).toBe(false);
|
|
81
92
|
});
|
|
82
93
|
|
|
94
|
+
test("missing enterpriseSalesProvisioned on an Enterprise org fails closed (not entitled)", () => {
|
|
95
|
+
expect(
|
|
96
|
+
organizationHasEnterprisePlatformAccess({
|
|
97
|
+
subscriptionTier: "Enterprise",
|
|
98
|
+
subscriptionStatus: "Active",
|
|
99
|
+
})
|
|
100
|
+
).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
83
103
|
test("organizationHasAuditLogAccess follows the shared enterprise gate", () => {
|
|
84
|
-
expect(organizationHasAuditLogAccess(
|
|
104
|
+
expect(organizationHasAuditLogAccess(salesProvisionedEnterprise)).toBe(true);
|
|
105
|
+
expect(organizationHasAuditLogAccess(selfServeEnterprise)).toBe(false);
|
|
85
106
|
expect(organizationHasAuditLogAccess(growthActive)).toBe(false);
|
|
86
107
|
});
|
|
87
108
|
|
|
88
109
|
test("organizationHasSsoAccess follows the shared enterprise gate", () => {
|
|
89
|
-
expect(organizationHasSsoAccess(
|
|
110
|
+
expect(organizationHasSsoAccess(salesProvisionedEnterprise)).toBe(true);
|
|
111
|
+
expect(organizationHasSsoAccess(selfServeEnterprise)).toBe(false);
|
|
90
112
|
expect(organizationHasSsoAccess(growthActive)).toBe(false);
|
|
91
113
|
});
|
|
92
114
|
|
|
93
115
|
test("organizationHasProcurementAccess follows the shared enterprise gate", () => {
|
|
94
|
-
expect(organizationHasProcurementAccess(
|
|
116
|
+
expect(organizationHasProcurementAccess(salesProvisionedEnterprise)).toBe(true);
|
|
117
|
+
expect(organizationHasProcurementAccess(selfServeEnterprise)).toBe(false);
|
|
95
118
|
expect(organizationHasProcurementAccess(growthActive)).toBe(false);
|
|
96
119
|
});
|
|
97
120
|
|
|
98
121
|
test("organizationHasApiAccess follows the shared enterprise gate", () => {
|
|
99
|
-
expect(organizationHasApiAccess(
|
|
122
|
+
expect(organizationHasApiAccess(salesProvisionedEnterprise)).toBe(true);
|
|
123
|
+
expect(organizationHasApiAccess(selfServeEnterprise)).toBe(false);
|
|
100
124
|
expect(organizationHasApiAccess(growthActive)).toBe(false);
|
|
101
125
|
});
|
|
102
126
|
|
|
103
127
|
test("organizationHasComplianceAccess follows the shared enterprise gate", () => {
|
|
104
|
-
expect(organizationHasComplianceAccess(
|
|
128
|
+
expect(organizationHasComplianceAccess(salesProvisionedEnterprise)).toBe(true);
|
|
129
|
+
expect(organizationHasComplianceAccess(selfServeEnterprise)).toBe(false);
|
|
105
130
|
expect(organizationHasComplianceAccess(growthActive)).toBe(false);
|
|
106
131
|
});
|
|
132
|
+
|
|
133
|
+
test("organizationHasCustomDomainAccess (white-label) is self-serve-eligible — no sales flag required", () => {
|
|
134
|
+
expect(organizationHasCustomDomainAccess(salesProvisionedEnterprise)).toBe(true);
|
|
135
|
+
expect(organizationHasCustomDomainAccess(selfServeEnterprise)).toBe(true);
|
|
136
|
+
expect(organizationHasCustomDomainAccess(growthActive)).toBe(false);
|
|
137
|
+
expect(organizationHasCustomDomainAccess(null)).toBe(false);
|
|
138
|
+
expect(organizationHasCustomDomainAccess(undefined)).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("organizationHasVanitySubdomain is self-serve-eligible — no sales flag required", () => {
|
|
142
|
+
expect(organizationHasVanitySubdomain(salesProvisionedEnterprise)).toBe(true);
|
|
143
|
+
expect(organizationHasVanitySubdomain(selfServeEnterprise)).toBe(true);
|
|
144
|
+
expect(organizationHasVanitySubdomain(growthActive)).toBe(false);
|
|
145
|
+
});
|
|
107
146
|
});
|