@riverbankcms/sdk 0.94.0 → 0.96.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 (189) hide show
  1. package/README.md +30 -4
  2. package/dist/{PreviewEditorSidebar-D3PAF7NW.mjs → PreviewEditorSidebar-46VT4QVU.mjs} +3 -3
  3. package/dist/{PreviewEditorUI-G5JVJEIJ.mjs → PreviewEditorUI-JD75GE5F.mjs} +3 -3
  4. package/dist/SdkPreviewModeRuntime-MJUSYUFB.mjs +8 -0
  5. package/dist/_dts/ai/src/contracts/proposals.d.ts +10 -10
  6. package/dist/_dts/api/src/bookingOperations.d.ts +7 -0
  7. package/dist/_dts/api/src/bookingRecords.d.ts +3 -0
  8. package/dist/_dts/api/src/cache-tags.d.ts +4 -0
  9. package/dist/_dts/api/src/contentRuntime.d.ts +14 -0
  10. package/dist/_dts/api/src/endpoints.d.ts +8 -0
  11. package/dist/_dts/api/src/event-presentation.d.ts +15 -0
  12. package/dist/_dts/api/src/index.d.ts +1 -1
  13. package/dist/_dts/api/src/public/endpoints.d.ts +1 -1
  14. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +17 -4
  15. package/dist/_dts/api/src/shared-contracts.d.ts +19 -9
  16. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +6 -1
  17. package/dist/_dts/block-form/src/client/index.d.ts +1 -1
  18. package/dist/_dts/block-form/src/fields/types.d.ts +9 -1
  19. package/dist/_dts/block-form/src/registry/schemas.d.ts +4 -0
  20. package/dist/_dts/block-form/src/registry/widgets.d.ts +1 -0
  21. package/dist/_dts/block-form/src/widgets/InlineTextField/InlineTextField.d.ts +4 -0
  22. package/dist/_dts/block-form/src/widgets/InlineTextField/index.d.ts +3 -0
  23. package/dist/_dts/block-form/src/widgets/InlineTextField/inlineTextFieldDefaults.d.ts +2 -0
  24. package/dist/_dts/block-form/src/widgets/InlineTextField/inlineTextFieldSchema.d.ts +3 -0
  25. package/dist/_dts/blocks/src/bindings/index.d.ts +1 -0
  26. package/dist/_dts/blocks/src/bindings/types.d.ts +1 -0
  27. package/dist/_dts/blocks/src/index.d.ts +2 -0
  28. package/dist/_dts/blocks/src/portal/types.d.ts +7 -0
  29. package/dist/_dts/blocks/src/system/blocks/appointment-booking.d.ts +2 -29631
  30. package/dist/_dts/blocks/src/system/blocks/blog-listing.d.ts +2 -29614
  31. package/dist/_dts/blocks/src/system/blocks/cart.d.ts +2 -29634
  32. package/dist/_dts/blocks/src/system/blocks/checkout.d.ts +2 -29627
  33. package/dist/_dts/blocks/src/system/blocks/collection.d.ts +2 -29585
  34. package/dist/_dts/blocks/src/system/blocks/faq.d.ts +2 -29677
  35. package/dist/_dts/blocks/src/system/blocks/gifting.d.ts +2 -29655
  36. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +220 -0
  37. package/dist/_dts/blocks/src/system/blocks/product-detail.d.ts +2 -29655
  38. package/dist/_dts/blocks/src/system/blocks/product-list.d.ts +2 -29685
  39. package/dist/_dts/blocks/src/system/blocks/shop.d.ts +2 -29761
  40. package/dist/_dts/blocks/src/system/fragments/library/heroCopy.d.ts +9 -0
  41. package/dist/_dts/blocks/src/system/inlineText.d.ts +78 -0
  42. package/dist/_dts/blocks/src/system/manifest/derivedContent.d.ts +3 -1
  43. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +15 -3
  44. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +188 -7
  45. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  46. package/dist/_dts/blocks/src/system/runtime/api/types.d.ts +21 -6
  47. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +2 -1
  48. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +5 -3
  49. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +5 -1
  50. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -2
  51. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/sessionStorage.d.ts +9 -6
  52. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.d.ts +36 -0
  53. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +7 -1
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +2 -2
  55. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -3
  56. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +19 -4
  57. package/dist/_dts/blocks/src/system/runtime/nodes/renderInlineText.d.ts +3 -0
  58. package/dist/_dts/content-editor/src/index.d.ts +1 -0
  59. package/dist/_dts/content-editor/src/primitives/PreviewInlineTextEditor.d.ts +9 -0
  60. package/dist/_dts/content-model/src/entry-slots/types.d.ts +6 -0
  61. package/dist/_dts/content-model/src/templates/bindingTypeCompatibility.d.ts +1 -1
  62. package/dist/_dts/content-model/src/templates/defaults.d.ts +30 -0
  63. package/dist/_dts/content-model/src/templates/types.d.ts +1 -0
  64. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +163 -2
  65. package/dist/_dts/editor/src/inline/FloatingToolbar.d.ts +18 -0
  66. package/dist/_dts/editor/src/inline/InlineTextEditor.d.ts +18 -1
  67. package/dist/_dts/editor/src/inline/InlineTextEditorStyles.d.ts +1 -0
  68. package/dist/_dts/editor/src/inline/domParse.d.ts +3 -0
  69. package/dist/_dts/editor/src/inline/presentation.d.ts +48 -0
  70. package/dist/_dts/editor/src/inline/selection.d.ts +12 -0
  71. package/dist/_dts/editor-blocks/src/widgets/index.d.ts +1 -0
  72. package/dist/_dts/editor-blocks/src/widgets/inline-text/InlineTextField.d.ts +3 -0
  73. package/dist/_dts/editor-blocks/src/widgets/inline-text/index.d.ts +1 -0
  74. package/dist/_dts/media-core/src/assetsInUseSummary.d.ts +7 -0
  75. package/dist/_dts/media-core/src/mediaUsageProjection.d.ts +29 -1
  76. package/dist/_dts/path-utils/src/index.d.ts +1 -0
  77. package/dist/_dts/preview-next/src/client/widgets/widgetOverrides.d.ts +1 -0
  78. package/dist/_dts/sdk/src/cli/commands/content.d.ts +56 -0
  79. package/dist/_dts/sdk/src/cli/commands/push/options.d.ts +4 -0
  80. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +2 -6
  81. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +2 -0
  82. package/dist/_dts/sdk/src/cli/commands/push/verification.d.ts +56 -0
  83. package/dist/_dts/sdk/src/cli/commands/push-execute.d.ts +2 -1
  84. package/dist/_dts/sdk/src/cli/content/edit/format.d.ts +2 -0
  85. package/dist/_dts/sdk/src/cli/content/edit/path.d.ts +14 -0
  86. package/dist/_dts/sdk/src/cli/content/edit/planner.d.ts +46 -0
  87. package/dist/_dts/sdk/src/cli/content/fs-utils.d.ts +4 -0
  88. package/dist/_dts/sdk/src/cli/content/writer.d.ts +2 -0
  89. package/dist/_dts/sdk/src/cli/merge-remote/mergeFieldChanges.d.ts +22 -0
  90. package/dist/_dts/sdk/src/cli/merge-remote/pageLocal.d.ts +3 -0
  91. package/dist/_dts/sdk/src/cli/merge-remote/pageMerge.d.ts +78 -0
  92. package/dist/_dts/sdk/src/cli/merge-remote/pageMergePlan.d.ts +36 -0
  93. package/dist/_dts/sdk/src/cli/merge-remote/pageMergePush.d.ts +21 -0
  94. package/dist/_dts/sdk/src/cli/merge-remote/pageMergeReport.d.ts +29 -0
  95. package/dist/_dts/sdk/src/cli/merge-remote/pageSnapshots.d.ts +48 -0
  96. package/dist/_dts/sdk/src/cli/sync/diff.d.ts +3 -6
  97. package/dist/_dts/sdk/src/cli/sync/field-diff.d.ts +39 -0
  98. package/dist/_dts/sdk/src/cli/sync/index.d.ts +1 -0
  99. package/dist/_dts/sdk/src/client/types.d.ts +7 -2
  100. package/dist/_dts/sdk/src/config/field-definitions.d.ts +8 -3
  101. package/dist/_dts/sdk/src/config/typed-entries.d.ts +5 -3
  102. package/dist/_dts/sdk/src/contracts/bindings.d.ts +1 -0
  103. package/dist/_dts/sdk/src/contracts/content.d.ts +18 -0
  104. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  105. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +3 -2
  106. package/dist/_dts/sdk/src/next/middleware.d.ts +1 -0
  107. package/dist/_dts/sdk/src/next/redirect-destination.d.ts +3 -0
  108. package/dist/_dts/sdk/src/next/route-outcome-proxy.d.ts +22 -0
  109. package/dist/_dts/sdk/src/public-api/contracts.d.ts +20 -1
  110. package/dist/_dts/sdk/src/public-api/runtime.d.ts +1 -1
  111. package/dist/_dts/sdk/src/rendering/helpers/bindings.d.ts +1 -0
  112. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +9 -1
  113. package/dist/_dts/sdk/src/version.d.ts +1 -1
  114. package/dist/_dts/theme-core/src/accordions/types.d.ts +1 -1
  115. package/dist/_dts/theme-core/src/buttons/types.d.ts +1 -1
  116. package/dist/_dts/theme-core/src/cards/types.d.ts +15 -15
  117. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  118. package/dist/_dts/theme-core/src/inputs/types.d.ts +1 -1
  119. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +42 -42
  120. package/dist/_dts/theme-core/src/palette/variants/types.d.ts +2 -2
  121. package/dist/_dts/theme-core/src/schema.d.ts +44 -44
  122. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  123. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +16 -16
  124. package/dist/_dts/theme-core/src/site-styles/pageDesignIntent.d.ts +4 -4
  125. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  126. package/dist/_dts/theme-core/src/tokens/themeColorTokens.d.ts +1 -1
  127. package/dist/{chunk-AQ6RT6LZ.mjs → chunk-4TLTV7OB.mjs} +2 -2
  128. package/dist/{chunk-NT76QGIJ.mjs → chunk-7ED5UNHL.mjs} +14 -0
  129. package/dist/{chunk-XIK2HYI3.mjs → chunk-EL55VV6Q.mjs} +49 -9
  130. package/dist/{chunk-A63UXN3P.mjs → chunk-VHIMXQ7Z.mjs} +106 -4
  131. package/dist/{chunk-AJZGT6YG.mjs → chunk-VTPPVAOL.mjs} +1 -1
  132. package/dist/{chunk-PCNCETBI.mjs → chunk-XMATDBXO.mjs} +2082 -408
  133. package/dist/cli/index.mjs +2809 -787
  134. package/dist/cli/init-docs/content/cli-reference.md +43 -4
  135. package/dist/cli/init-docs/content/content-management.md +22 -0
  136. package/dist/client/{accordion-RNQIPG55.mjs → accordion-GUNXIIZR.mjs} +1 -1
  137. package/dist/client/{bookingForm-HYEEVRLP.mjs → bookingForm-AOU4VHP5.mjs} +3 -3
  138. package/dist/client/bookings.mjs +211 -105
  139. package/dist/client/{carousel-W7DQ3YPU.mjs → carousel-4QSX2JPC.mjs} +1 -1
  140. package/dist/client/{cart-PXJ3SXXO.mjs → cart-JKN6C4ZB.mjs} +4 -4
  141. package/dist/client/{checkout-X3JYSOXI.mjs → checkout-YY6YMITW.mjs} +4 -4
  142. package/dist/client/{chunk-PICR3NTQ.mjs → chunk-6H27K6AA.mjs} +1 -1
  143. package/dist/client/{chunk-J6TMMMXW.mjs → chunk-A3YN7M3G.mjs} +1 -1
  144. package/dist/client/{chunk-YYVGSYBO.mjs → chunk-BFCJZODD.mjs} +3 -3
  145. package/dist/client/{chunk-IXDSXF2D.mjs → chunk-CPKMJVBD.mjs} +353 -187
  146. package/dist/client/{chunk-6SYGVVWA.mjs → chunk-GWCRCJLB.mjs} +9 -0
  147. package/dist/client/{chunk-Q47XGAGS.mjs → chunk-H7A6PUGM.mjs} +1 -1
  148. package/dist/client/{chunk-WERRJXNJ.mjs → chunk-SY543WZ3.mjs} +1 -1
  149. package/dist/client/{chunk-DUOHLWLF.mjs → chunk-VJROUYOQ.mjs} +2 -2
  150. package/dist/client/client.mjs +4495 -3855
  151. package/dist/client/{courseRegistration-QZ67PVHO.mjs → courseRegistration-UU2KMPSQ.mjs} +3 -3
  152. package/dist/client/{eventCalendar-V3BDHQNT.mjs → eventCalendar-WIVNS2TM.mjs} +6 -6
  153. package/dist/client/{eventCombined-XYFRN2RG.mjs → eventCombined-P3G5MW4J.mjs} +6 -6
  154. package/dist/client/{eventDetails-TFE5TWKV.mjs → eventDetails-2VHJ2RDG.mjs} +6 -6
  155. package/dist/client/{eventListing-IQ6AGWZ6.mjs → eventListing-TGO6JI3R.mjs} +6 -6
  156. package/dist/client/{eventRegistration-WGJVFKQJ.mjs → eventRegistration-B46HEJES.mjs} +6 -6
  157. package/dist/client/{form-6UZY6RUA.mjs → form-VKRTFUNQ.mjs} +1 -1
  158. package/dist/client/{headerSection-RSFXY6V3.mjs → headerSection-XUX4ZJLO.mjs} +2 -2
  159. package/dist/client/hooks.mjs +34 -3
  160. package/dist/client/{locationMap-24FH67I4.mjs → locationMap-NQFI5MJO.mjs} +2 -2
  161. package/dist/client/{newsletterForm-QVL3MLTN.mjs → newsletterForm-P2GETCQK.mjs} +1 -1
  162. package/dist/client/{productDetail-ZDYN3G4T.mjs → productDetail-7NQ7O5IF.mjs} +4 -4
  163. package/dist/client/{productList-4BMJLQHZ.mjs → productList-RX6ZJ7BL.mjs} +4 -4
  164. package/dist/client/rendering/client.mjs +4170 -3768
  165. package/dist/client/rendering/islands.mjs +18 -18
  166. package/dist/client/rendering.mjs +1700 -1065
  167. package/dist/client/{shop-U5C634FF.mjs → shop-BHN5AKY6.mjs} +4 -4
  168. package/dist/preview-next/before-render.mjs +14 -0
  169. package/dist/preview-next/client/runtime.mjs +5 -5
  170. package/dist/preview-next/middleware.mjs +14 -0
  171. package/dist/{sdk-runtime-VGV46WVB.mjs → sdk-runtime-X2LXQ5IC.mjs} +6 -6
  172. package/dist/server/components.mjs +3255 -2827
  173. package/dist/server/config-template-validation.mjs +29 -20
  174. package/dist/server/config-validation.mjs +36 -4
  175. package/dist/server/config.mjs +36 -4
  176. package/dist/server/data.mjs +34 -3
  177. package/dist/server/index.mjs +44 -1
  178. package/dist/server/next/middleware.mjs +120 -0
  179. package/dist/server/next.mjs +3405 -2859
  180. package/dist/server/page-converter.mjs +20 -3
  181. package/dist/server/prebuild.mjs +1 -1
  182. package/dist/server/rendering/server.mjs +3264 -2837
  183. package/dist/server/rendering.mjs +3295 -2844
  184. package/dist/server/routing.mjs +35 -4
  185. package/dist/server/server.mjs +2358 -2188
  186. package/dist/server/theme-bridge.mjs +140 -0
  187. package/dist/styles/index.css +139 -0
  188. package/package.json +5 -1
  189. package/dist/SdkPreviewModeRuntime-NHTLRNQD.mjs +0 -8
@@ -12446,6 +12446,9 @@ function resolveDiscountContext(input) {
12446
12446
  if (!input.eventRequiresPayment) {
12447
12447
  return null;
12448
12448
  }
12449
+ if (input.ticketsCount <= 0) {
12450
+ return null;
12451
+ }
12449
12452
  if (selection?.method && selection.method !== "stripe") {
12450
12453
  return null;
12451
12454
  }
@@ -16960,6 +16963,7 @@ function transformEligibilityResponse(response) {
16960
16963
  requiresPayment: eligibility.requiresPayment,
16961
16964
  pricingMode: eligibility.pricingMode,
16962
16965
  selectedTicketTypeId: eligibility.selectedTicketTypeId ?? null,
16966
+ ticketLines: eligibility.ticketLines ?? [],
16963
16967
  ticketTypes: eligibility.ticketTypes ?? [],
16964
16968
  isFull: eligibility.isFull,
16965
16969
  waitlistEnabled: eligibility.waitlistEnabled,
@@ -17002,6 +17006,48 @@ function findSelectedTicketType(input) {
17002
17006
  // ../blocks/src/system/runtime/nodes/event-registration/hooks.ts
17003
17007
  import { useState as useState3, useEffect as useEffect8, useCallback as useCallback5, useRef as useRef9, useReducer as useReducer7 } from "react";
17004
17008
 
17009
+ // ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
17010
+ function normalizeTicketSelectionLines(lines) {
17011
+ const quantitiesByTicketType = /* @__PURE__ */ new Map();
17012
+ for (const line of lines) {
17013
+ const quantity = Math.floor(line.quantity);
17014
+ if (quantity <= 0) continue;
17015
+ const key = line.ticketTypeId ?? null;
17016
+ quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
17017
+ }
17018
+ return [...quantitiesByTicketType.entries()].map(([ticketTypeId, quantity]) => ({
17019
+ ticketTypeId,
17020
+ quantity
17021
+ }));
17022
+ }
17023
+ function getTicketSelectionTotal(lines) {
17024
+ return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
17025
+ }
17026
+ function getSingleSelectedTicketTypeId(lines) {
17027
+ const normalized = normalizeTicketSelectionLines(lines);
17028
+ return normalized.length === 1 ? normalized[0]?.ticketTypeId ?? null : null;
17029
+ }
17030
+ function calculateTicketSelectionSubtotal(input) {
17031
+ return normalizeTicketSelectionLines(input.lines).reduce((subtotal, line) => {
17032
+ const ticketType = line.ticketTypeId ? input.ticketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null : null;
17033
+ const unitPriceCents = ticketType?.priceAmount ?? input.fallbackUnitPriceCents;
17034
+ return subtotal + Math.max(0, unitPriceCents) * line.quantity;
17035
+ }, 0);
17036
+ }
17037
+ function buildTicketSelectionSummaryLines(input) {
17038
+ return normalizeTicketSelectionLines(input.lines).map((line) => {
17039
+ const ticketType = line.ticketTypeId ? input.ticketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null : null;
17040
+ const unitPriceCents = ticketType?.priceAmount ?? input.fallbackUnitPriceCents;
17041
+ return {
17042
+ ticketTypeId: line.ticketTypeId,
17043
+ label: ticketType?.name ?? input.fallbackLabel,
17044
+ quantity: line.quantity,
17045
+ unitPriceCents,
17046
+ subtotalCents: Math.max(0, unitPriceCents) * line.quantity
17047
+ };
17048
+ });
17049
+ }
17050
+
17005
17051
  // ../blocks/src/system/runtime/nodes/event-registration/hooks.state.ts
17006
17052
  var initialEligibilityLookupState = {
17007
17053
  tag: "idle",
@@ -17108,6 +17154,7 @@ function usePortalAuth() {
17108
17154
  }
17109
17155
  function useEligibility(params) {
17110
17156
  const api = useBlockApi();
17157
+ const ticketSelectionsKey = JSON.stringify(normalizeTicketSelectionLines(params.ticketSelections));
17111
17158
  const [state, dispatch] = useReducer7(
17112
17159
  eligibilityLookupReducer,
17113
17160
  initialEligibilityLookupState
@@ -17121,7 +17168,8 @@ function useEligibility(params) {
17121
17168
  dispatch({ type: "reset" });
17122
17169
  return;
17123
17170
  }
17124
- if (!params.occurrenceId || !params.email.trim()) {
17171
+ const ticketSelections = JSON.parse(ticketSelectionsKey);
17172
+ if (!params.occurrenceId || !params.email.trim() || ticketSelections.length === 0) {
17125
17173
  requestIdRef.current += 1;
17126
17174
  dispatch({ type: "reset" });
17127
17175
  return;
@@ -17133,10 +17181,9 @@ function useEligibility(params) {
17133
17181
  const result = await api.checkEligibility({
17134
17182
  occurrenceId: params.occurrenceId,
17135
17183
  attendanceMode: params.attendanceMode ?? void 0,
17136
- ticketTypeId: params.ticketTypeId ?? void 0,
17184
+ ticketSelections,
17137
17185
  selectedSlidingAmount: params.selectedSlidingAmount ?? void 0,
17138
- email: params.email.trim().toLowerCase(),
17139
- ticketsCount: params.ticketsCount
17186
+ email: params.email.trim().toLowerCase()
17140
17187
  });
17141
17188
  if (cancelled || requestIdRef.current !== requestId) return;
17142
17189
  dispatch({
@@ -17155,7 +17202,15 @@ function useEligibility(params) {
17155
17202
  return () => {
17156
17203
  cancelled = true;
17157
17204
  };
17158
- }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.selectedSlidingAmount, params.email, params.ticketsCount]);
17205
+ }, [
17206
+ api,
17207
+ params.enabled,
17208
+ params.occurrenceId,
17209
+ params.attendanceMode,
17210
+ ticketSelectionsKey,
17211
+ params.selectedSlidingAmount,
17212
+ params.email
17213
+ ]);
17159
17214
  return {
17160
17215
  state,
17161
17216
  eligibility: state.eligibility,
@@ -17231,9 +17286,15 @@ function savePendingRegistration(siteId, occurrenceId, data) {
17231
17286
  }
17232
17287
  try {
17233
17288
  const key = getStorageKey(siteId, occurrenceId);
17289
+ const ticketSelections = normalizeTicketSelectionLines(data.ticketSelections);
17290
+ if (ticketSelections.length === 0) {
17291
+ console.warn("[sessionStorage] No ticket selections to save, skipping registration state");
17292
+ return;
17293
+ }
17234
17294
  const entry = {
17235
17295
  ...data,
17236
- version: 3,
17296
+ ticketSelections,
17297
+ version: 4,
17237
17298
  timestamp: Date.now()
17238
17299
  };
17239
17300
  sessionStorage.setItem(key, JSON.stringify(entry));
@@ -17279,8 +17340,8 @@ function parseAndValidateEntry(stored, key) {
17279
17340
  return null;
17280
17341
  }
17281
17342
  const version = raw.version;
17282
- if (version !== 1 && version !== 2 && version !== 3) {
17283
- console.warn("[sessionStorage] Unknown version, ignoring entry");
17343
+ if (version !== 4) {
17344
+ console.warn("[sessionStorage] Outdated version, ignoring entry");
17284
17345
  sessionStorage.removeItem(key);
17285
17346
  return null;
17286
17347
  }
@@ -17296,34 +17357,12 @@ function parseAndValidateEntry(stored, key) {
17296
17357
  sessionStorage.removeItem(key);
17297
17358
  return null;
17298
17359
  }
17299
- if (entry.version === 1) {
17300
- const legacy = entry;
17301
- return {
17302
- version: 3,
17303
- attendeeId: legacy.attendeeId,
17304
- formData: legacy.formData,
17305
- ticketsCount: legacy.ticketsCount,
17306
- selectedEventId: legacy.selectedEventId,
17307
- selectedAttendanceMode: null,
17308
- selectedTicketTypeId: null,
17309
- paymentSelection: { method: legacy.paymentMethod },
17310
- timestamp: legacy.timestamp
17311
- };
17312
- }
17313
- if (entry.version === 2) {
17314
- return {
17315
- version: 3,
17316
- attendeeId: entry.attendeeId,
17317
- formData: entry.formData,
17318
- ticketsCount: entry.ticketsCount,
17319
- selectedEventId: entry.selectedEventId,
17320
- selectedAttendanceMode: null,
17321
- selectedTicketTypeId: null,
17322
- paymentSelection: entry.paymentSelection,
17323
- timestamp: entry.timestamp
17324
- };
17360
+ const ticketSelections = normalizeTicketSelectionLines(entry.ticketSelections);
17361
+ if (ticketSelections.length === 0 || getTicketSelectionTotal(ticketSelections) <= 0) {
17362
+ sessionStorage.removeItem(key);
17363
+ return null;
17325
17364
  }
17326
- return entry;
17365
+ return { ...entry, ticketSelections };
17327
17366
  } catch {
17328
17367
  console.warn("[sessionStorage] Failed to parse entry, removing");
17329
17368
  sessionStorage.removeItem(key);
@@ -17355,8 +17394,9 @@ function cleanupStaleRegistrations() {
17355
17394
  const stored = sessionStorage.getItem(key);
17356
17395
  if (!stored) continue;
17357
17396
  const entry = JSON.parse(stored);
17358
- const age = Date.now() - entry.timestamp;
17359
- if (age > STALE_THRESHOLD_MS) {
17397
+ const timestamp = typeof entry.timestamp === "number" ? entry.timestamp : NaN;
17398
+ const age = Date.now() - timestamp;
17399
+ if (!Number.isFinite(timestamp) || age > STALE_THRESHOLD_MS) {
17360
17400
  keysToRemove.push(key);
17361
17401
  }
17362
17402
  } catch {
@@ -17681,7 +17721,7 @@ function useEventRegistrationStripeReturn(options) {
17681
17721
  setRegistrationResult,
17682
17722
  setSelectedEvent,
17683
17723
  setSelectedAttendanceMode,
17684
- setSelectedTicketTypeId,
17724
+ setTicketSelections,
17685
17725
  setStep,
17686
17726
  setTicketsCount,
17687
17727
  step
@@ -17700,13 +17740,13 @@ function useEventRegistrationStripeReturn(options) {
17700
17740
  const pending = getPendingRegistration(siteId, occurrenceId || void 0);
17701
17741
  if (!pending) return;
17702
17742
  setFormData(pending.formData);
17703
- setTicketsCount(pending.ticketsCount);
17743
+ setTicketSelections(pending.ticketSelections);
17744
+ setTicketsCount(getTicketSelectionTotal(pending.ticketSelections));
17704
17745
  if (pending.selectedEventId) {
17705
17746
  const event = events.find((e) => e.publicEventId === pending.selectedEventId) ?? null;
17706
17747
  if (event) setSelectedEvent(event);
17707
17748
  }
17708
17749
  setSelectedAttendanceMode(pending.selectedAttendanceMode ?? null);
17709
- setSelectedTicketTypeId(pending.selectedTicketTypeId ?? null);
17710
17750
  setPaymentSelection(pending.paymentSelection);
17711
17751
  if (registrationStatus === "cancelled") {
17712
17752
  setStep("select-payment");
@@ -17758,6 +17798,10 @@ function useEventRegistrationSubmit(options) {
17758
17798
  }
17759
17799
  options.setStep("submitting");
17760
17800
  try {
17801
+ const ticketSelections = normalizeTicketSelectionLines(options.ticketSelections);
17802
+ if (ticketSelections.length === 0) {
17803
+ throw new Error("Choose at least one ticket to continue.");
17804
+ }
17761
17805
  const isBundle = method === "pass_bundle" || method === "membership_bundle";
17762
17806
  const bundleProductId = isBundle ? passId || membershipId : void 0;
17763
17807
  const selectionSnapshot = (() => {
@@ -17786,11 +17830,10 @@ function useEventRegistrationSubmit(options) {
17786
17830
  const result = await options.api.registerForEvent({
17787
17831
  occurrenceId: submitOccurrenceId,
17788
17832
  attendanceMode: options.selectedAttendanceMode ?? void 0,
17789
- ticketTypeId: options.selectedTicketTypeId ?? void 0,
17833
+ ticketSelections,
17790
17834
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
17791
17835
  email: options.formData.email.trim().toLowerCase(),
17792
17836
  name: options.formData.name.trim(),
17793
- ticketsCount: options.ticketsCount,
17794
17837
  phone: options.formData.phone.trim() || void 0,
17795
17838
  notes: options.formData.notes.trim() || void 0,
17796
17839
  altchaSolution: options.altchaSolution,
@@ -17835,10 +17878,9 @@ function useEventRegistrationSubmit(options) {
17835
17878
  savePendingRegistration(siteId, submitOccurrenceId, {
17836
17879
  attendeeId: result.attendeeId,
17837
17880
  formData: options.formData,
17838
- ticketsCount: options.ticketsCount,
17881
+ ticketSelections,
17839
17882
  selectedEventId: options.registrationTarget?.selectedEventId ?? null,
17840
17883
  selectedAttendanceMode: options.selectedAttendanceMode,
17841
- selectedTicketTypeId: options.selectedTicketTypeId ?? null,
17842
17884
  paymentSelection: selectionSnapshot
17843
17885
  });
17844
17886
  }
@@ -17950,7 +17992,6 @@ function useEventRegistrationWizard(options) {
17950
17992
  createInitialRegistrationFlowState
17951
17993
  );
17952
17994
  const step = flowState.step;
17953
- const ticketsCount = flowState.ticketsCount;
17954
17995
  const paymentSelection = flowState.paymentSelection;
17955
17996
  const [selectedEvent, setSelectedEvent] = useState4(preSelectedEvent ?? null);
17956
17997
  const [formData, setFormData] = useState4({ name: "", email: "", phone: "", notes: "" });
@@ -17961,7 +18002,7 @@ function useEventRegistrationWizard(options) {
17961
18002
  const [selectedAttendanceMode, setSelectedAttendanceMode] = useState4(
17962
18003
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
17963
18004
  );
17964
- const [selectedTicketTypeId, setSelectedTicketTypeId] = useState4(null);
18005
+ const [selectedTicketLines, setSelectedTicketLines] = useState4([]);
17965
18006
  const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState4(() => {
17966
18007
  const slidingScale = getSlidingScaleForEvent(preSelectedEvent ?? null);
17967
18008
  return slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "";
@@ -17997,14 +18038,51 @@ function useEventRegistrationWizard(options) {
17997
18038
  [options.occurrenceContext, selectedEvent]
17998
18039
  );
17999
18040
  const occurrenceId = registrationTarget?.occurrenceId ?? "";
18041
+ const currentEvent = selectedEvent ?? preSelectedEvent;
18042
+ const currentEventPricing = currentEvent ? resolveRuntimeEventPricing(currentEvent) : null;
18043
+ const currentEventUsesTicketTypes = currentEventPricing !== null && publicEventPricingMode(currentEventPricing) === "ticket_types";
18044
+ const selectedModeAvailability = selectedAttendanceMode ? (currentEvent?.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
18045
+ const currentAvailableSpots = selectedModeAvailability?.availableSpots ?? currentEvent?.availableSpots;
18046
+ const currentWaitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? currentEvent?.waitlistEnabled ?? false;
18047
+ const currentCanJoinWaitlist = currentAvailableSpots === 0 && currentWaitlistEnabled === true;
18048
+ const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
18049
+ const eventTicketTypesForSelection = useMemo6(
18050
+ () => getVisibleTicketTypes({
18051
+ event: currentEvent,
18052
+ eligibility: null,
18053
+ selectedAttendanceMode
18054
+ }),
18055
+ [currentEvent, selectedAttendanceMode]
18056
+ );
18057
+ const normalizedSelectedTicketLines = useMemo6(
18058
+ () => normalizeTicketSelectionLines(selectedTicketLines),
18059
+ [selectedTicketLines]
18060
+ );
18061
+ const ticketTypeSelectionTotal = useMemo6(
18062
+ () => getTicketSelectionTotal(normalizedSelectedTicketLines),
18063
+ [normalizedSelectedTicketLines]
18064
+ );
18065
+ const ticketsCount = usesTicketTypeSelections ? ticketTypeSelectionTotal : flowState.ticketsCount;
18066
+ const ticketSelections = useMemo6(() => {
18067
+ if (waitlistClaim.claim) {
18068
+ return [{ ticketTypeId: null, quantity: 1 }];
18069
+ }
18070
+ if (usesTicketTypeSelections) {
18071
+ return normalizedSelectedTicketLines;
18072
+ }
18073
+ return [{ ticketTypeId: null, quantity: ticketsCount }];
18074
+ }, [normalizedSelectedTicketLines, ticketsCount, usesTicketTypeSelections, waitlistClaim.claim]);
18075
+ useEffect11(() => {
18076
+ if (flowState.ticketsCount === ticketsCount) return;
18077
+ dispatchFlow({ type: "set-tickets-count", ticketsCount });
18078
+ }, [flowState.ticketsCount, ticketsCount]);
18000
18079
  const eligibilityResult = useEligibility({
18001
18080
  enabled: portalAuthState === "authenticated" || step === "enter-details" || step === "select-payment",
18002
18081
  occurrenceId,
18003
18082
  attendanceMode: selectedAttendanceMode,
18004
- ticketTypeId: selectedTicketTypeId,
18083
+ ticketSelections,
18005
18084
  selectedSlidingAmount: parseCurrencyInputToCents(selectedSlidingAmountInput),
18006
- email: formData.email,
18007
- ticketsCount
18085
+ email: formData.email
18008
18086
  });
18009
18087
  const existingRegistrationResult = useExistingEventRegistration({
18010
18088
  isAuthenticated: portalAuthState === "authenticated",
@@ -18110,7 +18188,7 @@ function useEventRegistrationWizard(options) {
18110
18188
  setTicketsCount,
18111
18189
  setSelectedEvent,
18112
18190
  setSelectedAttendanceMode,
18113
- setSelectedTicketTypeId,
18191
+ setTicketSelections: (lines) => setSelectedTicketLines(normalizeTicketSelectionLines(lines)),
18114
18192
  setPaymentSelection: (selection) => dispatchFlow({ type: "set-manual-payment-selection", selection }),
18115
18193
  setPendingAttendeeId,
18116
18194
  setStep,
@@ -18123,20 +18201,21 @@ function useEventRegistrationWizard(options) {
18123
18201
  }, [selectedEvent, preSelectedEvent]);
18124
18202
  const visibleTicketTypes = useMemo6(
18125
18203
  () => getVisibleTicketTypes({
18126
- event: selectedEvent ?? preSelectedEvent,
18204
+ event: currentEvent,
18127
18205
  eligibility,
18128
18206
  selectedAttendanceMode
18129
18207
  }),
18130
- [selectedAttendanceMode, selectedEvent, preSelectedEvent, eligibility]
18208
+ [currentEvent, selectedAttendanceMode, eligibility]
18131
18209
  );
18210
+ const singleSelectedTicketTypeId = getSingleSelectedTicketTypeId(normalizedSelectedTicketLines);
18132
18211
  const selectedTicketType = useMemo6(
18133
18212
  () => findSelectedTicketType({
18134
- event: selectedEvent ?? preSelectedEvent,
18213
+ event: currentEvent,
18135
18214
  eligibility,
18136
- selectedTicketTypeId,
18215
+ selectedTicketTypeId: singleSelectedTicketTypeId,
18137
18216
  selectedAttendanceMode
18138
18217
  }),
18139
- [selectedAttendanceMode, selectedEvent, preSelectedEvent, eligibility, selectedTicketTypeId]
18218
+ [currentEvent, eligibility, selectedAttendanceMode, singleSelectedTicketTypeId]
18140
18219
  );
18141
18220
  const selectedSlidingAmount = useMemo6(
18142
18221
  () => parseCurrencyInputToCents(selectedSlidingAmountInput),
@@ -18151,37 +18230,44 @@ function useEventRegistrationWizard(options) {
18151
18230
  selectedAmount: selectedSlidingAmount
18152
18231
  });
18153
18232
  const ticketTypeSelectionRequired = useMemo6(() => {
18154
- const event = selectedEvent ?? preSelectedEvent;
18233
+ const event = currentEvent;
18155
18234
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") {
18156
18235
  return false;
18157
18236
  }
18158
18237
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) {
18159
18238
  return false;
18160
18239
  }
18161
- const selectedModeAvailability = selectedAttendanceMode ? (event.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
18162
- const canJoinWaitlist = (selectedModeAvailability?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability?.waitlistEnabled ?? event.waitlistEnabled) === true;
18240
+ const selectedModeAvailability2 = selectedAttendanceMode ? (event.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
18241
+ const canJoinWaitlist = (selectedModeAvailability2?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability2?.waitlistEnabled ?? event.waitlistEnabled) === true;
18163
18242
  return !canJoinWaitlist;
18164
- }, [selectedAttendanceMode, selectedEvent, preSelectedEvent]);
18165
- const hasValidSelectedTicketType = !ticketTypeSelectionRequired || Boolean(
18166
- selectedTicketType && selectedTicketType.availability.kind === "available"
18167
- );
18243
+ }, [currentEvent, selectedAttendanceMode]);
18244
+ const hasValidTicketSelection = !ticketTypeSelectionRequired || normalizedSelectedTicketLines.length > 0 && normalizedSelectedTicketLines.every((line) => {
18245
+ if (!line.ticketTypeId) return false;
18246
+ const ticketType = visibleTicketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null;
18247
+ return ticketType?.availability.kind === "available";
18248
+ });
18168
18249
  useEffect11(() => {
18169
- const event = selectedEvent ?? preSelectedEvent;
18170
- if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") {
18171
- if (selectedTicketTypeId !== null) {
18172
- setSelectedTicketTypeId(null);
18250
+ if (!usesTicketTypeSelections) {
18251
+ if (selectedTicketLines.length > 0) {
18252
+ setSelectedTicketLines([]);
18173
18253
  }
18174
18254
  return;
18175
18255
  }
18176
- const implicitTicketType = getImplicitSingleAvailableTicketType(visibleTicketTypes);
18177
- if (implicitTicketType && selectedTicketTypeId !== implicitTicketType.id) {
18178
- setSelectedTicketTypeId(implicitTicketType.id);
18256
+ const availableTicketTypes = eventTicketTypesForSelection.filter(
18257
+ (ticketType) => ticketType.availability.kind === "available"
18258
+ );
18259
+ const implicitTicketType = getImplicitSingleAvailableTicketType(eventTicketTypesForSelection);
18260
+ if (implicitTicketType && normalizedSelectedTicketLines.length === 0) {
18261
+ setSelectedTicketLines([{ ticketTypeId: implicitTicketType.id, quantity: 1 }]);
18179
18262
  return;
18180
18263
  }
18181
- if (selectedTicketTypeId && !visibleTicketTypes.some((ticketType) => ticketType.id === selectedTicketTypeId)) {
18182
- setSelectedTicketTypeId(null);
18264
+ const filteredLines = normalizedSelectedTicketLines.filter(
18265
+ (line) => line.ticketTypeId && availableTicketTypes.some((ticketType) => ticketType.id === line.ticketTypeId)
18266
+ );
18267
+ if (filteredLines.length !== normalizedSelectedTicketLines.length) {
18268
+ setSelectedTicketLines(filteredLines);
18183
18269
  }
18184
- }, [selectedEvent, preSelectedEvent, selectedTicketTypeId, visibleTicketTypes]);
18270
+ }, [eventTicketTypesForSelection, normalizedSelectedTicketLines, selectedTicketLines.length, usesTicketTypeSelections]);
18185
18271
  useEffect11(() => {
18186
18272
  if (!requireGuestDetails) {
18187
18273
  if (guests.length > 0) setGuests([]);
@@ -18225,21 +18311,20 @@ function useEventRegistrationWizard(options) {
18225
18311
  return resolveEventRequiresPayment({
18226
18312
  eligibility,
18227
18313
  selectedTicketType,
18228
- event: selectedEvent ?? preSelectedEvent
18314
+ event: currentEvent
18229
18315
  });
18230
18316
  },
18231
18317
  [
18232
18318
  eligibility,
18233
18319
  selectedTicketType,
18234
- selectedEvent,
18235
- preSelectedEvent,
18320
+ currentEvent,
18236
18321
  selectedEventSlidingScale,
18237
18322
  selectedSlidingAmount
18238
18323
  ]
18239
18324
  );
18240
18325
  const handleDetailsComplete = async () => {
18241
- if (ticketTypeSelectionRequired && !hasValidSelectedTicketType) {
18242
- const message = "Select a ticket type to continue.";
18326
+ if (ticketTypeSelectionRequired && !hasValidTicketSelection) {
18327
+ const message = "Choose at least one available ticket to continue.";
18243
18328
  setErrorMessage(message);
18244
18329
  setStep("error");
18245
18330
  return { ok: false, reason: "blocked", message };
@@ -18324,27 +18409,39 @@ function useEventRegistrationWizard(options) {
18324
18409
  const discountContext = useMemo6(
18325
18410
  () => resolveDiscountContext({
18326
18411
  paymentSelection,
18327
- event: selectedEvent ?? preSelectedEvent,
18412
+ event: currentEvent,
18328
18413
  eventRequiresPayment,
18329
18414
  ticketsCount
18330
18415
  }),
18331
- [eventRequiresPayment, paymentSelection, selectedEvent, preSelectedEvent, ticketsCount]
18416
+ [currentEvent, eventRequiresPayment, paymentSelection, ticketsCount]
18417
+ );
18418
+ const fallbackUnitPriceCents = useMemo6(
18419
+ () => currentEvent ? publicEventScalarPrice(resolveRuntimeEventPricing(currentEvent)) ?? 0 : 0,
18420
+ [currentEvent]
18421
+ );
18422
+ const ticketSelectionSubtotalCents = useMemo6(() => {
18423
+ if (!usesTicketTypeSelections) {
18424
+ return Math.max(0, eligibility?.price ?? fallbackUnitPriceCents * ticketsCount);
18425
+ }
18426
+ return calculateTicketSelectionSubtotal({
18427
+ lines: normalizedSelectedTicketLines,
18428
+ ticketTypes: visibleTicketTypes,
18429
+ fallbackUnitPriceCents
18430
+ });
18431
+ }, [eligibility, fallbackUnitPriceCents, normalizedSelectedTicketLines, ticketsCount, usesTicketTypeSelections, visibleTicketTypes]);
18432
+ const ticketSelectionSummaryLines = useMemo6(
18433
+ () => buildTicketSelectionSummaryLines({
18434
+ lines: ticketSelections,
18435
+ ticketTypes: visibleTicketTypes,
18436
+ fallbackLabel: "Ticket",
18437
+ fallbackUnitPriceCents
18438
+ }),
18439
+ [fallbackUnitPriceCents, ticketSelections, visibleTicketTypes]
18332
18440
  );
18333
18441
  const discountSubtotalCents = useMemo6(() => {
18334
18442
  if (discountContext?.type !== "event") return void 0;
18335
- const event = selectedEvent ?? preSelectedEvent;
18336
- const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (selectedEventSlidingScale ? selectedSlidingAmount : null) ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
18337
- return Math.max(0, eventPriceCents * ticketsCount);
18338
- }, [
18339
- discountContext,
18340
- selectedTicketType,
18341
- eligibility,
18342
- selectedEvent,
18343
- preSelectedEvent,
18344
- selectedEventSlidingScale,
18345
- selectedSlidingAmount,
18346
- ticketsCount
18347
- ]);
18443
+ return ticketSelectionSubtotalCents;
18444
+ }, [discountContext, ticketSelectionSubtotalCents]);
18348
18445
  const discountUi = useDiscountCodeUi({ api, discountContext, subtotalCents: discountSubtotalCents });
18349
18446
  const selectionMethod = paymentSelection.method;
18350
18447
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
@@ -18352,9 +18449,8 @@ function useEventRegistrationWizard(options) {
18352
18449
  api,
18353
18450
  registrationTarget,
18354
18451
  selectedAttendanceMode,
18355
- selectedTicketTypeId,
18452
+ ticketSelections,
18356
18453
  selectedSlidingAmount,
18357
- ticketsCount,
18358
18454
  formData,
18359
18455
  altchaSolution,
18360
18456
  requireGuestDetails,
@@ -18375,12 +18471,12 @@ function useEventRegistrationWizard(options) {
18375
18471
  setSelectedAttendanceMode(
18376
18472
  (event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person"
18377
18473
  );
18378
- setSelectedTicketTypeId(null);
18474
+ setSelectedTicketLines([]);
18379
18475
  dispatchFlow({ type: "event-changed", hasPreSelectedContext: true });
18380
18476
  };
18381
18477
  const handleRetry = () => {
18382
18478
  dispatchFlow({ type: "reset", hasPreSelectedContext });
18383
- setSelectedTicketTypeId(null);
18479
+ setSelectedTicketLines([]);
18384
18480
  setSelectedSlidingAmountInput("");
18385
18481
  setSelectedAttendanceMode(
18386
18482
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
@@ -18416,17 +18512,20 @@ function useEventRegistrationWizard(options) {
18416
18512
  setTicketsCount,
18417
18513
  selectedAttendanceMode,
18418
18514
  setSelectedAttendanceMode,
18419
- selectedTicketTypeId,
18420
- setSelectedTicketTypeId,
18515
+ ticketSelections,
18516
+ selectedTicketLines: normalizedSelectedTicketLines,
18517
+ setSelectedTicketLines: (lines) => setSelectedTicketLines(normalizeTicketSelectionLines(lines)),
18421
18518
  selectedSlidingAmountInput,
18422
18519
  setSelectedSlidingAmountInput,
18423
18520
  selectedSlidingAmount,
18424
18521
  selectedEventSlidingScale,
18425
18522
  slidingScaleSelectionMessage,
18426
18523
  selectedTicketType,
18524
+ ticketSelectionSummaryLines,
18525
+ ticketSelectionSubtotalCents,
18427
18526
  visibleTicketTypes,
18428
18527
  ticketTypeSelectionRequired,
18429
- hasValidSelectedTicketType,
18528
+ hasValidTicketSelection,
18430
18529
  eventRequiresPayment,
18431
18530
  formData,
18432
18531
  setFormData,
@@ -19304,11 +19403,11 @@ function useEventRegistrationController(options) {
19304
19403
  wizard.setStep("select-tickets");
19305
19404
  return { ok: true };
19306
19405
  case "select-tickets":
19307
- if (wizard.ticketTypeSelectionRequired && !wizard.hasValidSelectedTicketType) {
19406
+ if (wizard.ticketTypeSelectionRequired && !wizard.hasValidTicketSelection) {
19308
19407
  return {
19309
19408
  ok: false,
19310
19409
  reason: "blocked",
19311
- message: "Select a ticket type to continue."
19410
+ message: "Choose at least one available ticket to continue."
19312
19411
  };
19313
19412
  }
19314
19413
  wizard.setStep("enter-details");
@@ -19384,6 +19483,7 @@ function useEventRegistrationController(options) {
19384
19483
  },
19385
19484
  [directPaymentOptions, wizard]
19386
19485
  );
19486
+ const selectedTicketTypeId = getSingleSelectedTicketTypeId(wizard.selectedTicketLines);
19387
19487
  const showSpamProtection = options.spamProtectionEnabled ?? isSpamProtectionEnabled();
19388
19488
  const spamProtection = showSpamProtection ? {
19389
19489
  enabled: true,
@@ -19410,11 +19510,14 @@ function useEventRegistrationController(options) {
19410
19510
  tickets: {
19411
19511
  count: wizard.ticketsCount,
19412
19512
  max: wizard.maxTicketsNum,
19413
- selectedTicketTypeId: wizard.selectedTicketTypeId,
19513
+ selections: wizard.ticketSelections,
19514
+ summaryLines: wizard.ticketSelectionSummaryLines,
19515
+ subtotalCents: wizard.ticketSelectionSubtotalCents,
19516
+ selectedTicketTypeId,
19414
19517
  selectedTicketType: wizard.selectedTicketType,
19415
19518
  visibleTicketTypes: wizard.visibleTicketTypes,
19416
19519
  selectionRequired: wizard.ticketTypeSelectionRequired,
19417
- hasValidSelection: wizard.hasValidSelectedTicketType,
19520
+ hasValidSelection: wizard.hasValidTicketSelection,
19418
19521
  eventRequiresPayment: wizard.eventRequiresPayment,
19419
19522
  selectedAttendanceMode: wizard.selectedAttendanceMode
19420
19523
  },
@@ -19457,7 +19560,10 @@ function useEventRegistrationController(options) {
19457
19560
  actions: {
19458
19561
  selectEvent: wizard.handleEventSelect,
19459
19562
  selectAttendanceMode: wizard.setSelectedAttendanceMode,
19460
- selectTicketType: wizard.setSelectedTicketTypeId,
19563
+ selectTicketType: (ticketTypeId) => {
19564
+ wizard.setSelectedTicketLines(ticketTypeId ? [{ ticketTypeId, quantity: 1 }] : []);
19565
+ },
19566
+ setTicketSelections: wizard.setSelectedTicketLines,
19461
19567
  setTicketsCount: wizard.setTicketsCount,
19462
19568
  updateAttendee,
19463
19569
  updateGuests,
@@ -19535,7 +19641,7 @@ function resolveHeadlessDirectPaymentOptions(wizard) {
19535
19641
  return buildEventRegistrationPaymentOptionsViewModel({
19536
19642
  eventTitle: event?.title ?? "Event",
19537
19643
  paymentTerms: event?.paymentTerms,
19538
- totalPriceCents: eligibility.price * wizard.ticketsCount,
19644
+ totalPriceCents: eligibility.price,
19539
19645
  currency: eligibility.currency,
19540
19646
  startsAt: wizard.occurrenceContext?.startsAt ?? event?.startsAt
19541
19647
  }).options;
@@ -1,7 +1,7 @@
1
1
  import { normalizeCarouselProps, CarouselNodeClient } from './chunk-JNE6UI6U.mjs';
2
2
  import './chunk-6N7YRNYU.mjs';
3
3
  import { mountReactIsland } from './chunk-N7PBWBWQ.mjs';
4
- import { defineIslandPropsDecoder } from './chunk-6SYGVVWA.mjs';
4
+ import { defineIslandPropsDecoder } from './chunk-GWCRCJLB.mjs';
5
5
  import './chunk-PCHACQEB.mjs';
6
6
  import { jsx } from 'react/jsx-runtime';
7
7
 
@@ -1,14 +1,14 @@
1
- import { mountShopBlockIsland, CartClient } from './chunk-YYVGSYBO.mjs';
2
- import './chunk-Q47XGAGS.mjs';
1
+ import { mountShopBlockIsland, CartClient } from './chunk-BFCJZODD.mjs';
2
+ import './chunk-H7A6PUGM.mjs';
3
3
  import './chunk-JNE6UI6U.mjs';
4
4
  import './chunk-6N7YRNYU.mjs';
5
5
  import './chunk-L4J5Y62O.mjs';
6
- import './chunk-J6TMMMXW.mjs';
6
+ import './chunk-A3YN7M3G.mjs';
7
7
  import './chunk-3DCJ45Z2.mjs';
8
8
  import './chunk-WBQG7VJA.mjs';
9
9
  import './chunk-TNGL2WJF.mjs';
10
10
  import './chunk-N7PBWBWQ.mjs';
11
- import { defineIslandPropsDecoder } from './chunk-6SYGVVWA.mjs';
11
+ import { defineIslandPropsDecoder } from './chunk-GWCRCJLB.mjs';
12
12
  import './chunk-PCHACQEB.mjs';
13
13
 
14
14
  // src/rendering/islands/enhancers/cart.tsx
@@ -1,14 +1,14 @@
1
- import { mountShopBlockIsland, CheckoutClient } from './chunk-YYVGSYBO.mjs';
2
- import './chunk-Q47XGAGS.mjs';
1
+ import { mountShopBlockIsland, CheckoutClient } from './chunk-BFCJZODD.mjs';
2
+ import './chunk-H7A6PUGM.mjs';
3
3
  import './chunk-JNE6UI6U.mjs';
4
4
  import './chunk-6N7YRNYU.mjs';
5
5
  import './chunk-L4J5Y62O.mjs';
6
- import './chunk-J6TMMMXW.mjs';
6
+ import './chunk-A3YN7M3G.mjs';
7
7
  import './chunk-3DCJ45Z2.mjs';
8
8
  import './chunk-WBQG7VJA.mjs';
9
9
  import './chunk-TNGL2WJF.mjs';
10
10
  import './chunk-N7PBWBWQ.mjs';
11
- import { defineIslandPropsDecoder } from './chunk-6SYGVVWA.mjs';
11
+ import { defineIslandPropsDecoder } from './chunk-GWCRCJLB.mjs';
12
12
  import './chunk-PCHACQEB.mjs';
13
13
 
14
14
  // src/rendering/islands/enhancers/checkout.tsx
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { useBlockApi } from './chunk-TNGL2WJF.mjs';
3
- import { themeButtonClassName, assertNever } from './chunk-6SYGVVWA.mjs';
3
+ import { themeButtonClassName, assertNever } from './chunk-GWCRCJLB.mjs';
4
4
  import { resolvePlanningDateInput, formatBookingPriceDisplay } from './chunk-PCHACQEB.mjs';
5
5
  import { useState, useReducer, useSyncExternalStore, useEffect } from 'react';
6
6
  import { jsxs, jsx } from 'react/jsx-runtime';