@bash-app/bash-common 30.314.0 → 30.318.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__/appleIapProducts.test.d.ts +2 -0
- package/dist/__tests__/appleIapProducts.test.d.ts.map +1 -0
- package/dist/__tests__/appleIapProducts.test.js +32 -0
- package/dist/__tests__/appleIapProducts.test.js.map +1 -0
- package/dist/__tests__/hostCrmGrowthPack.test.d.ts +2 -0
- package/dist/__tests__/hostCrmGrowthPack.test.d.ts.map +1 -0
- package/dist/__tests__/hostCrmGrowthPack.test.js +25 -0
- package/dist/__tests__/hostCrmGrowthPack.test.js.map +1 -0
- package/dist/__tests__/hostCrmSegments.test.d.ts +2 -0
- package/dist/__tests__/hostCrmSegments.test.d.ts.map +1 -0
- package/dist/__tests__/hostCrmSegments.test.js +198 -0
- package/dist/__tests__/hostCrmSegments.test.js.map +1 -0
- package/dist/__tests__/relationshipHealth.test.d.ts +2 -0
- package/dist/__tests__/relationshipHealth.test.d.ts.map +1 -0
- package/dist/__tests__/relationshipHealth.test.js +265 -0
- package/dist/__tests__/relationshipHealth.test.js.map +1 -0
- package/dist/appleIapProducts.d.ts +64 -0
- package/dist/appleIapProducts.d.ts.map +1 -0
- package/dist/appleIapProducts.js +113 -0
- package/dist/appleIapProducts.js.map +1 -0
- package/dist/definitions.d.ts +3 -1
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +2 -0
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +27 -0
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/hostCrmAutomation.d.ts +89 -0
- package/dist/hostCrmAutomation.d.ts.map +1 -0
- package/dist/hostCrmAutomation.js +50 -0
- package/dist/hostCrmAutomation.js.map +1 -0
- package/dist/hostCrmInsights.d.ts +39 -0
- package/dist/hostCrmInsights.d.ts.map +1 -0
- package/dist/hostCrmInsights.js +2 -0
- package/dist/hostCrmInsights.js.map +1 -0
- package/dist/hostCrmSegments.d.ts +82 -2
- package/dist/hostCrmSegments.d.ts.map +1 -1
- package/dist/hostCrmSegments.js +93 -3
- package/dist/hostCrmSegments.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -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/relationshipHealth.d.ts +28 -0
- package/dist/relationshipHealth.d.ts.map +1 -0
- package/dist/relationshipHealth.js +91 -0
- package/dist/relationshipHealth.js.map +1 -0
- package/dist/sms/smsTemplates.d.ts +13 -1
- package/dist/sms/smsTemplates.d.ts.map +1 -1
- package/dist/sms/smsTemplates.js +3 -0
- package/dist/sms/smsTemplates.js.map +1 -1
- package/dist/utils/__tests__/slugUtils.test.js +9 -1
- package/dist/utils/__tests__/slugUtils.test.js.map +1 -1
- package/dist/utils/slugUtils.d.ts +7 -0
- package/dist/utils/slugUtils.d.ts.map +1 -1
- package/dist/utils/slugUtils.js +9 -0
- package/dist/utils/slugUtils.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +154 -2
- package/src/__tests__/appleIapProducts.test.ts +52 -0
- package/src/__tests__/hostCrmGrowthPack.test.ts +33 -0
- package/src/__tests__/hostCrmSegments.test.ts +247 -0
- package/src/__tests__/relationshipHealth.test.ts +370 -0
- package/src/appleIapProducts.ts +203 -0
- package/src/definitions.ts +2 -0
- package/src/extendedSchemas.ts +30 -1
- package/src/hostCrmAutomation.ts +138 -0
- package/src/hostCrmInsights.ts +39 -0
- package/src/hostCrmSegments.ts +208 -7
- package/src/index.ts +4 -0
- package/src/membershipDefinitions.ts +3 -0
- package/src/relationshipHealth.ts +134 -0
- package/src/sms/smsTemplates.ts +6 -0
- package/src/utils/__tests__/slugUtils.test.ts +15 -0
- package/src/utils/slugUtils.ts +13 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slugUtils.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/slugUtils.test.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"slugUtils.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/slugUtils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,13 @@ export declare function generateBashDetailUrl(bashEventId: string, slug: string)
|
|
|
6
6
|
* With a slug: `/bash/{id}-{slug}`; without: `/bash/{id}` (legacy id-only segment).
|
|
7
7
|
*/
|
|
8
8
|
export declare function getPublicBashDetailPath(bashEventId: string, slug: string | null | undefined): string;
|
|
9
|
+
/**
|
|
10
|
+
* Public, shareable Memories recap path for a bash — nested under the bash
|
|
11
|
+
* detail path so it inherits the same id/slug parsing (`/bash/{id}-{slug}/memories`).
|
|
12
|
+
* No-login-required by design: the whole point of Memories is that a shared
|
|
13
|
+
* link works for people who aren't signed in yet.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPublicBashMemoriesPath(bashEventId: string, slug: string | null | undefined): string;
|
|
9
16
|
/** Wizard URL with explicit step (see bash-app `/bash-event-wizard/$id/$major/$sub`). */
|
|
10
17
|
export declare function getBashEventWizardStepPath(bashEventId: string, majorStep: number, subStep: number): string;
|
|
11
18
|
/** Host redirect target: "How" → Payout (Stripe Connect / ticket proceeds). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slugUtils.d.ts","sourceRoot":"","sources":["../../src/utils/slugUtils.ts"],"names":[],"mappings":"AAQA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACvE,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,MAAM,CAMR;AAED,yFAAyF;AACzF,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED,+EAA+E;AAC/E,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAM5E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAqB5F;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlD"}
|
|
1
|
+
{"version":3,"file":"slugUtils.d.ts","sourceRoot":"","sources":["../../src/utils/slugUtils.ts"],"names":[],"mappings":"AAQA,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACvE,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,MAAM,CAER;AAED,yFAAyF;AACzF,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED,+EAA+E;AAC/E,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAM5E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAqB5F;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlD"}
|
package/dist/utils/slugUtils.js
CHANGED
|
@@ -34,6 +34,15 @@ export function getPublicBashDetailPath(bashEventId, slug) {
|
|
|
34
34
|
}
|
|
35
35
|
return `${BASH_DETAIL_URL}/${bashEventId}`;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Public, shareable Memories recap path for a bash — nested under the bash
|
|
39
|
+
* detail path so it inherits the same id/slug parsing (`/bash/{id}-{slug}/memories`).
|
|
40
|
+
* No-login-required by design: the whole point of Memories is that a shared
|
|
41
|
+
* link works for people who aren't signed in yet.
|
|
42
|
+
*/
|
|
43
|
+
export function getPublicBashMemoriesPath(bashEventId, slug) {
|
|
44
|
+
return `${getPublicBashDetailPath(bashEventId, slug)}/memories`;
|
|
45
|
+
}
|
|
37
46
|
/** Wizard URL with explicit step (see bash-app `/bash-event-wizard/$id/$major/$sub`). */
|
|
38
47
|
export function getBashEventWizardStepPath(bashEventId, majorStep, subStep) {
|
|
39
48
|
return `${BASH_EVENT_WIZARD_PATH_PREFIX}/${bashEventId}/${majorStep}/${subStep}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slugUtils.js","sourceRoot":"","sources":["../../src/utils/slugUtils.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,gCAAgC,EAChC,oCAAoC,EACpC,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,OAAO,CAAC,KAAK,EAAE;QACpB,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,gBAAgB;KACzB,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,eAAuE,EACvE,SAAkB;IAElB,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,GAAG,QAAQ,CAAC;IACpB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,GAAG,GAAG,QAAQ,IAAI,OAAO,EAAE,CAAC;QAChC,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,IAAY;IACrE,OAAO,SAAS,WAAW,IAAI,IAAI,EAAE,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAmB,EACnB,IAA+B;IAE/B,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,CAAC;QACN,OAAO,qBAAqB,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,eAAe,IAAI,WAAW,EAAE,CAAC;AAC7C,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,0BAA0B,CACxC,WAAmB,EACnB,SAAiB,EACjB,OAAe;IAEf,OAAO,GAAG,6BAA6B,IAAI,WAAW,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;AACnF,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gCAAgC,CAAC,WAAmB;IAClE,OAAO,0BAA0B,CAC/B,WAAW,EACX,gCAAgC,EAChC,oCAAoC,CACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,sEAAsE;IACtE,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,0BAA0B;QAC1B,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IAE5C,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sBAAsB;IACtB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,sEAAsE;IACtE,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
1
|
+
{"version":3,"file":"slugUtils.js","sourceRoot":"","sources":["../../src/utils/slugUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -810,6 +810,17 @@ model BashEvent {
|
|
|
810
810
|
bashPointsPayoutSource BashPointsPayoutSource @default(HostWallet)
|
|
811
811
|
/// When true, attendees may request a refund from the app/email (host approves in Tickets → Refunds). When false, self-serve refund CTAs are hidden.
|
|
812
812
|
allowAttendeeRefundRequests Boolean @default(true)
|
|
813
|
+
/// Free-text cancellation / refund policy shown at checkout and on the guest's ticket.
|
|
814
|
+
cancellationPolicy String?
|
|
815
|
+
/// When true (default), cancelling the bash auto-refunds paid ticket holders. Hosts may set false only before any ticket/RSVP exists.
|
|
816
|
+
refundsGuaranteedOnCancellation Boolean @default(true)
|
|
817
|
+
/// When true, host may postpone after tickets exist. May only flip false→true before any ticket/RSVP exists.
|
|
818
|
+
postponementAllowed Boolean @default(false)
|
|
819
|
+
/// True after the host has invoked Postpone at least once (cleared only by republish flows if desired; kept for guest banners).
|
|
820
|
+
isPostponed Boolean @default(false)
|
|
821
|
+
postponedAt DateTime?
|
|
822
|
+
/// Deadline for guests to keep or decline their ticket after a postponement.
|
|
823
|
+
postponeResponseDeadline DateTime?
|
|
813
824
|
// GuestPays / Split: Stripe processing + Bash platform fee presentation (see platformFeeEnabled).
|
|
814
825
|
feeHandling String @default("HostAbsorbs") // "GuestPays" | "HostAbsorbs" | "Split"
|
|
815
826
|
/// When true, Bash charges platformFeeRate on ticket gross (e.g. 3%) in addition to Stripe processing pass-through.
|
|
@@ -907,7 +918,7 @@ model BashEvent {
|
|
|
907
918
|
/// When false, included/allowed/not-allowed chips are read-only (no upvotes or suggestions)
|
|
908
919
|
collaborationEnabled Boolean @default(true)
|
|
909
920
|
/// Host opt-in: show icebreaker prompts on the bash detail page for guests
|
|
910
|
-
icebreakerEnabled Boolean @default(
|
|
921
|
+
icebreakerEnabled Boolean @default(false)
|
|
911
922
|
/// Optional host-authored / host-picked icebreaker prompt. When set, it is shown
|
|
912
923
|
/// instead of a random canned prompt on the bash detail page.
|
|
913
924
|
icebreakerPrompt String?
|
|
@@ -925,6 +936,8 @@ model BashEvent {
|
|
|
925
936
|
aiSuggestedTasks Json?
|
|
926
937
|
backgroundImage String? // Custom or curated background image URL for the bash detail page
|
|
927
938
|
backgroundEffect BackgroundEffect @default(None)
|
|
939
|
+
/// Soft blur on theme photo backgrounds (Spotify-style). Hosts can turn off for a sharp image.
|
|
940
|
+
backgroundBlurEnabled Boolean @default(true)
|
|
928
941
|
themeColor String? // Dominant hex color extracted from the event flyer (e.g. "#7c3aed")
|
|
929
942
|
/// RSVP-gated discussion thread on bash detail
|
|
930
943
|
discussionEnabled Boolean @default(true)
|
|
@@ -1924,6 +1937,8 @@ model PublicBashRsvp {
|
|
|
1924
1937
|
/// Guest-managed plus-ones for Rsvp-mode events. Names optional; count drives capacity.
|
|
1925
1938
|
plusOnesCount Int @default(0)
|
|
1926
1939
|
plusOneNames String[] @default([])
|
|
1940
|
+
/// Set when the guest keeps or declines after a postponement; cleared on each new Postpone.
|
|
1941
|
+
postponeRespondedAt DateTime?
|
|
1927
1942
|
createdAt DateTime @default(now())
|
|
1928
1943
|
updatedAt DateTime @updatedAt
|
|
1929
1944
|
|
|
@@ -2357,6 +2372,47 @@ model EventAlbumItem {
|
|
|
2357
2372
|
@@index([bashEventId])
|
|
2358
2373
|
}
|
|
2359
2374
|
|
|
2375
|
+
/// Curated Memories recap for a finished bash — best few photos/badge
|
|
2376
|
+
/// highlights, generated once by the memories cron (see
|
|
2377
|
+
/// api/src/utils/memories/) rather than recomputed per view, so the "moment"
|
|
2378
|
+
/// a host/attendee sees on a shared link stays stable over time. `viewCount`
|
|
2379
|
+
/// / `shareCount` back the share-rate instrumentation required before this
|
|
2380
|
+
/// feature is considered proven as a growth loop.
|
|
2381
|
+
model BashMemories {
|
|
2382
|
+
id String @id @default(cuid())
|
|
2383
|
+
bashEventId String @unique
|
|
2384
|
+
/// Best few photo URLs (capped at generation time — this is a curated
|
|
2385
|
+
/// handful, not every photo tagged to the event).
|
|
2386
|
+
photoUrls String[] @default([])
|
|
2387
|
+
/// Curated badge/award highlights: Array<{ name, imageUrl, awardCount }>.
|
|
2388
|
+
badgeHighlights Json @default("[]")
|
|
2389
|
+
attendeeCount Int @default(0)
|
|
2390
|
+
generatedAt DateTime @default(now())
|
|
2391
|
+
notifiedAt DateTime?
|
|
2392
|
+
viewCount Int @default(0)
|
|
2393
|
+
shareCount Int @default(0)
|
|
2394
|
+
createdAt DateTime @default(now())
|
|
2395
|
+
updatedAt DateTime @updatedAt
|
|
2396
|
+
|
|
2397
|
+
@@index([bashEventId])
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
/// "Tag your friends who were there" on a public Memories recap — a lightweight,
|
|
2401
|
+
/// event-scoped social edge (Zuck: "every tag is a new edge in the graph").
|
|
2402
|
+
/// Deliberately not wired into `UserFollowing` (that model's `isOrganizer` follow
|
|
2403
|
+
/// carries specific "notify me of new bashes" semantics a casual recap tag shouldn't imply).
|
|
2404
|
+
model BashMemoriesTag {
|
|
2405
|
+
id String @id @default(cuid())
|
|
2406
|
+
bashEventId String
|
|
2407
|
+
taggedByUserId String
|
|
2408
|
+
taggedUserId String
|
|
2409
|
+
createdAt DateTime @default(now())
|
|
2410
|
+
|
|
2411
|
+
@@unique([bashEventId, taggedByUserId, taggedUserId])
|
|
2412
|
+
@@index([bashEventId])
|
|
2413
|
+
@@index([taggedUserId])
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2360
2416
|
/// Post-event networking opt-in: attendees who agreed to be discoverable to others who went.
|
|
2361
2417
|
model EventNetworkingOptIn {
|
|
2362
2418
|
id String @id @default(cuid())
|
|
@@ -2379,12 +2435,45 @@ model HostAttendeeProfile {
|
|
|
2379
2435
|
createdAt DateTime @default(now())
|
|
2380
2436
|
updatedAt DateTime @updatedAt
|
|
2381
2437
|
|
|
2438
|
+
/// Relationship Health status as of the last time this host's CRM was read;
|
|
2439
|
+
/// compared to the freshly-computed status on each read to detect a
|
|
2440
|
+
/// Cooling/AtRisk transition for the host-approved win-back recommendation.
|
|
2441
|
+
lastHealthStatusSeen String?
|
|
2442
|
+
lastHealthStatusSeenAt DateTime?
|
|
2443
|
+
|
|
2382
2444
|
host User @relation("HostAttendeeProfilesOwned", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2383
2445
|
attendee User? @relation("HostAttendeeProfilesTarget", fields: [attendeeUserId], references: [id], onDelete: SetNull)
|
|
2384
2446
|
|
|
2385
2447
|
@@index([hostUserId])
|
|
2386
2448
|
}
|
|
2387
2449
|
|
|
2450
|
+
enum HostAttendeeActivityType {
|
|
2451
|
+
NoteAdded
|
|
2452
|
+
NoteUpdated
|
|
2453
|
+
TagAdded
|
|
2454
|
+
TagRemoved
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
/// Durable append-only log of host edits to an attendee's CRM profile (notes/tags).
|
|
2458
|
+
/// Everything else in the attendee detail-drawer timeline is read live from its
|
|
2459
|
+
/// existing source table (Ticket, AppliedDiscount, HostCrmEmailSendLog, SmsSendLog)
|
|
2460
|
+
/// rather than duplicated here — see attendeeAudienceApi.ts's `/detail` route.
|
|
2461
|
+
model HostAttendeeActivity {
|
|
2462
|
+
id String @id @default(cuid())
|
|
2463
|
+
hostUserId String
|
|
2464
|
+
attendeeUserId String?
|
|
2465
|
+
attendeeEmail String?
|
|
2466
|
+
type HostAttendeeActivityType
|
|
2467
|
+
detail String? @db.Text
|
|
2468
|
+
createdAt DateTime @default(now())
|
|
2469
|
+
|
|
2470
|
+
host User @relation("HostAttendeeActivityHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2471
|
+
attendee User? @relation("HostAttendeeActivityAttendee", fields: [attendeeUserId], references: [id], onDelete: SetNull)
|
|
2472
|
+
|
|
2473
|
+
@@index([hostUserId, attendeeUserId, createdAt])
|
|
2474
|
+
@@index([hostUserId, attendeeEmail, createdAt])
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2388
2477
|
/// Host-defined reusable CRM audience filters (tag, cadence, lapsed, cohorts).
|
|
2389
2478
|
model HostCrmSavedSegment {
|
|
2390
2479
|
id String @id @default(cuid())
|
|
@@ -2394,7 +2483,8 @@ model HostCrmSavedSegment {
|
|
|
2394
2483
|
createdAt DateTime @default(now())
|
|
2395
2484
|
updatedAt DateTime @updatedAt
|
|
2396
2485
|
|
|
2397
|
-
host
|
|
2486
|
+
host User @relation("HostCrmSavedSegments", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2487
|
+
automations HostCrmAutomation[]
|
|
2398
2488
|
|
|
2399
2489
|
@@unique([hostUserId, name])
|
|
2400
2490
|
@@index([hostUserId])
|
|
@@ -2417,6 +2507,53 @@ model HostCrmEmailSendLog {
|
|
|
2417
2507
|
@@index([email, templateKey, sentAt])
|
|
2418
2508
|
}
|
|
2419
2509
|
|
|
2510
|
+
enum HostCrmAutomationTrigger {
|
|
2511
|
+
Recurring
|
|
2512
|
+
NewlyAtRisk
|
|
2513
|
+
PostEvent
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
enum HostCrmAutomationChannel {
|
|
2517
|
+
Email
|
|
2518
|
+
Sms
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
enum HostCrmAutomationAudienceType {
|
|
2522
|
+
Preset
|
|
2523
|
+
Segment
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
/// Host-defined CRM automation: a named trigger+channel+audience+template
|
|
2527
|
+
/// combo (e.g. "Monthly win-back email to lapsed regulars"). Replaces the
|
|
2528
|
+
/// single hostCrmWinBackAutoEnabled toggle and single postEventCrmTemplate
|
|
2529
|
+
/// pair on UserPreferences with multiple, independently-configurable
|
|
2530
|
+
/// automations per host.
|
|
2531
|
+
model HostCrmAutomation {
|
|
2532
|
+
id String @id @default(cuid())
|
|
2533
|
+
hostUserId String
|
|
2534
|
+
name String
|
|
2535
|
+
trigger HostCrmAutomationTrigger
|
|
2536
|
+
channel HostCrmAutomationChannel
|
|
2537
|
+
/// Ignored for PostEvent — audience there is implicit (that event's ticket holders).
|
|
2538
|
+
audienceType HostCrmAutomationAudienceType?
|
|
2539
|
+
audiencePresetId String?
|
|
2540
|
+
audienceSegmentId String?
|
|
2541
|
+
subject String?
|
|
2542
|
+
body String @db.Text
|
|
2543
|
+
/// Only used by the Recurring trigger.
|
|
2544
|
+
cadenceDays Int? @default(30)
|
|
2545
|
+
enabled Boolean @default(true)
|
|
2546
|
+
lastRunAt DateTime?
|
|
2547
|
+
createdAt DateTime @default(now())
|
|
2548
|
+
updatedAt DateTime @updatedAt
|
|
2549
|
+
|
|
2550
|
+
host User @relation("HostCrmAutomationsHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2551
|
+
segment HostCrmSavedSegment? @relation(fields: [audienceSegmentId], references: [id], onDelete: SetNull)
|
|
2552
|
+
|
|
2553
|
+
@@index([hostUserId])
|
|
2554
|
+
@@index([trigger, enabled])
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2420
2557
|
// Tracks referrals: attendee A referred person B who bought a ticket
|
|
2421
2558
|
model EventReferral {
|
|
2422
2559
|
id String @id @default(cuid())
|
|
@@ -2811,7 +2948,10 @@ model Ticket {
|
|
|
2811
2948
|
lastReminded1hAt DateTime?
|
|
2812
2949
|
lastReminded24hAt DateTime?
|
|
2813
2950
|
lastReminded1weekAt DateTime?
|
|
2951
|
+
lastReminded3hAt DateTime?
|
|
2814
2952
|
refundRequestedAt DateTime? // Set when RefundPending; cleared once refund processes
|
|
2953
|
+
/// Set when the guest keeps or declines after a postponement; cleared on each new Postpone.
|
|
2954
|
+
postponeRespondedAt DateTime?
|
|
2815
2955
|
checkInLocation String?
|
|
2816
2956
|
checkOutLocation String?
|
|
2817
2957
|
checkOutMethod String? @default("manual")
|
|
@@ -3558,8 +3698,14 @@ model User {
|
|
|
3558
3698
|
membershipLastBilledAt DateTime?
|
|
3559
3699
|
membershipFeaturedEventsUsed Int @default(0)
|
|
3560
3700
|
membershipStripeSubscriptionId String? @unique
|
|
3701
|
+
/// App Store originalTransactionId when membership is billed via StoreKit IAP.
|
|
3702
|
+
membershipAppleOriginalTransactionId String? @unique
|
|
3703
|
+
/// Which provider owns the active membership billing relationship.
|
|
3704
|
+
membershipBillingProvider String? // "stripe" | "apple" | null
|
|
3561
3705
|
/// Optional consumer BashPass subscription (waives guest platform fee on tickets; monthly BashPoints).
|
|
3562
3706
|
bashPassStripeSubscriptionId String? @unique
|
|
3707
|
+
/// App Store originalTransactionId when BashPass is billed via StoreKit IAP.
|
|
3708
|
+
bashPassAppleOriginalTransactionId String? @unique
|
|
3563
3709
|
bashPassCurrentPeriodEnd DateTime?
|
|
3564
3710
|
/// Stripe price tier for standalone BashPass; effective tier may also come from membership bundle.
|
|
3565
3711
|
bashPassTier BashPassTier?
|
|
@@ -3793,6 +3939,9 @@ model User {
|
|
|
3793
3939
|
hostCrmSavedSegments HostCrmSavedSegment[] @relation("HostCrmSavedSegments")
|
|
3794
3940
|
hostCrmEmailSendLogsAsHost HostCrmEmailSendLog[] @relation("HostCrmEmailSendLogsHost")
|
|
3795
3941
|
hostCrmEmailSendLogsAsUser HostCrmEmailSendLog[] @relation("HostCrmEmailSendLogsUser")
|
|
3942
|
+
hostCrmAutomations HostCrmAutomation[] @relation("HostCrmAutomationsHost")
|
|
3943
|
+
hostAttendeeActivityAsHost HostAttendeeActivity[] @relation("HostAttendeeActivityHost")
|
|
3944
|
+
hostAttendeeActivityAsTarget HostAttendeeActivity[] @relation("HostAttendeeActivityAttendee")
|
|
3796
3945
|
bashListsCreated BashList[]
|
|
3797
3946
|
userItineraries UserItinerary[]
|
|
3798
3947
|
aiRecommendationLogs AIRecommendationLog[]
|
|
@@ -4298,6 +4447,8 @@ model UserSubscription {
|
|
|
4298
4447
|
stripeAccountId String @unique
|
|
4299
4448
|
stripeSubscriptionId String? @unique
|
|
4300
4449
|
stripeCheckoutSessionId String? @unique
|
|
4450
|
+
/// App Store originalTransactionId when listing seats are billed via StoreKit IAP.
|
|
4451
|
+
appleOriginalTransactionId String? @unique
|
|
4301
4452
|
type MembershipTier?
|
|
4302
4453
|
status UserSubscriptionStatus?
|
|
4303
4454
|
subscriptionCountId String @unique
|
|
@@ -4310,6 +4461,7 @@ model UserSubscription {
|
|
|
4310
4461
|
@@index([ownerId])
|
|
4311
4462
|
@@index([stripeCheckoutSessionId])
|
|
4312
4463
|
@@index([stripeSubscriptionId])
|
|
4464
|
+
@@index([appleOriginalTransactionId])
|
|
4313
4465
|
}
|
|
4314
4466
|
|
|
4315
4467
|
model Service {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
APPLE_BASHPASS_PRODUCTS,
|
|
3
|
+
APPLE_FEATURE_BOOST_PRODUCTS,
|
|
4
|
+
APPLE_IAP_PRODUCTS,
|
|
5
|
+
APPLE_LISTING_PRODUCTS,
|
|
6
|
+
APPLE_MEMBERSHIP_PRODUCTS,
|
|
7
|
+
findAppleMembershipProduct,
|
|
8
|
+
getAppleIapProduct,
|
|
9
|
+
isAppleIapProductId,
|
|
10
|
+
listAppleIapProductIds,
|
|
11
|
+
} from "../appleIapProducts";
|
|
12
|
+
import { MembershipTier } from "@prisma/client";
|
|
13
|
+
|
|
14
|
+
describe("appleIapProducts", () => {
|
|
15
|
+
it("defines unique product IDs across all families", () => {
|
|
16
|
+
const ids = APPLE_IAP_PRODUCTS.map((p) => p.productId);
|
|
17
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("covers membership monthly/yearly/lifetime for paid tiers", () => {
|
|
21
|
+
expect(APPLE_MEMBERSHIP_PRODUCTS).toHaveLength(12);
|
|
22
|
+
expect(
|
|
23
|
+
findAppleMembershipProduct(MembershipTier.Premium, "monthly")?.productId
|
|
24
|
+
).toBe("community.bash.us.membership.premium.monthly");
|
|
25
|
+
expect(
|
|
26
|
+
findAppleMembershipProduct(MembershipTier.Legend, "lifetime")?.productId
|
|
27
|
+
).toBe("community.bash.us.membership.legend.lifetime");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("covers listing Ally/Partner/Patron monthly", () => {
|
|
31
|
+
expect(APPLE_LISTING_PRODUCTS.map((p) => p.tier)).toEqual([
|
|
32
|
+
"Ally",
|
|
33
|
+
"Partner",
|
|
34
|
+
"Patron",
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("covers BashPass and feature boost catalogs", () => {
|
|
39
|
+
expect(APPLE_BASHPASS_PRODUCTS.length).toBe(3);
|
|
40
|
+
expect(APPLE_FEATURE_BOOST_PRODUCTS.length).toBe(5);
|
|
41
|
+
expect(
|
|
42
|
+
getAppleIapProduct("community.bash.us.boost.7d")?.family
|
|
43
|
+
).toBe("feature_boost");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("listAppleIapProductIds filters by family", () => {
|
|
47
|
+
const membershipIds = listAppleIapProductIds("membership");
|
|
48
|
+
expect(membershipIds.every((id) => id.includes(".membership."))).toBe(true);
|
|
49
|
+
expect(isAppleIapProductId(membershipIds[0]!)).toBe(true);
|
|
50
|
+
expect(isAppleIapProductId("not.a.product")).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hostCrmGrowthPackIsComplete,
|
|
3
|
+
hostCrmHasPostEventPackAutomation,
|
|
4
|
+
hostCrmHasWinBackPackAutomation,
|
|
5
|
+
HOST_CRM_GROWTH_PACK_WINBACK_PRESET_ID,
|
|
6
|
+
} from "../hostCrmAutomation.js";
|
|
7
|
+
|
|
8
|
+
describe("host CRM growth pack detection", () => {
|
|
9
|
+
it("detects win-back and post-event pack rows", () => {
|
|
10
|
+
const empty: Array<{ trigger: string; channel: string; audiencePresetId?: string | null }> =
|
|
11
|
+
[];
|
|
12
|
+
expect(hostCrmHasWinBackPackAutomation(empty)).toBe(false);
|
|
13
|
+
expect(hostCrmHasPostEventPackAutomation(empty)).toBe(false);
|
|
14
|
+
expect(hostCrmGrowthPackIsComplete(empty)).toBe(false);
|
|
15
|
+
|
|
16
|
+
const winBack = [
|
|
17
|
+
{
|
|
18
|
+
trigger: "Recurring",
|
|
19
|
+
channel: "Email",
|
|
20
|
+
audiencePresetId: HOST_CRM_GROWTH_PACK_WINBACK_PRESET_ID,
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
expect(hostCrmHasWinBackPackAutomation(winBack)).toBe(true);
|
|
24
|
+
expect(hostCrmGrowthPackIsComplete(winBack)).toBe(false);
|
|
25
|
+
|
|
26
|
+
const both = [
|
|
27
|
+
...winBack,
|
|
28
|
+
{ trigger: "PostEvent", channel: "Email", audiencePresetId: null },
|
|
29
|
+
];
|
|
30
|
+
expect(hostCrmHasPostEventPackAutomation(both)).toBe(true);
|
|
31
|
+
expect(hostCrmGrowthPackIsComplete(both)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyHostCrmSegmentRules,
|
|
3
|
+
HOST_CRM_PRESET_SEGMENTS,
|
|
4
|
+
type HostCrmAudienceMember,
|
|
5
|
+
} from "../hostCrmSegments.js";
|
|
6
|
+
|
|
7
|
+
const NOW = new Date("2026-07-14T12:00:00Z");
|
|
8
|
+
|
|
9
|
+
function member(
|
|
10
|
+
overrides: Partial<HostCrmAudienceMember> & { userId?: string | null } = {}
|
|
11
|
+
): HostCrmAudienceMember {
|
|
12
|
+
return {
|
|
13
|
+
userId: overrides.userId ?? "u1",
|
|
14
|
+
email: "u@example.com",
|
|
15
|
+
name: "Guest",
|
|
16
|
+
image: null,
|
|
17
|
+
eventCount: 1,
|
|
18
|
+
eventTitles: ["Bash"],
|
|
19
|
+
tags: [],
|
|
20
|
+
notes: null,
|
|
21
|
+
profileId: null,
|
|
22
|
+
socialEventCadence: null,
|
|
23
|
+
reachableEmail: true,
|
|
24
|
+
reachableSms: false,
|
|
25
|
+
lastAttendedAt: null,
|
|
26
|
+
firstAttendedAt: null,
|
|
27
|
+
city: null,
|
|
28
|
+
tierTitles: [],
|
|
29
|
+
sourceEventIds: ["evt-a"],
|
|
30
|
+
emailOnly: false,
|
|
31
|
+
totalSpendCents: 0,
|
|
32
|
+
noShowCount: 0,
|
|
33
|
+
discountResponsive: false,
|
|
34
|
+
fastDecider: false,
|
|
35
|
+
seriesLoyalist: false,
|
|
36
|
+
refundChurn: false,
|
|
37
|
+
bringsGuests: false,
|
|
38
|
+
discoverySource: "direct",
|
|
39
|
+
expectedGapDays: null,
|
|
40
|
+
nextExpectedAt: null,
|
|
41
|
+
...overrides,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("applyHostCrmSegmentRules", () => {
|
|
46
|
+
it("filters by sourceEventId (trims whitespace)", () => {
|
|
47
|
+
const audience = [
|
|
48
|
+
member({ userId: "a", sourceEventIds: ["evt-a", "evt-b"] }),
|
|
49
|
+
member({ userId: "b", sourceEventIds: ["evt-c"] }),
|
|
50
|
+
member({ userId: "c", sourceEventIds: undefined }),
|
|
51
|
+
];
|
|
52
|
+
const filtered = applyHostCrmSegmentRules(audience, {
|
|
53
|
+
sourceEventId: " evt-a ",
|
|
54
|
+
});
|
|
55
|
+
expect(filtered.map((r) => r.userId)).toEqual(["a"]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("filters by excludeEventId", () => {
|
|
59
|
+
const audience = [
|
|
60
|
+
member({ userId: "a", sourceEventIds: ["evt-a"] }),
|
|
61
|
+
member({ userId: "b", sourceEventIds: ["evt-b"] }),
|
|
62
|
+
member({ userId: "c", sourceEventIds: ["evt-a", "evt-b"] }),
|
|
63
|
+
];
|
|
64
|
+
const filtered = applyHostCrmSegmentRules(audience, {
|
|
65
|
+
excludeEventId: "evt-a",
|
|
66
|
+
});
|
|
67
|
+
expect(filtered.map((r) => r.userId)).toEqual(["b"]);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("keeps rows with no lastAttendedAt when applying lapsedDays", () => {
|
|
71
|
+
const audience = [
|
|
72
|
+
member({
|
|
73
|
+
userId: "never",
|
|
74
|
+
lastAttendedAt: null,
|
|
75
|
+
}),
|
|
76
|
+
member({
|
|
77
|
+
userId: "recent",
|
|
78
|
+
lastAttendedAt: new Date("2026-07-01T00:00:00Z").toISOString(),
|
|
79
|
+
}),
|
|
80
|
+
member({
|
|
81
|
+
userId: "old",
|
|
82
|
+
lastAttendedAt: new Date("2026-01-01T00:00:00Z").toISOString(),
|
|
83
|
+
}),
|
|
84
|
+
];
|
|
85
|
+
const filtered = applyHostCrmSegmentRules(
|
|
86
|
+
audience,
|
|
87
|
+
{ lapsedDays: 90 },
|
|
88
|
+
NOW
|
|
89
|
+
);
|
|
90
|
+
expect(filtered.map((r) => r.userId).sort()).toEqual(["never", "old"]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("filters by tag, cadence, minEventCount, tierTitle, and city together", () => {
|
|
94
|
+
const audience = [
|
|
95
|
+
member({
|
|
96
|
+
userId: "keep",
|
|
97
|
+
tags: ["vip"],
|
|
98
|
+
socialEventCadence: "Often",
|
|
99
|
+
eventCount: 3,
|
|
100
|
+
tierTitles: ["GA", "VIP"],
|
|
101
|
+
city: "Brooklyn",
|
|
102
|
+
sourceEventIds: ["evt-a"],
|
|
103
|
+
}),
|
|
104
|
+
member({
|
|
105
|
+
userId: "drop-tag",
|
|
106
|
+
tags: ["regular"],
|
|
107
|
+
socialEventCadence: "Often",
|
|
108
|
+
eventCount: 3,
|
|
109
|
+
tierTitles: ["VIP"],
|
|
110
|
+
city: "Brooklyn",
|
|
111
|
+
}),
|
|
112
|
+
member({
|
|
113
|
+
userId: "drop-city",
|
|
114
|
+
tags: ["vip"],
|
|
115
|
+
socialEventCadence: "Often",
|
|
116
|
+
eventCount: 3,
|
|
117
|
+
tierTitles: ["VIP"],
|
|
118
|
+
city: "Queens",
|
|
119
|
+
}),
|
|
120
|
+
];
|
|
121
|
+
const filtered = applyHostCrmSegmentRules(audience, {
|
|
122
|
+
tag: " vip ",
|
|
123
|
+
cadence: "Often",
|
|
124
|
+
minEventCount: 2,
|
|
125
|
+
tierTitle: "vip",
|
|
126
|
+
city: "brook",
|
|
127
|
+
});
|
|
128
|
+
expect(filtered.map((r) => r.userId)).toEqual(["keep"]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("filters by sourceEventIdIn (matches any of the given event ids)", () => {
|
|
132
|
+
const audience = [
|
|
133
|
+
member({ userId: "a", sourceEventIds: ["evt-a"] }),
|
|
134
|
+
member({ userId: "b", sourceEventIds: ["evt-b"] }),
|
|
135
|
+
member({ userId: "c", sourceEventIds: ["evt-c"] }),
|
|
136
|
+
member({ userId: "d", sourceEventIds: undefined }),
|
|
137
|
+
];
|
|
138
|
+
const filtered = applyHostCrmSegmentRules(audience, {
|
|
139
|
+
sourceEventIdIn: ["evt-a", "evt-b"],
|
|
140
|
+
});
|
|
141
|
+
expect(filtered.map((r) => r.userId).sort()).toEqual(["a", "b"]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("ignores an empty sourceEventIdIn array", () => {
|
|
145
|
+
const audience = [member({ userId: "a" })];
|
|
146
|
+
expect(applyHostCrmSegmentRules(audience, { sourceEventIdIn: [] })).toHaveLength(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("ignores blank source/exclude/tier/city/tag strings", () => {
|
|
150
|
+
const audience = [member({ userId: "a" })];
|
|
151
|
+
expect(
|
|
152
|
+
applyHostCrmSegmentRules(audience, {
|
|
153
|
+
tag: " ",
|
|
154
|
+
sourceEventId: "",
|
|
155
|
+
excludeEventId: " ",
|
|
156
|
+
tierTitle: "",
|
|
157
|
+
city: " ",
|
|
158
|
+
})
|
|
159
|
+
).toHaveLength(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("filters by maxEventCount (first-timers style segments)", () => {
|
|
163
|
+
const audience = [
|
|
164
|
+
member({ userId: "first-timer", eventCount: 1 }),
|
|
165
|
+
member({ userId: "repeat", eventCount: 4 }),
|
|
166
|
+
];
|
|
167
|
+
const filtered = applyHostCrmSegmentRules(audience, { maxEventCount: 1 });
|
|
168
|
+
expect(filtered.map((r) => r.userId)).toEqual(["first-timer"]);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("HOST_CRM_PRESET_SEGMENTS", () => {
|
|
173
|
+
it("includes repeat, lapsed, spend, and behavioral presets with expected rules", () => {
|
|
174
|
+
const ids = HOST_CRM_PRESET_SEGMENTS.map((p) => p.id);
|
|
175
|
+
expect(ids).toEqual(
|
|
176
|
+
expect.arrayContaining([
|
|
177
|
+
"repeat_guests",
|
|
178
|
+
"frequent_guest",
|
|
179
|
+
"lapsed_90",
|
|
180
|
+
"lapsed_180",
|
|
181
|
+
"top_spenders",
|
|
182
|
+
"no_show",
|
|
183
|
+
"uses_promo_codes",
|
|
184
|
+
"last_minute_buyer",
|
|
185
|
+
"refund_churn",
|
|
186
|
+
"series_regular",
|
|
187
|
+
"brings_friends",
|
|
188
|
+
"last3Events",
|
|
189
|
+
"first_timers",
|
|
190
|
+
])
|
|
191
|
+
);
|
|
192
|
+
expect(ids).not.toContain("cadence_often");
|
|
193
|
+
expect(
|
|
194
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "repeat_guests")?.rules
|
|
195
|
+
).toEqual({ minEventCount: 2 });
|
|
196
|
+
expect(
|
|
197
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "first_timers")?.rules
|
|
198
|
+
).toEqual({ maxEventCount: 1 });
|
|
199
|
+
expect(
|
|
200
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "top_spenders")?.rules
|
|
201
|
+
).toEqual({});
|
|
202
|
+
expect(
|
|
203
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "no_show")?.rules
|
|
204
|
+
).toEqual({ noShow: true });
|
|
205
|
+
// last3Events is resolved at query time (host's most recently ended
|
|
206
|
+
// events) rather than static rules — see filterHostCrmAudience.
|
|
207
|
+
expect(
|
|
208
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "last3Events")?.rules
|
|
209
|
+
).toEqual({});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe("applyHostCrmSegmentRules — behavioral + spend rules", () => {
|
|
214
|
+
it("filters by minTotalSpendCents, noShow, and the behavioral flags", () => {
|
|
215
|
+
const audience = [
|
|
216
|
+
member({ userId: "spender", totalSpendCents: 50000 }),
|
|
217
|
+
member({ userId: "casual", totalSpendCents: 500 }),
|
|
218
|
+
];
|
|
219
|
+
expect(
|
|
220
|
+
applyHostCrmSegmentRules(audience, { minTotalSpendCents: 10000 }).map(
|
|
221
|
+
(r) => r.userId
|
|
222
|
+
)
|
|
223
|
+
).toEqual(["spender"]);
|
|
224
|
+
|
|
225
|
+
const behavioral = [
|
|
226
|
+
member({ userId: "loyal", seriesLoyalist: true }),
|
|
227
|
+
member({ userId: "not-loyal", seriesLoyalist: false }),
|
|
228
|
+
];
|
|
229
|
+
expect(
|
|
230
|
+
applyHostCrmSegmentRules(behavioral, { seriesLoyalist: true }).map(
|
|
231
|
+
(r) => r.userId
|
|
232
|
+
)
|
|
233
|
+
).toEqual(["loyal"]);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("filters by healthStatus when relationshipHealth is attached to the row", () => {
|
|
237
|
+
const audience = [
|
|
238
|
+
{ ...member({ userId: "risky" }), relationshipHealth: { status: "AtRisk", reasons: [], daysSinceLastAttended: null, expectedGapDays: null } },
|
|
239
|
+
{ ...member({ userId: "steady" }), relationshipHealth: { status: "Steady", reasons: [], daysSinceLastAttended: null, expectedGapDays: null } },
|
|
240
|
+
];
|
|
241
|
+
expect(
|
|
242
|
+
applyHostCrmSegmentRules(audience, { healthStatus: "AtRisk" }).map(
|
|
243
|
+
(r) => r.userId
|
|
244
|
+
)
|
|
245
|
+
).toEqual(["risky"]);
|
|
246
|
+
});
|
|
247
|
+
});
|