@bash-app/bash-common 30.314.0 → 30.316.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__/hostCrmSegments.test.d.ts +2 -0
- package/dist/__tests__/hostCrmSegments.test.d.ts.map +1 -0
- package/dist/__tests__/hostCrmSegments.test.js +188 -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/hostCrmAutomation.d.ts +64 -0
- package/dist/hostCrmAutomation.d.ts.map +1 -0
- package/dist/hostCrmAutomation.js +21 -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 +80 -2
- package/dist/hostCrmSegments.d.ts.map +1 -1
- package/dist/hostCrmSegments.js +84 -3
- package/dist/hostCrmSegments.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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/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 +126 -1
- package/src/__tests__/hostCrmSegments.test.ts +234 -0
- package/src/__tests__/relationshipHealth.test.ts +370 -0
- package/src/hostCrmAutomation.ts +79 -0
- package/src/hostCrmInsights.ts +39 -0
- package/src/hostCrmSegments.ts +196 -7
- package/src/index.ts +3 -0
- package/src/membershipDefinitions.ts +3 -0
- package/src/relationshipHealth.ts +134 -0
- package/src/utils/__tests__/slugUtils.test.ts +15 -0
- package/src/utils/slugUtils.ts +13 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals";
|
|
2
2
|
import { BASH_DETAIL_URL, BASH_EVENT_WIZARD_HOW_MAJOR_STEP, BASH_EVENT_WIZARD_HOW_SUBSTEP_PAYOUT, BASH_EVENT_WIZARD_PATH_PREFIX, } from "../../definitions.js";
|
|
3
|
-
import { generateSlug, generateUniqueSlug, generateBashDetailUrl, getPublicBashDetailPath, getBashEventWizardStepPath, getBashEventWizardPayoutStepPath, parseBashUrlParams, validateSlug, } from "../slugUtils.js";
|
|
3
|
+
import { generateSlug, generateUniqueSlug, generateBashDetailUrl, getPublicBashDetailPath, getPublicBashMemoriesPath, getBashEventWizardStepPath, getBashEventWizardPayoutStepPath, parseBashUrlParams, validateSlug, } from "../slugUtils.js";
|
|
4
4
|
describe("generateSlug", () => {
|
|
5
5
|
test("lowercases and slugifies a title", () => {
|
|
6
6
|
expect(generateSlug("Summer Bash 2026!")).toBe("summer-bash-2026");
|
|
@@ -50,6 +50,14 @@ describe("getPublicBashDetailPath", () => {
|
|
|
50
50
|
expect(getPublicBashDetailPath("abc123", " ")).toBe(`${BASH_DETAIL_URL}/abc123`);
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
|
+
describe("getPublicBashMemoriesPath", () => {
|
|
54
|
+
test("nests /memories under the slug detail path", () => {
|
|
55
|
+
expect(getPublicBashMemoriesPath("abc123", "summer-bash")).toBe("/bash/abc123-summer-bash/memories");
|
|
56
|
+
});
|
|
57
|
+
test("nests /memories under the id-only path when slug is missing", () => {
|
|
58
|
+
expect(getPublicBashMemoriesPath("abc123", null)).toBe(`${BASH_DETAIL_URL}/abc123/memories`);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
53
61
|
describe("getBashEventWizardStepPath", () => {
|
|
54
62
|
test("builds wizard step path", () => {
|
|
55
63
|
expect(getBashEventWizardStepPath("evt-1", 3, 2)).toBe(`${BASH_EVENT_WIZARD_PATH_PREFIX}/evt-1/3/2`);
|
|
@@ -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
|
@@ -2357,6 +2357,47 @@ model EventAlbumItem {
|
|
|
2357
2357
|
@@index([bashEventId])
|
|
2358
2358
|
}
|
|
2359
2359
|
|
|
2360
|
+
/// Curated Memories recap for a finished bash — best few photos/badge
|
|
2361
|
+
/// highlights, generated once by the memories cron (see
|
|
2362
|
+
/// api/src/utils/memories/) rather than recomputed per view, so the "moment"
|
|
2363
|
+
/// a host/attendee sees on a shared link stays stable over time. `viewCount`
|
|
2364
|
+
/// / `shareCount` back the share-rate instrumentation required before this
|
|
2365
|
+
/// feature is considered proven as a growth loop.
|
|
2366
|
+
model BashMemories {
|
|
2367
|
+
id String @id @default(cuid())
|
|
2368
|
+
bashEventId String @unique
|
|
2369
|
+
/// Best few photo URLs (capped at generation time — this is a curated
|
|
2370
|
+
/// handful, not every photo tagged to the event).
|
|
2371
|
+
photoUrls String[] @default([])
|
|
2372
|
+
/// Curated badge/award highlights: Array<{ name, imageUrl, awardCount }>.
|
|
2373
|
+
badgeHighlights Json @default("[]")
|
|
2374
|
+
attendeeCount Int @default(0)
|
|
2375
|
+
generatedAt DateTime @default(now())
|
|
2376
|
+
notifiedAt DateTime?
|
|
2377
|
+
viewCount Int @default(0)
|
|
2378
|
+
shareCount Int @default(0)
|
|
2379
|
+
createdAt DateTime @default(now())
|
|
2380
|
+
updatedAt DateTime @updatedAt
|
|
2381
|
+
|
|
2382
|
+
@@index([bashEventId])
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
/// "Tag your friends who were there" on a public Memories recap — a lightweight,
|
|
2386
|
+
/// event-scoped social edge (Zuck: "every tag is a new edge in the graph").
|
|
2387
|
+
/// Deliberately not wired into `UserFollowing` (that model's `isOrganizer` follow
|
|
2388
|
+
/// carries specific "notify me of new bashes" semantics a casual recap tag shouldn't imply).
|
|
2389
|
+
model BashMemoriesTag {
|
|
2390
|
+
id String @id @default(cuid())
|
|
2391
|
+
bashEventId String
|
|
2392
|
+
taggedByUserId String
|
|
2393
|
+
taggedUserId String
|
|
2394
|
+
createdAt DateTime @default(now())
|
|
2395
|
+
|
|
2396
|
+
@@unique([bashEventId, taggedByUserId, taggedUserId])
|
|
2397
|
+
@@index([bashEventId])
|
|
2398
|
+
@@index([taggedUserId])
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2360
2401
|
/// Post-event networking opt-in: attendees who agreed to be discoverable to others who went.
|
|
2361
2402
|
model EventNetworkingOptIn {
|
|
2362
2403
|
id String @id @default(cuid())
|
|
@@ -2379,12 +2420,45 @@ model HostAttendeeProfile {
|
|
|
2379
2420
|
createdAt DateTime @default(now())
|
|
2380
2421
|
updatedAt DateTime @updatedAt
|
|
2381
2422
|
|
|
2423
|
+
/// Relationship Health status as of the last time this host's CRM was read;
|
|
2424
|
+
/// compared to the freshly-computed status on each read to detect a
|
|
2425
|
+
/// Cooling/AtRisk transition for the host-approved win-back recommendation.
|
|
2426
|
+
lastHealthStatusSeen String?
|
|
2427
|
+
lastHealthStatusSeenAt DateTime?
|
|
2428
|
+
|
|
2382
2429
|
host User @relation("HostAttendeeProfilesOwned", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2383
2430
|
attendee User? @relation("HostAttendeeProfilesTarget", fields: [attendeeUserId], references: [id], onDelete: SetNull)
|
|
2384
2431
|
|
|
2385
2432
|
@@index([hostUserId])
|
|
2386
2433
|
}
|
|
2387
2434
|
|
|
2435
|
+
enum HostAttendeeActivityType {
|
|
2436
|
+
NoteAdded
|
|
2437
|
+
NoteUpdated
|
|
2438
|
+
TagAdded
|
|
2439
|
+
TagRemoved
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
/// Durable append-only log of host edits to an attendee's CRM profile (notes/tags).
|
|
2443
|
+
/// Everything else in the attendee detail-drawer timeline is read live from its
|
|
2444
|
+
/// existing source table (Ticket, AppliedDiscount, HostCrmEmailSendLog, SmsSendLog)
|
|
2445
|
+
/// rather than duplicated here — see attendeeAudienceApi.ts's `/detail` route.
|
|
2446
|
+
model HostAttendeeActivity {
|
|
2447
|
+
id String @id @default(cuid())
|
|
2448
|
+
hostUserId String
|
|
2449
|
+
attendeeUserId String?
|
|
2450
|
+
attendeeEmail String?
|
|
2451
|
+
type HostAttendeeActivityType
|
|
2452
|
+
detail String? @db.Text
|
|
2453
|
+
createdAt DateTime @default(now())
|
|
2454
|
+
|
|
2455
|
+
host User @relation("HostAttendeeActivityHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2456
|
+
attendee User? @relation("HostAttendeeActivityAttendee", fields: [attendeeUserId], references: [id], onDelete: SetNull)
|
|
2457
|
+
|
|
2458
|
+
@@index([hostUserId, attendeeUserId, createdAt])
|
|
2459
|
+
@@index([hostUserId, attendeeEmail, createdAt])
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2388
2462
|
/// Host-defined reusable CRM audience filters (tag, cadence, lapsed, cohorts).
|
|
2389
2463
|
model HostCrmSavedSegment {
|
|
2390
2464
|
id String @id @default(cuid())
|
|
@@ -2394,7 +2468,8 @@ model HostCrmSavedSegment {
|
|
|
2394
2468
|
createdAt DateTime @default(now())
|
|
2395
2469
|
updatedAt DateTime @updatedAt
|
|
2396
2470
|
|
|
2397
|
-
host
|
|
2471
|
+
host User @relation("HostCrmSavedSegments", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2472
|
+
automations HostCrmAutomation[]
|
|
2398
2473
|
|
|
2399
2474
|
@@unique([hostUserId, name])
|
|
2400
2475
|
@@index([hostUserId])
|
|
@@ -2417,6 +2492,53 @@ model HostCrmEmailSendLog {
|
|
|
2417
2492
|
@@index([email, templateKey, sentAt])
|
|
2418
2493
|
}
|
|
2419
2494
|
|
|
2495
|
+
enum HostCrmAutomationTrigger {
|
|
2496
|
+
Recurring
|
|
2497
|
+
NewlyAtRisk
|
|
2498
|
+
PostEvent
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
enum HostCrmAutomationChannel {
|
|
2502
|
+
Email
|
|
2503
|
+
Sms
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
enum HostCrmAutomationAudienceType {
|
|
2507
|
+
Preset
|
|
2508
|
+
Segment
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
/// Host-defined CRM automation: a named trigger+channel+audience+template
|
|
2512
|
+
/// combo (e.g. "Monthly win-back email to lapsed regulars"). Replaces the
|
|
2513
|
+
/// single hostCrmWinBackAutoEnabled toggle and single postEventCrmTemplate
|
|
2514
|
+
/// pair on UserPreferences with multiple, independently-configurable
|
|
2515
|
+
/// automations per host.
|
|
2516
|
+
model HostCrmAutomation {
|
|
2517
|
+
id String @id @default(cuid())
|
|
2518
|
+
hostUserId String
|
|
2519
|
+
name String
|
|
2520
|
+
trigger HostCrmAutomationTrigger
|
|
2521
|
+
channel HostCrmAutomationChannel
|
|
2522
|
+
/// Ignored for PostEvent — audience there is implicit (that event's ticket holders).
|
|
2523
|
+
audienceType HostCrmAutomationAudienceType?
|
|
2524
|
+
audiencePresetId String?
|
|
2525
|
+
audienceSegmentId String?
|
|
2526
|
+
subject String?
|
|
2527
|
+
body String @db.Text
|
|
2528
|
+
/// Only used by the Recurring trigger.
|
|
2529
|
+
cadenceDays Int? @default(30)
|
|
2530
|
+
enabled Boolean @default(true)
|
|
2531
|
+
lastRunAt DateTime?
|
|
2532
|
+
createdAt DateTime @default(now())
|
|
2533
|
+
updatedAt DateTime @updatedAt
|
|
2534
|
+
|
|
2535
|
+
host User @relation("HostCrmAutomationsHost", fields: [hostUserId], references: [id], onDelete: Cascade)
|
|
2536
|
+
segment HostCrmSavedSegment? @relation(fields: [audienceSegmentId], references: [id], onDelete: SetNull)
|
|
2537
|
+
|
|
2538
|
+
@@index([hostUserId])
|
|
2539
|
+
@@index([trigger, enabled])
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2420
2542
|
// Tracks referrals: attendee A referred person B who bought a ticket
|
|
2421
2543
|
model EventReferral {
|
|
2422
2544
|
id String @id @default(cuid())
|
|
@@ -3793,6 +3915,9 @@ model User {
|
|
|
3793
3915
|
hostCrmSavedSegments HostCrmSavedSegment[] @relation("HostCrmSavedSegments")
|
|
3794
3916
|
hostCrmEmailSendLogsAsHost HostCrmEmailSendLog[] @relation("HostCrmEmailSendLogsHost")
|
|
3795
3917
|
hostCrmEmailSendLogsAsUser HostCrmEmailSendLog[] @relation("HostCrmEmailSendLogsUser")
|
|
3918
|
+
hostCrmAutomations HostCrmAutomation[] @relation("HostCrmAutomationsHost")
|
|
3919
|
+
hostAttendeeActivityAsHost HostAttendeeActivity[] @relation("HostAttendeeActivityHost")
|
|
3920
|
+
hostAttendeeActivityAsTarget HostAttendeeActivity[] @relation("HostAttendeeActivityAttendee")
|
|
3796
3921
|
bashListsCreated BashList[]
|
|
3797
3922
|
userItineraries UserItinerary[]
|
|
3798
3923
|
aiRecommendationLogs AIRecommendationLog[]
|
|
@@ -0,0 +1,234 @@
|
|
|
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
|
+
|
|
163
|
+
describe("HOST_CRM_PRESET_SEGMENTS", () => {
|
|
164
|
+
it("includes repeat, lapsed, spend, and behavioral presets with expected rules", () => {
|
|
165
|
+
const ids = HOST_CRM_PRESET_SEGMENTS.map((p) => p.id);
|
|
166
|
+
expect(ids).toEqual(
|
|
167
|
+
expect.arrayContaining([
|
|
168
|
+
"repeat_guests",
|
|
169
|
+
"frequent_guest",
|
|
170
|
+
"lapsed_90",
|
|
171
|
+
"lapsed_180",
|
|
172
|
+
"top_spenders",
|
|
173
|
+
"no_show",
|
|
174
|
+
"uses_promo_codes",
|
|
175
|
+
"last_minute_buyer",
|
|
176
|
+
"refund_churn",
|
|
177
|
+
"series_regular",
|
|
178
|
+
"brings_friends",
|
|
179
|
+
"last3Events",
|
|
180
|
+
])
|
|
181
|
+
);
|
|
182
|
+
expect(ids).not.toContain("cadence_often");
|
|
183
|
+
expect(
|
|
184
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "repeat_guests")?.rules
|
|
185
|
+
).toEqual({ minEventCount: 2 });
|
|
186
|
+
expect(
|
|
187
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "top_spenders")?.rules
|
|
188
|
+
).toEqual({});
|
|
189
|
+
expect(
|
|
190
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "no_show")?.rules
|
|
191
|
+
).toEqual({ noShow: true });
|
|
192
|
+
// last3Events is resolved at query time (host's most recently ended
|
|
193
|
+
// events) rather than static rules — see filterHostCrmAudience.
|
|
194
|
+
expect(
|
|
195
|
+
HOST_CRM_PRESET_SEGMENTS.find((p) => p.id === "last3Events")?.rules
|
|
196
|
+
).toEqual({});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe("applyHostCrmSegmentRules — behavioral + spend rules", () => {
|
|
201
|
+
it("filters by minTotalSpendCents, noShow, and the behavioral flags", () => {
|
|
202
|
+
const audience = [
|
|
203
|
+
member({ userId: "spender", totalSpendCents: 50000 }),
|
|
204
|
+
member({ userId: "casual", totalSpendCents: 500 }),
|
|
205
|
+
];
|
|
206
|
+
expect(
|
|
207
|
+
applyHostCrmSegmentRules(audience, { minTotalSpendCents: 10000 }).map(
|
|
208
|
+
(r) => r.userId
|
|
209
|
+
)
|
|
210
|
+
).toEqual(["spender"]);
|
|
211
|
+
|
|
212
|
+
const behavioral = [
|
|
213
|
+
member({ userId: "loyal", seriesLoyalist: true }),
|
|
214
|
+
member({ userId: "not-loyal", seriesLoyalist: false }),
|
|
215
|
+
];
|
|
216
|
+
expect(
|
|
217
|
+
applyHostCrmSegmentRules(behavioral, { seriesLoyalist: true }).map(
|
|
218
|
+
(r) => r.userId
|
|
219
|
+
)
|
|
220
|
+
).toEqual(["loyal"]);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("filters by healthStatus when relationshipHealth is attached to the row", () => {
|
|
224
|
+
const audience = [
|
|
225
|
+
{ ...member({ userId: "risky" }), relationshipHealth: { status: "AtRisk", reasons: [], daysSinceLastAttended: null, expectedGapDays: null } },
|
|
226
|
+
{ ...member({ userId: "steady" }), relationshipHealth: { status: "Steady", reasons: [], daysSinceLastAttended: null, expectedGapDays: null } },
|
|
227
|
+
];
|
|
228
|
+
expect(
|
|
229
|
+
applyHostCrmSegmentRules(audience, { healthStatus: "AtRisk" }).map(
|
|
230
|
+
(r) => r.userId
|
|
231
|
+
)
|
|
232
|
+
).toEqual(["risky"]);
|
|
233
|
+
});
|
|
234
|
+
});
|