@coras-io/embed 0.1.2 → 0.3.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.
Files changed (236) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +8 -7
  3. package/dist/components/accordion/accordion-item/accordion-item.styles.js +1 -1
  4. package/dist/components/alert/alert.styles.js +14 -14
  5. package/dist/components/attraction-card/base/attraction-card.styles.js +7 -3
  6. package/dist/components/attraction-discovery/attraction-discovery.styles.js +1 -1
  7. package/dist/components/attraction-header/attraction-header.styles.js +1 -1
  8. package/dist/components/attraction-option/attraction-option.styles.js +4 -4
  9. package/dist/components/basket/basket.styles.js +27 -26
  10. package/dist/components/basket-empty/basket-empty.styles.js +1 -0
  11. package/dist/components/basket-item/basket-item.component.js +10 -4
  12. package/dist/components/basket-item/basket-item.styles.js +9 -3
  13. package/dist/components/basket-item-ticket/basket-item-ticket.styles.js +1 -3
  14. package/dist/components/basket-mobile/basket-mobile.styles.js +5 -5
  15. package/dist/components/better-carousel/better-carousel.styles.js +2 -0
  16. package/dist/components/bottom-drawer/bottom-drawer.styles.js +6 -4
  17. package/dist/components/button/button.component.d.ts +0 -1
  18. package/dist/components/button/button.component.js +1 -8
  19. package/dist/components/button/button.constants.d.ts +1 -1
  20. package/dist/components/button/button.constants.js +1 -1
  21. package/dist/components/button/button.styles.js +66 -39
  22. package/dist/components/calendar/base/calendar.styles.js +3 -5
  23. package/dist/components/calendar/multi/multi.styles.js +7 -0
  24. package/dist/components/category-tag/category-tag.styles.js +10 -7
  25. package/dist/components/checkbox/checkbox.styles.js +2 -2
  26. package/dist/components/chip/chip.styles.js +2 -2
  27. package/dist/components/collect-fida/collect-fida.component.js +5 -3
  28. package/dist/components/collect-fida/collect-fida.styles.js +9 -16
  29. package/dist/components/crypto-auth-modal/crypto-auth-modal.component.js +6 -4
  30. package/dist/components/crypto-auth-modal/crypto-auth-modal.styles.js +2 -2
  31. package/dist/components/dialog/dialog.styles.js +10 -17
  32. package/dist/components/drawer/drawer.component.js +6 -0
  33. package/dist/components/drawer/drawer.styles.js +17 -3
  34. package/dist/components/dropdown/dropdown.component.js +7 -0
  35. package/dist/components/dropdown/dropdown.styles.js +3 -6
  36. package/dist/components/expiration-timer/expiration-timer.styles.js +3 -3
  37. package/dist/components/explore-card/explore-card.styles.js +1 -1
  38. package/dist/components/filter-chip/filter-chip.styles.js +11 -9
  39. package/dist/components/filters-drawer/filters-drawer.component.js +1 -1
  40. package/dist/components/filters-drawer/filters-drawer.styles.js +6 -5
  41. package/dist/components/image/image.styles.js +3 -3
  42. package/dist/components/image-chip/image-chip.styles.js +1 -1
  43. package/dist/components/input-field/input-field.styles.js +22 -17
  44. package/dist/components/itinerary/itinerary-item/itinerary-item.styles.js +1 -1
  45. package/dist/components/joined-fields/joined-fields.styles.js +10 -6
  46. package/dist/components/loading-spinner/loading-spinner.styles.js +1 -1
  47. package/dist/components/location-image/location-image.component.d.ts +6 -0
  48. package/dist/components/location-image/location-image.component.js +15 -2
  49. package/dist/components/location-image/location-image.styles.js +1 -1
  50. package/dist/components/locations-results/locations-results.styles.js +4 -6
  51. package/dist/components/marketing-tag/marketing-tag.styles.js +1 -1
  52. package/dist/components/message-card/message-card.styles.js +6 -6
  53. package/dist/components/option-group/option-chip/option-chip.styles.js +4 -3
  54. package/dist/components/order-confirmation/order-confirmation-item/order-confirmation-item.styles.js +9 -0
  55. package/dist/components/order-confirmation/order-confirmation.component.js +4 -3
  56. package/dist/components/order-confirmation/order-confirmation.styles.js +1 -1
  57. package/dist/components/otp/otp.styles.js +6 -6
  58. package/dist/components/overlay/overlay.styles.js +5 -1
  59. package/dist/components/pay-in/pay-in.styles.js +2 -2
  60. package/dist/components/payment-fiat-method/payment-fiat-method.styles.js +20 -4
  61. package/dist/components/payment-option/payment-option.styles.js +10 -12
  62. package/dist/components/performance-time/performance-time.styles.js +2 -2
  63. package/dist/components/phone-country-code/phone-country-code.styles.js +1 -1
  64. package/dist/components/points/points.styles.js +1 -1
  65. package/dist/components/search-button/search-button.styles.js +15 -7
  66. package/dist/components/seated-selector/seated-selector.component.d.ts +4 -1
  67. package/dist/components/seated-selector/seated-selector.component.js +90 -36
  68. package/dist/components/seated-selector/seated-selector.styles.js +89 -14
  69. package/dist/components/seats-io/seats-io.component.d.ts +2 -0
  70. package/dist/components/seats-io/seats-io.component.js +35 -2
  71. package/dist/components/seats-io/seats-io.constants.d.ts +5 -0
  72. package/dist/components/seats-io/seats-io.constants.js +12 -0
  73. package/dist/components/seats-io/seats-io.transform.d.ts +8 -2
  74. package/dist/components/seats-io/seats-io.transform.js +12 -4
  75. package/dist/components/select-list/select-list.styles.js +1 -0
  76. package/dist/components/select-list/select-option/select-option.styles.js +3 -3
  77. package/dist/components/sort-dropdown/sort-dropdown.styles.js +1 -1
  78. package/dist/components/sort-list/sort-list.styles.js +1 -1
  79. package/dist/components/sport-options/sport-option/sport-option.styles.js +2 -2
  80. package/dist/components/star-rating/star-rating.component.d.ts +1 -0
  81. package/dist/components/star-rating/star-rating.component.js +19 -23
  82. package/dist/components/star-rating/star-rating.styles.js +32 -6
  83. package/dist/components/tab/tab.styles.js +1 -1
  84. package/dist/components/tabs/tabs.styles.js +1 -1
  85. package/dist/components/tag/tag.styles.js +7 -7
  86. package/dist/components/text-section/text-section.styles.js +2 -1
  87. package/dist/components/time-tag/time-tag.styles.js +1 -2
  88. package/dist/components/tooltip/tooltip.styles.js +3 -3
  89. package/dist/components/tour-ticket-type-selector/tour-ticket-type/tour-ticket-type.styles.js +2 -2
  90. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.component.js +1 -1
  91. package/dist/components/tour-ticket-type-selector/tour-ticket-type-selector.styles.js +1 -9
  92. package/dist/components/transfer-modal/transfer-modal.component.js +3 -3
  93. package/dist/components/transfer-modal/transfer-modal.styles.js +6 -8
  94. package/dist/containers/basket/basket.component.d.ts +1 -1
  95. package/dist/containers/basket/basket.component.js +56 -25
  96. package/dist/containers/footer/footer.styles.js +4 -0
  97. package/dist/containers/navbar/navbar.component.d.ts +5 -1
  98. package/dist/containers/navbar/navbar.component.js +38 -9
  99. package/dist/containers/navbar/navbar.styles.js +38 -12
  100. package/dist/contexts/basket/basket.provider.d.ts +1 -0
  101. package/dist/contexts/basket/basket.provider.js +31 -4
  102. package/dist/contexts/properties.context.d.ts +4 -4
  103. package/dist/core/api/middlewares/booking-session/booking-session.constants.d.ts +1 -0
  104. package/dist/core/api/middlewares/booking-session/booking-session.constants.js +1 -0
  105. package/dist/core/api/middlewares/booking-session/booking-session.js +18 -27
  106. package/dist/core/api/middlewares/booking-session/booking-session.utils.d.ts +4 -4
  107. package/dist/core/api/middlewares/booking-session/booking-session.utils.js +13 -10
  108. package/dist/core/api/queries.js +3 -2
  109. package/dist/core/color-scheme.d.ts +14 -0
  110. package/dist/core/color-scheme.js +48 -0
  111. package/dist/core/currency/constants.d.ts +1 -1
  112. package/dist/core/currency/constants.js +4 -3
  113. package/dist/core/i18n/date.js +20 -9
  114. package/dist/core/i18n/locale-codes.d.ts +2 -2
  115. package/dist/core/i18n/locale-codes.js +4 -0
  116. package/dist/core/images/constants.d.ts +0 -5
  117. package/dist/core/images/constants.js +0 -5
  118. package/dist/core/images/helpers.d.ts +8 -0
  119. package/dist/core/images/helpers.js +14 -6
  120. package/dist/core/images/srcset.js +2 -2
  121. package/dist/core/storybook/demo.d.ts +4 -0
  122. package/dist/core/storybook/demo.js +17 -0
  123. package/dist/core/types/locales.d.ts +7 -0
  124. package/dist/core/utils/datetime.js +10 -4
  125. package/dist/core/utils/locale.d.ts +8 -1
  126. package/dist/core/utils/locale.js +10 -0
  127. package/dist/helpers/helpers.d.ts +1 -1
  128. package/dist/index.d.ts +2 -3
  129. package/dist/index.js +1 -2
  130. package/dist/locales/ar-SA.d.ts +279 -0
  131. package/dist/locales/ar-SA.js +288 -0
  132. package/dist/locales/da-DK.d.ts +100 -82
  133. package/dist/locales/da-DK.js +104 -89
  134. package/dist/locales/de-DE.d.ts +100 -82
  135. package/dist/locales/de-DE.js +104 -89
  136. package/dist/locales/en-IE.d.ts +250 -232
  137. package/dist/locales/en-IE.js +253 -238
  138. package/dist/locales/en-US.d.ts +100 -82
  139. package/dist/locales/en-US.js +105 -90
  140. package/dist/locales/es-ES.d.ts +100 -82
  141. package/dist/locales/es-ES.js +104 -89
  142. package/dist/locales/fr-FR.d.ts +100 -82
  143. package/dist/locales/fr-FR.js +104 -89
  144. package/dist/locales/fr-MA.d.ts +100 -82
  145. package/dist/locales/fr-MA.js +104 -89
  146. package/dist/locales/is-IS.d.ts +100 -82
  147. package/dist/locales/is-IS.js +104 -89
  148. package/dist/locales/it-IT.d.ts +100 -82
  149. package/dist/locales/it-IT.js +104 -89
  150. package/dist/locales/ms-MY.d.ts +100 -82
  151. package/dist/locales/ms-MY.js +104 -89
  152. package/dist/locales/nb-NO.d.ts +100 -82
  153. package/dist/locales/nb-NO.js +104 -89
  154. package/dist/locales/pl-PL.d.ts +100 -82
  155. package/dist/locales/pl-PL.js +104 -89
  156. package/dist/locales/pt-PT.d.ts +100 -82
  157. package/dist/locales/pt-PT.js +104 -89
  158. package/dist/locales/sv-SE.d.ts +100 -82
  159. package/dist/locales/sv-SE.js +104 -89
  160. package/dist/locales/th-TH.d.ts +100 -82
  161. package/dist/locales/th-TH.js +104 -89
  162. package/dist/locales/tr-TR.d.ts +100 -82
  163. package/dist/locales/tr-TR.js +104 -89
  164. package/dist/locales/zh-CN.d.ts +100 -82
  165. package/dist/locales/zh-CN.js +105 -90
  166. package/dist/locales/zh-HK.d.ts +279 -0
  167. package/dist/locales/zh-HK.js +288 -0
  168. package/dist/mixins/coinbase.mixin.js +1 -1
  169. package/dist/mixins/error-modal.styles.js +2 -2
  170. package/dist/mixins/location-search/location-search.js +3 -3
  171. package/dist/mixins/location-search/location-search.styles.js +6 -5
  172. package/dist/sdk/chrome.d.ts +9 -3
  173. package/dist/sdk/chrome.js +21 -9
  174. package/dist/sdk/element-adapter.js +11 -8
  175. package/dist/sdk/mount.d.ts +3 -3
  176. package/dist/sdk/mount.js +150 -42
  177. package/dist/sdk/navigation-intent.js +3 -0
  178. package/dist/sdk/page-registry.d.ts +14 -2
  179. package/dist/sdk/page-registry.js +33 -13
  180. package/dist/sdk/theme.d.ts +1 -15
  181. package/dist/sdk/theme.js +4 -39
  182. package/dist/sdk/types.d.ts +55 -24
  183. package/dist/sdk/url.d.ts +24 -3
  184. package/dist/sdk/url.js +66 -27
  185. package/dist/sdk/validation.d.ts +0 -7
  186. package/dist/sdk/validation.js +105 -47
  187. package/dist/styles/contrast-fixtures.d.ts +0 -5
  188. package/dist/styles/contrast-fixtures.js +79 -9
  189. package/dist/styles/contrast.js +4 -3
  190. package/dist/styles/defaults/component.js +17 -1
  191. package/dist/styles/primitives.d.ts +0 -10
  192. package/dist/styles/primitives.js +2 -12
  193. package/dist/styles/tokens.d.ts +1 -1
  194. package/dist/styles/tokens.generated.d.ts +1 -1
  195. package/dist/styles/tokens.generated.js +1 -1
  196. package/dist/styles/tokens.metadata.d.ts +1 -1
  197. package/dist/styles/tokens.metadata.js +360 -90
  198. package/dist/views/details-page/details-page.component.d.ts +5 -5
  199. package/dist/views/details-page/details-page.component.js +97 -71
  200. package/dist/views/details-page/details-page.styles.js +64 -9
  201. package/dist/views/details-page/partials/theater-description/theater-description.component.js +5 -0
  202. package/dist/views/help-page/help-page.component.d.ts +1 -0
  203. package/dist/views/help-page/help-page.component.js +11 -4
  204. package/dist/views/help-page/help-page.styles.js +21 -8
  205. package/dist/views/landing-page/landing-page.component.d.ts +4 -4
  206. package/dist/views/landing-page/landing-page.component.js +65 -65
  207. package/dist/views/landing-page/landing-page.styles.js +70 -3
  208. package/dist/views/payment-page/partials/activity-details/activity-details.styles.js +10 -33
  209. package/dist/views/payment-page/partials/billing-address/billing-address.styles.js +4 -9
  210. package/dist/views/payment-page/partials/confirmation/confirmation.styles.js +6 -6
  211. package/dist/views/payment-page/partials/hotel-pickup-list/hotel-pickup-list.styles.js +4 -4
  212. package/dist/views/payment-page/partials/payment-type-selector/payment-type-selector.styles.js +1 -0
  213. package/dist/views/payment-page/partials/stripe/stripe.component.d.ts +1 -1
  214. package/dist/views/payment-page/partials/stripe/stripe.component.js +11 -12
  215. package/dist/views/payment-page/partials/stripe/stripe.constants.d.ts +2 -15
  216. package/dist/views/payment-page/partials/stripe/stripe.constants.js +4 -17
  217. package/dist/views/payment-page/partials/stripe/stripe.styles.js +16 -23
  218. package/dist/views/payment-page/partials/stripe/stripe.utils.d.ts +25 -14
  219. package/dist/views/payment-page/partials/stripe/stripe.utils.js +111 -62
  220. package/dist/views/payment-page/partials/terms-checkbox/terms-checkbox.styles.js +1 -1
  221. package/dist/views/payment-page/payment-page.component.d.ts +1 -0
  222. package/dist/views/payment-page/payment-page.component.js +19 -9
  223. package/dist/views/payment-page/payment-page.styles.js +37 -32
  224. package/dist/views/payment-page/payment-page.utils.d.ts +7 -0
  225. package/dist/views/payment-page/payment-page.utils.js +29 -14
  226. package/dist/views/payment-page/payment-page.validators.js +9 -3
  227. package/dist/views/payment-page/services/payment-api.service.d.ts +1 -1
  228. package/dist/views/search-page/search-page.component.d.ts +1 -0
  229. package/dist/views/search-page/search-page.component.js +12 -6
  230. package/dist/views/search-page/search-page.styles.js +24 -2
  231. package/dist/views/suggestion-widget/suggestion-widget.styles.js +1 -1
  232. package/package.json +62 -75
  233. package/dist/custom-elements.json +0 -37378
  234. package/dist/sdk/meta.d.ts +0 -7
  235. package/dist/sdk/meta.js +0 -9
  236. package/dist/styles/tokens.css +0 -359
@@ -14,7 +14,7 @@ import { map } from "lit/directives/map.js";
14
14
  import { consume } from "@lit/context";
15
15
  import { SignalWatcher } from "@lit-labs/signals";
16
16
  import { isBasketDropdownOpen, isBasketMobileOpen, } from "../../core/signals.js";
17
- import { format, formatISO, getDate, isAfter, isBefore, parseISO, } from "date-fns";
17
+ import { formatISO, getDate, isAfter, isBefore, parseISO } from "date-fns";
18
18
  import { capitalize } from "../../core/utils/text.js";
19
19
  import { apiGet, apiPost, withErrorHandling } from "../../core/api/queries.js";
20
20
  import "../../components/button/button.js";
@@ -65,21 +65,19 @@ import { loyaltyPointsContext } from "../../contexts/properties.context.js";
65
65
  import { addRecentlySelectedSearches } from "../../containers/navbar/navbar.utils.js";
66
66
  import { filterMediaForGallery } from "../../core/images/filters.js";
67
67
  import { readSeatsIoConfig } from "../../core/types/seats-config.js";
68
- // TODO: can it be dynamic/responsive?
68
+ import { getApiLanguageCode } from "../../core/utils/locale.js";
69
69
  const IMAGE_CAROUSEL_WIDTH = 480;
70
70
  const IMAGE_CAROUSEL_HEIGHT = 320;
71
71
  /**
72
- * Details page component for viewing attraction details and creating reservations.
73
- *
74
- * @fires coras:navigate - Dispatched when navigating to another page
75
- * @fires coras:state-change - Dispatched when in-page params change (date selection)
76
- * @fires coras:reservation-created - Dispatched when a reservation is successfully created via the API
72
+ * @fires coras:navigate
73
+ * @fires coras:state-change
74
+ * @fires coras:reservation-created
77
75
  */
78
76
  let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixin(BaseMixin(SignalWatcher(LitElement)))) {
79
77
  constructor() {
80
78
  super(...arguments);
81
79
  this.attractionId = "";
82
- this.date = formatISO(new Date(), { representation: "date" });
80
+ this.date = "";
83
81
  this.currentPage = "details";
84
82
  this.loyaltyPoints = false;
85
83
  this.currentMonth = undefined;
@@ -155,7 +153,12 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
155
153
  this.availabilityDays = getAvailabilityDays({
156
154
  timezone: result.data.attraction.timezone,
157
155
  salePeriods: result.data.attraction.sale_periods ?? null,
158
- targetMonth: getCurrentMonth(this.date || result.data.first_instance_date, this.currentMonth),
156
+ targetMonth: getCurrentMonth(this.date ||
157
+ formatDateWithTimezone({
158
+ dateString: result.data.first_instance_date,
159
+ timezone: result.data.attraction.timezone,
160
+ formatString: "yyyy-MM-dd",
161
+ }), this.currentMonth),
159
162
  });
160
163
  this.checkDateAvailability(result.data.first_instance_date, result.data.attraction.timezone);
161
164
  try {
@@ -165,9 +168,7 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
165
168
  name: result.data.attraction.attraction_name,
166
169
  });
167
170
  }
168
- catch {
169
- // noop
170
- }
171
+ catch { }
171
172
  return result.data;
172
173
  },
173
174
  args: () => [this.locale, this.currency, this.attractionId],
@@ -175,7 +176,7 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
175
176
  this.bookingSessionTask = new Task(this, {
176
177
  autoRun: false,
177
178
  task: async () => {
178
- const bookingSession = getBookingSession();
179
+ const bookingSession = getBookingSession(this.distributorID);
179
180
  if (bookingSession) {
180
181
  return bookingSession;
181
182
  }
@@ -185,7 +186,7 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
185
186
  locale: this.locale,
186
187
  endpoint: "/{api_version}/booking-session",
187
188
  body: {
188
- language: this.locale.split("-")[0],
189
+ language: getApiLanguageCode(this.locale),
189
190
  user_id: null,
190
191
  analytics_id: null,
191
192
  distributor_session_id: null,
@@ -198,22 +199,22 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
198
199
  try {
199
200
  const expirationDate = new Date(expiresHeader);
200
201
  if (!isNaN(expirationDate.getTime())) {
201
- storeBookingSession(newBookingSession, expirationDate);
202
+ storeBookingSession(this.distributorID, newBookingSession, expirationDate);
202
203
  }
203
204
  else {
204
- const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000); // 20 minutes
205
- storeBookingSession(newBookingSession, defaultExpiration);
205
+ const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000);
206
+ storeBookingSession(this.distributorID, newBookingSession, defaultExpiration);
206
207
  }
207
208
  }
208
209
  catch (error) {
209
210
  console.warn("Failed to parse booking session expiration date:", error);
210
- const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000); // 20 minutes
211
- storeBookingSession(newBookingSession, defaultExpiration);
211
+ const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000);
212
+ storeBookingSession(this.distributorID, newBookingSession, defaultExpiration);
212
213
  }
213
214
  }
214
215
  else {
215
- const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000); // 20 minutes
216
- storeBookingSession(newBookingSession, defaultExpiration);
216
+ const defaultExpiration = new Date(Date.now() + 20 * 60 * 1000);
217
+ storeBookingSession(this.distributorID, newBookingSession, defaultExpiration);
217
218
  }
218
219
  return newBookingSession;
219
220
  }
@@ -239,9 +240,9 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
239
240
  bookingSession,
240
241
  body: {
241
242
  currency: this.currency,
242
- language_code: this.locale.split("-")[0],
243
+ language_code: getApiLanguageCode(this.locale),
243
244
  attraction_id: this.attractionId,
244
- date: this.date,
245
+ date: this.resolvedDate,
245
246
  tickets: Object.entries(this.selectedPriceTypes).map(([key, value]) => ({
246
247
  price_type_id: key,
247
248
  number_of_tickets: value,
@@ -261,8 +262,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
261
262
  this.hasAvailabilityError = true;
262
263
  }
263
264
  const availableItems = result.data.filter((item) => item.available) || [];
264
- // Auto-select the first performance time once the availability resolves,
265
- // keeping this state out of the render path.
266
265
  const sortedTimes = sortTimes(Array.from(groupAvailabilityByTime(availableItems, this.locale).keys()));
267
266
  this.selectedTime = sortedTimes[0] ?? null;
268
267
  availableItems.forEach((item) => {
@@ -301,8 +300,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
301
300
  throw new Error("Attraction data not available");
302
301
  }
303
302
  const seatedPayload = seated;
304
- // Seated selection books a specific performance instance with the chosen
305
- // seats; the section flow books a selection_id with ticket-type counts.
306
303
  const reservationFields = seatedPayload
307
304
  ? {
308
305
  selection_id: null,
@@ -323,8 +320,8 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
323
320
  body: {
324
321
  attraction_id: this.attractionId,
325
322
  currency: this.currency,
326
- date: this.date,
327
- language_code: this.locale.split("-")[0],
323
+ date: this.resolvedDate,
324
+ language_code: getApiLanguageCode(this.locale),
328
325
  tour_language: language,
329
326
  to_basket: true,
330
327
  pickup_id: null,
@@ -338,8 +335,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
338
335
  return {};
339
336
  }
340
337
  if (result.data) {
341
- // Emit only a minimal, redacted public payload; never the raw
342
- // reservation API response (P-038 data minimization).
343
338
  const reservation = result.data;
344
339
  fireEvent(this, "coras:reservation-created", {
345
340
  ...(reservation.reservation_id != null
@@ -392,7 +387,7 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
392
387
  this.seatsFallback = false;
393
388
  await this.bookingSessionTask.run();
394
389
  await this.availabilityTask.run();
395
- this.shouldRenderOptions = true; // TODO: move to request?
390
+ this.shouldRenderOptions = true;
396
391
  };
397
392
  this.renderAttractionHeader = () => {
398
393
  const apiResponse = this.attractionTask.value;
@@ -600,20 +595,16 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
600
595
  if (!apiResponse) {
601
596
  return nothing;
602
597
  }
603
- const date = this.date ||
604
- formatISO(parseISO(apiResponse.first_instance_date), {
605
- representation: "date",
606
- });
598
+ const date = this.resolvedDate;
607
599
  if (this.isSingleDayAvailability) {
608
600
  return html `
609
601
  <div id="calendar-dropdown">
610
602
  <coras-button
611
603
  variant="outlined"
612
- radius="small"
604
+ radius="field"
613
605
  height="md"
614
606
  .shadow=${false}
615
607
  .clickable=${false}
616
- background
617
608
  expanded
618
609
  >
619
610
  <coras-icon
@@ -636,10 +627,9 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
636
627
  <coras-button
637
628
  slot="trigger"
638
629
  variant="outlined"
639
- radius="small"
630
+ radius="field"
640
631
  height="md"
641
632
  border-color="light"
642
- background
643
633
  expanded
644
634
  @click=${this.toggleCalendarDropdown}
645
635
  ?error=${this.hasAvailabilityError}
@@ -664,8 +654,16 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
664
654
  <coras-month-calendar
665
655
  assets-url="${this.assetsURL}"
666
656
  locale=${this.locale}
667
- min-date=${format(parseISO(apiResponse.first_instance_date), "yyyy-MM")}
668
- max-date=${format(parseISO(apiResponse.last_instance_date), "yyyy-MM")}
657
+ min-date=${formatDateWithTimezone({
658
+ dateString: apiResponse.first_instance_date,
659
+ timezone: apiResponse.attraction.timezone,
660
+ formatString: "yyyy-MM",
661
+ })}
662
+ max-date=${formatDateWithTimezone({
663
+ dateString: apiResponse.last_instance_date,
664
+ timezone: apiResponse.attraction.timezone,
665
+ formatString: "yyyy-MM",
666
+ })}
669
667
  .selectedDates=${[date]}
670
668
  .availableDays=${this.availabilityDays}
671
669
  selected-month=${getCurrentMonth(date, this.currentMonth)}
@@ -785,12 +783,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
785
783
  };
786
784
  this.renderTourOptions = (items) => {
787
785
  return html `${map(items, ({ name, languages_spoken, selection_id, prices, start_datetime, timezone, max_full_price, }) => {
788
- // The availability API returns one `start_datetime` per item, so each
789
- // tour option is a single timed slot (a tour offered at several times
790
- // arrives as several items). There is thus one time per option: the
791
- // per-option time picker stays collapsed and the slot shows as a badge.
792
- // Grouping items into a single multi-time option is a separate feature
793
- // that also needs the selection_id<->time basket mapping reworked.
794
786
  const hasOnlyOneTimeOption = true;
795
787
  const hasMultipleLanguages = Object.keys(languages_spoken || {}).length > 1;
796
788
  return html `
@@ -849,14 +841,21 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
849
841
  })}`;
850
842
  };
851
843
  this.handleSeatsConfirmed = async (event) => {
852
- // Capture the selector now: currentTarget is nulled once dispatch ends, and
853
- // we only read it after awaiting the reservation.
854
844
  const selector = event.target;
855
- await this.reservationsTask.run(["", "", event.detail]);
845
+ if (selector) {
846
+ selector.reserving = true;
847
+ }
848
+ try {
849
+ await this.reservationsTask.run(["", "", event.detail]);
850
+ }
851
+ finally {
852
+ if (selector) {
853
+ selector.reserving = false;
854
+ }
855
+ }
856
856
  if (this.reservationsTask.status !== TaskStatus.ERROR &&
857
857
  this.reservationsTask.value &&
858
858
  Object.keys(this.reservationsTask.value).length > 0) {
859
- // Close the fixed seat-summary bar so it doesn't sit over the basket.
860
859
  selector?.reservationCreated();
861
860
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
862
861
  if (isMobile) {
@@ -867,6 +866,11 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
867
866
  isBasketDropdownOpen.set(true);
868
867
  scrollToTop();
869
868
  }
869
+ this.shouldRenderOptions = false;
870
+ this.selectedPriceTypes = {};
871
+ this.selectedTime = null;
872
+ this.selectedTimeOptions = {};
873
+ this.seatsFallback = false;
870
874
  }
871
875
  };
872
876
  this.renderAvailabilityOptions = () => {
@@ -899,9 +903,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
899
903
  ? Array.from(timeGroups.get(this.selectedTime) || [])
900
904
  : [];
901
905
  const seatedPerformance = this.findSeatedPerformance(filteredOptions);
902
- // Only offer the time picker when there is an actual choice. A single
903
- // performance time is auto-selected above, so showing one lone button
904
- // is noise - render straight to the seat/section options instead.
905
906
  const showTimePicker = sortedTimes.length > 1;
906
907
  return html `
907
908
  <div class="options">
@@ -1003,7 +1004,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1003
1004
  };
1004
1005
  })
1005
1006
  .filter((item) => item !== null);
1006
- // Calculate booking fee
1007
1007
  const bookingFee = convertToLocaleCurrency({
1008
1008
  value: summary.bookingFeePerTicket,
1009
1009
  locale: this.locale,
@@ -1053,7 +1053,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1053
1053
  @click="${async () => {
1054
1054
  await this.handleBasketModalToggle(this.selectedSectionId);
1055
1055
  this.isReservationPreviewModalOpened = false;
1056
- // Defer scroll past the dialog's scroll-lock restore (rAF-deferred)
1057
1056
  setTimeout(() => scrollToTop(), 0);
1058
1057
  }}"
1059
1058
  >
@@ -1099,7 +1098,12 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1099
1098
  };
1100
1099
  this.renderBasketModal = () => {
1101
1100
  const apiResponse = this.attractionTask.value;
1102
- const ticketsNumber = getTotalTicketsCount(this.selectedPriceTypes);
1101
+ const reservedTickets = this.reservationsTask.value && "tickets" in this.reservationsTask.value
1102
+ ? this.reservationsTask.value.tickets
1103
+ : null;
1104
+ const ticketsNumber = reservedTickets?.length
1105
+ ? reservedTickets.length
1106
+ : getTotalTicketsCount(this.selectedPriceTypes);
1103
1107
  const title = apiResponse?.attraction.attraction_name || "";
1104
1108
  let formattedDate = "";
1105
1109
  if (this.reservationsTask.value &&
@@ -1243,8 +1247,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1243
1247
  });
1244
1248
  }
1245
1249
  async handleBasketModalToggle(selection_id) {
1246
- // TODO: check if success, if not show error message
1247
- // TODO: it calls the task once again when modal is closed
1248
1250
  if (selection_id) {
1249
1251
  this.loadingSelectionIds = new Set([
1250
1252
  ...this.loadingSelectionIds,
@@ -1257,7 +1259,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1257
1259
  if (this.reservationsTask.status !== TaskStatus.ERROR &&
1258
1260
  this.reservationsTask.value &&
1259
1261
  Object.keys(this.reservationsTask.value).length > 0) {
1260
- // TODO: make reusable
1261
1262
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
1262
1263
  if (isMobile) {
1263
1264
  this.isBasketModalOpened = !this.isBasketModalOpened;
@@ -1282,6 +1283,18 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1282
1283
  return true;
1283
1284
  }
1284
1285
  }
1286
+ get resolvedDate() {
1287
+ if (this.date)
1288
+ return this.date;
1289
+ const apiResponse = this.attractionTask.value;
1290
+ if (!apiResponse)
1291
+ return "";
1292
+ return formatDateWithTimezone({
1293
+ dateString: apiResponse.first_instance_date,
1294
+ timezone: apiResponse.attraction.timezone,
1295
+ formatString: "yyyy-MM-dd",
1296
+ });
1297
+ }
1285
1298
  buildSectionTickets(selection_id) {
1286
1299
  const apiResponse = this.attractionTask.value;
1287
1300
  const availabilityItems = this.availabilityTask
@@ -1382,12 +1395,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1382
1395
  </coras-breadcrumbs>
1383
1396
  `;
1384
1397
  }
1385
- // Resolves the selected performance to the seat map's one prerequisite: the
1386
- // instance_id (the seats.io event key and the seats endpoint path param).
1387
- // Price bands and per-seat data come from the seats endpoint itself, so the
1388
- // availability item only has to supply the instance. Returns null to fall
1389
- // back to the section picker. All section items of a performance share the
1390
- // same instance_id, so the first option resolves it.
1391
1398
  findSeatedPerformance(options) {
1392
1399
  const apiResponse = this.attractionTask.value;
1393
1400
  if (this.seatsFallback ||
@@ -1395,8 +1402,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1395
1402
  apiResponse?.attraction_type !== "THEATER") {
1396
1403
  return null;
1397
1404
  }
1398
- // attraction_type is "THEATER" | "TOUR" on both variants, so it cannot
1399
- // narrow the union structurally; the runtime check above is the guard.
1400
1405
  const event = apiResponse;
1401
1406
  if (!event.attraction.seats_sales_enabled) {
1402
1407
  return null;
@@ -1412,6 +1417,8 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1412
1417
  }
1413
1418
  renderSeatedSelector(performance) {
1414
1419
  const seatsConfig = this.configTask.value;
1420
+ const event = this.attractionTask.value;
1421
+ const maxSeats = event?.attraction.max_tickets_per_order ?? 0;
1415
1422
  return html `
1416
1423
  <coras-seated-selector
1417
1424
  attraction-id="${this.attractionId}"
@@ -1420,7 +1427,7 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1420
1427
  workspace-key="${seatsConfig.workspaceKey}"
1421
1428
  region="${seatsConfig.region}"
1422
1429
  date-label="${formatDate(performance.startDatetime, this.locale, true)}"
1423
- ticket-count=${getTotalTicketsCount(this.selectedPriceTypes)}
1430
+ max-seats=${maxSeats}
1424
1431
  @seats-unavailable=${() => (this.seatsFallback = true)}
1425
1432
  @seats-confirmed=${this.handleSeatsConfirmed}
1426
1433
  ></coras-seated-selector>
@@ -1453,7 +1460,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1453
1460
  }
1454
1461
  handleGoToBasketButtonClicked() {
1455
1462
  this.handleBasketModalToggle();
1456
- // TODO: make reusable
1457
1463
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
1458
1464
  if (isMobile) {
1459
1465
  isBasketMobileOpen.set(true);
@@ -1464,11 +1470,31 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1464
1470
  scrollToTop();
1465
1471
  }
1466
1472
  }
1473
+ renderSkeleton() {
1474
+ return html `
1475
+ <div class="details-skeleton" aria-hidden="true">
1476
+ <div class="coras-skeleton sk-breadcrumb"></div>
1477
+ <div class="coras-skeleton sk-title"></div>
1478
+ <div class="sk-header">
1479
+ <div class="coras-skeleton sk-image"></div>
1480
+ <div class="coras-skeleton sk-panel"></div>
1481
+ </div>
1482
+ <div class="sk-description">
1483
+ <div class="coras-skeleton sk-line"></div>
1484
+ <div class="coras-skeleton sk-line"></div>
1485
+ <div class="coras-skeleton sk-line sk-short"></div>
1486
+ </div>
1487
+ </div>
1488
+ <span class="coras-sr-only" role="status">${msg("Loading")}</span>
1489
+ `;
1490
+ }
1467
1491
  render() {
1468
1492
  return html `
1469
1493
  <coras-layout with-basket>
1470
1494
  <slot name="navbar" slot="navbar"></slot>
1471
1495
  ${this.attractionTask.render({
1496
+ initial: () => this.renderSkeleton(),
1497
+ pending: () => this.renderSkeleton(),
1472
1498
  error: () => {
1473
1499
  this.showErrorModalOnce();
1474
1500
  return html ``;
@@ -1,11 +1,70 @@
1
1
  import { css } from "lit";
2
2
  import component from "../../styles/defaults/component.js";
3
+ import { skeleton, visuallyHidden } from "../../styles/primitives.js";
3
4
  export default css `
4
5
  ${component}
6
+ ${skeleton}
7
+ ${visuallyHidden}
8
+
9
+ .details-skeleton {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 1.75rem;
13
+
14
+ .sk-breadcrumb {
15
+ width: 14rem;
16
+ max-width: 60%;
17
+ height: 1rem;
18
+ }
19
+
20
+ .sk-title {
21
+ width: 26rem;
22
+ max-width: 80%;
23
+ height: 2.25rem;
24
+ }
25
+
26
+ .sk-header {
27
+ display: grid;
28
+ grid-template-columns: 1fr 1fr;
29
+ gap: 2.5rem;
30
+ width: 100%;
31
+
32
+ @container --coras-layout (max-width: 800px) {
33
+ grid-template-columns: 1fr;
34
+ }
35
+ }
36
+
37
+ .sk-image {
38
+ width: 100%;
39
+ max-width: 480px;
40
+ aspect-ratio: 4 / 3;
41
+ }
42
+
43
+ .sk-panel {
44
+ width: 100%;
45
+ max-width: 280px;
46
+ height: 20rem;
47
+ }
48
+
49
+ .sk-description {
50
+ display: flex;
51
+ flex-direction: column;
52
+ gap: 0.75rem;
53
+ }
54
+
55
+ .sk-line {
56
+ width: 100%;
57
+ height: 1rem;
58
+
59
+ &.sk-short {
60
+ width: 60%;
61
+ }
62
+ }
63
+ }
5
64
 
6
65
  :host {
7
66
  --details-page-ticket-availability-border-color: var(
8
- --coras-color-neutral-200
67
+ --coras-color-card-border
9
68
  );
10
69
  --details-page-ticket-availability-border-radius: var(--coras-radius-md);
11
70
  --details-page-ticket-availability-caption-font-size: var(
@@ -16,9 +75,7 @@ export default css `
16
75
  );
17
76
  --details-page-ticket-availability-caption-color: var(--coras-color-text);
18
77
 
19
- --details-page-date-field-open-chevron-color: var(
20
- --coras-color-primary-500
21
- );
78
+ --details-page-date-field-open-chevron-color: var(--coras-color-primary);
22
79
  --details-page-ticket-availability-caption-font-size-mobile: var(
23
80
  --coras-font-size-sm
24
81
  );
@@ -55,7 +112,7 @@ export default css `
55
112
 
56
113
  --details-page-reviews-footer-font-size: var(--coras-font-size-sm);
57
114
  --details-page-reviews-footer-font-weight: var(--coras-font-weight-normal);
58
- --details-page-reviews-footer-color: var(--coras-color-text-secondary);
115
+ --details-page-reviews-footer-color: var(--coras-color-text-muted);
59
116
 
60
117
  --details-page-top-panel-max-width: 820px;
61
118
  --details-page-details-max-width: 562px;
@@ -375,8 +432,6 @@ export default css `
375
432
  }
376
433
  }
377
434
 
378
- /* Basket modal */
379
-
380
435
  .basket-modal-header {
381
436
  display: flex;
382
437
  align-items: center;
@@ -391,7 +446,7 @@ export default css `
391
446
  height: 28px;
392
447
 
393
448
  &::part(svg) {
394
- fill: var(--coras-color-success-500);
449
+ fill: var(--coras-color-success);
395
450
  width: 28px;
396
451
  height: 28px;
397
452
  }
@@ -430,7 +485,7 @@ export default css `
430
485
 
431
486
  .basket-modal-date {
432
487
  margin-bottom: 1.125rem;
433
- color: var(--coras-color-text-secondary);
488
+ color: var(--coras-color-text-muted);
434
489
  font-size: var(--coras-font-size-sm);
435
490
  font-weight: var(--coras-font-weight-normal);
436
491
  line-height: 1.25rem;
@@ -40,6 +40,11 @@ let CorasTheaterDescription = class CorasTheaterDescription extends LitElement {
40
40
  `;
41
41
  }
42
42
  renderSeatMap(attraction) {
43
+ // When the event sells seats through the live picker, that interactive map
44
+ // supersedes this static illustration - showing both is redundant.
45
+ if (attraction.attraction.seats_sales_enabled) {
46
+ return nothing;
47
+ }
43
48
  // The seat map URL comes from the API; gate its scheme (https/relative/
44
49
  // data:image) before using it as an `img` source so a malformed or
45
50
  // `javascript:` value can't reach the DOM.
@@ -59,6 +59,7 @@ export declare class CorasHelpPage extends CorasHelpPage_base {
59
59
  private closeSubjectDropdown;
60
60
  private handleModalClose;
61
61
  private handleTabChange;
62
+ private renderHelpSkeleton;
62
63
  private renderFaqAccordion;
63
64
  private renderAttractionsTicketsTab;
64
65
  private renderMusicTicketsTab;
@@ -226,13 +226,21 @@ let CorasHelpPage = class CorasHelpPage extends BaseMixin(LitElement) {
226
226
  content: event.detail.tab,
227
227
  });
228
228
  }
229
+ renderHelpSkeleton() {
230
+ return html `
231
+ <div class="help-skeleton" aria-hidden="true">
232
+ ${Array.from({ length: 5 }, () => html `<div class="coras-skeleton sk-row"></div>`)}
233
+ </div>
234
+ <span class="coras-sr-only" role="status">${msg("Loading")}</span>
235
+ `;
236
+ }
229
237
  renderFaqAccordion() {
230
238
  return this.helpTabTask.render({
231
239
  error: () => {
232
240
  // this.showErrorModalOnce();
233
241
  return html ``;
234
242
  },
235
- loading: () => html `loading`,
243
+ pending: () => this.renderHelpSkeleton(),
236
244
  complete: (result) => {
237
245
  if (!result?.data)
238
246
  return nothing;
@@ -358,7 +366,6 @@ let CorasHelpPage = class CorasHelpPage extends BaseMixin(LitElement) {
358
366
  height="md"
359
367
  border-color="light"
360
368
  expanded
361
- background
362
369
  @click=${this.toggleSubjectDropdown}
363
370
  ?error=${hasError}
364
371
  aria-label=${label}
@@ -375,7 +382,7 @@ let CorasHelpPage = class CorasHelpPage extends BaseMixin(LitElement) {
375
382
  <coras-select-list spacious>
376
383
  ${this.content === "contact"
377
384
  ? this.subjectsTask.render({
378
- loading: () => html `
385
+ pending: () => html `
379
386
  <coras-select-option></coras-select-option>
380
387
  `,
381
388
  error: () => html `
@@ -409,7 +416,7 @@ let CorasHelpPage = class CorasHelpPage extends BaseMixin(LitElement) {
409
416
  renderInfoContent() {
410
417
  return this.helpTabTask.render({
411
418
  error: () => html ``,
412
- loading: () => html `loading`,
419
+ pending: () => this.renderHelpSkeleton(),
413
420
  complete: (result) => {
414
421
  if (!result?.data) {
415
422
  return nothing;