@session-services/web-elements 0.1.28 → 0.1.31
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/README.md +5 -0
- package/dist/cjs/{index-BYBgrATL.js → index-B2ZmNnji.js} +292 -245
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/session-services-elements.cjs.js +3 -3
- package/dist/cjs/session-services-formatted-text.cjs.entry.js +8 -20
- package/dist/cjs/session-services-ticketing.cjs.entry.js +2188 -23012
- package/dist/collection/collection-manifest.json +4 -3
- package/dist/collection/components/session-services-ticketing/bot-detection.js +83 -0
- package/dist/collection/components/session-services-ticketing/bot-detection.test.js +74 -0
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +164 -36
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +811 -262
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js +39 -8
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.test.js +47 -3
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js +611 -20
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -1311
- package/dist/components/session-services-formatted-text.js +1 -1802
- package/dist/components/session-services-ticketing.js +1 -25072
- package/dist/esm/{index-CNOpF6tk.js → index-DhbVMqIT.js} +292 -245
- package/dist/esm/loader.js +3 -3
- package/dist/esm/session-services-elements.js +4 -4
- package/dist/esm/session-services-formatted-text.entry.js +8 -20
- package/dist/esm/session-services-ticketing.entry.js +2188 -23012
- package/dist/session-services-elements/{p-703444ed.entry.js → p-8483ca88.entry.js} +1 -1
- package/dist/session-services-elements/p-DhbVMqIT.js +2 -0
- package/dist/session-services-elements/p-d7264f6b.entry.js +1 -0
- package/dist/session-services-elements/session-services-elements.esm.js +1 -1
- package/dist/types/components/session-services-ticketing/bot-detection.d.ts +13 -0
- package/dist/types/components/session-services-ticketing/bot-detection.test.d.ts +1 -0
- package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +67 -9
- package/dist/types/components/session-services-ticketing/session-services-ticketing.logic.d.ts +11 -2
- package/dist/types/components.d.ts +36 -8
- package/dist/types/index.d.ts +1 -1
- package/dist/types/stencil-public-runtime.d.ts +67 -5
- package/package.json +15 -11
- package/dist/session-services-elements/p-1acfcdfd.entry.js +0 -1
- package/dist/session-services-elements/p-CNOpF6tk.js +0 -2
package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isNaiveDateTimeFuture, isNaiveDateTimePast, parseNaiveDateTime, } from "@session-services/utils/naive-datetime";
|
|
2
|
+
import { getAdmissionReleaseLabel } from "@session-services/utils/admission-release";
|
|
2
3
|
import { getAdmissionSelectionCapacity, isAdmissionSoldOut, } from "@session-services/utils/ticket-availability";
|
|
3
4
|
import { createQuoteItemMap } from "@session-services/utils/ticket-quote";
|
|
4
5
|
const UNLIMITED_CAP = 10;
|
|
5
6
|
const SCARCITY_THRESHOLD = 20;
|
|
7
|
+
const DEFAULT_LOCALE = "en-US";
|
|
6
8
|
export function selectionKey(admissionId, sectionId) {
|
|
7
9
|
return `${admissionId}::${sectionId || "none"}`;
|
|
8
10
|
}
|
|
@@ -50,9 +52,11 @@ function isAdmissionOnSale(admission, zone) {
|
|
|
50
52
|
function hasAdmissionCapacity(admission, section) {
|
|
51
53
|
return getAdmissionSelectionCapacity({ admission, section }) > 0;
|
|
52
54
|
}
|
|
53
|
-
function isAdmissionPurchasable(admission, zone, sections) {
|
|
55
|
+
function isAdmissionPurchasable(admission, admissions, zone, sections) {
|
|
54
56
|
if (!hasAdmissionSection(admission, sections))
|
|
55
57
|
return false;
|
|
58
|
+
if (getAdmissionReleaseLabel(admission, admissions, zone))
|
|
59
|
+
return false;
|
|
56
60
|
if (!isAdmissionOnSale(admission, zone))
|
|
57
61
|
return false;
|
|
58
62
|
return hasAdmissionCapacity(admission, admissionSection(admission, sections));
|
|
@@ -60,17 +64,37 @@ function isAdmissionPurchasable(admission, zone, sections) {
|
|
|
60
64
|
function hasPurchasableAdmissions(event) {
|
|
61
65
|
const sections = sectionsById(event);
|
|
62
66
|
const zone = eventTimeZone(event);
|
|
63
|
-
return event.admissions.some((admission) => isAdmissionPurchasable(admission, zone, sections));
|
|
67
|
+
return event.admissions.some((admission) => isAdmissionPurchasable(admission, event.admissions, zone, sections));
|
|
64
68
|
}
|
|
65
|
-
function admissionSaleLabel(admission, zone) {
|
|
69
|
+
function admissionSaleLabel(admission, zone, locale) {
|
|
66
70
|
if (!isNaiveDateTimeFuture(admission.saleStartsAt, zone)) {
|
|
67
71
|
return null;
|
|
68
72
|
}
|
|
73
|
+
const date = parseNaiveDateTime(admission.saleStartsAt, "UTC");
|
|
74
|
+
const day = dateFormatter(locale, {
|
|
75
|
+
day: "numeric",
|
|
76
|
+
month: "short",
|
|
77
|
+
timeZone: "UTC",
|
|
78
|
+
weekday: "short",
|
|
79
|
+
}).format(date);
|
|
80
|
+
const time = dateFormatter(locale, {
|
|
81
|
+
hour: "numeric",
|
|
82
|
+
minute: "2-digit",
|
|
83
|
+
timeZone: "UTC",
|
|
84
|
+
}).format(date);
|
|
69
85
|
return {
|
|
70
|
-
text: `On sale ${
|
|
86
|
+
text: `On sale ${day} · ${time}`,
|
|
71
87
|
tone: "muted",
|
|
72
88
|
};
|
|
73
89
|
}
|
|
90
|
+
function dateFormatter(locale, options) {
|
|
91
|
+
try {
|
|
92
|
+
return new Intl.DateTimeFormat(locale, options);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return new Intl.DateTimeFormat(DEFAULT_LOCALE, options);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
74
98
|
function admissionStatus(input) {
|
|
75
99
|
if (input.value === "DOOR" && (input.soldOut || input.saleEnded)) {
|
|
76
100
|
return { text: "Available At Door", tone: "success" };
|
|
@@ -129,7 +153,8 @@ export function visibleAdmissions(event, mode) {
|
|
|
129
153
|
if (mode !== "door")
|
|
130
154
|
return event.admissions;
|
|
131
155
|
const zone = eventTimeZone(event);
|
|
132
|
-
return event.admissions.filter((admission) => admission.
|
|
156
|
+
return event.admissions.filter((admission) => !getAdmissionReleaseLabel(admission, event.admissions, zone) &&
|
|
157
|
+
admission.saleEndsBehavior === "DOOR" &&
|
|
133
158
|
isNaiveDateTimePast(admission.saleEndsAt, zone));
|
|
134
159
|
}
|
|
135
160
|
function isStackableAdmission(input) {
|
|
@@ -263,6 +288,7 @@ function showScarcity(admission, soldOut, disabled) {
|
|
|
263
288
|
}
|
|
264
289
|
export function getAdmissionDisplayState(input) {
|
|
265
290
|
const zone = eventTimeZone(input.event);
|
|
291
|
+
const releaseLabel = getAdmissionReleaseLabel(input.admission, input.event.admissions, zone);
|
|
266
292
|
const saleNotStarted = isNaiveDateTimeFuture(input.admission.saleStartsAt, zone);
|
|
267
293
|
const saleEnded = isNaiveDateTimePast(input.admission.saleEndsAt, zone);
|
|
268
294
|
const saleEndedSoldOut = saleEnded && input.admission.saleEndsBehavior === "SOLD_OUT";
|
|
@@ -279,8 +305,10 @@ export function getAdmissionDisplayState(input) {
|
|
|
279
305
|
value: input.admission.saleEndsBehavior,
|
|
280
306
|
});
|
|
281
307
|
return {
|
|
282
|
-
disabled,
|
|
283
|
-
label:
|
|
308
|
+
disabled: disabled || Boolean(releaseLabel),
|
|
309
|
+
label: releaseLabel
|
|
310
|
+
? { ...releaseLabel, tone: "muted" }
|
|
311
|
+
: admissionSaleLabel(input.admission, zone, input.locale ?? DEFAULT_LOCALE),
|
|
284
312
|
maxQuantity: maxQuantity(input.admission, input.section),
|
|
285
313
|
postSale: saleEnded,
|
|
286
314
|
showScarcity: showScarcity(input.admission, soldOut, disabled),
|
|
@@ -340,6 +368,9 @@ export function previewQuoteItems(event) {
|
|
|
340
368
|
const zone = eventTimeZone(event);
|
|
341
369
|
return event.admissions
|
|
342
370
|
.filter((admission) => {
|
|
371
|
+
if (getAdmissionReleaseLabel(admission, event.admissions, zone)) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
343
374
|
if (admission.sectionId && !ids.has(admission.sectionId))
|
|
344
375
|
return false;
|
|
345
376
|
if (isNaiveDateTimeFuture(admission.saleStartsAt, zone))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { admissionPriceSavings, discountCouponFromSearch, getAdmissionDisplayState, getAdmissionPriceDisplay, getTicketingState, topLevelTicketItems, visibleAdmissions, } from "./session-services-ticketing.logic";
|
|
2
|
+
import { admissionPriceSavings, discountCouponFromSearch, getAdmissionDisplayState, getAdmissionPriceDisplay, getTicketingState, previewQuoteItems, topLevelTicketItems, visibleAdmissions, } from "./session-services-ticketing.logic";
|
|
3
3
|
const futureStart = "2030-01-01T20:00:00";
|
|
4
4
|
const futureEnd = "2030-01-01T23:00:00";
|
|
5
5
|
const pastStart = "2020-01-01T20:00:00";
|
|
@@ -162,7 +162,7 @@ describe("session-services-ticketing logic", () => {
|
|
|
162
162
|
"adm_door",
|
|
163
163
|
]);
|
|
164
164
|
});
|
|
165
|
-
it("
|
|
165
|
+
it("formats future sale labels in the default en-US locale", () => {
|
|
166
166
|
const data = event({
|
|
167
167
|
admissions: [
|
|
168
168
|
admission({
|
|
@@ -176,7 +176,51 @@ describe("session-services-ticketing logic", () => {
|
|
|
176
176
|
event: data,
|
|
177
177
|
});
|
|
178
178
|
expect(state.disabled).toBe(true);
|
|
179
|
-
expect(state.label?.text).
|
|
179
|
+
expect(state.label?.text).toBe("On sale Tue, Jan 1 · 8:00 PM");
|
|
180
|
+
});
|
|
181
|
+
it("formats future sale labels in the requested locale", () => {
|
|
182
|
+
const data = event({
|
|
183
|
+
admissions: [
|
|
184
|
+
admission({
|
|
185
|
+
saleStartsAt: futureStart,
|
|
186
|
+
saleEndsAt: "2030-01-02T20:00:00",
|
|
187
|
+
}),
|
|
188
|
+
],
|
|
189
|
+
});
|
|
190
|
+
const state = getAdmissionDisplayState({
|
|
191
|
+
admission: data.admissions[0],
|
|
192
|
+
event: data,
|
|
193
|
+
locale: "fr-FR",
|
|
194
|
+
});
|
|
195
|
+
expect(state.disabled).toBe(true);
|
|
196
|
+
expect(state.label?.text).toBe("On sale mar. 1 janv. · 20:00");
|
|
197
|
+
});
|
|
198
|
+
it("disables dependent releases until the previous release ends", () => {
|
|
199
|
+
const first = admission({
|
|
200
|
+
id: "adm_first",
|
|
201
|
+
name: "First Release",
|
|
202
|
+
capacity: 100,
|
|
203
|
+
sold: 20,
|
|
204
|
+
});
|
|
205
|
+
const second = admission({
|
|
206
|
+
id: "adm_second",
|
|
207
|
+
name: "Second Release",
|
|
208
|
+
visibility: "AFTER_ADMISSION",
|
|
209
|
+
visibleAfterAdmissionId: first.id,
|
|
210
|
+
});
|
|
211
|
+
const data = event({ admissions: [first, second] });
|
|
212
|
+
const state = getAdmissionDisplayState({
|
|
213
|
+
admission: second,
|
|
214
|
+
event: data,
|
|
215
|
+
});
|
|
216
|
+
expect(state.disabled).toBe(true);
|
|
217
|
+
expect(state.label).toEqual({
|
|
218
|
+
text: "Available soon",
|
|
219
|
+
tone: "muted",
|
|
220
|
+
});
|
|
221
|
+
expect(previewQuoteItems(data).map((item) => item.admissionId)).toEqual([
|
|
222
|
+
first.id,
|
|
223
|
+
]);
|
|
180
224
|
});
|
|
181
225
|
it("treats sold-out sale endings as sold out instead of a price label", () => {
|
|
182
226
|
const data = event({
|