@bash-app/bash-common 30.408.0 → 30.413.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/__tests__/statusEnums.test.js +1 -0
- package/dist/__tests__/statusEnums.test.js.map +1 -1
- package/dist/definitions.d.ts +7 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +42 -0
- package/dist/definitions.js.map +1 -1
- package/dist/extendedSchemas.d.ts +101 -1
- package/dist/extendedSchemas.d.ts.map +1 -1
- package/dist/extendedSchemas.js +31 -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/membershipDefinitions.d.ts +2 -0
- package/dist/membershipDefinitions.d.ts.map +1 -1
- package/dist/membershipDefinitions.js +2 -0
- package/dist/membershipDefinitions.js.map +1 -1
- package/dist/mirroredPrismaEnums.d.ts +1 -0
- package/dist/mirroredPrismaEnums.d.ts.map +1 -1
- package/dist/mirroredPrismaEnums.js +1 -0
- package/dist/mirroredPrismaEnums.js.map +1 -1
- package/dist/utils/__tests__/slugUtils.test.js +4 -4
- package/dist/utils/__tests__/slugUtils.test.js.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/dist/utils/slugUtils.d.ts +4 -1
- package/dist/utils/slugUtils.d.ts.map +1 -1
- package/dist/utils/slugUtils.js +8 -2
- package/dist/utils/slugUtils.js.map +1 -1
- package/package.json +5 -2
- package/prisma/schema.prisma +69 -0
- 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/__tests__/statusEnums.test.ts +1 -0
- package/src/definitions.ts +53 -0
- package/src/extendedSchemas.ts +60 -0
- package/src/homepageSyntheticRows.ts +32 -0
- package/src/index.ts +4 -0
- package/src/membershipDefinitions.ts +2 -0
- package/src/mirroredPrismaEnums.ts +1 -0
- package/src/utils/__tests__/slugUtils.test.ts +4 -3
- package/src/utils/service/partnerDealflow.ts +47 -0
- package/src/utils/service/partnerRevenueShareOptIn.ts +70 -0
- package/src/utils/service/serviceListingDealPreferences.ts +184 -0
- package/src/utils/slugUtils.ts +12 -1
|
@@ -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";
|
|
@@ -386,6 +388,8 @@ export type DoorConfigPublic = {
|
|
|
386
388
|
doorPaymentRail: DoorPaymentRail;
|
|
387
389
|
/** Host has verified Square + location when rail is or can be Square. */
|
|
388
390
|
squareLocationReady: boolean;
|
|
391
|
+
/** How this viewer may use the door app. Omitted on older API. */
|
|
392
|
+
viewerDoorAccess?: "none" | "pin" | "staff";
|
|
389
393
|
};
|
|
390
394
|
|
|
391
395
|
/** Door scanner: ticket QR verification result. */
|
|
@@ -2182,6 +2186,55 @@ export function bashSectionTitleForBashType(bashType: string): string {
|
|
|
2182
2186
|
return pluralizeBashEventSingularLabel(spaced);
|
|
2183
2187
|
}
|
|
2184
2188
|
|
|
2189
|
+
function normalizeReservedCustomBashEventTypeLabel(label: string): string {
|
|
2190
|
+
return label.trim().toLowerCase().replace(/\s+/g, " ");
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
let reservedCustomBashEventTypeLabels: Set<string> | null = null;
|
|
2194
|
+
|
|
2195
|
+
function reservedCustomBashEventTypeLabelSet(): Set<string> {
|
|
2196
|
+
if (reservedCustomBashEventTypeLabels) {
|
|
2197
|
+
return reservedCustomBashEventTypeLabels;
|
|
2198
|
+
}
|
|
2199
|
+
const set = new Set<string>();
|
|
2200
|
+
const add = (value: string) => {
|
|
2201
|
+
const normalized = normalizeReservedCustomBashEventTypeLabel(value);
|
|
2202
|
+
if (normalized) set.add(normalized);
|
|
2203
|
+
};
|
|
2204
|
+
|
|
2205
|
+
for (const key of HOMEPAGE_SYNTHETIC_SECTION_ROW_KEYS) {
|
|
2206
|
+
add(key);
|
|
2207
|
+
add(bashSectionTitleForBashType(key));
|
|
2208
|
+
}
|
|
2209
|
+
for (const title of Object.values(DISCOVERY_SCOPE_SECTION_TITLE)) {
|
|
2210
|
+
add(title);
|
|
2211
|
+
}
|
|
2212
|
+
for (const title of Object.values(SYNTHETIC_BASH_SECTION_TITLE)) {
|
|
2213
|
+
add(title);
|
|
2214
|
+
}
|
|
2215
|
+
for (const enumKey of BASH_EVENT_TYPE_ENUM_VALUES) {
|
|
2216
|
+
const k = enumKey as BashEventType;
|
|
2217
|
+
add(enumKey);
|
|
2218
|
+
add(BashEventTypeToString[k]);
|
|
2219
|
+
add(bashSectionTitleForBashType(enumKey));
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
reservedCustomBashEventTypeLabels = set;
|
|
2223
|
+
return set;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
* Homepage section titles and built-in bash types cannot be created as custom
|
|
2228
|
+
* types in the wizard (would collide with synthetic discovery rows).
|
|
2229
|
+
*/
|
|
2230
|
+
export function isReservedCustomBashEventTypeDisplayName(
|
|
2231
|
+
displayName: string
|
|
2232
|
+
): boolean {
|
|
2233
|
+
return reservedCustomBashEventTypeLabelSet().has(
|
|
2234
|
+
normalizeReservedCustomBashEventTypeLabel(displayName)
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2185
2238
|
export type RecordKey = string | number | symbol;
|
|
2186
2239
|
export type PartialExcept<T, K extends keyof T> = Partial<T> & {
|
|
2187
2240
|
[P in K]: T[P];
|
package/src/extendedSchemas.ts
CHANGED
|
@@ -610,6 +610,8 @@ type BashEventExtBase = Override<
|
|
|
610
610
|
| "postponedAt"
|
|
611
611
|
| "postponeResponseDeadline"
|
|
612
612
|
| "seatMap"
|
|
613
|
+
| "eventAccessPasswordHash"
|
|
614
|
+
| "doorAccessCodeDigest"
|
|
613
615
|
>,
|
|
614
616
|
{
|
|
615
617
|
icebreakerPrompt: string | null;
|
|
@@ -751,6 +753,20 @@ type BashEventExtBase = Override<
|
|
|
751
753
|
* Host opt-in: guests must have a profile photo to RSVP or buy tickets.
|
|
752
754
|
*/
|
|
753
755
|
requireProfilePhoto: boolean;
|
|
756
|
+
/**
|
|
757
|
+
* True when the host set an event-page password. The hash is never returned.
|
|
758
|
+
*/
|
|
759
|
+
eventAccessPasswordRequired?: boolean;
|
|
760
|
+
/**
|
|
761
|
+
* True when the viewer still needs to unlock a password-protected bash.
|
|
762
|
+
*/
|
|
763
|
+
eventAccessLocked?: boolean;
|
|
764
|
+
/**
|
|
765
|
+
* Present on server reads. Stripped from JSON responses before they leave the API.
|
|
766
|
+
*/
|
|
767
|
+
eventAccessPasswordHash?: string | null;
|
|
768
|
+
/** Present on server reads. Stripped from JSON responses before they leave the API. */
|
|
769
|
+
doorAccessCodeDigest?: string | null;
|
|
754
770
|
};
|
|
755
771
|
|
|
756
772
|
export type BashEventExt = Omit<
|
|
@@ -786,6 +802,17 @@ export type BashEventUpsertWireInput = Override<
|
|
|
786
802
|
startDateTime?: Date | string | null;
|
|
787
803
|
endDateTime?: Date | string | null;
|
|
788
804
|
ideaExpiresAt?: Date | string | null;
|
|
805
|
+
pendingScheduleClock?:
|
|
806
|
+
| {
|
|
807
|
+
startHour: number;
|
|
808
|
+
startMinute: number;
|
|
809
|
+
endHour: number | null;
|
|
810
|
+
endMinute: number | null;
|
|
811
|
+
endsNextDay: boolean;
|
|
812
|
+
}
|
|
813
|
+
| import("@prisma/client").Prisma.JsonValue
|
|
814
|
+
| null;
|
|
815
|
+
advertiseRecurrence?: boolean;
|
|
789
816
|
}
|
|
790
817
|
>,
|
|
791
818
|
{
|
|
@@ -975,6 +1002,24 @@ export const SERVICE_SUBTYPE_CROWD_RANGE_INCLUDE = {
|
|
|
975
1002
|
serviceRange: true,
|
|
976
1003
|
} as const;
|
|
977
1004
|
|
|
1005
|
+
/** Rev-share, in-kind, and min-cash (all partner subtype tables + venue). */
|
|
1006
|
+
export const SERVICE_PARTNER_REV_INKIND_SELECT = {
|
|
1007
|
+
openToRevenueShareWithHost: true,
|
|
1008
|
+
revenueShareWithHostPercentage: true,
|
|
1009
|
+
revenueShareWithHostBasis: true,
|
|
1010
|
+
openToInKindDeals: true,
|
|
1011
|
+
inKindDealOfferText: true,
|
|
1012
|
+
inKindEstimatedValueCents: true,
|
|
1013
|
+
dealRequiresMinimumCash: true,
|
|
1014
|
+
dealMinimumCashCents: true,
|
|
1015
|
+
} as const;
|
|
1016
|
+
|
|
1017
|
+
/** Includes host-bid opt-in (vendor prefs, exhibitor, sponsor only). */
|
|
1018
|
+
export const SERVICE_PARTNER_DEAL_PREF_SELECT = {
|
|
1019
|
+
...SERVICE_PARTNER_REV_INKIND_SELECT,
|
|
1020
|
+
openToHostBids: true,
|
|
1021
|
+
} as const;
|
|
1022
|
+
|
|
978
1023
|
export const VENDOR_DATA_TO_INCLUDE = {
|
|
979
1024
|
...SERVICE_SUBTYPE_CROWD_RANGE_INCLUDE,
|
|
980
1025
|
spaceRequirements: true,
|
|
@@ -1069,6 +1114,14 @@ export const SERVICE_PUBLIC_LIST_DATA_TO_INCLUDE = {
|
|
|
1069
1114
|
capacity: true,
|
|
1070
1115
|
amenities: true,
|
|
1071
1116
|
features: true,
|
|
1117
|
+
revenueShareEnabled: true,
|
|
1118
|
+
revenueShareHostPercentage: true,
|
|
1119
|
+
revenueShareBasis: true,
|
|
1120
|
+
openToInKindDeals: true,
|
|
1121
|
+
inKindDealOfferText: true,
|
|
1122
|
+
inKindEstimatedValueCents: true,
|
|
1123
|
+
dealRequiresMinimumCash: true,
|
|
1124
|
+
dealMinimumCashCents: true,
|
|
1072
1125
|
},
|
|
1073
1126
|
},
|
|
1074
1127
|
eventService: {
|
|
@@ -1086,6 +1139,7 @@ export const SERVICE_PUBLIC_LIST_DATA_TO_INCLUDE = {
|
|
|
1086
1139
|
goodsOrServices: true,
|
|
1087
1140
|
fulfillmentModes: true,
|
|
1088
1141
|
leadTimeDays: true,
|
|
1142
|
+
...SERVICE_PARTNER_REV_INKIND_SELECT,
|
|
1089
1143
|
},
|
|
1090
1144
|
},
|
|
1091
1145
|
entertainmentService: {
|
|
@@ -1094,6 +1148,7 @@ export const SERVICE_PUBLIC_LIST_DATA_TO_INCLUDE = {
|
|
|
1094
1148
|
entertainmentServiceTypes: true,
|
|
1095
1149
|
genre: true,
|
|
1096
1150
|
crowdSize: true,
|
|
1151
|
+
...SERVICE_PARTNER_REV_INKIND_SELECT,
|
|
1097
1152
|
},
|
|
1098
1153
|
},
|
|
1099
1154
|
vendor: {
|
|
@@ -1108,18 +1163,23 @@ export const SERVICE_PUBLIC_LIST_DATA_TO_INCLUDE = {
|
|
|
1108
1163
|
venueTypes: true,
|
|
1109
1164
|
detailsText: true,
|
|
1110
1165
|
crowdSize: true,
|
|
1166
|
+
preferences: {
|
|
1167
|
+
select: SERVICE_PARTNER_DEAL_PREF_SELECT,
|
|
1168
|
+
},
|
|
1111
1169
|
},
|
|
1112
1170
|
},
|
|
1113
1171
|
exhibitor: {
|
|
1114
1172
|
select: {
|
|
1115
1173
|
id: true,
|
|
1116
1174
|
crowdSize: true,
|
|
1175
|
+
...SERVICE_PARTNER_DEAL_PREF_SELECT,
|
|
1117
1176
|
},
|
|
1118
1177
|
},
|
|
1119
1178
|
sponsor: {
|
|
1120
1179
|
select: {
|
|
1121
1180
|
id: true,
|
|
1122
1181
|
crowdSize: true,
|
|
1182
|
+
...SERVICE_PARTNER_DEAL_PREF_SELECT,
|
|
1123
1183
|
},
|
|
1124
1184
|
},
|
|
1125
1185
|
} satisfies Prisma.ServiceInclude;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DISCOVERY_SCOPE_ROW_KEYS } from "./discoveryScope.js";
|
|
2
|
+
|
|
3
|
+
/** Algorithm / geo / social rows — not host-picked `BashEventType` values. */
|
|
4
|
+
export const HOMEPAGE_DISCOVERY_SYNTHETIC_ROW_KEYS = [
|
|
5
|
+
"Ideas",
|
|
6
|
+
"Featured",
|
|
7
|
+
"Popular",
|
|
8
|
+
"Trending",
|
|
9
|
+
"CrewGoing",
|
|
10
|
+
...DISCOVERY_SCOPE_ROW_KEYS,
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
/** Membership-tier inventory rows (creator tier, not event type). */
|
|
14
|
+
export const MEMBERSHIP_TIER_HOMEPAGE_ROW_KEYS = [
|
|
15
|
+
"Pro Events",
|
|
16
|
+
"Elite Events",
|
|
17
|
+
"Legend Events",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
export const HOMEPAGE_SYNTHETIC_SECTION_ROW_KEYS: readonly string[] = [
|
|
21
|
+
...HOMEPAGE_DISCOVERY_SYNTHETIC_ROW_KEYS,
|
|
22
|
+
...MEMBERSHIP_TIER_HOMEPAGE_ROW_KEYS,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export function isHomepageSyntheticSectionRowKey(key: string): boolean {
|
|
26
|
+
return (HOMEPAGE_SYNTHETIC_SECTION_ROW_KEYS as readonly string[]).includes(
|
|
27
|
+
key
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @deprecated Prefer `HOMEPAGE_DISCOVERY_SYNTHETIC_ROW_KEYS` — kept for app imports. */
|
|
32
|
+
export const HOMEPAGE_DISCOVERY_ROW_KEYS = HOMEPAGE_DISCOVERY_SYNTHETIC_ROW_KEYS;
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./sideQuestTypes.js";
|
|
|
15
15
|
export * from "./icebreakerPrompts.js";
|
|
16
16
|
export * from "./causeTags.js";
|
|
17
17
|
export * from "./discoveryScope.js";
|
|
18
|
+
export * from "./homepageSyntheticRows.js";
|
|
18
19
|
export * from "./userTaste.js";
|
|
19
20
|
export * from "./crewSocialCopy.js";
|
|
20
21
|
export * from "./suggestionScore.js";
|
|
@@ -407,6 +408,9 @@ export * from "./utils/agencyBookingCheckoutCopy.js";
|
|
|
407
408
|
export * from "./utils/service/serviceUtils.js";
|
|
408
409
|
export * from "./utils/service/serviceListingMediaUtils.js";
|
|
409
410
|
export * from "./utils/service/venueSettlementUtils.js";
|
|
411
|
+
export * from "./utils/service/partnerRevenueShareOptIn.js";
|
|
412
|
+
export * from "./utils/service/partnerDealflow.js";
|
|
413
|
+
export * from "./utils/service/serviceListingDealPreferences.js";
|
|
410
414
|
export * from "./utils/service/offerAllowListUtils.js";
|
|
411
415
|
export * from "./utils/service/offerComparisonUtils.js";
|
|
412
416
|
export * from "./utils/service/unifiedPricingResolver.js";
|
|
@@ -266,6 +266,8 @@ export const BASHPOINTS_CONFIG = {
|
|
|
266
266
|
|
|
267
267
|
// Recurring engagement rewards (once per user per event)
|
|
268
268
|
rsvpBonus: 25, // First positive RSVP (Going/Maybe/WantsToGo) per event
|
|
269
|
+
/** Once per window period: Now/Tonight/Custom = UTC day, ThisWeekend = that weekend */
|
|
270
|
+
availabilityWindowBonus: 25,
|
|
269
271
|
ticketCheckoutBonus: 50, // Claiming or buying a ticket (free or paid) per event
|
|
270
272
|
attendanceBonus: 150, // Checking in at the event (attending > posting)
|
|
271
273
|
postBeforeEventBonus: 50, // First BashFeed post linked to event before it ends
|
|
@@ -186,6 +186,7 @@ export const CreditSourceType = {
|
|
|
186
186
|
BirthdayReward: "BirthdayReward",
|
|
187
187
|
EventReferralReward: "EventReferralReward",
|
|
188
188
|
RsvpBonus: "RsvpBonus",
|
|
189
|
+
AvailabilitySignal: "AvailabilitySignal",
|
|
189
190
|
TicketCheckout: "TicketCheckout",
|
|
190
191
|
AttendanceBonus: "AttendanceBonus",
|
|
191
192
|
PostBeforeEvent: "PostBeforeEvent",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from "@jest/globals";
|
|
2
2
|
import {
|
|
3
3
|
BASH_DETAIL_URL,
|
|
4
|
+
BASH_EVENT_WIZARD_GENERATE_SUBSTEP,
|
|
4
5
|
BASH_EVENT_WIZARD_HOW_MAJOR_STEP,
|
|
5
6
|
BASH_EVENT_WIZARD_HOW_SUBSTEP,
|
|
6
7
|
BASH_EVENT_WIZARD_HOW_SUBSTEP_PAYOUT,
|
|
@@ -143,13 +144,13 @@ describe("getBashEventWizardPayoutStepPath", () => {
|
|
|
143
144
|
});
|
|
144
145
|
|
|
145
146
|
describe("getBashEventWizardStartPath", () => {
|
|
146
|
-
test("
|
|
147
|
+
test("opens a new draft on Generate", () => {
|
|
147
148
|
expect(getBashEventWizardStartPath()).toBe(
|
|
148
|
-
`${BASH_EVENT_WIZARD_PATH_PREFIX}/new/${BASH_EVENT_WIZARD_MAJOR.
|
|
149
|
+
`${BASH_EVENT_WIZARD_PATH_PREFIX}/new/${BASH_EVENT_WIZARD_MAJOR.GENERATE}/${BASH_EVENT_WIZARD_GENERATE_SUBSTEP.START}`
|
|
149
150
|
);
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
test("
|
|
153
|
+
test("opens an existing event on Quick bash", () => {
|
|
153
154
|
expect(getBashEventWizardStartPath("evt-1")).toBe(
|
|
154
155
|
`${BASH_EVENT_WIZARD_PATH_PREFIX}/evt-1/${BASH_EVENT_WIZARD_MAJOR.TEMPLATES}/${BASH_EVENT_WIZARD_TEMPLATES_SUBSTEP.START}`
|
|
155
156
|
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ServiceExt } from "../../extendedSchemas";
|
|
2
|
+
|
|
3
|
+
export type PartnerDealflowFields = {
|
|
4
|
+
openToInKindDeals?: boolean | null;
|
|
5
|
+
inKindDealOfferText?: string | null;
|
|
6
|
+
inKindEstimatedValueCents?: number | null;
|
|
7
|
+
dealRequiresMinimumCash?: boolean | null;
|
|
8
|
+
dealMinimumCashCents?: number | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function rowFromPartner(
|
|
12
|
+
row: PartnerDealflowFields | null | undefined
|
|
13
|
+
): PartnerDealflowFields | null {
|
|
14
|
+
if (!row) return null;
|
|
15
|
+
return row;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Reads in-kind + hybrid cash prefs from any partner subtype payload. */
|
|
19
|
+
export function readPartnerDealflowFields(
|
|
20
|
+
service: ServiceExt
|
|
21
|
+
): PartnerDealflowFields | null {
|
|
22
|
+
if (service.vendor?.preferences) {
|
|
23
|
+
return rowFromPartner(service.vendor.preferences);
|
|
24
|
+
}
|
|
25
|
+
if (service.exhibitor) {
|
|
26
|
+
return rowFromPartner(service.exhibitor);
|
|
27
|
+
}
|
|
28
|
+
if (service.sponsor) {
|
|
29
|
+
return rowFromPartner(service.sponsor);
|
|
30
|
+
}
|
|
31
|
+
if (service.entertainmentService) {
|
|
32
|
+
return rowFromPartner(service.entertainmentService);
|
|
33
|
+
}
|
|
34
|
+
if (service.eventService) {
|
|
35
|
+
return rowFromPartner(service.eventService);
|
|
36
|
+
}
|
|
37
|
+
if (service.venue) {
|
|
38
|
+
return rowFromPartner(service.venue);
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function serviceListingOpenToInKindDeal(service: ServiceExt): boolean {
|
|
44
|
+
const row = readPartnerDealflowFields(service);
|
|
45
|
+
const text = row?.inKindDealOfferText?.trim();
|
|
46
|
+
return row?.openToInKindDeals === true && Boolean(text && text.length > 0);
|
|
47
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ServiceExt } from "../../extendedSchemas";
|
|
2
|
+
|
|
3
|
+
/** Which sales count when a partner shares revenue with the host. */
|
|
4
|
+
export const REVENUE_SHARE_WITH_HOST_BASIS = {
|
|
5
|
+
ALL_SALES: "ALL_SALES",
|
|
6
|
+
REFERRAL_ATTRIBUTED: "REFERRAL_ATTRIBUTED",
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export type RevenueShareWithHostBasis =
|
|
10
|
+
(typeof REVENUE_SHARE_WITH_HOST_BASIS)[keyof typeof REVENUE_SHARE_WITH_HOST_BASIS];
|
|
11
|
+
|
|
12
|
+
export const REVENUE_SHARE_WITH_HOST_BASIS_LABEL: Record<
|
|
13
|
+
RevenueShareWithHostBasis,
|
|
14
|
+
string
|
|
15
|
+
> = {
|
|
16
|
+
ALL_SALES: "All sales at the bash",
|
|
17
|
+
REFERRAL_ATTRIBUTED: "Sales from guests using my code only",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type PartnerRevenueShareFields = {
|
|
21
|
+
openToRevenueShareWithHost?: boolean | null;
|
|
22
|
+
revenueShareWithHostPercentage?: number | null;
|
|
23
|
+
revenueShareWithHostBasis?: string | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function partnerFieldsFromRecord(
|
|
27
|
+
row: PartnerRevenueShareFields | null | undefined
|
|
28
|
+
): PartnerRevenueShareFields | null {
|
|
29
|
+
if (!row) return null;
|
|
30
|
+
return row;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Reads partner rev-share opt-in from any service subtype payload. */
|
|
34
|
+
export function readPartnerRevenueShareFields(
|
|
35
|
+
service: ServiceExt
|
|
36
|
+
): PartnerRevenueShareFields | null {
|
|
37
|
+
if (service.vendor?.preferences) {
|
|
38
|
+
return partnerFieldsFromRecord(service.vendor.preferences);
|
|
39
|
+
}
|
|
40
|
+
if (service.exhibitor) {
|
|
41
|
+
return partnerFieldsFromRecord(service.exhibitor);
|
|
42
|
+
}
|
|
43
|
+
if (service.sponsor) {
|
|
44
|
+
return partnerFieldsFromRecord(service.sponsor);
|
|
45
|
+
}
|
|
46
|
+
if (service.entertainmentService) {
|
|
47
|
+
return partnerFieldsFromRecord(service.entertainmentService);
|
|
48
|
+
}
|
|
49
|
+
if (service.eventService) {
|
|
50
|
+
return partnerFieldsFromRecord(service.eventService);
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** True when listing is open to a revenue-share deal (partner or venue ticket split). */
|
|
56
|
+
export function serviceListingOpenToRevenueShare(service: ServiceExt): boolean {
|
|
57
|
+
const venue = service.venue;
|
|
58
|
+
if (
|
|
59
|
+
venue?.revenueShareEnabled === true &&
|
|
60
|
+
venue.revenueShareHostPercentage != null
|
|
61
|
+
) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const partner = readPartnerRevenueShareFields(service);
|
|
65
|
+
return (
|
|
66
|
+
partner?.openToRevenueShareWithHost === true &&
|
|
67
|
+
partner.revenueShareWithHostPercentage != null &&
|
|
68
|
+
partner.revenueShareWithHostPercentage >= 0
|
|
69
|
+
);
|
|
70
|
+
}
|