@coras-io/embed 0.2.0 → 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 (235) hide show
  1. package/CHANGELOG.md +127 -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 +2 -0
  67. package/dist/components/seated-selector/seated-selector.component.js +53 -10
  68. package/dist/components/seated-selector/seated-selector.styles.js +77 -14
  69. package/dist/components/seats-io/seats-io.component.d.ts +2 -0
  70. package/dist/components/seats-io/seats-io.component.js +25 -2
  71. package/dist/components/seats-io/seats-io.constants.d.ts +2 -1
  72. package/dist/components/seats-io/seats-io.constants.js +6 -1
  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 -10
  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 +72 -79
  200. package/dist/views/details-page/details-page.styles.js +64 -9
  201. package/dist/views/help-page/help-page.component.d.ts +1 -0
  202. package/dist/views/help-page/help-page.component.js +11 -4
  203. package/dist/views/help-page/help-page.styles.js +21 -8
  204. package/dist/views/landing-page/landing-page.component.d.ts +4 -4
  205. package/dist/views/landing-page/landing-page.component.js +65 -65
  206. package/dist/views/landing-page/landing-page.styles.js +70 -3
  207. package/dist/views/payment-page/partials/activity-details/activity-details.styles.js +10 -33
  208. package/dist/views/payment-page/partials/billing-address/billing-address.styles.js +4 -9
  209. package/dist/views/payment-page/partials/confirmation/confirmation.styles.js +6 -6
  210. package/dist/views/payment-page/partials/hotel-pickup-list/hotel-pickup-list.styles.js +4 -4
  211. package/dist/views/payment-page/partials/payment-type-selector/payment-type-selector.styles.js +1 -0
  212. package/dist/views/payment-page/partials/stripe/stripe.component.d.ts +1 -1
  213. package/dist/views/payment-page/partials/stripe/stripe.component.js +11 -12
  214. package/dist/views/payment-page/partials/stripe/stripe.constants.d.ts +2 -15
  215. package/dist/views/payment-page/partials/stripe/stripe.constants.js +4 -17
  216. package/dist/views/payment-page/partials/stripe/stripe.styles.js +16 -23
  217. package/dist/views/payment-page/partials/stripe/stripe.utils.d.ts +25 -14
  218. package/dist/views/payment-page/partials/stripe/stripe.utils.js +111 -62
  219. package/dist/views/payment-page/partials/terms-checkbox/terms-checkbox.styles.js +1 -1
  220. package/dist/views/payment-page/payment-page.component.d.ts +1 -0
  221. package/dist/views/payment-page/payment-page.component.js +19 -9
  222. package/dist/views/payment-page/payment-page.styles.js +37 -32
  223. package/dist/views/payment-page/payment-page.utils.d.ts +7 -0
  224. package/dist/views/payment-page/payment-page.utils.js +29 -14
  225. package/dist/views/payment-page/payment-page.validators.js +9 -3
  226. package/dist/views/payment-page/services/payment-api.service.d.ts +1 -1
  227. package/dist/views/search-page/search-page.component.d.ts +1 -0
  228. package/dist/views/search-page/search-page.component.js +12 -6
  229. package/dist/views/search-page/search-page.styles.js +24 -2
  230. package/dist/views/suggestion-widget/suggestion-widget.styles.js +1 -1
  231. package/package.json +62 -75
  232. package/dist/custom-elements.json +0 -37397
  233. package/dist/sdk/meta.d.ts +0 -7
  234. package/dist/sdk/meta.js +0 -9
  235. 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,10 +841,7 @@ 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
- // Show the confirm button's spinner while the basket call is in flight.
856
845
  if (selector) {
857
846
  selector.reserving = true;
858
847
  }
@@ -867,7 +856,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
867
856
  if (this.reservationsTask.status !== TaskStatus.ERROR &&
868
857
  this.reservationsTask.value &&
869
858
  Object.keys(this.reservationsTask.value).length > 0) {
870
- // Close the fixed seat-summary bar so it doesn't sit over the basket.
871
859
  selector?.reservationCreated();
872
860
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
873
861
  if (isMobile) {
@@ -878,10 +866,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
878
866
  isBasketDropdownOpen.set(true);
879
867
  scrollToTop();
880
868
  }
881
- // Return the page to a clean slate: collapse the seat map and reset the
882
- // ticket selector to its default so the shopper starts fresh for any
883
- // further booking. The just-added item lives in the basket now; the
884
- // "Added to basket" summary reads the reservation, not this state.
885
869
  this.shouldRenderOptions = false;
886
870
  this.selectedPriceTypes = {};
887
871
  this.selectedTime = null;
@@ -919,9 +903,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
919
903
  ? Array.from(timeGroups.get(this.selectedTime) || [])
920
904
  : [];
921
905
  const seatedPerformance = this.findSeatedPerformance(filteredOptions);
922
- // Only offer the time picker when there is an actual choice. A single
923
- // performance time is auto-selected above, so showing one lone button
924
- // is noise - render straight to the seat/section options instead.
925
906
  const showTimePicker = sortedTimes.length > 1;
926
907
  return html `
927
908
  <div class="options">
@@ -1023,7 +1004,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1023
1004
  };
1024
1005
  })
1025
1006
  .filter((item) => item !== null);
1026
- // Calculate booking fee
1027
1007
  const bookingFee = convertToLocaleCurrency({
1028
1008
  value: summary.bookingFeePerTicket,
1029
1009
  locale: this.locale,
@@ -1073,7 +1053,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1073
1053
  @click="${async () => {
1074
1054
  await this.handleBasketModalToggle(this.selectedSectionId);
1075
1055
  this.isReservationPreviewModalOpened = false;
1076
- // Defer scroll past the dialog's scroll-lock restore (rAF-deferred)
1077
1056
  setTimeout(() => scrollToTop(), 0);
1078
1057
  }}"
1079
1058
  >
@@ -1119,9 +1098,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1119
1098
  };
1120
1099
  this.renderBasketModal = () => {
1121
1100
  const apiResponse = this.attractionTask.value;
1122
- // Prefer the count actually reserved (seated bookings can differ from the
1123
- // upfront selection, which is also reset once the item is in the basket);
1124
- // fall back to the selection only before a reservation exists.
1125
1101
  const reservedTickets = this.reservationsTask.value && "tickets" in this.reservationsTask.value
1126
1102
  ? this.reservationsTask.value.tickets
1127
1103
  : null;
@@ -1271,8 +1247,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1271
1247
  });
1272
1248
  }
1273
1249
  async handleBasketModalToggle(selection_id) {
1274
- // TODO: check if success, if not show error message
1275
- // TODO: it calls the task once again when modal is closed
1276
1250
  if (selection_id) {
1277
1251
  this.loadingSelectionIds = new Set([
1278
1252
  ...this.loadingSelectionIds,
@@ -1285,7 +1259,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1285
1259
  if (this.reservationsTask.status !== TaskStatus.ERROR &&
1286
1260
  this.reservationsTask.value &&
1287
1261
  Object.keys(this.reservationsTask.value).length > 0) {
1288
- // TODO: make reusable
1289
1262
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
1290
1263
  if (isMobile) {
1291
1264
  this.isBasketModalOpened = !this.isBasketModalOpened;
@@ -1310,6 +1283,18 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1310
1283
  return true;
1311
1284
  }
1312
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
+ }
1313
1298
  buildSectionTickets(selection_id) {
1314
1299
  const apiResponse = this.attractionTask.value;
1315
1300
  const availabilityItems = this.availabilityTask
@@ -1410,12 +1395,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1410
1395
  </coras-breadcrumbs>
1411
1396
  `;
1412
1397
  }
1413
- // Resolves the selected performance to the seat map's one prerequisite: the
1414
- // instance_id (the seats.io event key and the seats endpoint path param).
1415
- // Price bands and per-seat data come from the seats endpoint itself, so the
1416
- // availability item only has to supply the instance. Returns null to fall
1417
- // back to the section picker. All section items of a performance share the
1418
- // same instance_id, so the first option resolves it.
1419
1398
  findSeatedPerformance(options) {
1420
1399
  const apiResponse = this.attractionTask.value;
1421
1400
  if (this.seatsFallback ||
@@ -1423,8 +1402,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1423
1402
  apiResponse?.attraction_type !== "THEATER") {
1424
1403
  return null;
1425
1404
  }
1426
- // attraction_type is "THEATER" | "TOUR" on both variants, so it cannot
1427
- // narrow the union structurally; the runtime check above is the guard.
1428
1405
  const event = apiResponse;
1429
1406
  if (!event.attraction.seats_sales_enabled) {
1430
1407
  return null;
@@ -1441,9 +1418,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1441
1418
  renderSeatedSelector(performance) {
1442
1419
  const seatsConfig = this.configTask.value;
1443
1420
  const event = this.attractionTask.value;
1444
- // Cap seat selection at the venue's per-order maximum rather than the
1445
- // upfront ticket count, so the shopper can revise how many seats they want
1446
- // straight on the map. 0 tells the chart there is no cap.
1447
1421
  const maxSeats = event?.attraction.max_tickets_per_order ?? 0;
1448
1422
  return html `
1449
1423
  <coras-seated-selector
@@ -1486,7 +1460,6 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1486
1460
  }
1487
1461
  handleGoToBasketButtonClicked() {
1488
1462
  this.handleBasketModalToggle();
1489
- // TODO: make reusable
1490
1463
  const isMobile = window.matchMedia("(max-width: 800px)").matches;
1491
1464
  if (isMobile) {
1492
1465
  isBasketMobileOpen.set(true);
@@ -1497,11 +1470,31 @@ let CorasDetailsPage = class CorasDetailsPage extends ErrorModalMixin(PointsMixi
1497
1470
  scrollToTop();
1498
1471
  }
1499
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
+ }
1500
1491
  render() {
1501
1492
  return html `
1502
1493
  <coras-layout with-basket>
1503
1494
  <slot name="navbar" slot="navbar"></slot>
1504
1495
  ${this.attractionTask.render({
1496
+ initial: () => this.renderSkeleton(),
1497
+ pending: () => this.renderSkeleton(),
1505
1498
  error: () => {
1506
1499
  this.showErrorModalOnce();
1507
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;
@@ -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;
@@ -1,10 +1,25 @@
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
+ .help-skeleton {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: 1rem;
13
+ width: 100%;
14
+
15
+ .sk-row {
16
+ width: 100%;
17
+ height: 3.5rem;
18
+ }
19
+ }
5
20
 
6
21
  :host {
7
- --help-center-header-font-size: var(--coras-font-size-2xl);
22
+ --help-center-header-font-size: var(--coras-font-size-heading);
8
23
  --help-center-header-font-weight: var(--coras-font-weight-semibold);
9
24
  --help-center-header-line-height: var(--coras-font-size-3xl);
10
25
  --help-center-header-color: var(--coras-color-text);
@@ -17,7 +32,7 @@ export default css `
17
32
  --help-center-modal-message-line-height: var(--coras-font-size-xl);
18
33
  --help-center-modal-message-font-weight: var(--coras-font-weight-normal);
19
34
  --help-center-modal-message-color: var(--coras-color-text);
20
- --help-center-modal-icon-color: var(--coras-color-success-500);
35
+ --help-center-modal-icon-color: var(--coras-color-success);
21
36
  --help-center-modal-icon-size: 32px;
22
37
 
23
38
  display: grid;
@@ -91,14 +106,12 @@ export default css `
91
106
  margin-block: 2rem;
92
107
  }
93
108
 
94
- /* Add spacing between numbered info items */
95
109
  .info-numbered {
96
110
  display: flex;
97
111
  flex-direction: column;
98
112
  gap: 1rem;
99
113
  }
100
114
 
101
- /* Numbering style for nested ordered lists within info content */
102
115
  .info-numbered ol {
103
116
  counter-reset: section;
104
117
  padding-left: 1.25rem;
@@ -107,10 +120,10 @@ export default css `
107
120
 
108
121
  .info-numbered ol > li {
109
122
  counter-increment: section;
110
- list-style: none; /* hide default numbering to use CSS counters */
123
+ list-style: none;
111
124
  position: relative;
112
- padding-left: 1rem; /* 1rem gap between number and content */
113
- margin: 1rem 0; /* ensure 1rem between items */
125
+ padding-left: 1rem;
126
+ margin: 1rem 0;
114
127
  }
115
128
 
116
129
  .info-numbered ol > li::before {
@@ -171,7 +184,7 @@ export default css `
171
184
  align-items: center;
172
185
  justify-content: center;
173
186
  padding: 3rem 1rem;
174
- border-top: 1px solid var(--coras-color-neutral-200);
187
+ border-top: 1px solid var(--coras-color-divider);
175
188
  }
176
189
 
177
190
  .container {
@@ -44,10 +44,8 @@ declare const CorasLandingPage_base: import("../../contexts/utils.js").Construct
44
44
  dispatchPageUpdate(params: import("../../contexts/events.context.js").PageDispatchInput): void;
45
45
  }> & typeof LitElement;
46
46
  /**
47
- * Landing page component for browsing attractions and experiences.
48
- *
49
- * @fires coras:navigate - Dispatched when navigating to another page (search result, page navigation)
50
- * @fires coras:state-change - Dispatched when in-page params change (filters, sorting, dates)
47
+ * @fires coras:navigate
48
+ * @fires coras:state-change
51
49
  */
52
50
  export declare class CorasLandingPage extends CorasLandingPage_base {
53
51
  static styles: CSSResultGroup;
@@ -121,6 +119,8 @@ export declare class CorasLandingPage extends CorasLandingPage_base {
121
119
  private renderPoints;
122
120
  private renderAttractionCards;
123
121
  private renderBreadcrumbs;
122
+ private renderAttractionsSkeleton;
123
+ private renderFiltersSkeleton;
124
124
  private renderAttractions;
125
125
  render(): import("lit").TemplateResult<1>;
126
126
  }