@bash-app/bash-common 30.186.0 → 30.196.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__/stripeListingSubscriptionMessages.test.d.ts +2 -0
- package/dist/__tests__/stripeListingSubscriptionMessages.test.d.ts.map +1 -0
- package/dist/__tests__/stripeListingSubscriptionMessages.test.js +18 -0
- package/dist/__tests__/stripeListingSubscriptionMessages.test.js.map +1 -0
- 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/stripeListingSubscriptionMessages.d.ts +13 -0
- package/dist/stripeListingSubscriptionMessages.d.ts.map +1 -0
- package/dist/stripeListingSubscriptionMessages.js +21 -0
- package/dist/stripeListingSubscriptionMessages.js.map +1 -0
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts +6 -0
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.d.ts.map +1 -0
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js +104 -0
- package/dist/utils/__tests__/cancellationPolicyRefundResolver.test.js.map +1 -0
- package/dist/utils/addressUtils.d.ts.map +1 -1
- package/dist/utils/addressUtils.js +157 -54
- package/dist/utils/addressUtils.js.map +1 -1
- package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.js +6 -2
- package/dist/utils/discountEngine/__tests__/eligibilityValidator.test.js.map +1 -1
- package/dist/utils/mediaClientDefaults.d.ts +7 -0
- package/dist/utils/mediaClientDefaults.d.ts.map +1 -0
- package/dist/utils/mediaClientDefaults.js +7 -0
- package/dist/utils/mediaClientDefaults.js.map +1 -0
- package/dist/utils/service/__tests__/partnerServiceProfileWizardPaymentMethod.test.d.ts +2 -0
- package/dist/utils/service/__tests__/partnerServiceProfileWizardPaymentMethod.test.d.ts.map +1 -0
- package/dist/utils/service/__tests__/partnerServiceProfileWizardPaymentMethod.test.js +18 -0
- package/dist/utils/service/__tests__/partnerServiceProfileWizardPaymentMethod.test.js.map +1 -0
- package/dist/utils/service/cancellationPolicyRefundResolver.d.ts +30 -0
- package/dist/utils/service/cancellationPolicyRefundResolver.d.ts.map +1 -0
- package/dist/utils/service/cancellationPolicyRefundResolver.js +82 -0
- package/dist/utils/service/cancellationPolicyRefundResolver.js.map +1 -0
- package/dist/utils/service/serviceUtils.d.ts +7 -0
- package/dist/utils/service/serviceUtils.d.ts.map +1 -1
- package/dist/utils/service/serviceUtils.js +178 -16
- package/dist/utils/service/serviceUtils.js.map +1 -1
- package/dist/utils/slugUtils.d.ts +5 -0
- package/dist/utils/slugUtils.d.ts.map +1 -1
- package/dist/utils/slugUtils.js +12 -0
- package/dist/utils/slugUtils.js.map +1 -1
- package/package.json +3 -2
- package/prisma/schema.prisma +6 -0
- package/src/__tests__/stripeListingSubscriptionMessages.test.ts +37 -0
- package/src/index.ts +3 -0
- package/src/stripeListingSubscriptionMessages.ts +29 -0
- package/src/utils/addressUtils.ts +175 -59
- package/src/utils/discountEngine/__tests__/eligibilityValidator.test.ts +6 -2
- package/src/utils/mediaClientDefaults.ts +6 -0
- package/src/utils/service/__tests__/partnerServiceProfileWizardPaymentMethod.test.ts +29 -0
- package/src/utils/service/cancellationPolicyRefundResolver.ts +112 -0
- package/src/utils/service/serviceUtils.ts +220 -20
- package/src/utils/slugUtils.ts +16 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ServiceCancellationPolicy } from "@prisma/client";
|
|
2
|
+
import {
|
|
3
|
+
SERVICE_CANCELLATION_POLICY_DATA,
|
|
4
|
+
type ServiceCancellationRefundPolicy,
|
|
5
|
+
} from "./serviceUtils.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Converts a tier threshold from SERVICE_CANCELLATION_POLICY_DATA to hours so
|
|
9
|
+
* rules that mix `days` and `hours` are compared on one timeline.
|
|
10
|
+
*/
|
|
11
|
+
export function cancellationRuleThresholdHours(
|
|
12
|
+
rule: ServiceCancellationRefundPolicy
|
|
13
|
+
): number {
|
|
14
|
+
if (rule.days !== undefined) {
|
|
15
|
+
return rule.days * 24;
|
|
16
|
+
}
|
|
17
|
+
if (rule.hours !== undefined) {
|
|
18
|
+
return rule.hours;
|
|
19
|
+
}
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ResolveCancellationRefundOptions = {
|
|
24
|
+
/** Event start time (UTC or local — caller must be consistent). */
|
|
25
|
+
eventStart: Date;
|
|
26
|
+
/** Defaults to `new Date()`. */
|
|
27
|
+
now?: Date;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Hours from `now` until `eventStart` (non-negative). Returns 0 if the event
|
|
32
|
+
* has started or `eventStart` is invalid.
|
|
33
|
+
*/
|
|
34
|
+
export function hoursUntilEventStart(
|
|
35
|
+
eventStart: Date,
|
|
36
|
+
now: Date = new Date()
|
|
37
|
+
): number {
|
|
38
|
+
const ms = eventStart.getTime() - now.getTime();
|
|
39
|
+
if (ms <= 0 || Number.isNaN(ms)) {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
return ms / (1000 * 60 * 60);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolves refund as a fraction in [0, 1] from {@link SERVICE_CANCELLATION_POLICY_DATA}.
|
|
47
|
+
* For each rule, if time until start is at least that rule's threshold, the rule
|
|
48
|
+
* applies; when multiple apply, the highest `refundPercentage` wins (matches
|
|
49
|
+
* tiered "full → partial → none" semantics).
|
|
50
|
+
*/
|
|
51
|
+
export function resolveCancellationRefundFraction(
|
|
52
|
+
policy: ServiceCancellationPolicy | null | undefined,
|
|
53
|
+
options: ResolveCancellationRefundOptions
|
|
54
|
+
): number {
|
|
55
|
+
if (policy == null || policy === ServiceCancellationPolicy.None) {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
const data = SERVICE_CANCELLATION_POLICY_DATA[policy];
|
|
59
|
+
if (!data?.refundPolicy?.length) {
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const now = options.now ?? new Date();
|
|
64
|
+
const hoursUntil = hoursUntilEventStart(options.eventStart, now);
|
|
65
|
+
if (hoursUntil <= 0) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let bestPercent = 0;
|
|
70
|
+
for (const rule of data.refundPolicy) {
|
|
71
|
+
const thresholdHours = cancellationRuleThresholdHours(rule);
|
|
72
|
+
if (hoursUntil >= thresholdHours && rule.refundPercentage > bestPercent) {
|
|
73
|
+
bestPercent = rule.refundPercentage;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return bestPercent / 100;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The winning tier rule for display (e.g. service booking UI), or null if none.
|
|
82
|
+
*/
|
|
83
|
+
export function getMatchingCancellationRefundRule(
|
|
84
|
+
policy: ServiceCancellationPolicy | null | undefined,
|
|
85
|
+
options: ResolveCancellationRefundOptions
|
|
86
|
+
): ServiceCancellationRefundPolicy | null {
|
|
87
|
+
if (policy == null || policy === ServiceCancellationPolicy.None) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const data = SERVICE_CANCELLATION_POLICY_DATA[policy];
|
|
91
|
+
if (!data?.refundPolicy?.length) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const now = options.now ?? new Date();
|
|
96
|
+
const hoursUntil = hoursUntilEventStart(options.eventStart, now);
|
|
97
|
+
if (hoursUntil <= 0) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let bestRule: ServiceCancellationRefundPolicy | null = null;
|
|
102
|
+
let bestPercent = 0;
|
|
103
|
+
for (const rule of data.refundPolicy) {
|
|
104
|
+
const thresholdHours = cancellationRuleThresholdHours(rule);
|
|
105
|
+
if (hoursUntil >= thresholdHours && rule.refundPercentage > bestPercent) {
|
|
106
|
+
bestPercent = rule.refundPercentage;
|
|
107
|
+
bestRule = rule;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return bestRule;
|
|
112
|
+
}
|
|
@@ -123,6 +123,91 @@ export const SERVICE_CANCELLATION_POLICY_DATA: ServiceCancellationPolicyMap = {
|
|
|
123
123
|
],
|
|
124
124
|
description: "Strict policy for vendor bookings with longer notice periods",
|
|
125
125
|
},
|
|
126
|
+
// Exhibitor / Sponsor: same tier math as vendor; separate enum + copy for role clarity
|
|
127
|
+
[ServiceCancellationPolicyOption.ExhibitorFlexible]: {
|
|
128
|
+
name: "Exhibitor Flexible",
|
|
129
|
+
refundPolicy: [
|
|
130
|
+
{
|
|
131
|
+
days: 7,
|
|
132
|
+
refundPercentage: 100,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
hours: 24,
|
|
136
|
+
refundPercentage: 50,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
description: "Flexible policy for exhibitor bookings",
|
|
140
|
+
},
|
|
141
|
+
[ServiceCancellationPolicyOption.ExhibitorStandard]: {
|
|
142
|
+
name: "Exhibitor Standard",
|
|
143
|
+
refundPolicy: [
|
|
144
|
+
{
|
|
145
|
+
days: 14,
|
|
146
|
+
refundPercentage: 100,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
days: 3,
|
|
150
|
+
refundPercentage: 50,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
description: "Standard policy for exhibitor bookings",
|
|
154
|
+
},
|
|
155
|
+
[ServiceCancellationPolicyOption.ExhibitorStrict]: {
|
|
156
|
+
name: "Exhibitor Strict",
|
|
157
|
+
refundPolicy: [
|
|
158
|
+
{
|
|
159
|
+
days: 30,
|
|
160
|
+
refundPercentage: 100,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
days: 7,
|
|
164
|
+
refundPercentage: 25,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
description: "Strict policy for exhibitor bookings",
|
|
168
|
+
},
|
|
169
|
+
[ServiceCancellationPolicyOption.SponsorFlexible]: {
|
|
170
|
+
name: "Sponsor Flexible",
|
|
171
|
+
refundPolicy: [
|
|
172
|
+
{
|
|
173
|
+
days: 7,
|
|
174
|
+
refundPercentage: 100,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
hours: 24,
|
|
178
|
+
refundPercentage: 50,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
description: "Flexible policy for sponsorship bookings",
|
|
182
|
+
},
|
|
183
|
+
[ServiceCancellationPolicyOption.SponsorStandard]: {
|
|
184
|
+
name: "Sponsor Standard",
|
|
185
|
+
refundPolicy: [
|
|
186
|
+
{
|
|
187
|
+
days: 14,
|
|
188
|
+
refundPercentage: 100,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
days: 3,
|
|
192
|
+
refundPercentage: 50,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
description: "Standard policy for sponsorship bookings",
|
|
196
|
+
},
|
|
197
|
+
[ServiceCancellationPolicyOption.SponsorStrict]: {
|
|
198
|
+
name: "Sponsor Strict",
|
|
199
|
+
refundPolicy: [
|
|
200
|
+
{
|
|
201
|
+
days: 30,
|
|
202
|
+
refundPercentage: 100,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
days: 7,
|
|
206
|
+
refundPercentage: 25,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
description: "Strict policy for sponsorship bookings",
|
|
210
|
+
},
|
|
126
211
|
} as const;
|
|
127
212
|
|
|
128
213
|
function generateDescription(
|
|
@@ -140,9 +225,10 @@ function generateDescription(
|
|
|
140
225
|
if (index === 0) {
|
|
141
226
|
return `Guests may cancel their Booking until ${timeValue} ${unit} before the event start time and will receive ${refundText} of their Booking Price.`;
|
|
142
227
|
} else {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
228
|
+
const prevPolicy = refundPolicy[index - 1];
|
|
229
|
+
const prevUnit = prevPolicy.days ? "days" : "hours";
|
|
230
|
+
const prevTime = prevPolicy.days ?? prevPolicy.hours;
|
|
231
|
+
return `Guests may cancel their Booking between ${prevTime} ${prevUnit} and ${timeValue} ${unit} before the event start time and receive ${refundText} of their Booking Price.`;
|
|
146
232
|
}
|
|
147
233
|
});
|
|
148
234
|
|
|
@@ -158,6 +244,12 @@ function generateDescription(
|
|
|
158
244
|
return descriptions.join(" ");
|
|
159
245
|
}
|
|
160
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Vendor cancellation policy copy shown in the service wizard. Hosts send
|
|
249
|
+
* **booking requests**; after acceptance and payment, the vendor's obligation
|
|
250
|
+
* is a **verified booking**. Use "verified booking" for vendor-initiated
|
|
251
|
+
* cancellation—never "booking request" in this generator.
|
|
252
|
+
*/
|
|
161
253
|
function generateVendorDescription(
|
|
162
254
|
refundPolicy: ServiceCancellationRefundPolicy[]
|
|
163
255
|
): string {
|
|
@@ -171,11 +263,12 @@ function generateVendorDescription(
|
|
|
171
263
|
: `${policy.refundPercentage}% refund from the Host (excluding Fees)`;
|
|
172
264
|
|
|
173
265
|
if (index === 0) {
|
|
174
|
-
return `
|
|
266
|
+
return `If you (the vendor) cancel your verified booking at least ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
175
267
|
} else {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
268
|
+
const prevPolicy = refundPolicy[index - 1];
|
|
269
|
+
const prevUnit = prevPolicy.days ? "days" : "hours";
|
|
270
|
+
const prevTime = prevPolicy.days ?? prevPolicy.hours;
|
|
271
|
+
return `If you (the vendor) cancel your verified booking between ${prevTime} ${prevUnit} and ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
179
272
|
}
|
|
180
273
|
});
|
|
181
274
|
|
|
@@ -183,14 +276,97 @@ function generateVendorDescription(
|
|
|
183
276
|
refundPolicy[refundPolicy.length - 1].days ??
|
|
184
277
|
refundPolicy[refundPolicy.length - 1].hours;
|
|
185
278
|
descriptions.push(
|
|
186
|
-
`
|
|
279
|
+
`If you (the vendor) cancel your verified booking less than ${lastTime} ${
|
|
187
280
|
refundPolicy[refundPolicy.length - 1].days ? "days" : "hours"
|
|
188
|
-
} before the
|
|
281
|
+
} before the event start time, your payment is not refundable under this policy.`
|
|
189
282
|
);
|
|
190
283
|
|
|
191
|
-
// Add host cancellation policy
|
|
192
284
|
descriptions.push(
|
|
193
|
-
`If the Host cancels the event or
|
|
285
|
+
`If the Host cancels the event or your verified booking, you will generally receive a full refund of what you paid, unless the cancellation was due to your non-compliance with the Host's or event's requirements.`
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
return descriptions.join(" ");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Exhibitor-facing copy: hosts send booking requests; after accept + pay, use
|
|
293
|
+
* "verified booking" for exhibitor-initiated cancellation (same convention as vendors).
|
|
294
|
+
*/
|
|
295
|
+
function generateExhibitorDescription(
|
|
296
|
+
refundPolicy: ServiceCancellationRefundPolicy[]
|
|
297
|
+
): string {
|
|
298
|
+
const descriptions = refundPolicy.map((policy, index) => {
|
|
299
|
+
const unit = policy.days ? "days" : "hours";
|
|
300
|
+
const timeValue = policy.days ?? policy.hours;
|
|
301
|
+
|
|
302
|
+
const refundText =
|
|
303
|
+
policy.refundPercentage === 100
|
|
304
|
+
? "a full refund from the Host (including all Fees)"
|
|
305
|
+
: `${policy.refundPercentage}% refund from the Host (excluding Fees)`;
|
|
306
|
+
|
|
307
|
+
if (index === 0) {
|
|
308
|
+
return `If you (the exhibitor) cancel your verified booking at least ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
309
|
+
} else {
|
|
310
|
+
const prevPolicy = refundPolicy[index - 1];
|
|
311
|
+
const prevUnit = prevPolicy.days ? "days" : "hours";
|
|
312
|
+
const prevTime = prevPolicy.days ?? prevPolicy.hours;
|
|
313
|
+
return `If you (the exhibitor) cancel your verified booking between ${prevTime} ${prevUnit} and ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const lastTime =
|
|
318
|
+
refundPolicy[refundPolicy.length - 1].days ??
|
|
319
|
+
refundPolicy[refundPolicy.length - 1].hours;
|
|
320
|
+
descriptions.push(
|
|
321
|
+
`If you (the exhibitor) cancel your verified booking less than ${lastTime} ${
|
|
322
|
+
refundPolicy[refundPolicy.length - 1].days ? "days" : "hours"
|
|
323
|
+
} before the event start time, your payment is not refundable under this policy.`
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
descriptions.push(
|
|
327
|
+
`If the Host cancels the event or your verified booking, you will generally receive a full refund of what you paid, unless the cancellation was due to your non-compliance with the Host's or event's requirements.`
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return descriptions.join(" ");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Sponsor-facing copy: same payment-to-host model; use "sponsorship commitment" where
|
|
335
|
+
* "verified booking" would be awkward for sponsors.
|
|
336
|
+
*/
|
|
337
|
+
function generateSponsorDescription(
|
|
338
|
+
refundPolicy: ServiceCancellationRefundPolicy[]
|
|
339
|
+
): string {
|
|
340
|
+
const descriptions = refundPolicy.map((policy, index) => {
|
|
341
|
+
const unit = policy.days ? "days" : "hours";
|
|
342
|
+
const timeValue = policy.days ?? policy.hours;
|
|
343
|
+
|
|
344
|
+
const refundText =
|
|
345
|
+
policy.refundPercentage === 100
|
|
346
|
+
? "a full refund from the Host (including all Fees)"
|
|
347
|
+
: `${policy.refundPercentage}% refund from the Host (excluding Fees)`;
|
|
348
|
+
|
|
349
|
+
if (index === 0) {
|
|
350
|
+
return `If you (the sponsor) cancel your verified sponsorship commitment at least ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
351
|
+
} else {
|
|
352
|
+
const prevPolicy = refundPolicy[index - 1];
|
|
353
|
+
const prevUnit = prevPolicy.days ? "days" : "hours";
|
|
354
|
+
const prevTime = prevPolicy.days ?? prevPolicy.hours;
|
|
355
|
+
return `If you (the sponsor) cancel your verified sponsorship commitment between ${prevTime} ${prevUnit} and ${timeValue} ${unit} before the event start time, you will receive ${refundText} of what you paid the Host.`;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const lastTime =
|
|
360
|
+
refundPolicy[refundPolicy.length - 1].days ??
|
|
361
|
+
refundPolicy[refundPolicy.length - 1].hours;
|
|
362
|
+
descriptions.push(
|
|
363
|
+
`If you (the sponsor) cancel your verified sponsorship commitment less than ${lastTime} ${
|
|
364
|
+
refundPolicy[refundPolicy.length - 1].days ? "days" : "hours"
|
|
365
|
+
} before the event start time, your payment is not refundable under this policy.`
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
descriptions.push(
|
|
369
|
+
`If the Host cancels the event or your sponsorship, you will generally receive a full refund of what you paid, unless the cancellation was due to your non-compliance with the Host's or event's requirements.`
|
|
194
370
|
);
|
|
195
371
|
|
|
196
372
|
return descriptions.join(" ");
|
|
@@ -207,11 +383,14 @@ Object.keys(SERVICE_CANCELLATION_POLICY_DATA)
|
|
|
207
383
|
SERVICE_CANCELLATION_POLICY_DATA[
|
|
208
384
|
policyKey as ServiceCancellationPolicyOption
|
|
209
385
|
];
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (isVendorPolicy) {
|
|
386
|
+
|
|
387
|
+
const keyStr = policyKey as string;
|
|
388
|
+
if (keyStr.startsWith("Vendor")) {
|
|
214
389
|
policy.description = generateVendorDescription(policy.refundPolicy);
|
|
390
|
+
} else if (keyStr.startsWith("Exhibitor")) {
|
|
391
|
+
policy.description = generateExhibitorDescription(policy.refundPolicy);
|
|
392
|
+
} else if (keyStr.startsWith("Sponsor")) {
|
|
393
|
+
policy.description = generateSponsorDescription(policy.refundPolicy);
|
|
215
394
|
} else {
|
|
216
395
|
policy.description = generateDescription(policy.refundPolicy);
|
|
217
396
|
}
|
|
@@ -277,6 +456,27 @@ export function serviceWizardUrl(
|
|
|
277
456
|
return `/wz/services/${serviceId}/${serviceType}/${specificId}/${step}/${substep}`;
|
|
278
457
|
}
|
|
279
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Vendor, Exhibitor, and Sponsor profile wizards share this layout: major step 6 = "How",
|
|
461
|
+
* sub-step 3 = "Payment method" (partner profile wizard payment step).
|
|
462
|
+
*/
|
|
463
|
+
export const PARTNER_SERVICE_PROFILE_WIZARD_PAYMENT_METHOD_MAJOR_STEP = 6;
|
|
464
|
+
export const PARTNER_SERVICE_PROFILE_WIZARD_PAYMENT_METHOD_SUB_STEP = 3;
|
|
465
|
+
|
|
466
|
+
export function partnerServiceProfileWizardPaymentMethodUrl(
|
|
467
|
+
serviceId: string,
|
|
468
|
+
serviceType: string,
|
|
469
|
+
specificId: string
|
|
470
|
+
): string {
|
|
471
|
+
return serviceWizardUrl(
|
|
472
|
+
serviceId,
|
|
473
|
+
serviceType,
|
|
474
|
+
specificId,
|
|
475
|
+
PARTNER_SERVICE_PROFILE_WIZARD_PAYMENT_METHOD_MAJOR_STEP,
|
|
476
|
+
PARTNER_SERVICE_PROFILE_WIZARD_PAYMENT_METHOD_SUB_STEP
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
280
480
|
export function serviceCheckoutUrl(
|
|
281
481
|
serviceId: string,
|
|
282
482
|
serviceType: string,
|
|
@@ -440,14 +640,14 @@ export const serviceSpecificInfoAI = {
|
|
|
440
640
|
isArray: false,
|
|
441
641
|
},
|
|
442
642
|
vendorType: {
|
|
443
|
-
description: `vendorType
|
|
643
|
+
description: `vendorType: use "products" for physical goods (retail food, merchandise, packaged goods) and "services" for experiential booth activities (face painting, henna, etc.). If the prompt is only a well-known business or brand name, infer the correct mode: restaurant chains, bakeries, coffee shops, snack brands → "products"; activity-only brands → "services" when appropriate.`,
|
|
444
644
|
example: `"products"`,
|
|
445
645
|
type: "string",
|
|
446
646
|
isArray: false,
|
|
447
647
|
},
|
|
448
648
|
vendorProductTypes: {
|
|
449
|
-
description: `vendorProductTypes (array of VendedProductType enum values for PRODUCT vendors: Art, HandcraftedItems, Pottery, Woodwork, LeatherGoods, Textiles, BakedGoods, Beverages, FoodItems, HoneyAndJams, PopcornAndSnacks, SweetTreats, Spices, Sauces, MakeupCosmetics, SkincareProducts, BodyCareProducts, Soaps, Perfumes, HairCareProducts, Clothing, TShirtsAndApparel, Jewelry, Accessories, Hats, Bags, Shoes, HomeDecor, Candles, Plants, Succulents, Furniture, Kitchenware, Books, Crystals, SpiritualItems, HerbalRemedies, VintageAntiques, Collectibles, PhotographyPrints, StickersAndStationery, Prints, Zines, Toys, BabyItems, PetProducts, TechAccessories, Other). Only use for vendors with vendorType="products".`,
|
|
450
|
-
example: `["
|
|
649
|
+
description: `vendorProductTypes (array of VendedProductType enum values for PRODUCT vendors: Art, HandcraftedItems, Pottery, Woodwork, LeatherGoods, Textiles, BakedGoods, Beverages, FoodItems, HoneyAndJams, PopcornAndSnacks, SweetTreats, Spices, Sauces, MakeupCosmetics, SkincareProducts, BodyCareProducts, Soaps, Perfumes, HairCareProducts, Clothing, TShirtsAndApparel, Jewelry, Accessories, Hats, Bags, Shoes, HomeDecor, Candles, Plants, Succulents, Furniture, Kitchenware, Books, Crystals, SpiritualItems, HerbalRemedies, VintageAntiques, Collectibles, PhotographyPrints, StickersAndStationery, Prints, Zines, Toys, BabyItems, PetProducts, TechAccessories, Other). Only use for vendors with vendorType="products". For famous food/dessert brands (e.g. doughnuts, coffee), use FoodItems and/or SweetTreats (both are valid when appropriate).`,
|
|
650
|
+
example: `["FoodItems", "SweetTreats"]`,
|
|
451
651
|
type: "string",
|
|
452
652
|
isArray: true,
|
|
453
653
|
},
|
|
@@ -458,8 +658,8 @@ export const serviceSpecificInfoAI = {
|
|
|
458
658
|
isArray: true,
|
|
459
659
|
},
|
|
460
660
|
goodsOrServices: {
|
|
461
|
-
description: `goodsOrServices (array of specific
|
|
462
|
-
example: `["
|
|
661
|
+
description: `goodsOrServices (array of specific items offered — e.g. "Donuts", "Coffee", "Glazed doughnuts", not just category names). When the user names a famous brand, infer typical menu or product lines for that brand.`,
|
|
662
|
+
example: `["Donuts", "Coffee", "Seasonal flavors"]`,
|
|
463
663
|
type: "string",
|
|
464
664
|
isArray: true,
|
|
465
665
|
},
|
package/src/utils/slugUtils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import slugify from 'slugify';
|
|
2
|
+
import { BASH_DETAIL_URL } from '../definitions.js';
|
|
2
3
|
|
|
3
4
|
export function generateSlug(title: string): string {
|
|
4
5
|
return slugify(title, {
|
|
@@ -31,6 +32,21 @@ export function generateBashDetailUrl(bashEventId: string, slug: string): string
|
|
|
31
32
|
return `/bash/${bashEventId}-${slug}`;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Public event path for links shared to social / QR (canonical frontend URL).
|
|
37
|
+
* With a slug: `/bash/{id}-{slug}`; without: `/bash/{id}` (legacy id-only segment).
|
|
38
|
+
*/
|
|
39
|
+
export function getPublicBashDetailPath(
|
|
40
|
+
bashEventId: string,
|
|
41
|
+
slug: string | null | undefined
|
|
42
|
+
): string {
|
|
43
|
+
const s = slug?.trim();
|
|
44
|
+
if (s) {
|
|
45
|
+
return generateBashDetailUrl(bashEventId, s);
|
|
46
|
+
}
|
|
47
|
+
return `${BASH_DETAIL_URL}/${bashEventId}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
export function parseBashUrlParams(param: string): { id: string; slug: string | null } | null {
|
|
35
51
|
// Expected format: "id-slug" where id is first part before first dash
|
|
36
52
|
const dashIndex = param.indexOf('-');
|