@bash-app/bash-common 30.406.0 → 30.410.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__/partnerDealflow.test.d.ts +2 -0
- package/dist/__tests__/partnerDealflow.test.d.ts.map +1 -0
- package/dist/__tests__/partnerDealflow.test.js +51 -0
- package/dist/__tests__/partnerDealflow.test.js.map +1 -0
- package/dist/__tests__/partnerRevenueShareOptIn.test.d.ts +2 -0
- package/dist/__tests__/partnerRevenueShareOptIn.test.d.ts.map +1 -0
- package/dist/__tests__/partnerRevenueShareOptIn.test.js +55 -0
- package/dist/__tests__/partnerRevenueShareOptIn.test.js.map +1 -0
- package/dist/__tests__/reservedCustomBashEventType.test.d.ts +2 -0
- package/dist/__tests__/reservedCustomBashEventType.test.d.ts.map +1 -0
- package/dist/__tests__/reservedCustomBashEventType.test.js +19 -0
- package/dist/__tests__/reservedCustomBashEventType.test.js.map +1 -0
- package/dist/__tests__/serviceListingDealPreferences.test.d.ts +2 -0
- package/dist/__tests__/serviceListingDealPreferences.test.d.ts.map +1 -0
- package/dist/__tests__/serviceListingDealPreferences.test.js +84 -0
- package/dist/__tests__/serviceListingDealPreferences.test.js.map +1 -0
- package/dist/definitions.d.ts +13 -2
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +57 -2
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +97 -4
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +35 -0
- package/dist/extendedSchemas.js.map +1 -1
- package/dist/homepageSyntheticRows.d.ts +9 -0
- package/dist/homepageSyntheticRows.d.ts.map +1 -0
- package/dist/homepageSyntheticRows.js +26 -0
- package/dist/homepageSyntheticRows.js.map +1 -0
- 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/sideQuestTypes.d.ts +1 -1
- package/dist/sideQuestTypes.d.ts.map +1 -1
- package/dist/utils/service/partnerDealflow.d.ts +12 -0
- package/dist/utils/service/partnerDealflow.d.ts.map +1 -0
- package/dist/utils/service/partnerDealflow.js +33 -0
- package/dist/utils/service/partnerDealflow.js.map +1 -0
- package/dist/utils/service/partnerRevenueShareOptIn.d.ts +18 -0
- package/dist/utils/service/partnerRevenueShareOptIn.d.ts.map +1 -0
- package/dist/utils/service/partnerRevenueShareOptIn.js +46 -0
- package/dist/utils/service/partnerRevenueShareOptIn.js.map +1 -0
- package/dist/utils/service/serviceListingDealPreferences.d.ts +28 -0
- package/dist/utils/service/serviceListingDealPreferences.d.ts.map +1 -0
- package/dist/utils/service/serviceListingDealPreferences.js +126 -0
- package/dist/utils/service/serviceListingDealPreferences.js.map +1 -0
- package/package.json +5 -2
- package/prisma/schema.prisma +67 -1
- package/src/__tests__/partnerDealflow.test.ts +62 -0
- package/src/__tests__/partnerRevenueShareOptIn.test.ts +68 -0
- package/src/__tests__/reservedCustomBashEventType.test.ts +27 -0
- package/src/__tests__/serviceListingDealPreferences.test.ts +112 -0
- package/src/definitions.ts +67 -2
- package/src/extendedSchemas.ts +51 -4
- package/src/homepageSyntheticRows.ts +32 -0
- package/src/index.ts +4 -0
- package/src/sideQuestTypes.ts +1 -1
- package/src/utils/service/partnerDealflow.ts +47 -0
- package/src/utils/service/partnerRevenueShareOptIn.ts +70 -0
- package/src/utils/service/serviceListingDealPreferences.ts +184 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { readPartnerDealflowFields, serviceListingOpenToInKindDeal, } from "./partnerDealflow.js";
|
|
2
|
+
import { readPartnerRevenueShareFields, REVENUE_SHARE_WITH_HOST_BASIS_LABEL, serviceListingOpenToRevenueShare, } from "./partnerRevenueShareOptIn.js";
|
|
3
|
+
function formatUsdFromCents(cents) {
|
|
4
|
+
const dollars = cents / 100;
|
|
5
|
+
return dollars % 1 === 0 ? `$${dollars.toFixed(0)}` : `$${dollars.toFixed(2)}`;
|
|
6
|
+
}
|
|
7
|
+
function minimumCashLabel(deal) {
|
|
8
|
+
if (deal?.dealRequiresMinimumCash !== true)
|
|
9
|
+
return null;
|
|
10
|
+
if (typeof deal.dealMinimumCashCents !== "number" || deal.dealMinimumCashCents <= 0) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return `${formatUsdFromCents(deal.dealMinimumCashCents)} min cash`;
|
|
14
|
+
}
|
|
15
|
+
function truncateOffer(text, maxLen) {
|
|
16
|
+
const t = text.trim();
|
|
17
|
+
if (t.length <= maxLen)
|
|
18
|
+
return t;
|
|
19
|
+
return `${t.slice(0, maxLen - 1).trim()}…`;
|
|
20
|
+
}
|
|
21
|
+
/** Compact chips for cards and list rows. */
|
|
22
|
+
export function getServiceListingDealBadges(service) {
|
|
23
|
+
const badges = [];
|
|
24
|
+
const deal = readPartnerDealflowFields(service);
|
|
25
|
+
if (serviceListingOpenToRevenueShare(service)) {
|
|
26
|
+
badges.push({ id: "rev-share", label: "Rev share" });
|
|
27
|
+
}
|
|
28
|
+
if (serviceListingOpenToInKindDeal(service)) {
|
|
29
|
+
badges.push({ id: "in-kind", label: "In-kind" });
|
|
30
|
+
}
|
|
31
|
+
const minCash = minimumCashLabel(deal);
|
|
32
|
+
if (minCash) {
|
|
33
|
+
badges.push({ id: "min-cash", label: minCash });
|
|
34
|
+
}
|
|
35
|
+
const hostBidOpen = service.vendor?.preferences?.openToHostBids === true ||
|
|
36
|
+
service.exhibitor?.openToHostBids === true ||
|
|
37
|
+
service.sponsor?.openToHostBids === true;
|
|
38
|
+
if (hostBidOpen) {
|
|
39
|
+
badges.push({ id: "host-bids", label: "Host bids" });
|
|
40
|
+
}
|
|
41
|
+
return badges;
|
|
42
|
+
}
|
|
43
|
+
/** Longer copy for public detail pages. */
|
|
44
|
+
export function getServiceListingDealDetailRows(service) {
|
|
45
|
+
const rows = [];
|
|
46
|
+
const deal = readPartnerDealflowFields(service);
|
|
47
|
+
const rev = readPartnerRevenueShareFields(service);
|
|
48
|
+
const venue = service.venue;
|
|
49
|
+
if (venue?.revenueShareEnabled === true && venue.revenueShareHostPercentage != null) {
|
|
50
|
+
rows.push({
|
|
51
|
+
label: "Venue revenue share",
|
|
52
|
+
value: `Host keeps ${venue.revenueShareHostPercentage}% of agreed ticket or bar revenue.`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else if (rev?.openToRevenueShareWithHost === true) {
|
|
56
|
+
const pct = rev.revenueShareWithHostPercentage;
|
|
57
|
+
const basis = rev.revenueShareWithHostBasis;
|
|
58
|
+
const basisLabel = basis && basis in REVENUE_SHARE_WITH_HOST_BASIS_LABEL
|
|
59
|
+
? REVENUE_SHARE_WITH_HOST_BASIS_LABEL[basis]
|
|
60
|
+
: null;
|
|
61
|
+
rows.push({
|
|
62
|
+
label: "Revenue share with host",
|
|
63
|
+
value: typeof pct === "number"
|
|
64
|
+
? `Shares ${pct}% with the host${basisLabel ? ` (${basisLabel.toLowerCase()})` : ""}.`
|
|
65
|
+
: "Open to sharing revenue with the host (percentage not set).",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (serviceListingOpenToInKindDeal(service) && deal?.inKindDealOfferText) {
|
|
69
|
+
const est = typeof deal.inKindEstimatedValueCents === "number" &&
|
|
70
|
+
deal.inKindEstimatedValueCents > 0
|
|
71
|
+
? ` Approx. value ${formatUsdFromCents(deal.inKindEstimatedValueCents)}.`
|
|
72
|
+
: "";
|
|
73
|
+
rows.push({
|
|
74
|
+
label: "In-kind / trade offer",
|
|
75
|
+
value: `${deal.inKindDealOfferText.trim()}${est}`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const minCash = minimumCashLabel(deal);
|
|
79
|
+
if (minCash) {
|
|
80
|
+
rows.push({
|
|
81
|
+
label: "Minimum cash on hybrid deals",
|
|
82
|
+
value: `Requires ${minCash.replace(" min cash", "")} minimum cash alongside rev-share or in-kind terms so hosts know you're serious.`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return rows;
|
|
86
|
+
}
|
|
87
|
+
/** Card preview line for in-kind (truncated). */
|
|
88
|
+
export function getServiceListingInKindPreview(service, maxLen = 80) {
|
|
89
|
+
const deal = readPartnerDealflowFields(service);
|
|
90
|
+
if (!serviceListingOpenToInKindDeal(service) || !deal?.inKindDealOfferText) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return truncateOffer(deal.inKindDealOfferText, maxLen);
|
|
94
|
+
}
|
|
95
|
+
/** Copy live listing deal prefs onto a booking at confirmation time. */
|
|
96
|
+
export function buildPartnerDealflowBookingSnapshot(service) {
|
|
97
|
+
const rev = readPartnerRevenueShareFields(service);
|
|
98
|
+
const deal = readPartnerDealflowFields(service);
|
|
99
|
+
const venue = service.venue;
|
|
100
|
+
const snap = {};
|
|
101
|
+
if (venue?.revenueShareEnabled === true) {
|
|
102
|
+
snap.openToRevenueShareWithHostSnapshot = true;
|
|
103
|
+
snap.revenueShareWithHostPercentageSnapshot =
|
|
104
|
+
venue.revenueShareHostPercentage ?? null;
|
|
105
|
+
snap.revenueShareWithHostBasisSnapshot = venue.revenueShareBasis ?? null;
|
|
106
|
+
}
|
|
107
|
+
else if (rev?.openToRevenueShareWithHost === true) {
|
|
108
|
+
snap.openToRevenueShareWithHostSnapshot = true;
|
|
109
|
+
snap.revenueShareWithHostPercentageSnapshot =
|
|
110
|
+
rev.revenueShareWithHostPercentage ?? null;
|
|
111
|
+
snap.revenueShareWithHostBasisSnapshot =
|
|
112
|
+
rev.revenueShareWithHostBasis ?? null;
|
|
113
|
+
}
|
|
114
|
+
if (deal?.openToInKindDeals === true) {
|
|
115
|
+
snap.openToInKindDealsSnapshot = true;
|
|
116
|
+
snap.inKindDealOfferTextSnapshot = deal.inKindDealOfferText?.trim() ?? null;
|
|
117
|
+
snap.inKindEstimatedValueCentsSnapshot =
|
|
118
|
+
deal.inKindEstimatedValueCents ?? null;
|
|
119
|
+
}
|
|
120
|
+
if (deal?.dealRequiresMinimumCash === true) {
|
|
121
|
+
snap.dealRequiresMinimumCashSnapshot = true;
|
|
122
|
+
snap.dealMinimumCashCentsSnapshot = deal.dealMinimumCashCents ?? null;
|
|
123
|
+
}
|
|
124
|
+
return snap;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=serviceListingDealPreferences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceListingDealPreferences.js","sourceRoot":"","sources":["../../../src/utils/service/serviceListingDealPreferences.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bash-app/bash-common",
|
|
3
|
-
"version": "30.
|
|
3
|
+
"version": "30.410.0",
|
|
4
4
|
"description": "Common data and scripts to use on the frontend and backend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@aws-sdk/client-textract": "^3.1004.0",
|
|
38
38
|
"@aws-sdk/s3-request-presigner": "^3.1004.0",
|
|
39
39
|
"@types/qrcode": "^1.5.5",
|
|
40
|
-
"lodash-es": "
|
|
40
|
+
"lodash-es": "4.18.1",
|
|
41
41
|
"mixpanel-browser": "^2.75.0",
|
|
42
42
|
"qrcode": "^1.5.3",
|
|
43
43
|
"slugify": "^1.6.6"
|
|
@@ -76,5 +76,8 @@
|
|
|
76
76
|
],
|
|
77
77
|
"publishConfig": {
|
|
78
78
|
"access": "public"
|
|
79
|
+
},
|
|
80
|
+
"overrides": {
|
|
81
|
+
"lodash-es": "4.18.1"
|
|
79
82
|
}
|
|
80
83
|
}
|
package/prisma/schema.prisma
CHANGED
|
@@ -549,6 +549,8 @@ model EventTask {
|
|
|
549
549
|
status TaskStatus?
|
|
550
550
|
neededCount Int? // Number of volunteers needed
|
|
551
551
|
estimatedDuration Int? // Estimated duration in minutes
|
|
552
|
+
doBy DateTime? // Suggested "do this by" — stay-ahead, not a deadline
|
|
553
|
+
dueBy DateTime? // Hard deadline for the volunteer
|
|
552
554
|
createdAt DateTime? @default(now())
|
|
553
555
|
organizationId String? // Organization association
|
|
554
556
|
assignedTo User? @relation("TasksAssignedToMe", fields: [assignedToId], references: [id], onDelete: Cascade)
|
|
@@ -1003,6 +1005,12 @@ model BashEvent {
|
|
|
1003
1005
|
allowAttendeeRefundRequests Boolean @default(true)
|
|
1004
1006
|
/// Free-text cancellation / refund policy shown at checkout and on the guest's ticket.
|
|
1005
1007
|
cancellationPolicy String?
|
|
1008
|
+
/// When true, the cancellation policy block is hidden on the bash detail page (default false for ticketed events, effectively true for RSVP).
|
|
1009
|
+
hideCancellationPolicy Boolean @default(false)
|
|
1010
|
+
/// When true, the "Group up" section is hidden on the bash detail page.
|
|
1011
|
+
hideGroupUp Boolean @default(false)
|
|
1012
|
+
/// When true, the "Services open to" box is hidden on the bash detail page.
|
|
1013
|
+
hideServicesOpenTo Boolean @default(false)
|
|
1006
1014
|
/// When true (default), cancelling the bash auto-refunds paid ticket holders. Hosts may set false only before any ticket/RSVP exists.
|
|
1007
1015
|
refundsGuaranteedOnCancellation Boolean @default(true)
|
|
1008
1016
|
/// When true, host may postpone after tickets exist. May only flip false→true before any ticket/RSVP exists.
|
|
@@ -1059,7 +1067,7 @@ model BashEvent {
|
|
|
1059
1067
|
topPromoterPrize String? // Optional prize label: "Free table for your crew"
|
|
1060
1068
|
promoterLeaderboardIsPublic Boolean @default(false) // Host opts in to public leaderboard
|
|
1061
1069
|
/// When true, the bash detail promo-code request CTA is hidden from guests (in-app notifications still work for hosts).
|
|
1062
|
-
hideGuestPromoCodeRequest Boolean @default(
|
|
1070
|
+
hideGuestPromoCodeRequest Boolean @default(true)
|
|
1063
1071
|
showAttendees Boolean @default(true)
|
|
1064
1072
|
/// Host opt-in: show soft "public interest" controls on the bash detail page (signed-in viewers). Not a guest list; invite RSVP stays separate.
|
|
1065
1073
|
allowPublicInterestRsvp Boolean @default(false)
|
|
@@ -5892,6 +5900,14 @@ model EventService {
|
|
|
5892
5900
|
fulfillmentModes String[] @default([])
|
|
5893
5901
|
/// Minimum advance-notice days required for F&B bookings (e.g. 7 = one week).
|
|
5894
5902
|
leadTimeDays Int?
|
|
5903
|
+
openToRevenueShareWithHost Boolean @default(false)
|
|
5904
|
+
revenueShareWithHostPercentage Int?
|
|
5905
|
+
revenueShareWithHostBasis String?
|
|
5906
|
+
openToInKindDeals Boolean @default(false)
|
|
5907
|
+
inKindDealOfferText String?
|
|
5908
|
+
inKindEstimatedValueCents Int?
|
|
5909
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
5910
|
+
dealMinimumCashCents Int?
|
|
5895
5911
|
crowdSize AmountOfGuests? @relation(fields: [crowdSizeId], references: [id], onDelete: Cascade)
|
|
5896
5912
|
serviceRange ServiceRange? @relation(fields: [serviceRangeId], references: [id])
|
|
5897
5913
|
service Service?
|
|
@@ -5950,6 +5966,14 @@ model EntertainmentService {
|
|
|
5950
5966
|
minimumQuantity Int?
|
|
5951
5967
|
tierPricing Json?
|
|
5952
5968
|
pricingNotes String?
|
|
5969
|
+
openToRevenueShareWithHost Boolean @default(false)
|
|
5970
|
+
revenueShareWithHostPercentage Int?
|
|
5971
|
+
revenueShareWithHostBasis String?
|
|
5972
|
+
openToInKindDeals Boolean @default(false)
|
|
5973
|
+
inKindDealOfferText String?
|
|
5974
|
+
inKindEstimatedValueCents Int?
|
|
5975
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
5976
|
+
dealMinimumCashCents Int?
|
|
5953
5977
|
crowdSize AmountOfGuests? @relation(fields: [crowdSizeId], references: [id], onDelete: Cascade)
|
|
5954
5978
|
serviceRange ServiceRange? @relation(fields: [serviceRangeId], references: [id])
|
|
5955
5979
|
service Service?
|
|
@@ -6064,6 +6088,18 @@ model VendorPreferences {
|
|
|
6064
6088
|
bidMinLeadDays Int?
|
|
6065
6089
|
/// small | medium | large — typical event size the vendor prefers.
|
|
6066
6090
|
bidProjectSize String?
|
|
6091
|
+
/// Partner offers a revenue share with the host instead of (or in addition to) upfront fees.
|
|
6092
|
+
openToRevenueShareWithHost Boolean @default(false)
|
|
6093
|
+
/// 0..100 — share of the partner's relevant sales paid to the host.
|
|
6094
|
+
revenueShareWithHostPercentage Int?
|
|
6095
|
+
/// ALL_SALES | REFERRAL_ATTRIBUTED — which sales count toward the share.
|
|
6096
|
+
revenueShareWithHostBasis String?
|
|
6097
|
+
openToInKindDeals Boolean @default(false)
|
|
6098
|
+
inKindDealOfferText String?
|
|
6099
|
+
inKindEstimatedValueCents Int?
|
|
6100
|
+
/// Minimum cash (cents) required alongside rev-share or in-kind deals.
|
|
6101
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
6102
|
+
dealMinimumCashCents Int?
|
|
6067
6103
|
createdAt DateTime @default(now())
|
|
6068
6104
|
updatedAt DateTime @updatedAt
|
|
6069
6105
|
vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Cascade)
|
|
@@ -6142,6 +6178,14 @@ model Exhibitor {
|
|
|
6142
6178
|
bidMinLeadDays Int?
|
|
6143
6179
|
/// small | medium | large — typical event size the exhibitor is comfortable with.
|
|
6144
6180
|
bidProjectSize String?
|
|
6181
|
+
openToRevenueShareWithHost Boolean @default(false)
|
|
6182
|
+
revenueShareWithHostPercentage Int?
|
|
6183
|
+
revenueShareWithHostBasis String?
|
|
6184
|
+
openToInKindDeals Boolean @default(false)
|
|
6185
|
+
inKindDealOfferText String?
|
|
6186
|
+
inKindEstimatedValueCents Int?
|
|
6187
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
6188
|
+
dealMinimumCashCents Int?
|
|
6145
6189
|
|
|
6146
6190
|
crowdSize AmountOfGuests? @relation(fields: [crowdSizeId], references: [id], onDelete: Cascade)
|
|
6147
6191
|
serviceRange ServiceRange? @relation(fields: [serviceRangeId], references: [id])
|
|
@@ -6178,6 +6222,14 @@ model Sponsor {
|
|
|
6178
6222
|
bidMinLeadDays Int?
|
|
6179
6223
|
/// small | medium | large — typical event size the sponsor is comfortable with.
|
|
6180
6224
|
bidProjectSize String?
|
|
6225
|
+
openToRevenueShareWithHost Boolean @default(false)
|
|
6226
|
+
revenueShareWithHostPercentage Int?
|
|
6227
|
+
revenueShareWithHostBasis String?
|
|
6228
|
+
openToInKindDeals Boolean @default(false)
|
|
6229
|
+
inKindDealOfferText String?
|
|
6230
|
+
inKindEstimatedValueCents Int?
|
|
6231
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
6232
|
+
dealMinimumCashCents Int?
|
|
6181
6233
|
|
|
6182
6234
|
service Service?
|
|
6183
6235
|
crowdSize AmountOfGuests? @relation(fields: [crowdSizeId], references: [id])
|
|
@@ -6269,6 +6321,11 @@ model Venue {
|
|
|
6269
6321
|
revenueShareBasis String?
|
|
6270
6322
|
/// Free-form notes shown next to the rev-share split.
|
|
6271
6323
|
revenueShareNotes String?
|
|
6324
|
+
openToInKindDeals Boolean @default(false)
|
|
6325
|
+
inKindDealOfferText String?
|
|
6326
|
+
inKindEstimatedValueCents Int?
|
|
6327
|
+
dealRequiresMinimumCash Boolean @default(false)
|
|
6328
|
+
dealMinimumCashCents Int?
|
|
6272
6329
|
|
|
6273
6330
|
fbCarryoverCredits VenueFBCarryoverCredit[]
|
|
6274
6331
|
fbMinimumOverrides VenueFBMinimumOverride[]
|
|
@@ -8067,6 +8124,15 @@ model ServiceBooking {
|
|
|
8067
8124
|
revenueShareHostPctSnapshot Int?
|
|
8068
8125
|
/// Snapshot of `Venue.revenueShareBasis`.
|
|
8069
8126
|
revenueShareBasisSnapshot String?
|
|
8127
|
+
/// Partner / venue deal prefs at confirmation (rev-share, in-kind, min cash).
|
|
8128
|
+
openToRevenueShareWithHostSnapshot Boolean?
|
|
8129
|
+
revenueShareWithHostPercentageSnapshot Int?
|
|
8130
|
+
revenueShareWithHostBasisSnapshot String?
|
|
8131
|
+
openToInKindDealsSnapshot Boolean?
|
|
8132
|
+
inKindDealOfferTextSnapshot String?
|
|
8133
|
+
inKindEstimatedValueCentsSnapshot Int?
|
|
8134
|
+
dealRequiresMinimumCashSnapshot Boolean?
|
|
8135
|
+
dealMinimumCashCentsSnapshot Int?
|
|
8070
8136
|
/// Computed venue rev-share owed (cents) at settlement.
|
|
8071
8137
|
revenueShareCalculatedCents Int?
|
|
8072
8138
|
// Settlement lifecycle state (drives dunning, retries, dispute window).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ServiceExt } from "../extendedSchemas";
|
|
2
|
+
import {
|
|
3
|
+
readPartnerDealflowFields,
|
|
4
|
+
serviceListingOpenToInKindDeal,
|
|
5
|
+
} from "../utils/service/partnerDealflow";
|
|
6
|
+
|
|
7
|
+
describe("partnerDealflow", () => {
|
|
8
|
+
it("detects in-kind listings with non-empty offer text", () => {
|
|
9
|
+
const service = {
|
|
10
|
+
sponsor: {
|
|
11
|
+
openToInKindDeals: true,
|
|
12
|
+
inKindDealOfferText: "100 free socks",
|
|
13
|
+
},
|
|
14
|
+
} as ServiceExt;
|
|
15
|
+
|
|
16
|
+
expect(readPartnerDealflowFields(service)?.openToInKindDeals).toBe(true);
|
|
17
|
+
expect(serviceListingOpenToInKindDeal(service)).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("rejects in-kind when offer text is blank", () => {
|
|
21
|
+
const service = {
|
|
22
|
+
sponsor: {
|
|
23
|
+
openToInKindDeals: true,
|
|
24
|
+
inKindDealOfferText: " ",
|
|
25
|
+
},
|
|
26
|
+
} as ServiceExt;
|
|
27
|
+
|
|
28
|
+
expect(serviceListingOpenToInKindDeal(service)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("reads dealflow from vendor preferences before flat partner rows", () => {
|
|
32
|
+
const service = {
|
|
33
|
+
vendor: {
|
|
34
|
+
preferences: {
|
|
35
|
+
openToInKindDeals: true,
|
|
36
|
+
inKindDealOfferText: "Sample trays",
|
|
37
|
+
dealRequiresMinimumCash: true,
|
|
38
|
+
dealMinimumCashCents: 5000,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
sponsor: {
|
|
42
|
+
openToInKindDeals: true,
|
|
43
|
+
inKindDealOfferText: "should not win",
|
|
44
|
+
},
|
|
45
|
+
} as ServiceExt;
|
|
46
|
+
|
|
47
|
+
const row = readPartnerDealflowFields(service);
|
|
48
|
+
expect(row?.inKindDealOfferText).toBe("Sample trays");
|
|
49
|
+
expect(row?.dealMinimumCashCents).toBe(5000);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("does not treat in-kind flag alone as a listing when toggle is off", () => {
|
|
53
|
+
const service = {
|
|
54
|
+
venue: {
|
|
55
|
+
openToInKindDeals: false,
|
|
56
|
+
inKindDealOfferText: "Free room night",
|
|
57
|
+
},
|
|
58
|
+
} as ServiceExt;
|
|
59
|
+
|
|
60
|
+
expect(serviceListingOpenToInKindDeal(service)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ServiceExt } from "../extendedSchemas";
|
|
2
|
+
import {
|
|
3
|
+
readPartnerRevenueShareFields,
|
|
4
|
+
serviceListingOpenToRevenueShare,
|
|
5
|
+
} from "../utils/service/partnerRevenueShareOptIn";
|
|
6
|
+
|
|
7
|
+
describe("partnerRevenueShareOptIn", () => {
|
|
8
|
+
it("treats venue ticket/bar rev share as discoverable without partner opt-in", () => {
|
|
9
|
+
const service = {
|
|
10
|
+
venue: {
|
|
11
|
+
revenueShareEnabled: true,
|
|
12
|
+
revenueShareHostPercentage: 10,
|
|
13
|
+
},
|
|
14
|
+
} as ServiceExt;
|
|
15
|
+
|
|
16
|
+
expect(serviceListingOpenToRevenueShare(service)).toBe(true);
|
|
17
|
+
expect(readPartnerRevenueShareFields(service)).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("requires partner opt-in plus a non-null percentage", () => {
|
|
21
|
+
const openNoPct = {
|
|
22
|
+
vendor: {
|
|
23
|
+
preferences: {
|
|
24
|
+
openToRevenueShareWithHost: true,
|
|
25
|
+
revenueShareWithHostPercentage: null,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
} as ServiceExt;
|
|
29
|
+
|
|
30
|
+
expect(serviceListingOpenToRevenueShare(openNoPct)).toBe(false);
|
|
31
|
+
|
|
32
|
+
const closedWithPct = {
|
|
33
|
+
vendor: {
|
|
34
|
+
preferences: {
|
|
35
|
+
openToRevenueShareWithHost: false,
|
|
36
|
+
revenueShareWithHostPercentage: 20,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
} as ServiceExt;
|
|
40
|
+
|
|
41
|
+
expect(serviceListingOpenToRevenueShare(closedWithPct)).toBe(false);
|
|
42
|
+
|
|
43
|
+
const openZero = {
|
|
44
|
+
exhibitor: {
|
|
45
|
+
openToRevenueShareWithHost: true,
|
|
46
|
+
revenueShareWithHostPercentage: 0,
|
|
47
|
+
},
|
|
48
|
+
} as ServiceExt;
|
|
49
|
+
|
|
50
|
+
expect(serviceListingOpenToRevenueShare(openZero)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("reads rev-share fields from the first matching partner subtype", () => {
|
|
54
|
+
const service = {
|
|
55
|
+
eventService: {
|
|
56
|
+
openToRevenueShareWithHost: true,
|
|
57
|
+
revenueShareWithHostPercentage: 12,
|
|
58
|
+
revenueShareWithHostBasis: "REFERRAL_ATTRIBUTED",
|
|
59
|
+
},
|
|
60
|
+
} as ServiceExt;
|
|
61
|
+
|
|
62
|
+
expect(readPartnerRevenueShareFields(service)).toEqual({
|
|
63
|
+
openToRevenueShareWithHost: true,
|
|
64
|
+
revenueShareWithHostPercentage: 12,
|
|
65
|
+
revenueShareWithHostBasis: "REFERRAL_ATTRIBUTED",
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isReservedCustomBashEventTypeDisplayName,
|
|
3
|
+
} from "../definitions.js";
|
|
4
|
+
|
|
5
|
+
describe("isReservedCustomBashEventTypeDisplayName", () => {
|
|
6
|
+
it("blocks homepage synthetic section titles", () => {
|
|
7
|
+
expect(isReservedCustomBashEventTypeDisplayName("Featured")).toBe(true);
|
|
8
|
+
expect(isReservedCustomBashEventTypeDisplayName("Popular")).toBe(true);
|
|
9
|
+
expect(isReservedCustomBashEventTypeDisplayName("Trending")).toBe(true);
|
|
10
|
+
expect(isReservedCustomBashEventTypeDisplayName("Ideas")).toBe(true);
|
|
11
|
+
expect(isReservedCustomBashEventTypeDisplayName("Pro Events")).toBe(true);
|
|
12
|
+
expect(isReservedCustomBashEventTypeDisplayName("The crew's going")).toBe(
|
|
13
|
+
true
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("blocks built-in bash type labels", () => {
|
|
18
|
+
expect(isReservedCustomBashEventTypeDisplayName("Birthday")).toBe(true);
|
|
19
|
+
expect(isReservedCustomBashEventTypeDisplayName("House Party")).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("allows novel custom labels", () => {
|
|
23
|
+
expect(isReservedCustomBashEventTypeDisplayName("Rooftop Party")).toBe(
|
|
24
|
+
false
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ServiceExt } from "../extendedSchemas";
|
|
2
|
+
import {
|
|
3
|
+
buildPartnerDealflowBookingSnapshot,
|
|
4
|
+
getServiceListingDealBadges,
|
|
5
|
+
getServiceListingDealDetailRows,
|
|
6
|
+
} from "../utils/service/serviceListingDealPreferences";
|
|
7
|
+
|
|
8
|
+
describe("serviceListingDealPreferences", () => {
|
|
9
|
+
it("shows min cash badge only when amount is set", () => {
|
|
10
|
+
const withMin = {
|
|
11
|
+
sponsor: {
|
|
12
|
+
dealRequiresMinimumCash: true,
|
|
13
|
+
dealMinimumCashCents: 25000,
|
|
14
|
+
},
|
|
15
|
+
} as ServiceExt;
|
|
16
|
+
|
|
17
|
+
expect(getServiceListingDealBadges(withMin).some((b) => b.id === "min-cash")).toBe(
|
|
18
|
+
true
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const checkboxOnly = {
|
|
22
|
+
sponsor: {
|
|
23
|
+
dealRequiresMinimumCash: true,
|
|
24
|
+
dealMinimumCashCents: null,
|
|
25
|
+
},
|
|
26
|
+
} as ServiceExt;
|
|
27
|
+
|
|
28
|
+
expect(
|
|
29
|
+
getServiceListingDealBadges(checkboxOnly).some((b) => b.id === "min-cash")
|
|
30
|
+
).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("snapshots venue rev share instead of partner fields when venue is enabled", () => {
|
|
34
|
+
const service = {
|
|
35
|
+
venue: {
|
|
36
|
+
revenueShareEnabled: true,
|
|
37
|
+
revenueShareHostPercentage: 18,
|
|
38
|
+
revenueShareBasis: "ALL_SALES",
|
|
39
|
+
},
|
|
40
|
+
eventService: {
|
|
41
|
+
openToRevenueShareWithHost: true,
|
|
42
|
+
revenueShareWithHostPercentage: 99,
|
|
43
|
+
},
|
|
44
|
+
} as ServiceExt;
|
|
45
|
+
|
|
46
|
+
const snap = buildPartnerDealflowBookingSnapshot(service);
|
|
47
|
+
|
|
48
|
+
expect(snap.openToRevenueShareWithHostSnapshot).toBe(true);
|
|
49
|
+
expect(snap.revenueShareWithHostPercentageSnapshot).toBe(18);
|
|
50
|
+
expect(snap.revenueShareWithHostBasisSnapshot).toBe("ALL_SALES");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("snapshots hybrid min-cash flag even when cents are unset (Stripe amount is separate)", () => {
|
|
54
|
+
const service = {
|
|
55
|
+
sponsor: {
|
|
56
|
+
dealRequiresMinimumCash: true,
|
|
57
|
+
dealMinimumCashCents: null,
|
|
58
|
+
openToInKindDeals: true,
|
|
59
|
+
inKindDealOfferText: "Branded socks",
|
|
60
|
+
},
|
|
61
|
+
} as ServiceExt;
|
|
62
|
+
|
|
63
|
+
const snap = buildPartnerDealflowBookingSnapshot(service);
|
|
64
|
+
|
|
65
|
+
expect(snap.dealRequiresMinimumCashSnapshot).toBe(true);
|
|
66
|
+
expect(snap.dealMinimumCashCentsSnapshot).toBeNull();
|
|
67
|
+
expect(snap.openToInKindDealsSnapshot).toBe(true);
|
|
68
|
+
expect(snap.inKindDealOfferTextSnapshot).toBe("Branded socks");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("snapshots rev-share opt-in with null percentage when partner saved incomplete prefs", () => {
|
|
72
|
+
const service = {
|
|
73
|
+
sponsor: {
|
|
74
|
+
openToRevenueShareWithHost: true,
|
|
75
|
+
revenueShareWithHostPercentage: null,
|
|
76
|
+
},
|
|
77
|
+
} as ServiceExt;
|
|
78
|
+
|
|
79
|
+
expect(buildPartnerDealflowBookingSnapshot(service)).toEqual({
|
|
80
|
+
openToRevenueShareWithHostSnapshot: true,
|
|
81
|
+
revenueShareWithHostPercentageSnapshot: null,
|
|
82
|
+
revenueShareWithHostBasisSnapshot: null,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("includes min-cash detail copy only when cents are positive", () => {
|
|
87
|
+
const hybrid = {
|
|
88
|
+
sponsor: {
|
|
89
|
+
dealRequiresMinimumCash: true,
|
|
90
|
+
dealMinimumCashCents: 25000,
|
|
91
|
+
openToInKindDeals: true,
|
|
92
|
+
inKindDealOfferText: "100 pairs",
|
|
93
|
+
},
|
|
94
|
+
} as ServiceExt;
|
|
95
|
+
|
|
96
|
+
const rows = getServiceListingDealDetailRows(hybrid);
|
|
97
|
+
expect(rows.some((r) => r.label.includes("Minimum cash"))).toBe(true);
|
|
98
|
+
|
|
99
|
+
const flagOnly = {
|
|
100
|
+
sponsor: {
|
|
101
|
+
dealRequiresMinimumCash: true,
|
|
102
|
+
dealMinimumCashCents: 0,
|
|
103
|
+
},
|
|
104
|
+
} as ServiceExt;
|
|
105
|
+
|
|
106
|
+
expect(
|
|
107
|
+
getServiceListingDealDetailRows(flagOnly).some((r) =>
|
|
108
|
+
r.label.includes("Minimum cash")
|
|
109
|
+
)
|
|
110
|
+
).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
});
|
package/src/definitions.ts
CHANGED
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
ServiceBookingExt,
|
|
33
33
|
type UserExt,
|
|
34
34
|
} from "./extendedSchemas.js";
|
|
35
|
+
import { DISCOVERY_SCOPE_SECTION_TITLE } from "./discoveryScope.js";
|
|
36
|
+
import { HOMEPAGE_SYNTHETIC_SECTION_ROW_KEYS } from "./homepageSyntheticRows.js";
|
|
35
37
|
import { LuxonDateRange } from "./utils/luxonUtils.js";
|
|
36
38
|
import { ApiServiceCantBookReason } from "./utils/service/apiServiceBookingApiUtils.js";
|
|
37
39
|
import { ServiceAttendeeOption } from "./utils/service/attendeeOptionUtils.js";
|
|
@@ -705,8 +707,8 @@ export const BASH_EVENT_WIZARD_WHAT_SUBSTEP = {
|
|
|
705
707
|
} as const;
|
|
706
708
|
|
|
707
709
|
export const BASH_EVENT_WIZARD_WHERE_SUBSTEP = {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
+
UPLOAD_MEDIA: 0,
|
|
711
|
+
LOCATION: 1,
|
|
710
712
|
} as const;
|
|
711
713
|
|
|
712
714
|
export const BASH_EVENT_WIZARD_WHEN_SUBSTEP = {
|
|
@@ -1904,6 +1906,20 @@ export const DIETARY_OPTION_VALUES: ReadonlyArray<DietaryOption> = [
|
|
|
1904
1906
|
"AlcoholFreeOptions",
|
|
1905
1907
|
];
|
|
1906
1908
|
|
|
1909
|
+
/**
|
|
1910
|
+
* Guest-facing filter chips — everyday accommodations only.
|
|
1911
|
+
* Religious, drink, and diet-trend tags stay on the host form / enum
|
|
1912
|
+
* so existing events keep their data, but they do not crowd the filter menu.
|
|
1913
|
+
*/
|
|
1914
|
+
export const DIETARY_OPTION_FILTER_VALUES: ReadonlyArray<DietaryOption> = [
|
|
1915
|
+
"VegetarianOptions",
|
|
1916
|
+
"VeganOptions",
|
|
1917
|
+
"GlutenFreeOptions",
|
|
1918
|
+
"DairyFreeOptions",
|
|
1919
|
+
"NutFreeOptions",
|
|
1920
|
+
"SugarFreeOptions",
|
|
1921
|
+
];
|
|
1922
|
+
|
|
1907
1923
|
/** Display labels for `IncomeRange` (wizard primary/secondary chips). */
|
|
1908
1924
|
export type IncomeRangeToStringType = {
|
|
1909
1925
|
[key in IncomeRange]: string;
|
|
@@ -2168,6 +2184,55 @@ export function bashSectionTitleForBashType(bashType: string): string {
|
|
|
2168
2184
|
return pluralizeBashEventSingularLabel(spaced);
|
|
2169
2185
|
}
|
|
2170
2186
|
|
|
2187
|
+
function normalizeReservedCustomBashEventTypeLabel(label: string): string {
|
|
2188
|
+
return label.trim().toLowerCase().replace(/\s+/g, " ");
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
let reservedCustomBashEventTypeLabels: Set<string> | null = null;
|
|
2192
|
+
|
|
2193
|
+
function reservedCustomBashEventTypeLabelSet(): Set<string> {
|
|
2194
|
+
if (reservedCustomBashEventTypeLabels) {
|
|
2195
|
+
return reservedCustomBashEventTypeLabels;
|
|
2196
|
+
}
|
|
2197
|
+
const set = new Set<string>();
|
|
2198
|
+
const add = (value: string) => {
|
|
2199
|
+
const normalized = normalizeReservedCustomBashEventTypeLabel(value);
|
|
2200
|
+
if (normalized) set.add(normalized);
|
|
2201
|
+
};
|
|
2202
|
+
|
|
2203
|
+
for (const key of HOMEPAGE_SYNTHETIC_SECTION_ROW_KEYS) {
|
|
2204
|
+
add(key);
|
|
2205
|
+
add(bashSectionTitleForBashType(key));
|
|
2206
|
+
}
|
|
2207
|
+
for (const title of Object.values(DISCOVERY_SCOPE_SECTION_TITLE)) {
|
|
2208
|
+
add(title);
|
|
2209
|
+
}
|
|
2210
|
+
for (const title of Object.values(SYNTHETIC_BASH_SECTION_TITLE)) {
|
|
2211
|
+
add(title);
|
|
2212
|
+
}
|
|
2213
|
+
for (const enumKey of BASH_EVENT_TYPE_ENUM_VALUES) {
|
|
2214
|
+
const k = enumKey as BashEventType;
|
|
2215
|
+
add(enumKey);
|
|
2216
|
+
add(BashEventTypeToString[k]);
|
|
2217
|
+
add(bashSectionTitleForBashType(enumKey));
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
reservedCustomBashEventTypeLabels = set;
|
|
2221
|
+
return set;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
/**
|
|
2225
|
+
* Homepage section titles and built-in bash types cannot be created as custom
|
|
2226
|
+
* types in the wizard (would collide with synthetic discovery rows).
|
|
2227
|
+
*/
|
|
2228
|
+
export function isReservedCustomBashEventTypeDisplayName(
|
|
2229
|
+
displayName: string
|
|
2230
|
+
): boolean {
|
|
2231
|
+
return reservedCustomBashEventTypeLabelSet().has(
|
|
2232
|
+
normalizeReservedCustomBashEventTypeLabel(displayName)
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2171
2236
|
export type RecordKey = string | number | symbol;
|
|
2172
2237
|
export type PartialExcept<T, K extends keyof T> = Partial<T> & {
|
|
2173
2238
|
[P in K]: T[P];
|