@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.
Files changed (37) hide show
  1. package/README.md +5 -0
  2. package/dist/cjs/{index-BYBgrATL.js → index-B2ZmNnji.js} +292 -245
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/session-services-elements.cjs.js +3 -3
  5. package/dist/cjs/session-services-formatted-text.cjs.entry.js +8 -20
  6. package/dist/cjs/session-services-ticketing.cjs.entry.js +2188 -23012
  7. package/dist/collection/collection-manifest.json +4 -3
  8. package/dist/collection/components/session-services-ticketing/bot-detection.js +83 -0
  9. package/dist/collection/components/session-services-ticketing/bot-detection.test.js +74 -0
  10. package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +164 -36
  11. package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +811 -262
  12. package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.js +39 -8
  13. package/dist/collection/components/session-services-ticketing/session-services-ticketing.logic.test.js +47 -3
  14. package/dist/collection/components/session-services-ticketing/session-services-ticketing.test.js +611 -20
  15. package/dist/components/index.d.ts +2 -0
  16. package/dist/components/index.js +1 -1311
  17. package/dist/components/session-services-formatted-text.js +1 -1802
  18. package/dist/components/session-services-ticketing.js +1 -25072
  19. package/dist/esm/{index-CNOpF6tk.js → index-DhbVMqIT.js} +292 -245
  20. package/dist/esm/loader.js +3 -3
  21. package/dist/esm/session-services-elements.js +4 -4
  22. package/dist/esm/session-services-formatted-text.entry.js +8 -20
  23. package/dist/esm/session-services-ticketing.entry.js +2188 -23012
  24. package/dist/session-services-elements/{p-703444ed.entry.js → p-8483ca88.entry.js} +1 -1
  25. package/dist/session-services-elements/p-DhbVMqIT.js +2 -0
  26. package/dist/session-services-elements/p-d7264f6b.entry.js +1 -0
  27. package/dist/session-services-elements/session-services-elements.esm.js +1 -1
  28. package/dist/types/components/session-services-ticketing/bot-detection.d.ts +13 -0
  29. package/dist/types/components/session-services-ticketing/bot-detection.test.d.ts +1 -0
  30. package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +67 -9
  31. package/dist/types/components/session-services-ticketing/session-services-ticketing.logic.d.ts +11 -2
  32. package/dist/types/components.d.ts +36 -8
  33. package/dist/types/index.d.ts +1 -1
  34. package/dist/types/stencil-public-runtime.d.ts +67 -5
  35. package/package.json +15 -11
  36. package/dist/session-services-elements/p-1acfcdfd.entry.js +0 -1
  37. package/dist/session-services-elements/p-CNOpF6tk.js +0 -2
@@ -1,8 +1,10 @@
1
- import { formatNaiveDateTime, isNaiveDateTimeFuture, isNaiveDateTimePast, } from "@session-services/utils/naive-datetime";
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 ${formatNaiveDateTime(admission.saleStartsAt, "EEE, MMM d · h:mm a")}`,
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.saleEndsBehavior === "DOOR" &&
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: admissionSaleLabel(input.admission, zone),
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("disables admissions outside their online sale window", () => {
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).toContain("On sale");
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({