@riverbankcms/sdk 0.136.0 → 0.138.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 (343) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +7 -4
  3. package/dist/{PreviewEditorSidebar-BU5ZlhQR.mjs → PreviewEditorSidebar-BVPur_CU.mjs} +12 -18
  4. package/dist/{PreviewEditorUI-9XkHL-bC.mjs → PreviewEditorUI-DLhWX8oh.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-wbEpXryy.mjs → PreviewQueryProvider-BguJY1SW.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-CnWmj-2B.mjs → SdkPreviewModeRuntime-Tda6qw4z.mjs} +1375 -495
  7. package/dist/{SiteChromeCustomizeContext-B6d5zkog.mjs → SiteChromeCustomizeContext-BHFgMjde.mjs} +35665 -33602
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +7226 -3057
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +1223 -811
  10. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  11. package/dist/_dts/api/src/accessAdmin.d.ts +16 -18
  12. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  13. package/dist/_dts/api/src/appointmentServiceContracts.d.ts +83 -0
  14. package/dist/_dts/api/src/appointmentServiceLocation.d.ts +12 -0
  15. package/dist/_dts/api/src/appointmentServiceVisibility.d.ts +14 -0
  16. package/dist/_dts/api/src/appointmentSetup.d.ts +8 -0
  17. package/dist/_dts/api/src/bookingConfig.d.ts +35 -87
  18. package/dist/_dts/api/src/bookingManagementEndpoints.d.ts +1 -1
  19. package/dist/_dts/api/src/bookingOperations.d.ts +2 -69
  20. package/dist/_dts/api/src/bookingRecords.d.ts +2 -2
  21. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +2 -2
  22. package/dist/_dts/api/src/bookingServicePricing.d.ts +47 -0
  23. package/dist/_dts/api/src/bookings.d.ts +2 -2
  24. package/dist/_dts/api/src/cache-tags.d.ts +7 -2
  25. package/dist/_dts/api/src/common/index.d.ts +1 -0
  26. package/dist/_dts/api/src/common/zodFieldErrors.d.ts +34 -0
  27. package/dist/_dts/api/src/communications.d.ts +14 -8
  28. package/dist/_dts/api/src/contentRuntime.d.ts +3 -6
  29. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  30. package/dist/_dts/api/src/endpoints.d.ts +1636 -1637
  31. package/dist/_dts/api/src/eventAttendanceLabels.d.ts +2 -2
  32. package/dist/_dts/api/src/eventManagementContracts.d.ts +10 -14
  33. package/dist/_dts/api/src/index.d.ts +11 -9
  34. package/dist/_dts/api/src/indexCoreTypes.d.ts +1 -1
  35. package/dist/_dts/api/src/indexExtendedTypes.d.ts +1 -1
  36. package/dist/_dts/api/src/media.d.ts +19 -2
  37. package/dist/_dts/api/src/occurrenceScheduling.d.ts +1 -1
  38. package/dist/_dts/api/src/participantMessaging.d.ts +3 -3
  39. package/dist/_dts/api/src/public/endpoints.d.ts +1 -1
  40. package/dist/_dts/api/src/publicBookingAvailabilityEndpointDefinitions.d.ts +62 -0
  41. package/dist/_dts/api/src/publicBookingService.d.ts +109 -2
  42. package/dist/_dts/api/src/publicEventCheckoutEndpointDefinitions.d.ts +27 -0
  43. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  44. package/dist/_dts/api/src/publicExperience/commerceEndpoints.d.ts +2 -1
  45. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +30 -148
  46. package/dist/_dts/api/src/publicExperience/sharedTypes.d.ts +8 -26
  47. package/dist/_dts/api/src/publicExperience/siteEndpoints.d.ts +5 -13
  48. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +2 -3
  49. package/dist/_dts/api/src/schemas/admin-allowed-domains.d.ts +76 -0
  50. package/dist/_dts/api/src/schemas/admin-observability-logs.d.ts +61 -0
  51. package/dist/_dts/api/src/schemas/admin-users.d.ts +80 -0
  52. package/dist/_dts/api/src/schemas/newsletter-memberships.d.ts +98 -0
  53. package/dist/_dts/api/src/schemas/sdk-entries.d.ts +128 -0
  54. package/dist/_dts/api/src/schemas/site-api-keys.d.ts +112 -0
  55. package/dist/_dts/api/src/schemas/support.d.ts +35 -0
  56. package/dist/_dts/api/src/sdk-event-booking-offer.d.ts +14 -0
  57. package/dist/_dts/api/src/sdk-event-payment-terms.d.ts +4 -16
  58. package/dist/_dts/api/src/sdk-event-upsert.d.ts +11 -21
  59. package/dist/_dts/api/src/sdkContracts.d.ts +8 -23
  60. package/dist/_dts/api/src/sdkManagedEventContracts.d.ts +37 -0
  61. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  62. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +66 -0
  63. package/dist/_dts/api/src/sdkThemeWire.d.ts +157 -146
  64. package/dist/_dts/api/src/siteInfrastructure.d.ts +37 -37
  65. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +35 -54
  66. package/dist/_dts/api/src/siteOnboarding.d.ts +3 -3
  67. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +38 -16
  68. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  69. package/dist/_dts/api/src/types.d.ts +2 -2
  70. package/dist/_dts/api/src/{wholeSeriesOfferings.d.ts → wholeSeriesRuns.d.ts} +11 -67
  71. package/dist/_dts/block-form/src/FormRenderer.d.ts +3 -1
  72. package/dist/_dts/block-form/src/backend/store-backend.d.ts +2 -1
  73. package/dist/_dts/block-form/src/backend/types.d.ts +2 -0
  74. package/dist/_dts/block-form/src/context/FormFocusContext.d.ts +7 -0
  75. package/dist/_dts/block-form/src/index.d.ts +2 -0
  76. package/dist/_dts/block-form/src/manifest/ManifestForm.d.ts +2 -1
  77. package/dist/_dts/block-form/src/utils/promotedFieldBackend.d.ts +8 -0
  78. package/dist/_dts/block-form/src/utils/promotedFieldPlan.d.ts +18 -0
  79. package/dist/_dts/block-form/src/utils/resolvePromotedFields.d.ts +8 -1
  80. package/dist/_dts/blocks/src/BlockErrorBoundary.d.ts +46 -0
  81. package/dist/_dts/blocks/src/PageRenderer.d.ts +2 -141
  82. package/dist/_dts/blocks/src/contracts/index.d.ts +2 -0
  83. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +12 -6
  84. package/dist/_dts/blocks/src/index.d.ts +1 -0
  85. package/dist/_dts/blocks/src/pageRenderAudience.d.ts +54 -0
  86. package/dist/_dts/blocks/src/pageRenderDiagnostics.d.ts +26 -0
  87. package/dist/_dts/blocks/src/pageRenderPlan.d.ts +116 -0
  88. package/dist/_dts/blocks/src/pageRenderPreparation.d.ts +109 -0
  89. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +1 -0
  90. package/dist/_dts/blocks/src/pageRendererContracts.d.ts +154 -0
  91. package/dist/_dts/blocks/src/portal/types.d.ts +1 -1
  92. package/dist/_dts/blocks/src/server.d.ts +1 -0
  93. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +12 -20
  94. package/dist/_dts/blocks/src/system/blocks/events/event-calendar.d.ts +1 -1
  95. package/dist/_dts/blocks/src/system/blocks/events/eventCalendarDataLoaders.d.ts +3 -0
  96. package/dist/_dts/blocks/src/system/blocks/events/shared/calendarWindow.d.ts +95 -0
  97. package/dist/_dts/blocks/src/system/blocks/events/shared/index.d.ts +1 -0
  98. package/dist/_dts/blocks/src/system/blocks/events/shared/runGrouping.d.ts +37 -0
  99. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +1046 -773
  100. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +59 -46
  101. package/dist/_dts/blocks/src/system/blocks/location-map.d.ts +3 -3
  102. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +161 -161
  103. package/dist/_dts/blocks/src/system/blocks/offerings-preview/model.d.ts +13 -6
  104. package/dist/_dts/blocks/src/system/blocks/offerings-preview/scheduleSummary.d.ts +10 -4
  105. package/dist/_dts/blocks/src/system/constants/buttonRoles.d.ts +2 -2
  106. package/dist/_dts/blocks/src/system/constants/events.d.ts +4 -0
  107. package/dist/_dts/blocks/src/system/customize/facets.d.ts +4 -4
  108. package/dist/_dts/blocks/src/system/data/loaderEndpoints.d.ts +1 -1
  109. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +5 -9
  110. package/dist/_dts/blocks/src/system/manifest/schema.d.ts +83 -83
  111. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +12 -3
  112. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -8
  113. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  114. package/dist/_dts/blocks/src/system/runtime/api/types/core.d.ts +13 -0
  115. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +12 -20
  116. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +32 -37
  117. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +15 -18
  118. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +11 -8
  119. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +38 -22
  120. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessEventRegistrationFlow.d.ts +7 -0
  121. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/WholeSeriesPricingDisplay.d.ts +2 -2
  122. package/dist/_dts/blocks/src/system/runtime/nodes/event-details/logic.d.ts +16 -4
  123. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  124. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/EventRegistrationWizardActions.d.ts +4 -2
  125. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.d.ts +1 -1
  126. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +5 -3
  127. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  128. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventScope.d.ts +1 -1
  129. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  130. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +15 -0
  131. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +2 -2
  132. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  133. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  134. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.d.ts +14 -3
  135. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationSurface.d.ts +3 -21
  136. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  137. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.d.ts +0 -3
  138. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.d.ts +14 -16
  139. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +13 -15
  140. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -3
  141. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +9 -3
  142. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +25 -23
  143. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  144. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +11 -4
  145. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration.interactive.d.ts +8 -13
  146. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +5 -0
  147. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +1 -0
  148. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +11 -3
  149. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  150. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  151. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  152. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  153. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  154. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +24 -0
  155. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  156. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +1 -1
  157. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +1 -1
  158. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +1080 -560
  159. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventProductEligibility.d.ts +2 -2
  160. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  161. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  162. package/dist/_dts/blocks/src/system/runtime/nodes/offerings-preview.d.ts +2 -2
  163. package/dist/_dts/blocks/src/system/runtime/nodes/shared/contract-schemas.d.ts +7 -7
  164. package/dist/_dts/blocks/src/system/transforms/registry/eventCalendar.d.ts +11 -0
  165. package/dist/_dts/commerce/src/moneyReport.d.ts +1 -1
  166. package/dist/_dts/commerce/src/purchase/checkoutErrorCodes.d.ts +1 -1
  167. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  168. package/dist/_dts/commerce/src/purchase/eventOrderOptionSnapshot.d.ts +3 -0
  169. package/dist/_dts/commerce/src/purchase/index.d.ts +1 -0
  170. package/dist/_dts/commerce/src/purchase/orderItemMetadata.d.ts +6 -3
  171. package/dist/_dts/commerce/src/purchase/types.d.ts +3 -2
  172. package/dist/_dts/commerce/src/waitlists.d.ts +1 -1
  173. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +4 -0
  174. package/dist/_dts/content-editor/src/controller/useBlockedPublishPlan.d.ts +11 -0
  175. package/dist/_dts/content-editor/src/hooks/applyBlockedPublishPlan.d.ts +1 -1
  176. package/dist/_dts/content-editor/src/hooks/blockedPublishPlan.d.ts +13 -5
  177. package/dist/_dts/content-editor/src/hooks/useEntryActions.d.ts +5 -1
  178. package/dist/_dts/content-editor/src/hooks/validateBlocksForPublish.d.ts +34 -2
  179. package/dist/_dts/content-editor/src/index.d.ts +2 -1
  180. package/dist/_dts/content-editor/src/ui/blockValidationManifestRegistry.d.ts +2 -0
  181. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +4 -2
  182. package/dist/_dts/content-editor/src/ui/stores/types.d.ts +27 -5
  183. package/dist/_dts/content-model/src/content-types/schema.d.ts +3 -3
  184. package/dist/_dts/content-model/src/templates/defaults.d.ts +11 -0
  185. package/dist/_dts/core/src/branded.d.ts +28 -9
  186. package/dist/_dts/core/src/event-booking-offer-parser.d.ts +82 -0
  187. package/dist/_dts/core/src/event-booking-offer.d.ts +89 -0
  188. package/dist/_dts/core/src/event-page-surface-subject.d.ts +81 -4
  189. package/dist/_dts/core/src/event-pricing.d.ts +81 -89
  190. package/dist/_dts/core/src/index.d.ts +3 -0
  191. package/dist/_dts/core/src/non-empty-array.d.ts +3 -0
  192. package/dist/_dts/core/src/whole-series-run-schedule.d.ts +92 -0
  193. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +495 -279
  194. package/dist/_dts/db/src/index.d.ts +1 -0
  195. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  196. package/dist/_dts/editor-blocks/src/BlockForm.d.ts +2 -6
  197. package/dist/_dts/editor-blocks/src/block-editor/BlockCard.d.ts +1 -1
  198. package/dist/_dts/editor-blocks/src/block-editor/BlockCardHeader.d.ts +2 -2
  199. package/dist/_dts/editor-blocks/src/block-editor/BlockSettingsModal.d.ts +4 -2
  200. package/dist/_dts/editor-blocks/src/block-editor/hooks/useBlockValidationManifests.d.ts +1 -0
  201. package/dist/_dts/editor-blocks/src/block-editor/types.d.ts +22 -7
  202. package/dist/_dts/editor-blocks/src/block-editor/useSettingsFieldFocus.d.ts +18 -0
  203. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  204. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  205. package/dist/_dts/preview-next/src/client/blocks/previewBatchLoaderParams.d.ts +2 -0
  206. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  207. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  208. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  209. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  210. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  211. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  212. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  213. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  214. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  215. package/dist/_dts/sdk/src/client/{eventSubrouteReadOperations.d.ts → publicEventReadOperations.d.ts} +4 -3
  216. package/dist/_dts/sdk/src/client/types.d.ts +6 -10
  217. package/dist/_dts/sdk/src/config/types.d.ts +1 -1
  218. package/dist/_dts/sdk/src/config/validation.d.ts +3 -3
  219. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +5 -0
  220. package/dist/_dts/sdk/src/data/prefetchBlockData.d.ts +2 -3
  221. package/dist/_dts/sdk/src/manifest/passes/pass1-fields.d.ts +8 -8
  222. package/dist/_dts/sdk/src/next/tags.d.ts +5 -8
  223. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -26
  224. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +9 -1
  225. package/dist/_dts/sdk/src/rendering/index.d.ts +1 -0
  226. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +6 -53
  227. package/dist/_dts/sdk/src/rendering/server.d.ts +1 -0
  228. package/dist/_dts/sdk/src/version.d.ts +1 -1
  229. package/dist/_dts/theme-core/src/accordions/types.d.ts +5 -5
  230. package/dist/_dts/theme-core/src/buttons/styles/recipes/flatInk.d.ts +25 -25
  231. package/dist/_dts/theme-core/src/buttons/styles/registry.d.ts +25 -25
  232. package/dist/_dts/theme-core/src/buttons/styles/selection.d.ts +7 -7
  233. package/dist/_dts/theme-core/src/buttons/types.d.ts +22 -22
  234. package/dist/_dts/theme-core/src/cards/types.d.ts +49 -49
  235. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  236. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  237. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +26 -3
  238. package/dist/_dts/theme-core/src/inputs/types.d.ts +2 -2
  239. package/dist/_dts/theme-core/src/interactive/baseSchema.d.ts +1 -1
  240. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +195 -195
  241. package/dist/_dts/theme-core/src/navigation/types.d.ts +3 -3
  242. package/dist/_dts/theme-core/src/palette/colors.d.ts +1 -1
  243. package/dist/_dts/theme-core/src/palette/curated/types.d.ts +2 -2
  244. package/dist/_dts/theme-core/src/schema.d.ts +258 -257
  245. package/dist/_dts/theme-core/src/shared/componentStyles.d.ts +1 -1
  246. package/dist/_dts/theme-core/src/site-styles/backgroundAppearance.d.ts +2 -2
  247. package/dist/_dts/theme-core/src/site-styles/cardRoleVocabulary.d.ts +1 -1
  248. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  249. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +10 -0
  250. package/dist/_dts/theme-core/src/site-styles/designState.d.ts +4 -4
  251. package/dist/_dts/theme-core/src/site-styles/generatedSchemas.d.ts +2 -2
  252. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  253. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  254. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  255. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -9
  256. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  257. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  258. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  259. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +10 -1
  260. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +1 -1
  261. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +7 -0
  262. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  263. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  264. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  265. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  266. package/dist/_dts/theme-core/src/site-styles/tokenRecipes.d.ts +1 -1
  267. package/dist/_dts/theme-core/src/tokens/themeColorTokens.d.ts +3 -3
  268. package/dist/_dts/ui/src/badge.d.ts +1 -1
  269. package/dist/_dts/ui/src/button.d.ts +2 -2
  270. package/dist/client/{BookingConsentAgreement-DCch21a6.mjs → BookingConsentAgreement-DQUik1Mx.mjs} +2 -2
  271. package/dist/client/{DiscountCodeDisclosure-DIj1T59K.mjs → DiscountCodeDisclosure-CU67Q4vH.mjs} +1 -1
  272. package/dist/client/{EventCombined.client-BWVNlXTx.mjs → EventCombined.client-a02TXUgg.mjs} +233 -348
  273. package/dist/client/{EventListing.client-DVFCDe3i.mjs → EventListing.client-99qx7jFl.mjs} +2 -2
  274. package/dist/client/{LeafletStaticMap-BY5mIHw9.mjs → LeafletStaticMap-CWzcq73L.mjs} +1 -1
  275. package/dist/client/{accordion-CK29E8rG.mjs → accordion-D1nb3-Oj.mjs} +1 -1
  276. package/dist/client/{bookingForm-DFvnet-N.mjs → bookingForm-BGrmLhaO.mjs} +5 -5
  277. package/dist/client/bookings.mjs +10555 -10475
  278. package/dist/client/{carousel-BVU5NQlb.mjs → carousel-DSmOx3ad.mjs} +1 -1
  279. package/dist/client/{cart-BNItcZVa.mjs → cart-COWeOIUk.mjs} +3 -3
  280. package/dist/client/{checkout-Bu6iCdkP.mjs → checkout-CeysaIJK.mjs} +3 -3
  281. package/dist/client/client.mjs +58968 -57441
  282. package/dist/client/{context-BImarOAq.mjs → context-DBzX85Xc.mjs} +46 -18
  283. package/dist/client/dates-DCYhZBlW.mjs +264 -0
  284. package/dist/client/{eventCalendar-DS2h-UXD.mjs → eventCalendar-BgsN73Ul.mjs} +21 -44
  285. package/dist/client/{eventCombined-Drt4Oq9p.mjs → eventCombined-DcZQAMQP.mjs} +3 -3
  286. package/dist/client/{eventDetails-Bl2vWgTX.mjs → eventDetails-BWwGJ54l.mjs} +131 -51
  287. package/dist/client/{eventListing-CS7PHY7v.mjs → eventListing-DqYqV8vp.mjs} +3 -3
  288. package/dist/client/{eventRegistration-D-j8eNy2.mjs → eventRegistration-DQGDgu_e.mjs} +1122 -803
  289. package/dist/client/{form-RMpoLTyr.mjs → form-ClfpasVT.mjs} +2 -2
  290. package/dist/client/{headerSection-BYE3Lb3k.mjs → headerSection-BuOMe98N.mjs} +2 -2
  291. package/dist/client/hooks.mjs +9630 -9526
  292. package/dist/client/{locationMap-BxSr7Kkh.mjs → locationMap-CEqu_sfa.mjs} +2 -2
  293. package/dist/client/{newsletterForm-DerbXwFC.mjs → newsletterForm-IbrHjMxy.mjs} +2 -2
  294. package/dist/client/pricing-xj7MPZlK.mjs +314 -0
  295. package/dist/client/{productDetail-4SZCLhAf.mjs → productDetail-vxqTt5Xj.mjs} +3 -3
  296. package/dist/client/{productList-D7AP4GNq.mjs → productList-OTdZu8sb.mjs} +3 -3
  297. package/dist/client/{propsDecoder-pzBeCh14.mjs → propsDecoder-CY9gGEF7.mjs} +786 -1194
  298. package/dist/client/rendering/client.mjs +11481 -10469
  299. package/dist/client/rendering/islands.mjs +17 -17
  300. package/dist/client/rendering.mjs +26871 -25232
  301. package/dist/client/{scrollToBlock-CduAKf8T.mjs → scrollToBlock--BFV6kl-.mjs} +1 -1
  302. package/dist/client/{shop-Cp5p_kGe.mjs → shop-CP4YbDL9.mjs} +6 -6
  303. package/dist/client/{shop-commerce-DicXYaYX.mjs → shop-commerce-APs4XBXE.mjs} +5 -5
  304. package/dist/client/{shop-commerce-store-CGWnpY4K.mjs → shop-commerce-store-CAVLxLFr.mjs} +1 -1
  305. package/dist/client/{shopShared-BDf9ljkm.mjs → shopShared-BnSRrwXI.mjs} +3 -3
  306. package/dist/client/{useEventFilters-DeeyGYuk.mjs → useEventFilters-1a1by4c8.mjs} +33 -18
  307. package/dist/preview-next/before-render.mjs +186 -148
  308. package/dist/preview-next/client/runtime.mjs +4 -4
  309. package/dist/preview-next/keepAlive.mjs +186 -148
  310. package/dist/preview-next/middleware.mjs +186 -148
  311. package/dist/{previewAuthStore-DbqBol0V.mjs → previewAuthStore-BkEgnYPP.mjs} +186 -148
  312. package/dist/{sdk-runtime-19MO27F0.mjs → sdk-runtime-DwGfsT0S.mjs} +2 -2
  313. package/dist/{separator-DaPLtJ1f.mjs → separator-BeqDLneQ.mjs} +1 -1
  314. package/dist/server/components.mjs +53243 -51879
  315. package/dist/server/config-validation.mjs +1015 -906
  316. package/dist/server/config.mjs +1019 -910
  317. package/dist/server/data.mjs +10304 -10200
  318. package/dist/server/index.mjs +217 -198
  319. package/dist/server/next/tags.mjs +18 -4
  320. package/dist/server/next.mjs +62787 -61617
  321. package/dist/server/page-converter.mjs +797 -917
  322. package/dist/server/prebuild.mjs +1 -1
  323. package/dist/server/rendering/server.mjs +52557 -51365
  324. package/dist/server/rendering.mjs +54373 -53054
  325. package/dist/server/routing.mjs +13274 -13142
  326. package/dist/server/server.mjs +1076 -1154
  327. package/dist/server/theme-bridge.mjs +69 -9
  328. package/dist/server/theme.mjs +27 -8
  329. package/dist/styles/index.css +32 -1
  330. package/package.json +1 -1
  331. package/dist/_dts/api/src/publicExperience/wholeSeriesEndpoints.d.ts +0 -65
  332. package/dist/_dts/api/src/publicWholeSeriesEndpointDefinitions.d.ts +0 -20
  333. package/dist/_dts/api/src/sdk-event-pricing.d.ts +0 -61
  334. package/dist/_dts/blocks/src/system/blocks/events/whole-series/index.d.ts +0 -3
  335. package/dist/_dts/blocks/src/system/blocks/events/whole-series/pricing.d.ts +0 -50
  336. package/dist/_dts/blocks/src/system/blocks/events/whole-series/schemas.d.ts +0 -135
  337. package/dist/_dts/blocks/src/system/blocks/events/whole-series/types.d.ts +0 -87
  338. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  339. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  340. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  341. package/dist/_dts/sdk/src/client/wholeSeriesReadOperations.d.ts +0 -11
  342. package/dist/_dts/sdk/src/public-api/wholeSeriesContracts.d.ts +0 -2
  343. /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement-DCch21a6.mjs";
3
- import { $t as publicEventScalarPrice, At as assertNever, Bt as formatBookingDateTime, C as getStepNumber, Ht as formatBookingTime, Jt as toBookingTimeZone, O as resolveEventRegistrationScopedEvents, Qt as publicEventPricingType, S as WIZARD_STEPS, T as planEventRegistrationSurface, V as formatEventPriceDisplay, Xt as publicEventPricingCurrency, Y as resolveRuntimeEventPricing, Yt as toScheduledBookingDateTime, Zt as publicEventPricingMode, _ as findEventByOccurrenceId, b as resolveEventRequiresPayment, en as publicEventTicketTypes, g as toWholeSeriesRegistrationEvents, h as toRegistrationEvents, st as themeButtonClassName, t as defineIslandPropsDecoder, tn as formatCurrencyAmount, v as findPreSelectedEvent, w as getWizardStepsForContext, x as resolveRegistrationTarget, y as resolveDiscountContext, zt as formatBookingDate } from "./propsDecoder-pzBeCh14.mjs";
4
- import { n as useBlockApi, t as BlockApiProvider } from "./context-BImarOAq.mjs";
2
+ import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement-DQUik1Mx.mjs";
3
+ import { $t as toBookingTimeZone, C as WIZARD_STEPS, Gt as formatBookingDateTime, Nt as assertNever, O as resolveEventRegistrationScopedEvents, S as resolveRegistrationTarget, T as getWizardStepsForContext, Ut as formatBookingDate, _ as toWholeSeriesRegistrationEvents, an as publicEventPricingCurrency, at as groupPublicEventsByRun, b as resolveDiscountContext, cn as publicEventPricingStrategy, g as toRegistrationEvents, h as getRegistrationStartAt, ln as publicEventPricingType, qt as formatBookingTime, sn as publicEventPricingOptions, t as defineIslandPropsDecoder, tn as toScheduledBookingDateTime, un as formatCurrencyAmount, ut as themeButtonClassName, v as findEventByOccurrenceId, w as getStepNumber, x as resolveEventRequiresPayment, y as findPreSelectedEvent } from "./propsDecoder-CY9gGEF7.mjs";
4
+ import { a as canJoinEventWaitlistOnAnyMode, c as formatTicketTypeRemaining, f as getRemainingPlaces, h as selectMostOpenCapacitySource, i as canJoinEventWaitlist, m as selectEventCapacitySource, o as formatEventRemainingPlaces, p as isEventFull, r as formatEventPriceDisplay, s as formatRemainingPlaces, u as getEventAvailability } from "./pricing-xj7MPZlK.mjs";
5
+ import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-DBzX85Xc.mjs";
5
6
  import { n as isSpamProtectionEnabled, r as AltchaWidget, t as getChallengeUrl } from "./src-B833rYRr.mjs";
6
- import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-CduAKf8T.mjs";
7
- import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-DIj1T59K.mjs";
7
+ import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock--BFV6kl-.mjs";
8
+ import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-CU67Q4vH.mjs";
8
9
  import { t as FeedbackMessage } from "./FeedbackMessage-CtLvvS_d.mjs";
9
10
  import { n as getFieldValidationControlProps, t as FieldValidationMessage } from "./FieldValidationMessage-AYanWFb-.mjs";
10
11
  import { t as buildCheckoutReturnUrls } from "./checkoutUrls-BuseL0dw.mjs";
@@ -597,11 +598,11 @@ function transformRegistrationOptionsResponse(response) {
597
598
  } : null,
598
599
  eligible: response.registration.kind === "none" && registrationAvailable,
599
600
  requiresPayment: monetaryPaymentAvailable,
600
- pricingMode: response.pricing.mode,
601
+ pricingStrategy: response.pricing.strategy,
601
602
  paymentTerms: response.pricing.paymentTerms,
602
- selectedTicketTypeId: response.pricing.lines.length === 1 ? response.pricing.lines[0]?.pricingOptionId ?? null : null,
603
+ selectedPricingOptionId: response.pricing.lines.length === 1 ? response.pricing.lines[0]?.pricingOptionId ?? null : null,
603
604
  ticketLines: response.pricing.lines.map((line) => ({
604
- ticketTypeId: line.pricingOptionId,
605
+ pricingOptionId: line.pricingOptionId,
605
606
  label: line.label,
606
607
  description: line.description,
607
608
  quantity: line.quantity,
@@ -609,7 +610,7 @@ function transformRegistrationOptionsResponse(response) {
609
610
  subtotalCents: line.subtotalCents,
610
611
  currency: response.pricing.currency
611
612
  })),
612
- ticketTypes: projectRegistrationPricingOptions(response.pricing.options),
613
+ ticketTypes: response.pricing.options,
613
614
  isFull,
614
615
  waitlistEnabled: response.availability.waitlistEnabled,
615
616
  canJoinWaitlist,
@@ -629,39 +630,6 @@ function transformRegistrationOptionsResponse(response) {
629
630
  function hasMonetaryPaymentOption(response) {
630
631
  return response.paymentOptions.some((option) => option.kind === "payInFull" || option.kind === "deferredManual" || option.kind === "instalmentPlan" || option.kind === "flexibleBalance");
631
632
  }
632
- function projectRegistrationPricingOptions(options) {
633
- return options.map((option) => ({
634
- id: option.pricingOptionId,
635
- name: option.label,
636
- description: option.description,
637
- priceAmount: option.unitPriceCents,
638
- isActive: option.availability.kind === "available" || option.availability.reason !== "inactive",
639
- sortOrder: option.sortOrder,
640
- capacityMode: option.availability.remainingPlaces === null ? "unlimited" : "limited",
641
- capacityLimit: option.capacityLimit,
642
- attendanceScope: option.attendanceScope,
643
- saleStartsAt: option.saleStartsAt,
644
- saleEndsAt: option.saleEndsAt,
645
- availability: option.availability.kind === "available" ? {
646
- kind: "available",
647
- remainingCapacity: option.availability.remainingPlaces
648
- } : {
649
- kind: "unavailable",
650
- reason: toLegacyAvailabilityReason(option.availability.reason),
651
- remainingCapacity: option.availability.remainingPlaces
652
- }
653
- }));
654
- }
655
- function toLegacyAvailabilityReason(reason) {
656
- switch (reason) {
657
- case "inactive": return "inactive";
658
- case "notOnSaleYet": return "not_on_sale_yet";
659
- case "saleEnded": return "sale_ended";
660
- case "soldOut":
661
- case "attendanceModeUnavailable": return "sold_out";
662
- case "overallCapacityUnavailable": return "event_sold_out";
663
- }
664
- }
665
633
  function getTicketTypeAvailabilityLabel(ticketType) {
666
634
  const availability = ticketType.availability;
667
635
  if (availability.kind === "available") return null;
@@ -670,24 +638,28 @@ function getTicketTypeAvailabilityLabel(ticketType) {
670
638
  case "not_on_sale_yet": return "Not on sale yet";
671
639
  case "sale_ended": return "Sales ended";
672
640
  case "sold_out":
673
- case "event_sold_out": return "Sold out";
641
+ case "attendance_mode_unavailable":
642
+ case "overallCapacityUnavailable": return "Sold out";
643
+ default: return "Unavailable";
674
644
  }
675
645
  }
676
646
  function getVisibleTicketTypes(input) {
677
- const eligibilityTicketTypes = input.eligibility?.ticketTypes;
678
- const eventTicketTypes = input.event ? publicEventTicketTypes(resolveRuntimeEventPricing(input.event)) : [];
679
- return (eligibilityTicketTypes && eligibilityTicketTypes.length > 0 ? eligibilityTicketTypes : eventTicketTypes).filter((ticketType) => ticketType.isActive && (!input.selectedAttendanceMode || (ticketType.attendanceScope ?? "both") === "both" || (ticketType.attendanceScope ?? "both") === input.selectedAttendanceMode));
647
+ const eventTicketTypes = input.event ? publicEventPricingOptions(input.event.pricing).map(({ id, ...option }) => ({
648
+ ...option,
649
+ pricingOptionId: id
650
+ })) : [];
651
+ return (input.eligibility?.ticketTypes ?? eventTicketTypes).filter((ticketType) => ticketType.isActive && (!input.selectedAttendanceMode || (ticketType.attendanceScope ?? "both") === "both" || (ticketType.attendanceScope ?? "both") === input.selectedAttendanceMode));
680
652
  }
681
653
  function getImplicitSingleAvailableTicketType(ticketTypes) {
682
654
  return ticketTypes.length === 1 && ticketTypes[0]?.availability.kind === "available" ? ticketTypes[0] : null;
683
655
  }
684
656
  function findSelectedTicketType(input) {
685
- if (!input.selectedTicketTypeId) return null;
657
+ if (!input.selectedPricingOptionId) return null;
686
658
  return getVisibleTicketTypes({
687
659
  event: input.event,
688
660
  eligibility: input.eligibility,
689
661
  selectedAttendanceMode: input.selectedAttendanceMode
690
- }).find((ticketType) => ticketType.id === input.selectedTicketTypeId) ?? null;
662
+ }).find((ticketType) => ticketType.pricingOptionId === input.selectedPricingOptionId) ?? null;
691
663
  }
692
664
  //#endregion
693
665
  //#region ../blocks/src/system/runtime/nodes/event-registration/EventSelectionStep.tsx
@@ -713,10 +685,12 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
713
685
  className: "er-event-grid",
714
686
  children: events.map((event) => {
715
687
  const isSelected = selectedEvent?.selectionId === event.selectionId;
716
- const isSoldOut = event.capacity !== null && event.availableSpots === 0;
717
- const canJoinWaitlist = isSoldOut && event.waitlistEnabled === true;
688
+ const remaining = getRemainingPlaces(selectMostOpenCapacitySource(event));
689
+ const isSoldOut = isEventFull(event);
690
+ const canJoinWaitlist = canJoinEventWaitlistOnAnyMode(event);
718
691
  const isUnavailable = isSoldOut && !canJoinWaitlist;
719
- const isLowCapacity = event.availableSpots !== null && event.availableSpots <= 5 && !isSoldOut;
692
+ const isLowCapacity = remaining.kind === "low";
693
+ const capacityLabel = formatEventRemainingPlaces(event, { full: "Sold out" });
720
694
  const timeZone = event.timeZone ?? null;
721
695
  const cardClasses = [
722
696
  "er-event-card",
@@ -748,9 +722,9 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
748
722
  children: " · "
749
723
  }), event.venue.name] })]
750
724
  })]
751
- }), showCapacity && event.capacity !== null && /* @__PURE__ */ jsx("span", {
725
+ }), showCapacity && capacityLabel && /* @__PURE__ */ jsx("span", {
752
726
  className: badgeClasses,
753
- children: canJoinWaitlist ? "Waitlist" : isUnavailable ? "Sold Out" : `${event.availableSpots ?? 0} spots left`
727
+ children: capacityLabel
754
728
  })]
755
729
  })
756
730
  }, event.selectionId);
@@ -759,6 +733,61 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
759
733
  });
760
734
  };
761
735
  //#endregion
736
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
737
+ function resolveRegistrationRecoveryAction(step, action) {
738
+ if (step === "payment-failed") return "payment-options";
739
+ if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
740
+ return action;
741
+ }
742
+ function canRecoverToWaitlist(eligibility) {
743
+ return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
744
+ }
745
+ function shouldRefreshAfterRegistrationError(step, action, enabled) {
746
+ return enabled || step === "error" && action === "places";
747
+ }
748
+ function resetRegistrationAfterExpiry(input) {
749
+ input.dispatchFlow({
750
+ type: "reset",
751
+ hasPreSelectedContext: input.hasPreSelectedContext
752
+ });
753
+ input.setSelectedTicketLines([]);
754
+ input.setSelectedSlidingAmountInput("");
755
+ const mode = input.preSelectedEvent?.deliveryMode;
756
+ input.setSelectedAttendanceMode(mode === "online" || mode === "in_person" ? mode : null);
757
+ input.setErrorMessage("");
758
+ input.setPendingRegistrationReference(null);
759
+ }
760
+ /** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
761
+ function recoverRegistrationError(action, handlers) {
762
+ switch (action) {
763
+ case "none": return;
764
+ case "portal":
765
+ case "payment-status":
766
+ window.location.assign("/portal");
767
+ return;
768
+ case "event":
769
+ window.location.assign(window.location.pathname);
770
+ return;
771
+ case "retry":
772
+ window.location.reload();
773
+ return;
774
+ case "details":
775
+ handlers.clearError();
776
+ handlers.setStep("enter-details");
777
+ return;
778
+ case "payment-options":
779
+ case "places":
780
+ handlers.refreshOptions();
781
+ handlers.clearError();
782
+ handlers.setStep(action === "places" ? handlers.canJoinWaitlist ? "enter-details" : "select-tickets" : "select-payment");
783
+ return;
784
+ case "start":
785
+ handlers.refreshOptions();
786
+ handlers.reset();
787
+ return;
788
+ }
789
+ }
790
+ //#endregion
762
791
  //#region ../blocks/src/system/runtime/nodes/event-registration/validation.ts
763
792
  function createInitialRegistrationFormData() {
764
793
  return {
@@ -784,30 +813,36 @@ function normalizeTicketSelectionLines(lines) {
784
813
  for (const line of lines) {
785
814
  const quantity = Math.floor(line.quantity);
786
815
  if (quantity <= 0) continue;
787
- const key = line.ticketTypeId ?? null;
816
+ const key = line.pricingOptionId ?? null;
788
817
  quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
789
818
  }
790
- return [...quantitiesByTicketType.entries()].map(([ticketTypeId, quantity]) => ({
791
- ticketTypeId,
819
+ return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
820
+ pricingOptionId,
792
821
  quantity
793
822
  }));
794
823
  }
795
824
  function getTicketSelectionTotal(lines) {
796
825
  return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
797
826
  }
798
- function getTicketLineQuantity(lines, ticketTypeId) {
799
- return normalizeTicketSelectionLines(lines).find((line) => line.ticketTypeId === ticketTypeId)?.quantity ?? 0;
827
+ function getTicketLineQuantity(lines, pricingOptionId) {
828
+ return normalizeTicketSelectionLines(lines).find((line) => line.pricingOptionId === pricingOptionId)?.quantity ?? 0;
800
829
  }
801
- function getSingleSelectedTicketTypeId(lines) {
830
+ function getSingleSelectedPricingOptionId(lines) {
802
831
  const normalized = normalizeTicketSelectionLines(lines);
803
- return normalized.length === 1 ? normalized[0]?.ticketTypeId ?? null : null;
832
+ return normalized.length === 1 ? normalized[0]?.pricingOptionId ?? null : null;
833
+ }
834
+ function hasValidPricingOptionSelection(input) {
835
+ if (!input.selectionRequired) return true;
836
+ if (input.lines.length === 0) return false;
837
+ const availableOptionIds = new Set(input.visibleOptions.filter((option) => option.availability.kind === "available").map((option) => option.pricingOptionId));
838
+ return input.lines.every((line) => line.pricingOptionId !== null && availableOptionIds.has(line.pricingOptionId));
804
839
  }
805
840
  function setTicketLineQuantity(input) {
806
841
  const nextQuantity = Math.max(0, Math.floor(input.quantity));
807
- const normalized = normalizeTicketSelectionLines(input.lines).filter((line) => line.ticketTypeId !== input.ticketTypeId);
842
+ const normalized = normalizeTicketSelectionLines(input.lines).filter((line) => line.pricingOptionId !== input.pricingOptionId);
808
843
  if (nextQuantity <= 0) return normalized;
809
844
  return [...normalized, {
810
- ticketTypeId: input.ticketTypeId,
845
+ pricingOptionId: input.pricingOptionId,
811
846
  quantity: nextQuantity
812
847
  }];
813
848
  }
@@ -818,25 +853,6 @@ function getTicketTypeLineMax(input) {
818
853
  const maxByTicketType = input.ticketTypeRemainingCapacity ?? input.maxTickets;
819
854
  return Math.max(0, Math.min(maxByBlock, maxByEvent, maxByTicketType));
820
855
  }
821
- function calculateTicketSelectionSubtotal(input) {
822
- return normalizeTicketSelectionLines(input.lines).reduce((subtotal, line) => {
823
- const unitPriceCents = (line.ticketTypeId ? input.ticketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null : null)?.priceAmount ?? input.fallbackUnitPriceCents;
824
- return subtotal + Math.max(0, unitPriceCents) * line.quantity;
825
- }, 0);
826
- }
827
- function buildTicketSelectionSummaryLines(input) {
828
- return normalizeTicketSelectionLines(input.lines).map((line) => {
829
- const ticketType = line.ticketTypeId ? input.ticketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null : null;
830
- const unitPriceCents = ticketType?.priceAmount ?? input.fallbackUnitPriceCents;
831
- return {
832
- ticketTypeId: line.ticketTypeId,
833
- label: ticketType?.name ?? input.fallbackLabel,
834
- quantity: line.quantity,
835
- unitPriceCents,
836
- subtotalCents: Math.max(0, unitPriceCents) * line.quantity
837
- };
838
- });
839
- }
840
856
  //#endregion
841
857
  //#region ../blocks/src/system/runtime/nodes/event-registration/hooks.state.ts
842
858
  const initialEligibilityLookupState = {
@@ -992,7 +1008,7 @@ function useEligibilityWithDeps(params, deps) {
992
1008
  const result = await api.getRegistrationOptions({
993
1009
  subject,
994
1010
  selections: ticketSelections.map((selection) => ({
995
- pricingOptionId: selection.ticketTypeId,
1011
+ pricingOptionId: selection.pricingOptionId,
996
1012
  quantity: selection.quantity
997
1013
  })),
998
1014
  selectedSlidingAmount: params.selectedSlidingAmount ?? void 0
@@ -1002,11 +1018,11 @@ function useEligibilityWithDeps(params, deps) {
1002
1018
  type: "resolved",
1003
1019
  eligibility: transformRegistrationOptionsResponse(result)
1004
1020
  });
1005
- } catch (err) {
1021
+ } catch {
1006
1022
  if (cancelled || requestIdRef.current !== requestId) return;
1007
1023
  dispatch({
1008
1024
  type: "failed",
1009
- message: err instanceof Error ? err.message : "Failed to check eligibility"
1025
+ message: "We couldn’t load the booking options for this event. Please refresh this page and try again. If this keeps happening, contact the organiser for help."
1010
1026
  });
1011
1027
  }
1012
1028
  }
@@ -1018,6 +1034,7 @@ function useEligibilityWithDeps(params, deps) {
1018
1034
  }, [
1019
1035
  api,
1020
1036
  params.enabled,
1037
+ params.refreshKey,
1021
1038
  params.subject?.kind,
1022
1039
  params.subject?.kind === "occurrence" ? params.subject.occurrenceId : params.subject?.eventSeriesId,
1023
1040
  params.attendanceMode,
@@ -1127,8 +1144,252 @@ function logoutPortal(anchorId) {
1127
1144
  form.submit();
1128
1145
  }
1129
1146
  //#endregion
1147
+ //#region ../commerce/src/purchase/checkoutPaymentFlow.ts
1148
+ function planCheckoutPaymentFlow(options) {
1149
+ const amountDueCents = toNonNegativeCents(options.amountDueCents);
1150
+ const discountCents = toNonNegativeCents(options.discountCents);
1151
+ const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
1152
+ const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
1153
+ if (amountDueCents <= 0) return {
1154
+ kind: "bypass",
1155
+ bypassReason: discountCents > 0 ? "discount_free" : "free",
1156
+ reserveCreditCents: 0
1157
+ };
1158
+ if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
1159
+ kind: "stripe",
1160
+ reserveCreditCents: toNonNegativeCents(0)
1161
+ };
1162
+ if (availableCreditCents >= amountDueCents) return {
1163
+ kind: "store_credit_only",
1164
+ reserveCreditCents: amountDueCents
1165
+ };
1166
+ return {
1167
+ kind: "stripe",
1168
+ reserveCreditCents: planMixedPaymentCreditReservation({
1169
+ amountDueCents,
1170
+ availableCreditCents,
1171
+ minimumStripeChargeCents
1172
+ })
1173
+ };
1174
+ }
1175
+ function planMixedPaymentCreditReservation(input) {
1176
+ const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
1177
+ if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
1178
+ if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
1179
+ return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
1180
+ }
1181
+ /**
1182
+ * Apply site credit to a flexible-balance booking before card payment.
1183
+ *
1184
+ * Credit first reduces the configured payment due now. Once that payment is
1185
+ * covered, any remaining credit reduces the later balance. If a partial credit
1186
+ * application would leave a positive card charge below Stripe's minimum, less
1187
+ * credit is used so the configured payment can still be collected.
1188
+ */
1189
+ function planFlexibleBalanceCreditFirst(input) {
1190
+ const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
1191
+ const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
1192
+ const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
1193
+ const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
1194
+ if (availableCreditCents >= totalCents && totalCents > 0) return {
1195
+ kind: "credit_only",
1196
+ reserveCreditCents: totalCents,
1197
+ cardDueNowCents: toNonNegativeCents(0),
1198
+ laterBalanceCents: toNonNegativeCents(0)
1199
+ };
1200
+ let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
1201
+ if (reserveCreditCents >= configuredDueNowCents) return {
1202
+ kind: "flexible_balance",
1203
+ reserveCreditCents,
1204
+ cardDueNowCents: toNonNegativeCents(0),
1205
+ laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
1206
+ };
1207
+ const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
1208
+ if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
1209
+ if (configuredDueNowCents < minimumStripeChargeCents) return {
1210
+ kind: "card_below_minimum",
1211
+ reserveCreditCents: toNonNegativeCents(0),
1212
+ cardDueNowCents: configuredDueNowCents,
1213
+ laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
1214
+ };
1215
+ reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
1216
+ }
1217
+ const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
1218
+ return {
1219
+ kind: "flexible_balance",
1220
+ reserveCreditCents,
1221
+ cardDueNowCents,
1222
+ laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
1223
+ };
1224
+ }
1225
+ //#endregion
1226
+ //#region ../commerce/src/purchase/settlement.ts
1227
+ function normalizeCurrencyCode(raw) {
1228
+ const normalized = raw.trim().toUpperCase();
1229
+ if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
1230
+ return normalized;
1231
+ }
1232
+ //#endregion
1233
+ //#region ../commerce/src/purchase/checkoutSessionMetadata.ts
1234
+ const datedOfferingSharedEngineMetadataShape = {
1235
+ datedOfferingEngineContractVersion: z.literal("shared_v1"),
1236
+ datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
1237
+ datedOfferingCapacityHoldId: z.string().uuid(),
1238
+ datedOfferingCapacityHoldAuditRef: z.string().uuid(),
1239
+ customerId: z.string().uuid(),
1240
+ eventSeriesId: z.string().uuid()
1241
+ };
1242
+ z.object(datedOfferingSharedEngineMetadataShape);
1243
+ const orderCheckoutSessionMetadataBaseShape = {
1244
+ type: z.literal("order"),
1245
+ site_id: z.string().uuid(),
1246
+ order_id: z.string().uuid().optional(),
1247
+ customer_email: z.string().email(),
1248
+ customer_name: z.string().optional(),
1249
+ product_id: z.string().uuid().optional(),
1250
+ attendee_id: z.string().uuid().optional(),
1251
+ occurrence_id: z.string().uuid().optional(),
1252
+ tickets_count: z.string().optional(),
1253
+ notes: z.string().optional(),
1254
+ discount_redemption_id: z.string().uuid().optional(),
1255
+ discount_code_id: z.string().uuid().optional(),
1256
+ discount_code: z.string().optional(),
1257
+ discount_public_label: z.string().optional(),
1258
+ credit_reservation_id: z.string().uuid().optional(),
1259
+ credit_reserved_cents: z.string().optional(),
1260
+ discount_code_amount_cents: z.string().optional(),
1261
+ order_payment_reason: z.enum([
1262
+ "instalment_payoff",
1263
+ "flexible_balance_initial",
1264
+ "flexible_balance_paydown"
1265
+ ]).optional(),
1266
+ order_payment_plan_id: z.string().uuid().optional(),
1267
+ order_flexible_balance_contract_id: z.string().uuid().optional()
1268
+ };
1269
+ const absentDatedOfferingSharedEngineMetadataShape = {
1270
+ datedOfferingEngineContractVersion: z.never().optional(),
1271
+ datedOfferingEngineRouteKind: z.never().optional(),
1272
+ datedOfferingCapacityHoldId: z.never().optional(),
1273
+ datedOfferingCapacityHoldAuditRef: z.never().optional(),
1274
+ customerId: z.never().optional(),
1275
+ eventSeriesId: z.never().optional()
1276
+ };
1277
+ const LegacyOrderCheckoutSessionMetadataSchema = z.object({
1278
+ ...orderCheckoutSessionMetadataBaseShape,
1279
+ ...absentDatedOfferingSharedEngineMetadataShape
1280
+ });
1281
+ const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
1282
+ ...orderCheckoutSessionMetadataBaseShape,
1283
+ ...datedOfferingSharedEngineMetadataShape
1284
+ });
1285
+ z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
1286
+ toNonNegativeCents(0);
1287
+ //#endregion
1288
+ //#region ../commerce/src/purchase/stripeMinimumCharge.ts
1289
+ const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
1290
+ GBP: 30,
1291
+ USD: 50,
1292
+ EUR: 50,
1293
+ AED: 200,
1294
+ AUD: 50,
1295
+ BGN: 100,
1296
+ BRL: 50,
1297
+ CAD: 50,
1298
+ CHF: 50,
1299
+ CZK: 1500,
1300
+ DKK: 250,
1301
+ HKD: 400,
1302
+ HUF: 17500,
1303
+ JPY: 50,
1304
+ MXN: 1e3,
1305
+ MYR: 200,
1306
+ NOK: 300,
1307
+ NZD: 50,
1308
+ PLN: 200,
1309
+ RON: 200,
1310
+ SEK: 300,
1311
+ SGD: 50,
1312
+ THB: 1e3
1313
+ };
1314
+ function isStripeMinimumChargeCurrency(currency) {
1315
+ return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
1316
+ }
1317
+ function getStripeMinimumChargeRequirement(rawCurrency) {
1318
+ let currency;
1319
+ try {
1320
+ currency = normalizeCurrencyCode(rawCurrency);
1321
+ } catch {
1322
+ return null;
1323
+ }
1324
+ const currencyKey = currency;
1325
+ if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
1326
+ return {
1327
+ currency,
1328
+ minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
1329
+ };
1330
+ }
1331
+ //#endregion
1332
+ //#region ../commerce/src/purchase/event-registration-errors.ts
1333
+ const eventRegistrationErrors = {
1334
+ registrationClosed: {
1335
+ message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
1336
+ action: "none"
1337
+ },
1338
+ paymentUncertain: {
1339
+ message: "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.",
1340
+ action: "payment-status"
1341
+ },
1342
+ paymentPageUnavailable: {
1343
+ message: "We couldn’t open the payment page. Please refresh this page and try again. If this keeps happening, contact the organiser for help.",
1344
+ action: "retry"
1345
+ },
1346
+ paymentUnavailable: {
1347
+ message: "Online payment is not available for this event right now. Please contact the organiser for help with booking.",
1348
+ action: "none"
1349
+ },
1350
+ capacityChanged: {
1351
+ message: "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.",
1352
+ action: "places"
1353
+ },
1354
+ selectionChanged: {
1355
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
1356
+ action: "places"
1357
+ },
1358
+ registrationPreparationChanged: {
1359
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
1360
+ action: "places"
1361
+ },
1362
+ registrationAlreadyExists: {
1363
+ message: "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser.",
1364
+ action: "portal"
1365
+ },
1366
+ holdExpired: {
1367
+ message: "Your reservation has timed out. Please check availability and choose your places again.",
1368
+ action: "start"
1369
+ },
1370
+ waitlistUnavailable: {
1371
+ message: "Sorry, the waiting list place offered in this invitation is no longer available. Please check the event for available places or contact the organiser for help.",
1372
+ action: "event"
1373
+ },
1374
+ paymentContextMismatch: {
1375
+ message: "The payment options for this event have changed. Please check the latest options before continuing.",
1376
+ action: "payment-options"
1377
+ },
1378
+ idempotencyConflict: {
1379
+ message: "You’ve already started booking this event. Please finish in the other tab if it’s still open, or check your booking status before starting again.",
1380
+ action: "portal"
1381
+ },
1382
+ invalidCommitInput: {
1383
+ message: "We couldn’t accept your booking details. Please check the information you entered and try again. If this keeps happening, contact the organiser for help.",
1384
+ action: "details"
1385
+ }
1386
+ };
1387
+ function parseEventRegistrationErrorReason(value) {
1388
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(eventRegistrationErrors, value) ? value : null;
1389
+ }
1390
+ //#endregion
1130
1391
  //#region ../blocks/src/system/runtime/shared/useWaitlistClaim.state.ts
1131
- const DEFAULT_WAITLIST_CLAIM_ERROR = "Failed to resolve waitlist claim";
1392
+ const DEFAULT_WAITLIST_CLAIM_ERROR = "Online booking is unavailable right now. Please contact the organiser for help with booking.";
1132
1393
  function createInitialWaitlistClaimState(hasToken) {
1133
1394
  return hasToken ? { kind: "resolving" } : { kind: "idle" };
1134
1395
  }
@@ -1144,14 +1405,13 @@ function waitlistClaimReducer(_state, action) {
1144
1405
  }
1145
1406
  }
1146
1407
  function toWaitlistClaimResolveFailure(error) {
1147
- const message = error instanceof Error ? error.message : DEFAULT_WAITLIST_CLAIM_ERROR;
1148
1408
  if (isUnauthorizedBlockApiError(error)) return {
1149
1409
  kind: "portal-login-required",
1150
- message
1410
+ message: "Please log in to continue with your waiting list invitation."
1151
1411
  };
1152
1412
  return {
1153
1413
  kind: "error",
1154
- message
1414
+ message: hasStatus(error) && (error.status === 404 || error.status === 409 || error.status === 410) ? eventRegistrationErrors.waitlistUnavailable.message : DEFAULT_WAITLIST_CLAIM_ERROR
1155
1415
  };
1156
1416
  }
1157
1417
  function isUnauthorizedBlockApiError(error) {
@@ -1512,11 +1772,21 @@ function cleanupStaleRegistrations() {
1512
1772
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.ts
1513
1773
  const eventRegistrationStripeReturnDeps = {
1514
1774
  usePaymentVerification,
1515
- consumeReturnParams: () => consumeSearchParams([
1516
- "registration",
1517
- EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
1518
- "session_id"
1519
- ]),
1775
+ readReturnParams: () => {
1776
+ const params = new URLSearchParams(window.location.search);
1777
+ return {
1778
+ registration: params.get("registration"),
1779
+ registration_attempt: params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM),
1780
+ session_id: params.get("session_id")
1781
+ };
1782
+ },
1783
+ clearReturnParams: () => {
1784
+ consumeSearchParams([
1785
+ "registration",
1786
+ EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
1787
+ "session_id"
1788
+ ]);
1789
+ },
1520
1790
  scrollToBlock,
1521
1791
  cleanupStaleRegistrations,
1522
1792
  clearPendingRegistration,
@@ -1537,52 +1807,66 @@ function showReturnReconciliation(options, message) {
1537
1807
  options.setErrorMessage(message);
1538
1808
  options.setStep("verifying-timeout");
1539
1809
  }
1810
+ function checkoutReturnCommand(params) {
1811
+ const checkoutAttemptId = params.registration_attempt;
1812
+ if (!checkoutAttemptId) return null;
1813
+ if (params.registration === "cancelled") return {
1814
+ kind: "abandon",
1815
+ checkoutAttemptId
1816
+ };
1817
+ if (params.registration === "success" && params.session_id) return {
1818
+ kind: "resolveSuccess",
1819
+ checkoutAttemptId,
1820
+ sessionId: params.session_id
1821
+ };
1822
+ return null;
1823
+ }
1540
1824
  async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
1541
1825
  deps.cleanupStaleRegistrations();
1542
- const params = deps.consumeReturnParams();
1543
- if (params.registration !== "cancelled" && params.registration !== "success") return;
1826
+ const params = deps.readReturnParams();
1827
+ if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
1544
1828
  deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
1545
1829
  const siteId = options.api.config.siteId;
1546
- const checkoutAttemptId = params.registration_attempt;
1547
- if (!siteId) return;
1548
- if (!checkoutAttemptId) {
1549
- showReturnReconciliation(options, "We could not identify this payment attempt. Please contact us before trying again.");
1550
- return;
1830
+ const command = checkoutReturnCommand(params);
1831
+ if (!siteId) return "settled";
1832
+ if (!command) {
1833
+ showReturnReconciliation(options, "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.");
1834
+ return "settled";
1551
1835
  }
1836
+ const { checkoutAttemptId } = command;
1552
1837
  const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
1553
1838
  checkoutAttemptIdRef.current = checkoutAttemptId;
1554
1839
  if (pending) restorePendingPresentation(options, pending);
1840
+ options.setStep("verifying-payment");
1555
1841
  try {
1556
- const outcome = params.registration === "success" ? params.session_id ? await options.api.resolveEventCheckoutReturn({
1557
- kind: "resolveSuccess",
1558
- checkoutAttemptId,
1559
- sessionId: params.session_id
1560
- }) : { kind: "notFound" } : await options.api.resolveEventCheckoutReturn({
1561
- kind: "abandon",
1562
- checkoutAttemptId
1563
- });
1842
+ const outcome = await options.api.resolveEventCheckoutReturn(command);
1564
1843
  switch (outcome.kind) {
1565
1844
  case "resolved":
1845
+ deps.clearReturnParams();
1566
1846
  options.setPendingRegistrationReference(outcome.reference);
1567
1847
  options.setStep("verifying-payment");
1568
- return;
1848
+ return "settled";
1569
1849
  case "abandoned":
1850
+ deps.clearReturnParams();
1570
1851
  options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1571
1852
  options.setStep("select-payment");
1572
- return;
1853
+ return "settled";
1573
1854
  case "notFound":
1574
1855
  if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
1856
+ deps.clearReturnParams();
1575
1857
  options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1576
1858
  options.setStep("select-payment");
1577
- return;
1859
+ return "settled";
1578
1860
  }
1579
- break;
1861
+ showReturnReconciliation(options, "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.");
1862
+ return "settled";
1580
1863
  case "reconciliationRequired": break;
1581
1864
  }
1582
- showReturnReconciliation(options, "We’re checking your payment. Please wait a moment before trying again.");
1865
+ showReturnReconciliation(options, "We’re checking your booking and payment. Please don’t try to book again yet. If you’ve already paid, you don’t need to pay again.");
1583
1866
  } catch {
1584
- showReturnReconciliation(options, "We could not check your payment yet. Please try again shortly.");
1867
+ showReturnReconciliation(options, "We couldn’t check your payment just now. Please check again in a moment. Don’t make another booking while we’re checking.");
1585
1868
  }
1869
+ return "retryable";
1586
1870
  }
1587
1871
  function useEventRegistrationStripeReturn(options) {
1588
1872
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
@@ -1590,10 +1874,34 @@ function useEventRegistrationStripeReturn(options) {
1590
1874
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
1591
1875
  const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
1592
1876
  const checkoutAttemptIdRef = useRef(null);
1877
+ const lookupInFlightRef = useRef(false);
1878
+ const [lookupState, setLookupState] = useState("settled");
1879
+ const resolveReturn = useCallback(async () => {
1880
+ if (lookupInFlightRef.current) return;
1881
+ lookupInFlightRef.current = true;
1882
+ setLookupState("loading");
1883
+ try {
1884
+ setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
1885
+ } finally {
1886
+ lookupInFlightRef.current = false;
1887
+ }
1888
+ }, [options, deps]);
1593
1889
  const verificationConfig = useMemo(() => step === "verifying-payment" && pendingRegistrationReference ? { reference: pendingRegistrationReference } : null, [step, pendingRegistrationReference]);
1594
1890
  const verification = deps.usePaymentVerification(verificationConfig);
1891
+ const hasVerificationTimeout = step === "verifying-timeout" && pendingRegistrationReference !== null;
1892
+ const retry = useCallback(() => {
1893
+ if (hasVerificationTimeout) {
1894
+ setStep("verifying-payment");
1895
+ return;
1896
+ }
1897
+ verification.retry();
1898
+ }, [
1899
+ hasVerificationTimeout,
1900
+ setStep,
1901
+ verification.retry
1902
+ ]);
1595
1903
  useEffect(() => {
1596
- handleCheckoutReturn(options, deps, checkoutAttemptIdRef);
1904
+ resolveReturn();
1597
1905
  }, []);
1598
1906
  useEffect(() => {
1599
1907
  if (verification.status === "completed") {
@@ -1603,7 +1911,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
1603
1911
  setRegistrationResult({ status: "confirmed" });
1604
1912
  setStep("success");
1605
1913
  } else if (verification.status === "failed") {
1606
- setErrorMessage(verification.error ?? "Payment failed. Please try again.");
1914
+ setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
1607
1915
  setStep("payment-failed");
1608
1916
  } else if (verification.status === "timeout") setStep("verifying-timeout");
1609
1917
  }, [
@@ -1616,11 +1924,26 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
1616
1924
  setRegistrationResult,
1617
1925
  setStep
1618
1926
  ]);
1619
- return verification;
1927
+ return {
1928
+ ...verification,
1929
+ status: hasVerificationTimeout ? "timeout" : verification.status,
1930
+ retry,
1931
+ retryReturn: lookupState === "retryable" ? () => {
1932
+ resolveReturn();
1933
+ } : void 0
1934
+ };
1935
+ }
1936
+ //#endregion
1937
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.ts
1938
+ function registrationErrorPresentation(error) {
1939
+ const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
1940
+ if (reason) return eventRegistrationErrors[reason];
1941
+ if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
1942
+ return eventRegistrationErrors.paymentUncertain;
1620
1943
  }
1621
1944
  //#endregion
1622
1945
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
1623
- function createCheckoutAttemptId(cryptoSource = {
1946
+ function createClientCommandId(cryptoSource = {
1624
1947
  randomUUID: globalThis.crypto.randomUUID?.bind(globalThis.crypto),
1625
1948
  getRandomValues: (bytes) => globalThis.crypto.getRandomValues(bytes)
1626
1949
  }) {
@@ -1633,7 +1956,7 @@ function createCheckoutAttemptId(cryptoSource = {
1633
1956
  }
1634
1957
  const eventRegistrationSubmitDeps = {
1635
1958
  savePendingRegistration,
1636
- createCheckoutAttemptId
1959
+ createClientCommandId
1637
1960
  };
1638
1961
  function useEventRegistrationSubmit(options) {
1639
1962
  return useEventRegistrationSubmitWithDeps(options, eventRegistrationSubmitDeps);
@@ -1666,6 +1989,23 @@ function toRegistrationResult(result) {
1666
1989
  };
1667
1990
  }
1668
1991
  function useEventRegistrationSubmitWithDeps(options, deps) {
1992
+ /**
1993
+ * The command identity for the submission currently in progress.
1994
+ *
1995
+ * Held across attempts rather than minted per click, so a customer who
1996
+ * re-submits after a timeout replays their first attempt instead of asking
1997
+ * for a second booking. It is released only once an attempt has actually
1998
+ * landed — a success, or a hand-off to checkout — because until then nobody
1999
+ * knows whether the first one committed.
2000
+ *
2001
+ * Reusing it after a failure is safe even if the customer edits their
2002
+ * details. The server consults the command record before it re-checks
2003
+ * anything, so a submission that already booked is answered with the booking
2004
+ * that exists — not refused for spending a credit or a claim that this very
2005
+ * command already spent, and not treated as a second booking because a name
2006
+ * or a note changed in between.
2007
+ */
2008
+ const commandIdRef = useRef(null);
1669
2009
  return async (method, passId, membershipId, selection, submitOptions) => {
1670
2010
  const target = options.registrationTarget;
1671
2011
  if (!target) return {
@@ -1674,15 +2014,21 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1674
2014
  message: "Select an event to continue."
1675
2015
  };
1676
2016
  options.setStep("submitting");
2017
+ const registrationCommandId = commandIdRef.current ?? deps.createClientCommandId();
2018
+ commandIdRef.current = registrationCommandId;
1677
2019
  try {
1678
- const prepared = prepareSubmitContext(options, target, method, passId, membershipId, selection);
1679
- if (prepared.subject.kind === "wholeSeries" && method === "stripe") return await submitWholeSeriesCheckout(options, deps, {
1680
- ...prepared,
1681
- subject: prepared.subject
1682
- }, submitOptions);
2020
+ const prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
2021
+ if (prepared.subject.kind === "wholeSeries" && method === "stripe") {
2022
+ commandIdRef.current = null;
2023
+ return await submitWholeSeriesCheckout(options, deps, {
2024
+ ...prepared,
2025
+ subject: prepared.subject
2026
+ }, submitOptions);
2027
+ }
1683
2028
  const result = await options.api.registerForEvent({
2029
+ registrationCommandId: prepared.registrationCommandId,
1684
2030
  subject: prepared.subject,
1685
- ticketSelections: prepared.ticketSelections,
2031
+ ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
1686
2032
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
1687
2033
  email: options.formData.email.trim().toLowerCase(),
1688
2034
  name: options.formData.name.trim(),
@@ -1697,6 +2043,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1697
2043
  claimToken: options.claimToken ?? void 0,
1698
2044
  guests: prepared.guestsPayload
1699
2045
  });
2046
+ commandIdRef.current = null;
1700
2047
  if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
1701
2048
  options.setRegistrationResult(toRegistrationResult(result));
1702
2049
  options.setStep("success");
@@ -1705,7 +2052,8 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1705
2052
  outcome: { kind: "completed" }
1706
2053
  };
1707
2054
  } catch (error) {
1708
- const message = error instanceof Error ? error.message : "Registration failed";
2055
+ const { message, action } = registrationErrorPresentation(error);
2056
+ options.setErrorAction?.(action);
1709
2057
  options.setErrorMessage(message);
1710
2058
  options.setStep("error");
1711
2059
  return {
@@ -1716,11 +2064,22 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1716
2064
  }
1717
2065
  };
1718
2066
  }
1719
- function prepareSubmitContext(options, target, method, passId, membershipId, selection) {
2067
+ function toTicketSelectionRequests(lines) {
2068
+ return lines.map((line) => ({
2069
+ pricingOptionId: line.pricingOptionId,
2070
+ quantity: line.quantity
2071
+ }));
2072
+ }
2073
+ /**
2074
+ * Freeze everything this submission is about to send, including the command
2075
+ * identity the server keys the commit on.
2076
+ */
2077
+ function prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection) {
1720
2078
  const ticketSelections = normalizeTicketSelectionLines(options.ticketSelections);
1721
2079
  if (ticketSelections.length === 0) throw new Error("Choose at least one ticket to continue.");
1722
2080
  const bundleProductId = method === "pass_bundle" || method === "membership_bundle" ? passId || membershipId : void 0;
1723
2081
  return {
2082
+ registrationCommandId,
1724
2083
  ticketSelections,
1725
2084
  bundleProductId,
1726
2085
  selectionSnapshot: resolveSelectionSnapshot(method, passId, membershipId, bundleProductId, selection),
@@ -1764,11 +2123,11 @@ function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId,
1764
2123
  async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions) {
1765
2124
  options.setStep("redirecting");
1766
2125
  if (prepared.selectionSnapshot.method !== "stripe") throw new Error("Choose an available payment option.");
1767
- const checkoutAttemptId = deps.createCheckoutAttemptId();
2126
+ const checkoutAttemptId = deps.createClientCommandId();
1768
2127
  const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
1769
2128
  const result = await options.api.createEventCheckout({
1770
2129
  subject: prepared.subject,
1771
- ticketSelections: prepared.ticketSelections,
2130
+ ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
1772
2131
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
1773
2132
  email: options.formData.email.trim().toLowerCase(),
1774
2133
  name: options.formData.name.trim(),
@@ -1819,7 +2178,7 @@ async function submitOccurrenceCheckout(options, deps, prepared, registration, m
1819
2178
  };
1820
2179
  options.setPendingRegistrationReference(reference);
1821
2180
  options.setStep("redirecting");
1822
- const checkoutAttemptId = deps.createCheckoutAttemptId();
2181
+ const checkoutAttemptId = deps.createClientCommandId();
1823
2182
  const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
1824
2183
  const result = await options.api.createEventCheckout({
1825
2184
  subject: {
@@ -1870,6 +2229,13 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
1870
2229
  }
1871
2230
  //#endregion
1872
2231
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.ts
2232
+ function getEventSelectionDefaults(event) {
2233
+ const suggestedAmount = getSlidingScaleForEvent(event)?.suggestedAmount;
2234
+ return {
2235
+ slidingAmountInput: suggestedAmount == null ? "" : formatCentsAsCurrencyInput(suggestedAmount),
2236
+ attendanceMode: event.deliveryMode === "hybrid" ? null : event.deliveryMode ?? "in_person"
2237
+ };
2238
+ }
1873
2239
  function parseCurrencyInputToCents(value) {
1874
2240
  const trimmed = value.trim();
1875
2241
  if (!trimmed) return null;
@@ -1881,11 +2247,11 @@ function formatCentsAsCurrencyInput(amount) {
1881
2247
  return Number.isInteger(major) ? String(major) : major.toFixed(2);
1882
2248
  }
1883
2249
  function getSlidingScaleForEvent(event) {
1884
- const pricing = event ? resolveRuntimeEventPricing(event) : null;
2250
+ const pricing = event?.pricing ?? null;
1885
2251
  return pricing?.kind === "sliding_scale" ? {
1886
- minAmount: pricing.minAmount,
1887
- maxAmount: pricing.maxAmount,
1888
- suggestedAmount: pricing.suggestedAmount
2252
+ minAmount: pricing.minimum,
2253
+ maxAmount: pricing.maximum,
2254
+ suggestedAmount: pricing.suggested
1889
2255
  } : null;
1890
2256
  }
1891
2257
  function getSlidingScaleSelectionMessage(input) {
@@ -1917,14 +2283,14 @@ function resolveWaitlistClaimRestoration(input) {
1917
2283
  const event = findEventByOccurrenceId(input.scopedEvents, subject.occurrenceId);
1918
2284
  if (!event && input.occurrenceContextId !== subject.occurrenceId) return {
1919
2285
  ok: false,
1920
- message: "This waitlist claim is for a different event."
2286
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
1921
2287
  };
1922
2288
  return {
1923
2289
  ok: true,
1924
2290
  event,
1925
2291
  attendanceMode: subject.attendanceMode,
1926
2292
  ticketSelections: [{
1927
- ticketTypeId: null,
2293
+ pricingOptionId: null,
1928
2294
  quantity: 1
1929
2295
  }],
1930
2296
  ticketsCount: 1,
@@ -1934,7 +2300,7 @@ function resolveWaitlistClaimRestoration(input) {
1934
2300
  const event = input.scopedEvents.find((candidate) => candidate.registrationSubject.kind === "wholeSeries" && candidate.registrationSubject.eventSeriesId === subject.eventSeriesId);
1935
2301
  if (!event) return {
1936
2302
  ok: false,
1937
- message: "This waitlist claim is for a different event run."
2303
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
1938
2304
  };
1939
2305
  return {
1940
2306
  ok: true,
@@ -1948,7 +2314,7 @@ function resolveWaitlistClaimRestoration(input) {
1948
2314
  function resolveClaimedTicketSelections(claim) {
1949
2315
  if (!claim) return null;
1950
2316
  return claim.subject.kind === "whole_series" ? claim.subject.ticketSelections : [{
1951
- ticketTypeId: null,
2317
+ pricingOptionId: null,
1952
2318
  quantity: 1
1953
2319
  }];
1954
2320
  }
@@ -1967,6 +2333,7 @@ function useWaitlistClaimRestoration(input) {
1967
2333
  occurrenceContextId: input.occurrenceContextId
1968
2334
  });
1969
2335
  if (!restoration.ok) {
2336
+ input.setters.setErrorAction?.("none");
1970
2337
  input.setters.setErrorMessage(restoration.message);
1971
2338
  input.setters.setStep("error");
1972
2339
  return;
@@ -1996,7 +2363,31 @@ function useWaitlistClaimRestoration(input) {
1996
2363
  ]);
1997
2364
  }
1998
2365
  //#endregion
2366
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationCompletion.ts
2367
+ function resolveRegistrationAvailabilityBlocker(input) {
2368
+ if (input.hasWaitlistClaim) return null;
2369
+ if (input.eligibilityError) return input.eligibilityError;
2370
+ if (!input.eligibility) return "Registration is not available for this event.";
2371
+ if (input.eligibility.availability.kind === "unavailable" && input.eligibility.availability.reason === "paymentUnavailable") return "Payment options are not available for this event.";
2372
+ if (input.eligibility.availability.kind === "unavailable" || !input.eligibility.eligible) return "Registration is not available for this event.";
2373
+ return null;
2374
+ }
2375
+ function resolvePricingSelectionBlocker(input) {
2376
+ if (input.selectionRequired && !input.hasValidSelection) return "Choose at least one available ticket to continue.";
2377
+ return input.slidingScaleSelectionMessage;
2378
+ }
2379
+ function isSoldOutWithoutWaitlist(input) {
2380
+ return Boolean(!input.hasWaitlistClaim && input.eligibility?.isFull && !input.eligibility.canJoinWaitlist);
2381
+ }
2382
+ //#endregion
1999
2383
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
2384
+ function resolveDiscountSubtotalCents(context, subtotalCents) {
2385
+ if (context?.type !== "event" || subtotalCents === null) return void 0;
2386
+ return subtotalCents;
2387
+ }
2388
+ function usesTieredPricing(event) {
2389
+ return event !== null && event !== void 0 ? publicEventPricingStrategy(event.pricing) === "tiered" : false;
2390
+ }
2000
2391
  const eventRegistrationWizardDeps = {
2001
2392
  usePortalAuth,
2002
2393
  useEligibility,
@@ -2011,7 +2402,7 @@ function useEventRegistrationWizard(options) {
2011
2402
  }
2012
2403
  function useEventRegistrationWizardWithDeps(options, deps) {
2013
2404
  const api = useBlockApi();
2014
- const registrationEvents = useMemo(() => [...toRegistrationEvents(options.events ?? []), ...toWholeSeriesRegistrationEvents(options.wholeSeriesOfferings ?? [])], [options.events, options.wholeSeriesOfferings]);
2405
+ const registrationEvents = useMemo(() => [...toRegistrationEvents(options.events ?? []), ...toWholeSeriesRegistrationEvents(options.events ?? [])], [options.events]);
2015
2406
  const scopedEvents = useMemo(() => {
2016
2407
  return resolveEventRegistrationScopedEvents(registrationEvents, {
2017
2408
  occurrenceContext: options.occurrenceContext,
@@ -2021,7 +2412,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2021
2412
  registrationEvents,
2022
2413
  options.occurrenceContext?.seriesId,
2023
2414
  options.subject?.kind,
2024
- options.subject?.kind === "eventSeries" ? options.subject.eventSeriesId : options.subject?.kind === "eventOffering" ? `${options.subject.eventOfferingId}:${options.subject.availableEventSeriesIds.join(",")}` : options.subject?.reason
2415
+ options.subject?.kind === "eventSeries" ? options.subject.eventSeriesId : options.subject?.kind === "eventOffering" ? `${options.subject.eventOfferingId}:${options.subject.catalogueEventSeriesIds.join(",")}` : options.subject?.reason
2025
2416
  ]);
2026
2417
  const preSelectedEvent = useMemo(() => findPreSelectedEvent({
2027
2418
  scopedEvents,
@@ -2045,6 +2436,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2045
2436
  });
2046
2437
  const [registrationResult, setRegistrationResult] = useState(null);
2047
2438
  const [errorMessage, setErrorMessage] = useState("");
2439
+ const [errorAction, setErrorAction] = useState("retry");
2440
+ const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
2048
2441
  const [altchaSolution, setAltchaSolution] = useState(null);
2049
2442
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
2050
2443
  const waitlistClaim = deps.useWaitlistClaim();
@@ -2072,12 +2465,9 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2072
2465
  }), [options.occurrenceContext, selectedEvent]);
2073
2466
  const registrationOccurrenceId = getRegistrationOccurrenceId(registrationTarget);
2074
2467
  const currentEvent = selectedEvent ?? preSelectedEvent;
2075
- const currentEventPricing = currentEvent ? resolveRuntimeEventPricing(currentEvent) : null;
2076
- const currentEventUsesTicketTypes = currentEventPricing !== null && publicEventPricingMode(currentEventPricing) === "ticket_types";
2077
- const selectedModeAvailability = selectedAttendanceMode ? (currentEvent?.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
2078
- const currentAvailableSpots = selectedModeAvailability?.availableSpots ?? currentEvent?.availableSpots;
2079
- const currentWaitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? currentEvent?.waitlistEnabled ?? false;
2080
- const usesTicketTypeSelections = currentEventUsesTicketTypes && !(currentAvailableSpots === 0 && currentWaitlistEnabled === true);
2468
+ const currentEventUsesTicketTypes = usesTieredPricing(currentEvent);
2469
+ const currentCanJoinWaitlist = currentEvent ? canJoinEventWaitlist(currentEvent, { attendanceMode: selectedAttendanceMode }) : false;
2470
+ const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
2081
2471
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
2082
2472
  event: currentEvent,
2083
2473
  eligibility: null,
@@ -2091,7 +2481,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2091
2481
  if (claimedTicketSelections) return [...claimedTicketSelections];
2092
2482
  if (usesTicketTypeSelections) return normalizedSelectedTicketLines;
2093
2483
  return [{
2094
- ticketTypeId: null,
2484
+ pricingOptionId: null,
2095
2485
  quantity: ticketsCount
2096
2486
  }];
2097
2487
  }, [
@@ -2108,7 +2498,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2108
2498
  });
2109
2499
  }, [flowState.ticketsCount, ticketsCount]);
2110
2500
  const eligibilityResult = deps.useEligibility({
2111
- enabled: shouldLoadRegistrationOptions(portalAuthState, step),
2501
+ refreshKey: optionsRefreshKey,
2502
+ enabled: shouldRefreshAfterRegistrationError(step, errorAction, shouldLoadRegistrationOptions(portalAuthState, step)),
2112
2503
  subject: registrationTarget?.subject ?? null,
2113
2504
  attendanceMode: selectedAttendanceMode,
2114
2505
  ticketSelections,
@@ -2137,6 +2528,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2137
2528
  });
2138
2529
  }, [waitlistClaim.claim]);
2139
2530
  const waitlistRestorationSetters = useMemo(() => ({
2531
+ setErrorAction,
2140
2532
  setErrorMessage,
2141
2533
  setStep,
2142
2534
  setSelectedEvent,
@@ -2160,6 +2552,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2160
2552
  if (waitlistClaim.requiresPortalLogin) return;
2161
2553
  if (!waitlistClaim.error) return;
2162
2554
  setErrorMessage(waitlistClaim.error);
2555
+ setErrorAction("event");
2163
2556
  setStep("error");
2164
2557
  }, [
2165
2558
  waitlistClaim.error,
@@ -2240,17 +2633,17 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2240
2633
  selectedAttendanceMode,
2241
2634
  eligibility
2242
2635
  ]);
2243
- const singleSelectedTicketTypeId = getSingleSelectedTicketTypeId(normalizedSelectedTicketLines);
2636
+ const singleSelectedPricingOptionId = getSingleSelectedPricingOptionId(normalizedSelectedTicketLines);
2244
2637
  const selectedTicketType = useMemo(() => findSelectedTicketType({
2245
2638
  event: currentEvent,
2246
2639
  eligibility,
2247
- selectedTicketTypeId: singleSelectedTicketTypeId,
2640
+ selectedPricingOptionId: singleSelectedPricingOptionId,
2248
2641
  selectedAttendanceMode
2249
2642
  }), [
2250
2643
  currentEvent,
2251
2644
  eligibility,
2252
2645
  selectedAttendanceMode,
2253
- singleSelectedTicketTypeId
2646
+ singleSelectedPricingOptionId
2254
2647
  ]);
2255
2648
  const selectedSlidingAmount = useMemo(() => parseCurrencyInputToCents(selectedSlidingAmountInput), [selectedSlidingAmountInput]);
2256
2649
  const selectedEventSlidingScale = useMemo(() => getSlidingScaleForEvent(selectedEvent ?? preSelectedEvent), [selectedEvent, preSelectedEvent]);
@@ -2260,14 +2653,14 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2260
2653
  });
2261
2654
  const ticketTypeSelectionRequired = useMemo(() => {
2262
2655
  const event = currentEvent;
2263
- if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") return false;
2656
+ if (!usesTieredPricing(event)) return false;
2264
2657
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
2265
- const selectedModeAvailability = selectedAttendanceMode ? (event.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
2266
- return !((selectedModeAvailability?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability?.waitlistEnabled ?? event.waitlistEnabled) === true);
2658
+ return !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
2267
2659
  }, [currentEvent, selectedAttendanceMode]);
2268
- const hasValidTicketSelection = !ticketTypeSelectionRequired || normalizedSelectedTicketLines.length > 0 && normalizedSelectedTicketLines.every((line) => {
2269
- if (!line.ticketTypeId) return false;
2270
- return (visibleTicketTypes.find((candidate) => candidate.id === line.ticketTypeId) ?? null)?.availability.kind === "available";
2660
+ const hasValidTicketSelection = hasValidPricingOptionSelection({
2661
+ selectionRequired: ticketTypeSelectionRequired,
2662
+ lines: normalizedSelectedTicketLines,
2663
+ visibleOptions: visibleTicketTypes
2271
2664
  });
2272
2665
  useEffect(() => {
2273
2666
  if (!usesTicketTypeSelections) {
@@ -2278,12 +2671,12 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2278
2671
  const implicitTicketType = getImplicitSingleAvailableTicketType(eventTicketTypesForSelection);
2279
2672
  if (implicitTicketType && normalizedSelectedTicketLines.length === 0) {
2280
2673
  setSelectedTicketLines([{
2281
- ticketTypeId: implicitTicketType.id,
2674
+ pricingOptionId: implicitTicketType.pricingOptionId,
2282
2675
  quantity: 1
2283
2676
  }]);
2284
2677
  return;
2285
2678
  }
2286
- const filteredLines = normalizedSelectedTicketLines.filter((line) => line.ticketTypeId && availableTicketTypes.some((ticketType) => ticketType.id === line.ticketTypeId));
2679
+ const filteredLines = normalizedSelectedTicketLines.filter((line) => line.pricingOptionId && availableTicketTypes.some((ticketType) => ticketType.pricingOptionId === line.pricingOptionId));
2287
2680
  if (filteredLines.length !== normalizedSelectedTicketLines.length) setSelectedTicketLines(filteredLines);
2288
2681
  }, [
2289
2682
  eventTicketTypesForSelection,
@@ -2343,8 +2736,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2343
2736
  selectedSlidingAmount
2344
2737
  ]);
2345
2738
  const handleDetailsComplete = async () => {
2346
- if (ticketTypeSelectionRequired && !hasValidTicketSelection) {
2347
- const message = "Choose at least one available ticket to continue.";
2739
+ const blockDetailsCompletion = (message) => {
2740
+ setErrorAction("places");
2348
2741
  setErrorMessage(message);
2349
2742
  setStep("error");
2350
2743
  return {
@@ -2352,16 +2745,19 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2352
2745
  reason: "blocked",
2353
2746
  message
2354
2747
  };
2355
- }
2356
- if (slidingScaleSelectionMessage) {
2357
- setErrorMessage(slidingScaleSelectionMessage);
2358
- setStep("error");
2359
- return {
2360
- ok: false,
2361
- reason: "blocked",
2362
- message: slidingScaleSelectionMessage
2363
- };
2364
- }
2748
+ };
2749
+ const availabilityBlocker = resolveRegistrationAvailabilityBlocker({
2750
+ hasWaitlistClaim: waitlistClaim.claim !== null,
2751
+ eligibility,
2752
+ eligibilityError
2753
+ });
2754
+ if (availabilityBlocker) return blockDetailsCompletion(availabilityBlocker);
2755
+ const pricingSelectionBlocker = resolvePricingSelectionBlocker({
2756
+ selectionRequired: ticketTypeSelectionRequired,
2757
+ hasValidSelection: hasValidTicketSelection,
2758
+ slidingScaleSelectionMessage
2759
+ });
2760
+ if (pricingSelectionBlocker) return blockDetailsCompletion(pricingSelectionBlocker);
2365
2761
  if (!validateForm()) {
2366
2762
  setGuestSectionShake(true);
2367
2763
  return {
@@ -2370,16 +2766,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2370
2766
  message: "Check the highlighted fields and try again."
2371
2767
  };
2372
2768
  }
2373
- if (waitlistClaim.claim === null && eligibility?.isFull && !eligibility.canJoinWaitlist) {
2374
- const message = "This event is sold out.";
2375
- setErrorMessage(message);
2376
- setStep("error");
2377
- return {
2378
- ok: false,
2379
- reason: "blocked",
2380
- message
2381
- };
2382
- }
2769
+ if (isSoldOutWithoutWaitlist({
2770
+ hasWaitlistClaim: waitlistClaim.claim !== null,
2771
+ eligibility
2772
+ })) return blockDetailsCompletion("Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.");
2383
2773
  if (!eligibility?.canJoinWaitlist && requiresPaymentStep({
2384
2774
  eventRequiresPayment,
2385
2775
  paymentSelection
@@ -2395,15 +2785,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2395
2785
  eventRequiresPayment,
2396
2786
  canJoinWaitlist: eligibility?.canJoinWaitlist
2397
2787
  });
2398
- if (!submitPlan.ok) {
2399
- setErrorMessage(submitPlan.message);
2400
- setStep("error");
2401
- return {
2402
- ok: false,
2403
- reason: "blocked",
2404
- message: submitPlan.message
2405
- };
2406
- }
2788
+ if (!submitPlan.ok) return blockDetailsCompletion(submitPlan.message);
2407
2789
  const submitResult = await handleSubmit(submitPlan.args.method, submitPlan.args.passId, submitPlan.args.membershipId, submitPlan.args.selection);
2408
2790
  if (!submitResult.ok) return {
2409
2791
  ok: false,
@@ -2449,36 +2831,15 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2449
2831
  paymentSelection,
2450
2832
  ticketsCount
2451
2833
  ]);
2452
- const fallbackUnitPriceCents = useMemo(() => currentEvent ? publicEventScalarPrice(resolveRuntimeEventPricing(currentEvent)) ?? 0 : 0, [currentEvent]);
2453
- const ticketSelectionSubtotalCents = useMemo(() => {
2454
- if (!usesTicketTypeSelections) return Math.max(0, eligibility?.price ?? fallbackUnitPriceCents * ticketsCount);
2455
- return calculateTicketSelectionSubtotal({
2456
- lines: normalizedSelectedTicketLines,
2457
- ticketTypes: visibleTicketTypes,
2458
- fallbackUnitPriceCents
2459
- });
2460
- }, [
2461
- eligibility,
2462
- fallbackUnitPriceCents,
2463
- normalizedSelectedTicketLines,
2464
- ticketsCount,
2465
- usesTicketTypeSelections,
2466
- visibleTicketTypes
2467
- ]);
2468
- const ticketSelectionSummaryLines = useMemo(() => buildTicketSelectionSummaryLines({
2469
- lines: ticketSelections,
2470
- ticketTypes: visibleTicketTypes,
2471
- fallbackLabel: "Ticket",
2472
- fallbackUnitPriceCents
2473
- }), [
2474
- fallbackUnitPriceCents,
2475
- ticketSelections,
2476
- visibleTicketTypes
2477
- ]);
2478
- const discountSubtotalCents = useMemo(() => {
2479
- if (discountContext?.type !== "event") return void 0;
2480
- return ticketSelectionSubtotalCents;
2481
- }, [discountContext, ticketSelectionSubtotalCents]);
2834
+ const ticketSelectionSubtotalCents = eligibility?.price ?? null;
2835
+ const ticketSelectionSummaryLines = useMemo(() => eligibility?.ticketLines.map((line) => ({
2836
+ pricingOptionId: line.pricingOptionId,
2837
+ label: line.label,
2838
+ quantity: line.quantity,
2839
+ unitPriceCents: line.unitPriceCents,
2840
+ subtotalCents: line.subtotalCents
2841
+ })) ?? [], [eligibility]);
2842
+ const discountSubtotalCents = resolveDiscountSubtotalCents(discountContext, ticketSelectionSubtotalCents);
2482
2843
  const discountUi = deps.useDiscountCodeUi({
2483
2844
  api,
2484
2845
  discountContext,
@@ -2487,6 +2848,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2487
2848
  const selectionMethod = paymentSelection.method;
2488
2849
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
2489
2850
  const handleSubmit = deps.useEventRegistrationSubmit({
2851
+ setErrorAction,
2490
2852
  api,
2491
2853
  registrationTarget,
2492
2854
  selectedAttendanceMode,
@@ -2509,26 +2871,31 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2509
2871
  ...current,
2510
2872
  consentAccepted: false
2511
2873
  }));
2512
- const slidingScale = getSlidingScaleForEvent(event);
2513
- setSelectedSlidingAmountInput(slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "");
2514
- setSelectedAttendanceMode((event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person");
2874
+ const defaults = getEventSelectionDefaults(event);
2875
+ setSelectedSlidingAmountInput(defaults.slidingAmountInput);
2876
+ setSelectedAttendanceMode(defaults.attendanceMode);
2515
2877
  setSelectedTicketLines([]);
2516
2878
  dispatchFlow({
2517
2879
  type: "event-changed",
2518
2880
  hasPreSelectedContext: true
2519
2881
  });
2520
2882
  };
2521
- const handleRetry = () => {
2522
- dispatchFlow({
2523
- type: "reset",
2524
- hasPreSelectedContext
2525
- });
2526
- setSelectedTicketLines([]);
2527
- setSelectedSlidingAmountInput("");
2528
- setSelectedAttendanceMode(preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null);
2529
- setErrorMessage("");
2530
- setPendingRegistrationReference(null);
2531
- };
2883
+ const handleRetry = () => recoverRegistrationError(resolveRegistrationRecoveryAction(step, errorAction), {
2884
+ canJoinWaitlist: canRecoverToWaitlist(eligibility),
2885
+ refreshOptions: () => setOptionsRefreshKey((value) => value + 1),
2886
+ clearError: () => setErrorMessage(""),
2887
+ setStep,
2888
+ reset: () => resetRegistrationAfterExpiry({
2889
+ hasPreSelectedContext,
2890
+ preSelectedEvent,
2891
+ dispatchFlow,
2892
+ setSelectedTicketLines,
2893
+ setSelectedSlidingAmountInput,
2894
+ setSelectedAttendanceMode,
2895
+ setErrorMessage,
2896
+ setPendingRegistrationReference
2897
+ })
2898
+ });
2532
2899
  const handlePaymentStepStateChange = useCallback((state) => {
2533
2900
  setPaymentStepState(state);
2534
2901
  }, []);
@@ -2591,6 +2958,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2591
2958
  handleGuestShakeEnd,
2592
2959
  registrationResult,
2593
2960
  errorMessage,
2961
+ errorAction,
2594
2962
  altchaSolution,
2595
2963
  setAltchaSolution,
2596
2964
  portalAuthState,
@@ -2832,7 +3200,7 @@ function getSelectedDirectPaymentOption(input) {
2832
3200
  paymentTerms: eligibility.paymentTerms,
2833
3201
  totalPriceCents: input.totalCents,
2834
3202
  currency: input.currency,
2835
- startsAt: input.wizard.occurrenceContext?.startsAt ?? summaryEvent.startsAt
3203
+ startsAt: getRegistrationStartAt(summaryEvent, input.wizard.occurrenceContext)
2836
3204
  }).options;
2837
3205
  const requestedKind = input.wizard.paymentSelection.collectionOptionKind;
2838
3206
  return options.find((option) => option.kind === requestedKind) ?? options.find((option) => option.kind === "upfront") ?? options[0] ?? null;
@@ -2867,7 +3235,7 @@ function EntitlementSummaryRow(props) {
2867
3235
  children: props.copy.productPrice ? /* @__PURE__ */ jsx("span", {
2868
3236
  className: "er-summary-bar-included",
2869
3237
  children: props.copy.productPrice
2870
- }) : productPrice ? formatPrice(productPrice, productCurrency ?? "usd") : null
3238
+ }) : productPrice && productCurrency ? formatPrice(productPrice, productCurrency) : null
2871
3239
  })]
2872
3240
  });
2873
3241
  }
@@ -2875,10 +3243,10 @@ function EventRegistrationSummaryBar(props) {
2875
3243
  const { wizard } = props;
2876
3244
  const summaryEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
2877
3245
  const summaryTitle = wizard.occurrenceContext?.overrides?.title ?? summaryEvent?.title ?? "";
2878
- const summaryCurrency = (summaryEvent ? publicEventPricingCurrency(summaryEvent.pricing) : null) ?? "usd";
3246
+ const summaryCurrency = summaryEvent ? publicEventPricingCurrency(summaryEvent.pricing) : null;
2879
3247
  const teamMemberNames = (summaryEvent?.teamMembers ?? []).map((teamMember) => teamMember.name).filter(Boolean);
2880
- const fallbackSubtotalCents = Math.max(0, ((summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0) * wizard.ticketsCount);
2881
- const subtotalCents = Math.max(0, wizard.ticketSelectionSubtotalCents ?? fallbackSubtotalCents);
3248
+ const subtotalCents = wizard.ticketSelectionSubtotalCents;
3249
+ if (!summaryTitle || !summaryCurrency || subtotalCents === null) return null;
2882
3250
  const autoAppliedDiscount = wizard.discountCode.trim().length > 0 ? null : wizard.autoAppliedDiscount;
2883
3251
  const autoAppliedDiscountCents = Math.max(0, autoAppliedDiscount?.computedAmountCents ?? 0);
2884
3252
  const discountedTotalCents = Math.max(0, subtotalCents - autoAppliedDiscountCents);
@@ -2894,7 +3262,6 @@ function EventRegistrationSummaryBar(props) {
2894
3262
  balanceDueBy: getSummaryRow(selectedDirectPaymentOption, "Balance due by"),
2895
3263
  depositPolicy: getSummaryRow(selectedDirectPaymentOption, "Deposit policy")
2896
3264
  } : null;
2897
- if (!summaryTitle) return null;
2898
3265
  const scheduleSummary = summaryEvent ? getScheduleSummary(wizard, summaryEvent) : null;
2899
3266
  const bookingUnit = summaryEvent?.registrationSubject.kind === "wholeSeries" ? "place" : "ticket";
2900
3267
  const productMethod = isProductMethod(wizard.paymentSelection.method);
@@ -2917,7 +3284,8 @@ function EventRegistrationSummaryBar(props) {
2917
3284
  /* @__PURE__ */ jsxs("span", {
2918
3285
  className: "er-summary-bar-meta",
2919
3286
  children: [scheduleSummary, wizard.step !== "select-event" && wizard.step !== "select-tickets" && wizard.ticketsCount > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2920
- " · ",
3287
+ " ",
3288
+ "· ",
2921
3289
  wizard.ticketsCount,
2922
3290
  " ",
2923
3291
  bookingUnit,
@@ -2997,11 +3365,11 @@ function EventRegistrationSummaryBar(props) {
2997
3365
  */
2998
3366
  function resolveDetailsButtonText(input) {
2999
3367
  const { wizard } = input;
3000
- if (wizard.eligibility?.canJoinWaitlist) return "Join Waitlist";
3368
+ if (wizard.eligibility?.canJoinWaitlist) return "Join waitlist";
3001
3369
  if (!input.hasWaitlistClaim && wizard.eligibility?.isFull) return "Sold out";
3002
3370
  if (wizard.paymentSelection.method === "pass") return "Book using class pass";
3003
3371
  if (wizard.paymentSelection.method === "membership") return input.fallbackText;
3004
- return wizard.eventRequiresPayment ? "Continue to Payment" : input.fallbackText;
3372
+ return wizard.eventRequiresPayment ? "Continue to payment" : input.fallbackText;
3005
3373
  }
3006
3374
  function resolveExistingRegistrationLabel(wizard) {
3007
3375
  return wizard.registrationState.kind === "waitlisted" ? "Already on waitlist" : "Already registered";
@@ -3013,18 +3381,114 @@ function isExistingRegistrationBlocking$1(wizard) {
3013
3381
  hasWaitlistClaim: wizard.waitlistClaim.claim !== null
3014
3382
  });
3015
3383
  }
3016
- function EventRegistrationWizardActions(props) {
3384
+ /**
3385
+ * Full for the visitor's selected attendance mode (or the whole event when no
3386
+ * mode is chosen) with no waitlist to fall back on. Reads the same capacity
3387
+ * source as the steps above the action bar so the button never disagrees
3388
+ * with them on a hybrid event.
3389
+ */
3390
+ function isFullWithNoWaitlist(event, attendanceMode) {
3391
+ if (!event) return false;
3392
+ const source = selectEventCapacitySource(event, attendanceMode);
3393
+ return getEventAvailability(source).isFull && !source.waitlistEnabled;
3394
+ }
3395
+ function resolveTicketSelectionContinueLabel(input) {
3396
+ if (input.isCheckingExistingRegistration) return "Checking registration...";
3397
+ if (input.hasExistingRegistration) return resolveExistingRegistrationLabel(input.wizard);
3398
+ if (input.isSoldOutWithoutWaitlist) return "Sold out";
3399
+ if (input.ticketTypeSelectionBlocked) return "Choose a ticket";
3400
+ return "Continue";
3401
+ }
3402
+ function SelectTicketsActions(props) {
3017
3403
  const { wizard } = props;
3018
- const nextStepAfterTickets = "enter-details";
3019
- const prevStepForDetails = "select-tickets";
3020
3404
  const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
3021
3405
  const currentEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
3022
- const isSoldOutWithoutWaitlist = !hasWaitlistClaim && currentEvent?.availableSpots === 0 && currentEvent.waitlistEnabled !== true;
3023
- const getDetailsButtonText = () => resolveDetailsButtonText({
3406
+ const isSoldOutWithoutWaitlist = !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
3407
+ const isWaitingForEligibility = wizard.portalAuthState === "authenticated" && wizard.eligibilityLoading;
3408
+ const isCheckingExistingRegistration = wizard.portalAuthState === "authenticated" && wizard.existingRegistrationLoading;
3409
+ const hasExistingRegistration = isExistingRegistrationBlocking$1(wizard);
3410
+ const ticketTypeSelectionBlocked = wizard.ticketTypeSelectionRequired && !wizard.hasValidTicketSelection;
3411
+ const isContinueDisabled = isWaitingForEligibility || isCheckingExistingRegistration || hasExistingRegistration || isSoldOutWithoutWaitlist || ticketTypeSelectionBlocked;
3412
+ const continueLabel = resolveTicketSelectionContinueLabel({
3413
+ wizard,
3414
+ isCheckingExistingRegistration,
3415
+ hasExistingRegistration,
3416
+ isSoldOutWithoutWaitlist,
3417
+ ticketTypeSelectionBlocked
3418
+ });
3419
+ return /* @__PURE__ */ jsxs("div", {
3420
+ className: "rb-wizard-actions er-actions",
3421
+ children: [!wizard.hasPreSelectedContext && /* @__PURE__ */ jsx("button", {
3422
+ type: "button",
3423
+ onClick: () => wizard.setStep("select-event"),
3424
+ className: "er-back-btn",
3425
+ children: "← Back"
3426
+ }), /* @__PURE__ */ jsx("button", {
3427
+ type: "button",
3428
+ onClick: () => wizard.setStep("enter-details"),
3429
+ disabled: isContinueDisabled,
3430
+ className: props.buttonClass,
3431
+ children: isWaitingForEligibility ? /* @__PURE__ */ jsxs("span", {
3432
+ className: "er-button-with-spinner",
3433
+ children: [/* @__PURE__ */ jsx(SpinnerNode, {
3434
+ size: "xs",
3435
+ color: "inherit",
3436
+ className: "er-button-spinner",
3437
+ "aria-hidden": true
3438
+ }), continueLabel]
3439
+ }) : continueLabel
3440
+ })]
3441
+ });
3442
+ }
3443
+ function EnterDetailsActions(props) {
3444
+ const { wizard } = props;
3445
+ const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
3446
+ const isCheckingEligibility = wizard.eligibilityLoading;
3447
+ const eligibilityUnavailable = !hasWaitlistClaim && (Boolean(wizard.eligibilityError) || !wizard.eligibility || wizard.eligibility.availability.kind === "unavailable" || !wizard.eligibility.eligible);
3448
+ const isSoldOut = !hasWaitlistClaim && wizard.eligibility?.isFull === true && wizard.eligibility.canJoinWaitlist !== true;
3449
+ const isContinueDisabled = isCheckingEligibility || eligibilityUnavailable || isSoldOut;
3450
+ const continueLabel = isCheckingEligibility ? "Checking availability..." : wizard.eligibilityError ? "Availability unavailable" : eligibilityUnavailable ? "Registration unavailable" : resolveDetailsButtonText({
3024
3451
  wizard,
3025
3452
  hasWaitlistClaim,
3026
3453
  fallbackText: props.buttonText
3027
3454
  });
3455
+ return /* @__PURE__ */ jsxs("div", {
3456
+ className: "rb-wizard-actions er-actions",
3457
+ children: [/* @__PURE__ */ jsx("button", {
3458
+ type: "button",
3459
+ onClick: () => wizard.setStep("select-tickets"),
3460
+ className: "er-back-btn",
3461
+ children: "← Back"
3462
+ }), /* @__PURE__ */ jsx("button", {
3463
+ type: "button",
3464
+ onClick: wizard.handleDetailsComplete,
3465
+ disabled: isContinueDisabled,
3466
+ className: props.buttonClass,
3467
+ children: continueLabel
3468
+ })]
3469
+ });
3470
+ }
3471
+ function SelectPaymentActions(props) {
3472
+ const { wizard } = props;
3473
+ const { status, canContinue, buttonText } = wizard.paymentStepState;
3474
+ return /* @__PURE__ */ jsxs("div", {
3475
+ className: "rb-wizard-actions er-actions",
3476
+ children: [/* @__PURE__ */ jsx("button", {
3477
+ type: "button",
3478
+ onClick: () => wizard.setStep("enter-details"),
3479
+ className: "er-back-btn",
3480
+ children: "← Back"
3481
+ }), status === "ready" && /* @__PURE__ */ jsx("button", {
3482
+ type: "button",
3483
+ onClick: () => void wizard.handlePaymentSelect(),
3484
+ disabled: !canContinue,
3485
+ className: props.buttonClass,
3486
+ children: buttonText
3487
+ })]
3488
+ });
3489
+ }
3490
+ function EventRegistrationWizardActions(props) {
3491
+ const { wizard } = props;
3028
3492
  switch (wizard.step) {
3029
3493
  case "error":
3030
3494
  case "success":
@@ -3034,268 +3498,13 @@ function EventRegistrationWizardActions(props) {
3034
3498
  case "verifying-timeout":
3035
3499
  case "payment-failed": return null;
3036
3500
  case "select-event": return null;
3037
- case "select-tickets": {
3038
- const isWaitingForEligibility = wizard.portalAuthState === "authenticated" && wizard.eligibilityLoading;
3039
- const isCheckingExistingRegistration = wizard.portalAuthState === "authenticated" && wizard.existingRegistrationLoading;
3040
- const hasExistingRegistration = isExistingRegistrationBlocking$1(wizard);
3041
- const ticketTypeSelectionBlocked = wizard.ticketTypeSelectionRequired && !wizard.hasValidTicketSelection;
3042
- const isContinueDisabled = isWaitingForEligibility || isCheckingExistingRegistration || hasExistingRegistration || isSoldOutWithoutWaitlist || ticketTypeSelectionBlocked;
3043
- const continueLabel = isCheckingExistingRegistration ? "Checking registration..." : hasExistingRegistration ? resolveExistingRegistrationLabel(wizard) : isSoldOutWithoutWaitlist ? "Sold out" : ticketTypeSelectionBlocked ? "Choose a ticket" : "Continue";
3044
- return /* @__PURE__ */ jsxs("div", {
3045
- className: "rb-wizard-actions er-actions",
3046
- children: [!wizard.hasPreSelectedContext && /* @__PURE__ */ jsx("button", {
3047
- type: "button",
3048
- onClick: () => wizard.setStep("select-event"),
3049
- className: "er-back-btn",
3050
- children: "← Back"
3051
- }), /* @__PURE__ */ jsx("button", {
3052
- type: "button",
3053
- onClick: () => wizard.setStep(nextStepAfterTickets),
3054
- disabled: isContinueDisabled,
3055
- className: props.buttonClass,
3056
- children: isWaitingForEligibility ? /* @__PURE__ */ jsxs("span", {
3057
- className: "er-button-with-spinner",
3058
- children: [/* @__PURE__ */ jsx(SpinnerNode, {
3059
- size: "xs",
3060
- color: "inherit",
3061
- className: "er-button-spinner",
3062
- "aria-hidden": true
3063
- }), continueLabel]
3064
- }) : continueLabel
3065
- })]
3066
- });
3067
- }
3068
- case "enter-details": {
3069
- const isCheckingEligibility = wizard.eligibilityLoading;
3070
- const isSoldOut = !hasWaitlistClaim && wizard.eligibility?.isFull === true && wizard.eligibility.canJoinWaitlist !== true;
3071
- const isContinueDisabled = isCheckingEligibility || isSoldOut;
3072
- const continueLabel = isCheckingEligibility ? "Checking availability..." : getDetailsButtonText();
3073
- return /* @__PURE__ */ jsxs("div", {
3074
- className: "rb-wizard-actions er-actions",
3075
- children: [/* @__PURE__ */ jsx("button", {
3076
- type: "button",
3077
- onClick: () => wizard.setStep(prevStepForDetails),
3078
- className: "er-back-btn",
3079
- children: "← Back"
3080
- }), /* @__PURE__ */ jsx("button", {
3081
- type: "button",
3082
- onClick: wizard.handleDetailsComplete,
3083
- disabled: isContinueDisabled,
3084
- className: props.buttonClass,
3085
- children: continueLabel
3086
- })]
3087
- });
3088
- }
3089
- case "select-payment": {
3090
- const { status, canContinue, buttonText } = wizard.paymentStepState;
3091
- const handlePaymentContinue = () => {
3092
- wizard.handlePaymentSelect();
3093
- };
3094
- return /* @__PURE__ */ jsxs("div", {
3095
- className: "rb-wizard-actions er-actions",
3096
- children: [/* @__PURE__ */ jsx("button", {
3097
- type: "button",
3098
- onClick: () => wizard.setStep("enter-details"),
3099
- className: "er-back-btn",
3100
- children: "← Back"
3101
- }), status === "ready" && /* @__PURE__ */ jsx("button", {
3102
- type: "button",
3103
- onClick: handlePaymentContinue,
3104
- disabled: !canContinue,
3105
- className: props.buttonClass,
3106
- children: buttonText
3107
- })]
3108
- });
3109
- }
3501
+ case "select-tickets": return /* @__PURE__ */ jsx(SelectTicketsActions, { ...props });
3502
+ case "enter-details": return /* @__PURE__ */ jsx(EnterDetailsActions, { ...props });
3503
+ case "select-payment": return /* @__PURE__ */ jsx(SelectPaymentActions, { ...props });
3110
3504
  default: return null;
3111
3505
  }
3112
3506
  }
3113
3507
  //#endregion
3114
- //#region ../commerce/src/purchase/checkoutPaymentFlow.ts
3115
- function planCheckoutPaymentFlow(options) {
3116
- const amountDueCents = toNonNegativeCents(options.amountDueCents);
3117
- const discountCents = toNonNegativeCents(options.discountCents);
3118
- const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
3119
- const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
3120
- if (amountDueCents <= 0) return {
3121
- kind: "bypass",
3122
- bypassReason: discountCents > 0 ? "discount_free" : "free",
3123
- reserveCreditCents: 0
3124
- };
3125
- if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
3126
- kind: "stripe",
3127
- reserveCreditCents: toNonNegativeCents(0)
3128
- };
3129
- if (availableCreditCents >= amountDueCents) return {
3130
- kind: "store_credit_only",
3131
- reserveCreditCents: amountDueCents
3132
- };
3133
- return {
3134
- kind: "stripe",
3135
- reserveCreditCents: planMixedPaymentCreditReservation({
3136
- amountDueCents,
3137
- availableCreditCents,
3138
- minimumStripeChargeCents
3139
- })
3140
- };
3141
- }
3142
- function planMixedPaymentCreditReservation(input) {
3143
- const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
3144
- if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
3145
- if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
3146
- return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
3147
- }
3148
- /**
3149
- * Apply site credit to a flexible-balance booking before card payment.
3150
- *
3151
- * Credit first reduces the configured payment due now. Once that payment is
3152
- * covered, any remaining credit reduces the later balance. If a partial credit
3153
- * application would leave a positive card charge below Stripe's minimum, less
3154
- * credit is used so the configured payment can still be collected.
3155
- */
3156
- function planFlexibleBalanceCreditFirst(input) {
3157
- const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
3158
- const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
3159
- const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
3160
- const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
3161
- if (availableCreditCents >= totalCents && totalCents > 0) return {
3162
- kind: "credit_only",
3163
- reserveCreditCents: totalCents,
3164
- cardDueNowCents: toNonNegativeCents(0),
3165
- laterBalanceCents: toNonNegativeCents(0)
3166
- };
3167
- let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
3168
- if (reserveCreditCents >= configuredDueNowCents) return {
3169
- kind: "flexible_balance",
3170
- reserveCreditCents,
3171
- cardDueNowCents: toNonNegativeCents(0),
3172
- laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
3173
- };
3174
- const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
3175
- if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
3176
- if (configuredDueNowCents < minimumStripeChargeCents) return {
3177
- kind: "card_below_minimum",
3178
- reserveCreditCents: toNonNegativeCents(0),
3179
- cardDueNowCents: configuredDueNowCents,
3180
- laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
3181
- };
3182
- reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
3183
- }
3184
- const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
3185
- return {
3186
- kind: "flexible_balance",
3187
- reserveCreditCents,
3188
- cardDueNowCents,
3189
- laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
3190
- };
3191
- }
3192
- //#endregion
3193
- //#region ../commerce/src/purchase/settlement.ts
3194
- function normalizeCurrencyCode(raw) {
3195
- const normalized = raw.trim().toUpperCase();
3196
- if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
3197
- return normalized;
3198
- }
3199
- //#endregion
3200
- //#region ../commerce/src/purchase/checkoutSessionMetadata.ts
3201
- const datedOfferingSharedEngineMetadataShape = {
3202
- datedOfferingEngineContractVersion: z.literal("shared_v1"),
3203
- datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
3204
- datedOfferingCapacityHoldId: z.string().uuid(),
3205
- datedOfferingCapacityHoldAuditRef: z.string().uuid(),
3206
- customerId: z.string().uuid(),
3207
- eventSeriesId: z.string().uuid()
3208
- };
3209
- z.object(datedOfferingSharedEngineMetadataShape);
3210
- const orderCheckoutSessionMetadataBaseShape = {
3211
- type: z.literal("order"),
3212
- site_id: z.string().uuid(),
3213
- order_id: z.string().uuid().optional(),
3214
- customer_email: z.string().email(),
3215
- customer_name: z.string().optional(),
3216
- product_id: z.string().uuid().optional(),
3217
- attendee_id: z.string().uuid().optional(),
3218
- occurrence_id: z.string().uuid().optional(),
3219
- tickets_count: z.string().optional(),
3220
- notes: z.string().optional(),
3221
- discount_redemption_id: z.string().uuid().optional(),
3222
- discount_code_id: z.string().uuid().optional(),
3223
- discount_code: z.string().optional(),
3224
- discount_public_label: z.string().optional(),
3225
- credit_reservation_id: z.string().uuid().optional(),
3226
- credit_reserved_cents: z.string().optional(),
3227
- discount_code_amount_cents: z.string().optional(),
3228
- order_payment_reason: z.enum([
3229
- "instalment_payoff",
3230
- "flexible_balance_initial",
3231
- "flexible_balance_paydown"
3232
- ]).optional(),
3233
- order_payment_plan_id: z.string().uuid().optional(),
3234
- order_flexible_balance_contract_id: z.string().uuid().optional()
3235
- };
3236
- const absentDatedOfferingSharedEngineMetadataShape = {
3237
- datedOfferingEngineContractVersion: z.never().optional(),
3238
- datedOfferingEngineRouteKind: z.never().optional(),
3239
- datedOfferingCapacityHoldId: z.never().optional(),
3240
- datedOfferingCapacityHoldAuditRef: z.never().optional(),
3241
- customerId: z.never().optional(),
3242
- eventSeriesId: z.never().optional()
3243
- };
3244
- const LegacyOrderCheckoutSessionMetadataSchema = z.object({
3245
- ...orderCheckoutSessionMetadataBaseShape,
3246
- ...absentDatedOfferingSharedEngineMetadataShape
3247
- });
3248
- const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
3249
- ...orderCheckoutSessionMetadataBaseShape,
3250
- ...datedOfferingSharedEngineMetadataShape
3251
- });
3252
- z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
3253
- toNonNegativeCents(0);
3254
- //#endregion
3255
- //#region ../commerce/src/purchase/stripeMinimumCharge.ts
3256
- const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
3257
- GBP: 30,
3258
- USD: 50,
3259
- EUR: 50,
3260
- AED: 200,
3261
- AUD: 50,
3262
- BGN: 100,
3263
- BRL: 50,
3264
- CAD: 50,
3265
- CHF: 50,
3266
- CZK: 1500,
3267
- DKK: 250,
3268
- HKD: 400,
3269
- HUF: 17500,
3270
- JPY: 50,
3271
- MXN: 1e3,
3272
- MYR: 200,
3273
- NOK: 300,
3274
- NZD: 50,
3275
- PLN: 200,
3276
- RON: 200,
3277
- SEK: 300,
3278
- SGD: 50,
3279
- THB: 1e3
3280
- };
3281
- function isStripeMinimumChargeCurrency(currency) {
3282
- return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
3283
- }
3284
- function getStripeMinimumChargeRequirement(rawCurrency) {
3285
- let currency;
3286
- try {
3287
- currency = normalizeCurrencyCode(rawCurrency);
3288
- } catch {
3289
- return null;
3290
- }
3291
- const currencyKey = currency;
3292
- if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
3293
- return {
3294
- currency,
3295
- minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
3296
- };
3297
- }
3298
- //#endregion
3299
3508
  //#region ../blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.ts
3300
3509
  function calculateEventRegistrationPreviewTotal(input) {
3301
3510
  const discountCents = input.manualDiscountCode.trim() ? 0 : input.automaticDiscount?.computedAmountCents ?? 0;
@@ -3325,49 +3534,203 @@ function buildEventRegistrationSiteCreditPreview(input) {
3325
3534
  creditCurrencyMatches: true,
3326
3535
  minimumStripeChargeCents: minimumChargeCents
3327
3536
  });
3328
- const appliedCreditCents = plan.reserveCreditCents;
3329
- return {
3330
- kind: "applied",
3331
- outcome: plan.kind === "store_credit_only" ? "credit_only" : "mixed",
3332
- availableCreditCents: summary.availableCents,
3333
- appliedCreditCents,
3334
- cardDueNowCents: Math.max(0, input.option.totalCents - appliedCreditCents),
3335
- laterBalanceCents: 0,
3336
- remainingCreditCents: Math.max(0, summary.availableCents - appliedCreditCents),
3337
- manualDiscountPending: input.manualDiscountPending
3338
- };
3537
+ const appliedCreditCents = plan.reserveCreditCents;
3538
+ return {
3539
+ kind: "applied",
3540
+ outcome: plan.kind === "store_credit_only" ? "credit_only" : "mixed",
3541
+ availableCreditCents: summary.availableCents,
3542
+ appliedCreditCents,
3543
+ cardDueNowCents: Math.max(0, input.option.totalCents - appliedCreditCents),
3544
+ laterBalanceCents: 0,
3545
+ remainingCreditCents: Math.max(0, summary.availableCents - appliedCreditCents),
3546
+ manualDiscountPending: input.manualDiscountPending
3547
+ };
3548
+ }
3549
+ const plan = planFlexibleBalanceCreditFirst({
3550
+ totalAfterDiscountCents: input.option.totalCents,
3551
+ configuredPaymentDueNowCents: input.option.configuredPaymentDueNowCents,
3552
+ availableCreditCents: summary.availableCents,
3553
+ creditCurrencyMatches: true,
3554
+ minimumStripeChargeCents: minimumChargeCents
3555
+ });
3556
+ if (plan.kind === "card_below_minimum") return { kind: "not_applicable" };
3557
+ return {
3558
+ kind: "applied",
3559
+ outcome: plan.kind === "credit_only" ? "credit_only" : "mixed",
3560
+ availableCreditCents: summary.availableCents,
3561
+ appliedCreditCents: plan.reserveCreditCents,
3562
+ cardDueNowCents: plan.cardDueNowCents,
3563
+ laterBalanceCents: plan.laterBalanceCents,
3564
+ remainingCreditCents: Math.max(0, summary.availableCents - plan.reserveCreditCents),
3565
+ manualDiscountPending: input.manualDiscountPending
3566
+ };
3567
+ }
3568
+ function buildSelectedSiteCreditPreview(input) {
3569
+ if (!input.eligibility) return { kind: "unavailable" };
3570
+ const selectedKind = input.selectionState.kind === "valid" ? input.selectionState.selection.collectionOptionKind : null;
3571
+ const option = input.options.find((candidate) => candidate.kind === selectedKind) ?? null;
3572
+ return buildEventRegistrationSiteCreditPreview({
3573
+ ...input,
3574
+ currency: input.eligibility.currency,
3575
+ option
3576
+ });
3577
+ }
3578
+ //#endregion
3579
+ //#region ../blocks/src/system/runtime/nodes/event-registration/PaymentBlockingPanel.tsx
3580
+ function resolvePaymentStepStatus(input) {
3581
+ if (input.loading) return "loading";
3582
+ if (input.error || !input.eligibility) return "error";
3583
+ if (input.eligibility.registration.kind !== "none" || !input.eligibility.eligible && !input.eligibility.isFree) return "not-eligible";
3584
+ return "ready";
3585
+ }
3586
+ function shouldPromptForLogin(status, isAuthenticated, eligibility) {
3587
+ return Boolean(status === "not-eligible" && !isAuthenticated && eligibility && (eligibility.requiresAuthForPasses || eligibility.requiresAuthForMemberships));
3588
+ }
3589
+ function MissingEligibilityPanel() {
3590
+ return /* @__PURE__ */ jsx("div", {
3591
+ className: "rb-reg-stack er-stack",
3592
+ children: /* @__PURE__ */ jsx("div", {
3593
+ className: "er-center",
3594
+ children: /* @__PURE__ */ jsx("p", {
3595
+ className: "er-text er-text--error",
3596
+ children: "We couldn’t load the booking options for this event. Please refresh this page and try again. If this keeps happening, contact the organiser for help."
3597
+ })
3598
+ })
3599
+ });
3600
+ }
3601
+ function UnavailableEntitlementMessage(props) {
3602
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3603
+ /* @__PURE__ */ jsx("p", {
3604
+ className: "er-text er-text--error",
3605
+ children: "This event requires a valid pass or membership to register."
3606
+ }),
3607
+ props.eligibility.eventAcceptsPasses && !props.eligibility.eventAcceptsMemberships && /* @__PURE__ */ jsx("p", {
3608
+ className: "er-text er-text--muted",
3609
+ children: "You need a pass with credits for this event category."
3610
+ }),
3611
+ props.eligibility.eventAcceptsMemberships && !props.eligibility.eventAcceptsPasses && /* @__PURE__ */ jsx("p", {
3612
+ className: "er-text er-text--muted",
3613
+ children: "You need an active membership for this event category."
3614
+ })
3615
+ ] });
3616
+ }
3617
+ function IneligiblePaymentPanel(props) {
3618
+ if (props.eligibility.isFull && !props.eligibility.canJoinWaitlist) return /* @__PURE__ */ jsxs("div", {
3619
+ className: "rb-reg-stack er-stack",
3620
+ children: [/* @__PURE__ */ jsx("h3", {
3621
+ className: "er-title",
3622
+ children: "Sold Out"
3623
+ }), /* @__PURE__ */ jsx("div", {
3624
+ className: "er-center",
3625
+ children: /* @__PURE__ */ jsx("p", {
3626
+ className: "er-text er-text--muted",
3627
+ children: "This event is fully booked and there is no waitlist available for this booking."
3628
+ })
3629
+ })]
3630
+ });
3631
+ const promptLogin = shouldPromptForLogin("not-eligible", props.isAuthenticated, props.eligibility);
3632
+ return /* @__PURE__ */ jsxs("div", {
3633
+ className: "rb-reg-stack er-stack",
3634
+ children: [/* @__PURE__ */ jsx("h3", {
3635
+ className: "er-title",
3636
+ children: promptLogin ? "Log In Required" : "Payment Required"
3637
+ }), /* @__PURE__ */ jsx("div", {
3638
+ className: "er-center",
3639
+ children: promptLogin ? /* @__PURE__ */ jsxs(Fragment, { children: [
3640
+ /* @__PURE__ */ jsx("p", {
3641
+ className: "er-text er-text--muted",
3642
+ children: "Have a pass or membership? Log in to use it."
3643
+ }),
3644
+ /* @__PURE__ */ jsx("button", {
3645
+ type: "button",
3646
+ className: themeButtonClassName({
3647
+ variant: "primary",
3648
+ size: "md"
3649
+ }),
3650
+ disabled: !props.email.trim(),
3651
+ onClick: props.onRequestLogin,
3652
+ children: "Email me a login link"
3653
+ }),
3654
+ !props.email.trim() && /* @__PURE__ */ jsx("p", {
3655
+ className: "er-text er-text--muted",
3656
+ children: "Enter your email to request a login link."
3657
+ })
3658
+ ] }) : /* @__PURE__ */ jsx(UnavailableEntitlementMessage, { eligibility: props.eligibility })
3659
+ })]
3660
+ });
3661
+ }
3662
+ function PaymentBlockingPanel(props) {
3663
+ if (props.eligibilityLoading) return /* @__PURE__ */ jsx("div", {
3664
+ className: "rb-reg-stack er-stack",
3665
+ children: /* @__PURE__ */ jsxs("div", {
3666
+ className: "er-center",
3667
+ children: [/* @__PURE__ */ jsx(SpinnerNode, {
3668
+ size: "lg",
3669
+ srLabel: "Checking payment options"
3670
+ }), /* @__PURE__ */ jsx("p", {
3671
+ className: "er-text--muted",
3672
+ children: "Checking payment options..."
3673
+ })]
3674
+ })
3675
+ });
3676
+ if (props.eligibilityError) return /* @__PURE__ */ jsx("div", {
3677
+ className: "rb-reg-stack er-stack",
3678
+ children: /* @__PURE__ */ jsx("div", {
3679
+ className: "er-center",
3680
+ children: /* @__PURE__ */ jsx("p", {
3681
+ className: "er-text er-text--error",
3682
+ children: props.eligibilityError
3683
+ })
3684
+ })
3685
+ });
3686
+ if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {});
3687
+ if (props.eligibility.registration.kind !== "none") {
3688
+ const isBooked = props.eligibility.registration.kind === "booked";
3689
+ return /* @__PURE__ */ jsxs("div", {
3690
+ className: "rb-reg-stack er-stack",
3691
+ children: [/* @__PURE__ */ jsx("h3", {
3692
+ className: "er-title",
3693
+ children: isBooked ? "Already booked" : "Already on the waitlist"
3694
+ }), /* @__PURE__ */ jsxs("div", {
3695
+ className: "er-center",
3696
+ children: [/* @__PURE__ */ jsx("p", {
3697
+ className: "er-text er-text--muted",
3698
+ children: isBooked ? "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser." : "You're already on the waitlist for this event."
3699
+ }), /* @__PURE__ */ jsx("a", {
3700
+ href: "/portal",
3701
+ className: "er-retry-btn",
3702
+ children: "View my bookings"
3703
+ })]
3704
+ })]
3705
+ });
3339
3706
  }
3340
- const plan = planFlexibleBalanceCreditFirst({
3341
- totalAfterDiscountCents: input.option.totalCents,
3342
- configuredPaymentDueNowCents: input.option.configuredPaymentDueNowCents,
3343
- availableCreditCents: summary.availableCents,
3344
- creditCurrencyMatches: true,
3345
- minimumStripeChargeCents: minimumChargeCents
3707
+ if (props.eligibility.isFree) return /* @__PURE__ */ jsxs("div", {
3708
+ className: "rb-reg-stack er-stack",
3709
+ children: [/* @__PURE__ */ jsx("h3", {
3710
+ className: "er-title",
3711
+ children: "Payment"
3712
+ }), /* @__PURE__ */ jsxs("div", {
3713
+ className: "er-payment-summary",
3714
+ children: [/* @__PURE__ */ jsx("p", {
3715
+ className: "er-text er-text--success",
3716
+ children: "This event is free!"
3717
+ }), /* @__PURE__ */ jsxs("p", {
3718
+ className: "er-text er-text--muted",
3719
+ children: [
3720
+ props.ticketsCount,
3721
+ " ticket",
3722
+ props.ticketsCount > 1 ? "s" : ""
3723
+ ]
3724
+ })]
3725
+ })]
3346
3726
  });
3347
- if (plan.kind === "card_below_minimum") return { kind: "not_applicable" };
3348
- return {
3349
- kind: "applied",
3350
- outcome: plan.kind === "credit_only" ? "credit_only" : "mixed",
3351
- availableCreditCents: summary.availableCents,
3352
- appliedCreditCents: plan.reserveCreditCents,
3353
- cardDueNowCents: plan.cardDueNowCents,
3354
- laterBalanceCents: plan.laterBalanceCents,
3355
- remainingCreditCents: Math.max(0, summary.availableCents - plan.reserveCreditCents),
3356
- manualDiscountPending: input.manualDiscountPending
3357
- };
3358
- }
3359
- function buildSelectedSiteCreditPreview(input) {
3360
- const selectedKind = input.selectionState.kind === "valid" ? input.selectionState.selection.collectionOptionKind : null;
3361
- const option = input.options.find((candidate) => candidate.kind === selectedKind) ?? null;
3362
- return buildEventRegistrationSiteCreditPreview({
3363
- ...input,
3364
- currency: input.eligibility?.currency ?? "GBP",
3365
- option
3727
+ return props.eligibility.eligible ? null : /* @__PURE__ */ jsx(IneligiblePaymentPanel, {
3728
+ eligibility: props.eligibility,
3729
+ email: props.email,
3730
+ isAuthenticated: props.isAuthenticated,
3731
+ onRequestLogin: props.onRequestLogin
3366
3732
  });
3367
3733
  }
3368
- function resolveDirectPaymentTotalCents(explicitTotalCents, eligibility) {
3369
- return explicitTotalCents ?? eligibility?.price ?? 0;
3370
- }
3371
3734
  //#endregion
3372
3735
  //#region ../blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.tsx
3373
3736
  function pluralizeBookingUnit(kind, quantity) {
@@ -3409,9 +3772,16 @@ function TicketCounter(props) {
3409
3772
  });
3410
3773
  }
3411
3774
  function TicketCounterLimit(props) {
3775
+ const showBookingLimit = !props.availabilityLabel || props.maxAllowed < (props.availableSpots ?? Infinity);
3412
3776
  return /* @__PURE__ */ jsxs("p", {
3413
3777
  className: "er-ticket-counter-limit",
3414
- children: [/* @__PURE__ */ jsx("span", { children: props.maximumLabel }), props.availabilityLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx("span", { children: props.availabilityLabel })] }) : null]
3778
+ children: [props.availabilityLabel ? /* @__PURE__ */ jsx("span", { children: props.availabilityLabel }) : null, showBookingLimit ? /* @__PURE__ */ jsxs(Fragment, { children: [props.availabilityLabel ? /* @__PURE__ */ jsx("br", {}) : null, /* @__PURE__ */ jsxs("span", { children: [
3779
+ "Up to ",
3780
+ props.maxAllowed,
3781
+ " ",
3782
+ props.quantityLabel,
3783
+ " per booking."
3784
+ ] })] }) : null]
3415
3785
  });
3416
3786
  }
3417
3787
  function BookingUnitSessionHint(props) {
@@ -3427,7 +3797,7 @@ function BookingUnitSessionHint(props) {
3427
3797
  }
3428
3798
  function bookingQuantityLabel(unit, waitlist) {
3429
3799
  if (!waitlist) return pluralizeBookingUnit(unit, 2);
3430
- return unit === "place" ? "waitlist places" : "waitlist spots";
3800
+ return "waitlist places";
3431
3801
  }
3432
3802
  function BookingQuantityPrompt(props) {
3433
3803
  return /* @__PURE__ */ jsxs("h3", {
@@ -3486,6 +3856,7 @@ function SlidingScaleAmountField(props) {
3486
3856
  }
3487
3857
  function TicketTypeQuantityRows(props) {
3488
3858
  const totalSelectedQuantity = getTicketSelectionTotal(props.ticketSelections);
3859
+ const effectiveMaxTickets = props.availableSpots == null ? props.maxTickets : Math.min(props.maxTickets, props.availableSpots);
3489
3860
  return /* @__PURE__ */ jsxs("div", {
3490
3861
  className: "er-ticket-lines",
3491
3862
  children: [
@@ -3494,16 +3865,17 @@ function TicketTypeQuantityRows(props) {
3494
3865
  role: "list",
3495
3866
  "aria-label": props.bookingUnit === "place" ? "Places" : "Tickets",
3496
3867
  children: props.ticketTypes.map((ticketType) => {
3497
- const quantity = getTicketLineQuantity(props.ticketSelections, ticketType.id);
3868
+ const quantity = getTicketLineQuantity(props.ticketSelections, ticketType.pricingOptionId);
3498
3869
  const disabled = ticketType.availability.kind === "unavailable";
3499
3870
  const availabilityLabel = getTicketTypeAvailabilityLabel(ticketType);
3871
+ const ticketTypeRemainingLabel = formatTicketTypeRemaining(ticketType.availability.remainingPlaces);
3500
3872
  const priceLabel = ticketType.priceAmount > 0 ? formatPrice(ticketType.priceAmount, props.currency) : "Free";
3501
3873
  const maxAllowed = disabled ? 0 : getTicketTypeLineMax({
3502
3874
  currentQuantity: quantity,
3503
3875
  totalSelectedQuantity,
3504
3876
  maxTickets: props.maxTickets,
3505
3877
  availableSpots: props.availableSpots,
3506
- ticketTypeRemainingCapacity: ticketType.availability.remainingCapacity
3878
+ ticketTypeRemainingCapacity: ticketType.availability.remainingPlaces
3507
3879
  });
3508
3880
  return /* @__PURE__ */ jsxs("div", {
3509
3881
  className: `er-ticket-line${disabled ? " er-ticket-line--disabled" : ""}`,
@@ -3528,9 +3900,9 @@ function TicketTypeQuantityRows(props) {
3528
3900
  availabilityLabel ? /* @__PURE__ */ jsx("p", {
3529
3901
  className: "er-ticket-line-detail",
3530
3902
  children: availabilityLabel
3531
- }) : ticketType.availability.remainingCapacity !== null ? /* @__PURE__ */ jsxs("p", {
3903
+ }) : ticketTypeRemainingLabel ? /* @__PURE__ */ jsx("p", {
3532
3904
  className: "er-ticket-line-detail",
3533
- children: [ticketType.availability.remainingCapacity, " remaining for this ticket type"]
3905
+ children: ticketTypeRemainingLabel
3534
3906
  }) : null
3535
3907
  ]
3536
3908
  }), /* @__PURE__ */ jsx(TicketCounter, {
@@ -3541,17 +3913,19 @@ function TicketTypeQuantityRows(props) {
3541
3913
  onTicketsChange: (nextQuantity) => {
3542
3914
  props.onTicketSelectionsChange?.(setTicketLineQuantity({
3543
3915
  lines: props.ticketSelections,
3544
- ticketTypeId: ticketType.id,
3916
+ pricingOptionId: ticketType.pricingOptionId,
3545
3917
  quantity: nextQuantity
3546
3918
  }));
3547
3919
  }
3548
3920
  })]
3549
- }, ticketType.id);
3921
+ }, ticketType.pricingOptionId);
3550
3922
  })
3551
3923
  }),
3552
3924
  /* @__PURE__ */ jsx(TicketCounterLimit, {
3553
- maximumLabel: `${props.maxTickets} ${pluralizeBookingUnit(props.bookingUnit, props.maxTickets)} maximum`,
3554
- availabilityLabel: props.availableSpots != null ? `${props.availableSpots} spots available` : null
3925
+ maxAllowed: effectiveMaxTickets,
3926
+ quantityLabel: pluralizeBookingUnit(props.bookingUnit, effectiveMaxTickets),
3927
+ availableSpots: props.availableSpots,
3928
+ availabilityLabel: props.availabilityLabel
3555
3929
  }),
3556
3930
  totalSelectedQuantity === 0 && props.hasAvailableTicketTypes ? /* @__PURE__ */ jsxs("p", {
3557
3931
  className: "er-text er-text--muted",
@@ -3572,9 +3946,13 @@ function TicketTypeQuantityRows(props) {
3572
3946
  });
3573
3947
  }
3574
3948
  function getAttendanceModeDetail(attendanceMode) {
3575
- if (attendanceMode.availableSpots === null) return "Unlimited spots";
3576
- if (attendanceMode.availableSpots === 0) return attendanceMode.waitlistEnabled ? "Currently full, waitlist available" : "Currently full";
3577
- return `${attendanceMode.availableSpots} spots available`;
3949
+ const remaining = getRemainingPlaces(attendanceMode);
3950
+ switch (remaining.kind) {
3951
+ case "unlimited": return "Unlimited places";
3952
+ case "open": return "Places available";
3953
+ case "low": return formatRemainingPlaces(attendanceMode);
3954
+ case "full": return remaining.waitlist ? "Currently full, waitlist available" : "Currently full";
3955
+ }
3578
3956
  }
3579
3957
  function AttendanceModeTiles(props) {
3580
3958
  return /* @__PURE__ */ jsx("div", {
@@ -3608,6 +3986,12 @@ function getMaxAllowedTicketCount(input) {
3608
3986
  if (!input.hasSelectedTicketType) return 1;
3609
3987
  return Math.max(1, Math.min(input.maxTickets, input.selectedTicketTypeRemainingCapacity ?? input.maxTickets, input.availableSpots ?? input.maxTickets));
3610
3988
  }
3989
+ function resolveTicketSelectionPricing(event) {
3990
+ return {
3991
+ mode: event ? publicEventPricingStrategy(event.pricing) : void 0,
3992
+ currency: event ? publicEventPricingCurrency(event.pricing) : null
3993
+ };
3994
+ }
3611
3995
  /**
3612
3996
  * Ticket Selection Step
3613
3997
  *
@@ -3620,15 +4004,13 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3620
4004
  const unit = bookingUnit.kind;
3621
4005
  const deliveryMode = event?.deliveryMode ?? "in_person";
3622
4006
  const attendanceModes = event?.attendanceModes ?? [];
3623
- const selectedModeAvailability = selectedAttendanceMode ? attendanceModes.find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
3624
- const availableSpots = selectedModeAvailability?.availableSpots ?? event?.availableSpots;
3625
- const waitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? event?.waitlistEnabled ?? false;
3626
- const isSoldOut = availableSpots === 0;
3627
- const canJoinWaitlist = isSoldOut && waitlistEnabled === true;
3628
- const eventPricing = event ? resolveRuntimeEventPricing(event) : null;
3629
- const eventPricingMode = eventPricing ? publicEventPricingMode(eventPricing) : void 0;
3630
- const eventPricingCurrency = eventPricing ? publicEventPricingCurrency(eventPricing) : null;
3631
- const usesTicketTypes = eventPricingMode === "ticket_types" && !canJoinWaitlist;
4007
+ const capacitySource = event ? selectEventCapacitySource(event, selectedAttendanceMode) : null;
4008
+ const availability = capacitySource ? getEventAvailability(capacitySource) : null;
4009
+ const availableSpots = availability?.available;
4010
+ const isSoldOut = availability?.isFull ?? false;
4011
+ const canJoinWaitlist = capacitySource ? canJoinEventWaitlist(capacitySource) : false;
4012
+ const { mode: eventPricingMode, currency: eventPricingCurrency } = resolveTicketSelectionPricing(event);
4013
+ const usesTicketTypes = eventPricingMode === "tiered" && !canJoinWaitlist;
3632
4014
  const visibleTicketTypes = usesTicketTypes ? visibleTicketTypesOverride ?? getVisibleTicketTypes({
3633
4015
  event,
3634
4016
  eligibility: null,
@@ -3642,10 +4024,10 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3642
4024
  usesTicketTypes,
3643
4025
  maxTickets,
3644
4026
  availableSpots,
3645
- selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingCapacity ?? null,
4027
+ selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingPlaces ?? null,
3646
4028
  hasSelectedTicketType: implicitTicketType !== null
3647
4029
  });
3648
- const eventAvailabilityLabel = !canJoinWaitlist && event?.availableSpots != null ? `${event.availableSpots} spots available` : null;
4030
+ const eventAvailabilityLabel = !canJoinWaitlist && capacitySource ? formatRemainingPlaces(capacitySource) : null;
3649
4031
  if (deliveryMode === "hybrid" && !selectedAttendanceMode) return /* @__PURE__ */ jsxs("div", {
3650
4032
  className: "rb-reg-stack er-stack",
3651
4033
  children: [
@@ -3668,12 +4050,19 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3668
4050
  className: "rb-reg-stack er-stack",
3669
4051
  children: [/* @__PURE__ */ jsx("h3", {
3670
4052
  className: "er-title",
3671
- children: "Sold Out"
4053
+ children: "Sold out"
3672
4054
  }), /* @__PURE__ */ jsx("p", {
3673
4055
  className: "er-text er-text--muted",
3674
4056
  children: "Sorry you missed out! This event is fully booked."
3675
4057
  })]
3676
4058
  });
4059
+ if (!eventPricingCurrency) return /* @__PURE__ */ jsx("div", {
4060
+ className: "rb-reg-stack er-stack",
4061
+ children: /* @__PURE__ */ jsx("p", {
4062
+ className: "er-text er-text--muted",
4063
+ children: "Pricing is unavailable for this event right now."
4064
+ })
4065
+ });
3677
4066
  return /* @__PURE__ */ jsxs("div", {
3678
4067
  className: "rb-reg-stack er-stack",
3679
4068
  children: [
@@ -3699,7 +4088,8 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3699
4088
  ticketSelections,
3700
4089
  maxTickets,
3701
4090
  availableSpots,
3702
- currency: eventPricingCurrency ?? "usd",
4091
+ availabilityLabel: eventAvailabilityLabel,
4092
+ currency: eventPricingCurrency,
3703
4093
  hasAvailableTicketTypes,
3704
4094
  onTicketSelectionsChange,
3705
4095
  bookingUnit: unit
@@ -3707,7 +4097,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3707
4097
  ] }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3708
4098
  slidingScale ? /* @__PURE__ */ jsx(SlidingScaleAmountField, {
3709
4099
  slidingScale,
3710
- currency: eventPricingCurrency ?? "usd",
4100
+ currency: eventPricingCurrency,
3711
4101
  value: selectedSlidingAmountInput,
3712
4102
  message: slidingScaleSelectionMessage,
3713
4103
  onChange: onSlidingAmountChange
@@ -3725,14 +4115,16 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3725
4115
  label: bookingQuantityLabel(unit, canJoinWaitlist),
3726
4116
  onTicketsChange
3727
4117
  }), /* @__PURE__ */ jsx(TicketCounterLimit, {
3728
- maximumLabel: `${maxAllowed} ${canJoinWaitlist ? bookingQuantityLabel(unit, true) : pluralizeBookingUnit(unit, maxAllowed)} maximum`,
4118
+ maxAllowed,
4119
+ quantityLabel: canJoinWaitlist ? bookingQuantityLabel(unit, true) : pluralizeBookingUnit(unit, maxAllowed),
4120
+ availableSpots: canJoinWaitlist ? null : availableSpots,
3729
4121
  availabilityLabel: eventAvailabilityLabel
3730
4122
  })]
3731
4123
  })
3732
4124
  ] }),
3733
4125
  canJoinWaitlist && /* @__PURE__ */ jsx("p", {
3734
4126
  className: "er-text er-text--muted",
3735
- children: "This event is currently full. Continue to join the waitlist if a spot opens up."
4127
+ children: "This event is currently full. Continue to join the waitlist if a place opens up."
3736
4128
  })
3737
4129
  ]
3738
4130
  });
@@ -4204,162 +4596,6 @@ function SiteCreditPreviewPanel(props) {
4204
4596
  });
4205
4597
  }
4206
4598
  //#endregion
4207
- //#region ../blocks/src/system/runtime/nodes/event-registration/PaymentBlockingPanel.tsx
4208
- function resolvePaymentStepStatus(input) {
4209
- if (input.loading) return "loading";
4210
- if (input.error || !input.eligibility) return "error";
4211
- if (input.eligibility.registration.kind !== "none" || !input.eligibility.eligible && !input.eligibility.isFree) return "not-eligible";
4212
- return "ready";
4213
- }
4214
- function shouldPromptForLogin(status, isAuthenticated, eligibility) {
4215
- return Boolean(status === "not-eligible" && !isAuthenticated && eligibility && (eligibility.requiresAuthForPasses || eligibility.requiresAuthForMemberships));
4216
- }
4217
- function MissingEligibilityPanel(props) {
4218
- const missing = [!props.occurrenceId && "occurrenceId", !props.email && "email"].filter((part) => Boolean(part));
4219
- return /* @__PURE__ */ jsx("div", {
4220
- className: "rb-reg-stack er-stack",
4221
- children: /* @__PURE__ */ jsx("div", {
4222
- className: "er-center",
4223
- children: /* @__PURE__ */ jsxs("p", {
4224
- className: "er-text er-text--error",
4225
- children: ["Unable to determine payment options", missing.length > 0 && ` (missing: ${missing.join(", ")})`]
4226
- })
4227
- })
4228
- });
4229
- }
4230
- function UnavailableEntitlementMessage(props) {
4231
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4232
- /* @__PURE__ */ jsx("p", {
4233
- className: "er-text er-text--error",
4234
- children: "This event requires a valid pass or membership to register."
4235
- }),
4236
- props.eligibility.eventAcceptsPasses && !props.eligibility.eventAcceptsMemberships && /* @__PURE__ */ jsx("p", {
4237
- className: "er-text er-text--muted",
4238
- children: "You need a pass with credits for this event category."
4239
- }),
4240
- props.eligibility.eventAcceptsMemberships && !props.eligibility.eventAcceptsPasses && /* @__PURE__ */ jsx("p", {
4241
- className: "er-text er-text--muted",
4242
- children: "You need an active membership for this event category."
4243
- })
4244
- ] });
4245
- }
4246
- function IneligiblePaymentPanel(props) {
4247
- if (props.eligibility.isFull && !props.eligibility.canJoinWaitlist) return /* @__PURE__ */ jsxs("div", {
4248
- className: "rb-reg-stack er-stack",
4249
- children: [/* @__PURE__ */ jsx("h3", {
4250
- className: "er-title",
4251
- children: "Sold Out"
4252
- }), /* @__PURE__ */ jsx("div", {
4253
- className: "er-center",
4254
- children: /* @__PURE__ */ jsx("p", {
4255
- className: "er-text er-text--muted",
4256
- children: "This event is fully booked and there is no waitlist available for this booking."
4257
- })
4258
- })]
4259
- });
4260
- const promptLogin = shouldPromptForLogin("not-eligible", props.isAuthenticated, props.eligibility);
4261
- return /* @__PURE__ */ jsxs("div", {
4262
- className: "rb-reg-stack er-stack",
4263
- children: [/* @__PURE__ */ jsx("h3", {
4264
- className: "er-title",
4265
- children: promptLogin ? "Log In Required" : "Payment Required"
4266
- }), /* @__PURE__ */ jsx("div", {
4267
- className: "er-center",
4268
- children: promptLogin ? /* @__PURE__ */ jsxs(Fragment, { children: [
4269
- /* @__PURE__ */ jsx("p", {
4270
- className: "er-text er-text--muted",
4271
- children: "Have a pass or membership? Log in to use it."
4272
- }),
4273
- /* @__PURE__ */ jsx("button", {
4274
- type: "button",
4275
- className: themeButtonClassName({
4276
- variant: "primary",
4277
- size: "md"
4278
- }),
4279
- disabled: !props.email.trim(),
4280
- onClick: props.onRequestLogin,
4281
- children: "Email me a login link"
4282
- }),
4283
- !props.email.trim() && /* @__PURE__ */ jsx("p", {
4284
- className: "er-text er-text--muted",
4285
- children: "Enter your email to request a login link."
4286
- })
4287
- ] }) : /* @__PURE__ */ jsx(UnavailableEntitlementMessage, { eligibility: props.eligibility })
4288
- })]
4289
- });
4290
- }
4291
- function PaymentBlockingPanel(props) {
4292
- if (props.eligibilityLoading) return /* @__PURE__ */ jsx("div", {
4293
- className: "rb-reg-stack er-stack",
4294
- children: /* @__PURE__ */ jsxs("div", {
4295
- className: "er-center",
4296
- children: [/* @__PURE__ */ jsx(SpinnerNode, {
4297
- size: "lg",
4298
- srLabel: "Checking payment options"
4299
- }), /* @__PURE__ */ jsx("p", {
4300
- className: "er-text--muted",
4301
- children: "Checking payment options..."
4302
- })]
4303
- })
4304
- });
4305
- if (props.eligibilityError) return /* @__PURE__ */ jsx("div", {
4306
- className: "rb-reg-stack er-stack",
4307
- children: /* @__PURE__ */ jsx("div", {
4308
- className: "er-center",
4309
- children: /* @__PURE__ */ jsx("p", {
4310
- className: "er-text er-text--error",
4311
- children: props.eligibilityError
4312
- })
4313
- })
4314
- });
4315
- if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {
4316
- occurrenceId: props.occurrenceId,
4317
- email: props.email
4318
- });
4319
- if (props.eligibility.registration.kind !== "none") {
4320
- const isBooked = props.eligibility.registration.kind === "booked";
4321
- return /* @__PURE__ */ jsxs("div", {
4322
- className: "rb-reg-stack er-stack",
4323
- children: [/* @__PURE__ */ jsx("h3", {
4324
- className: "er-title",
4325
- children: isBooked ? "Already booked" : "Already on the waitlist"
4326
- }), /* @__PURE__ */ jsx("div", {
4327
- className: "er-center",
4328
- children: /* @__PURE__ */ jsx("p", {
4329
- className: "er-text er-text--muted",
4330
- children: isBooked ? "You're already booked on this event." : "You're already on the waitlist for this event."
4331
- })
4332
- })]
4333
- });
4334
- }
4335
- if (props.eligibility.isFree) return /* @__PURE__ */ jsxs("div", {
4336
- className: "rb-reg-stack er-stack",
4337
- children: [/* @__PURE__ */ jsx("h3", {
4338
- className: "er-title",
4339
- children: "Payment"
4340
- }), /* @__PURE__ */ jsxs("div", {
4341
- className: "er-payment-summary",
4342
- children: [/* @__PURE__ */ jsx("p", {
4343
- className: "er-text er-text--success",
4344
- children: "This event is free!"
4345
- }), /* @__PURE__ */ jsxs("p", {
4346
- className: "er-text er-text--muted",
4347
- children: [
4348
- props.ticketsCount,
4349
- " ticket",
4350
- props.ticketsCount > 1 ? "s" : ""
4351
- ]
4352
- })]
4353
- })]
4354
- });
4355
- return props.eligibility.eligible ? null : /* @__PURE__ */ jsx(IneligiblePaymentPanel, {
4356
- eligibility: props.eligibility,
4357
- email: props.email,
4358
- isAuthenticated: props.isAuthenticated,
4359
- onRequestLogin: props.onRequestLogin
4360
- });
4361
- }
4362
- //#endregion
4363
4599
  //#region ../blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.tsx
4364
4600
  /**
4365
4601
  * Payment Selection Step Component
@@ -4448,12 +4684,12 @@ function DirectPaymentTiles(props) {
4448
4684
  * names the pass instead of describing the step.
4449
4685
  */
4450
4686
  function resolvePaymentButtonText(method, siteCreditPreview, eligibility, selectedDirectOption) {
4451
- const currency = eligibility?.currency ?? "GBP";
4452
4687
  switch (method) {
4453
4688
  case "stripe":
4454
- if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to Payment";
4455
- if (siteCreditPreview.cardDueNowCents > 0) return `Pay ${formatPrice(siteCreditPreview.cardDueNowCents, currency)} by card`;
4456
- return `Book with ${formatPrice(siteCreditPreview.appliedCreditCents, currency)} site credit`;
4689
+ if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to payment";
4690
+ if (!eligibility) return "Continue to Payment";
4691
+ if (siteCreditPreview.cardDueNowCents > 0) return `Pay ${formatPrice(siteCreditPreview.cardDueNowCents, eligibility.currency)} by card`;
4692
+ return `Book with ${formatPrice(siteCreditPreview.appliedCreditCents, eligibility.currency)} site credit`;
4457
4693
  case "pass": return "Book using class pass";
4458
4694
  case "pass_bundle":
4459
4695
  case "membership_bundle": return "Purchase & Register";
@@ -4483,7 +4719,7 @@ function canContinueWithPaymentSelection(selection, directPaymentSelection) {
4483
4719
  const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, isAuthenticated = false, creditState = { kind: "not_authenticated" }, directPaymentTotalCents, manualDiscountPending = false, selectedSelection, onSelectionChange, eligibility, eligibilityLoading, eligibilityError = null, onRequestLogin, onStateChange }) => {
4484
4720
  const occurrenceId = occurrenceContext?.id ?? (event?.registrationSubject.kind === "occurrence" ? event.registrationSubject.occurrenceId : "");
4485
4721
  const selectedMethod = selectedSelection.method;
4486
- const totalPrice = resolveDirectPaymentTotalCents(directPaymentTotalCents, eligibility);
4722
+ const totalPrice = directPaymentTotalCents;
4487
4723
  const canShowDirectPaymentOptions = Boolean(eligibility?.eligible && eligibility.canPayDirectly && eligibility.price);
4488
4724
  const directPaymentOptions = eligibility && canShowDirectPaymentOptions ? buildEventRegistrationPaymentOptionsViewModel({
4489
4725
  eventTitle: event?.title ?? "Event",
@@ -4491,7 +4727,7 @@ const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, i
4491
4727
  paymentTerms: eligibility.paymentTerms,
4492
4728
  totalPriceCents: totalPrice,
4493
4729
  currency: eligibility.currency,
4494
- startsAt: occurrenceContext?.startsAt ?? event?.startsAt
4730
+ startsAt: getRegistrationStartAt(event, occurrenceContext)
4495
4731
  }).options : [];
4496
4732
  const directPaymentSelectionState = resolveEventRegistrationDirectPaymentSelection({
4497
4733
  options: directPaymentOptions,
@@ -4724,6 +4960,53 @@ function AttendeeDetailsContent(props) {
4724
4960
  })
4725
4961
  ] });
4726
4962
  }
4963
+ function PaymentSelectionContent(props) {
4964
+ const { wizard } = props;
4965
+ const subtotalCents = wizard.ticketSelectionSubtotalCents;
4966
+ if (!wizard.api.config.siteId) return null;
4967
+ if (subtotalCents === null) return /* @__PURE__ */ jsx(PaymentBlockingPanel, {
4968
+ eligibility: wizard.eligibility,
4969
+ eligibilityLoading: wizard.eligibilityLoading,
4970
+ eligibilityError: wizard.eligibilityError,
4971
+ occurrenceId: wizard.occurrenceContext?.id ?? "",
4972
+ email: wizard.formData.email,
4973
+ ticketsCount: wizard.ticketsCount,
4974
+ isAuthenticated: wizard.portalAuthState === "authenticated",
4975
+ onRequestLogin: () => wizard.setShowMagicLinkModal(true)
4976
+ });
4977
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PaymentSelectionStep, {
4978
+ event: wizard.selectedEvent,
4979
+ occurrenceContext: wizard.occurrenceContext,
4980
+ ticketsCount: wizard.ticketsCount,
4981
+ email: wizard.formData.email,
4982
+ isAuthenticated: wizard.portalAuthState === "authenticated",
4983
+ creditState: wizard.portalCreditState,
4984
+ directPaymentTotalCents: calculateEventRegistrationPreviewTotal({
4985
+ subtotalCents,
4986
+ manualDiscountCode: wizard.discountCode,
4987
+ automaticDiscount: wizard.autoAppliedDiscount
4988
+ }),
4989
+ manualDiscountPending: wizard.discountCode.trim().length > 0,
4990
+ selectedSelection: wizard.paymentSelection,
4991
+ onSelectionChange: wizard.handlePaymentSelectionChange,
4992
+ eligibility: wizard.eligibility,
4993
+ eligibilityLoading: wizard.eligibilityLoading,
4994
+ eligibilityError: wizard.eligibilityError,
4995
+ onRequestLogin: () => wizard.setShowMagicLinkModal(true),
4996
+ onStateChange: wizard.handlePaymentStepStateChange
4997
+ }), wizard.discountsAvailableLoading ? null : wizard.shouldShowDiscountToggle ? /* @__PURE__ */ jsx(DiscountCodeDisclosure, {
4998
+ surface: "event-registration",
4999
+ isOpen: wizard.discountUiOpen,
5000
+ code: wizard.discountCode,
5001
+ onOpen: () => wizard.setDiscountUiOpen(true),
5002
+ onChange: wizard.setDiscountCode,
5003
+ onRemove: () => {
5004
+ wizard.setDiscountCode("");
5005
+ wizard.setDiscountUiOpen(false);
5006
+ },
5007
+ id: "er-discount-code"
5008
+ }) : null] });
5009
+ }
4727
5010
  function EventRegistrationWizardContent(props) {
4728
5011
  const { wizard } = props;
4729
5012
  const showSpamProtection = props.showSpamProtection ?? isSpamProtectionEnabled();
@@ -4768,40 +5051,7 @@ function EventRegistrationWizardContent(props) {
4768
5051
  wizard,
4769
5052
  showSpamProtection
4770
5053
  });
4771
- case "select-payment":
4772
- if (!wizard.api.config.siteId) return null;
4773
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PaymentSelectionStep, {
4774
- event: wizard.selectedEvent,
4775
- occurrenceContext: wizard.occurrenceContext,
4776
- ticketsCount: wizard.ticketsCount,
4777
- email: wizard.formData.email,
4778
- isAuthenticated: wizard.portalAuthState === "authenticated",
4779
- creditState: wizard.portalCreditState,
4780
- directPaymentTotalCents: calculateEventRegistrationPreviewTotal({
4781
- subtotalCents: wizard.ticketSelectionSubtotalCents,
4782
- manualDiscountCode: wizard.discountCode,
4783
- automaticDiscount: wizard.autoAppliedDiscount
4784
- }),
4785
- manualDiscountPending: wizard.discountCode.trim().length > 0,
4786
- selectedSelection: wizard.paymentSelection,
4787
- onSelectionChange: wizard.handlePaymentSelectionChange,
4788
- eligibility: wizard.eligibility,
4789
- eligibilityLoading: wizard.eligibilityLoading,
4790
- eligibilityError: wizard.eligibilityError,
4791
- onRequestLogin: () => wizard.setShowMagicLinkModal(true),
4792
- onStateChange: wizard.handlePaymentStepStateChange
4793
- }), wizard.discountsAvailableLoading ? null : wizard.shouldShowDiscountToggle ? /* @__PURE__ */ jsx(DiscountCodeDisclosure, {
4794
- surface: "event-registration",
4795
- isOpen: wizard.discountUiOpen,
4796
- code: wizard.discountCode,
4797
- onOpen: () => wizard.setDiscountUiOpen(true),
4798
- onChange: wizard.setDiscountCode,
4799
- onRemove: () => {
4800
- wizard.setDiscountCode("");
4801
- wizard.setDiscountUiOpen(false);
4802
- },
4803
- id: "er-discount-code"
4804
- }) : null] });
5054
+ case "select-payment": return /* @__PURE__ */ jsx(PaymentSelectionContent, { wizard });
4805
5055
  default: return null;
4806
5056
  }
4807
5057
  }
@@ -4827,7 +5077,19 @@ function StepIndicator(props) {
4827
5077
  *
4828
5078
  * Displays an error message with a retry button when registration fails.
4829
5079
  */
4830
- const ErrorStep = ({ message, onRetry }) => {
5080
+ const labels = {
5081
+ none: "",
5082
+ retry: "Try again",
5083
+ places: "Choose places",
5084
+ start: "Start again",
5085
+ details: "Check my details",
5086
+ "payment-options": "View payment options",
5087
+ "payment-status": "Check payment status",
5088
+ portal: "View my bookings",
5089
+ event: "View event"
5090
+ };
5091
+ const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false }) => {
5092
+ const offerWaitlist = action === "places" && canJoinWaitlist;
4831
5093
  return /* @__PURE__ */ jsx(TerminalPanel, {
4832
5094
  tone: "error",
4833
5095
  className: "er-center",
@@ -4836,16 +5098,20 @@ const ErrorStep = ({ message, onRetry }) => {
4836
5098
  variant: "error",
4837
5099
  children: /* @__PURE__ */ jsx(CrossIcon, {})
4838
5100
  }),
4839
- title: "Registration Failed",
5101
+ title: "We couldn’t complete your booking",
4840
5102
  body: /* @__PURE__ */ jsx("p", {
4841
5103
  className: "er-text--muted er-message",
4842
- children: message
5104
+ children: offerWaitlist ? "Sorry, this event is now full. You can join the waiting list, and we’ll email you if a place becomes available." : message
4843
5105
  }),
4844
- actions: /* @__PURE__ */ jsx("button", {
5106
+ actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
5107
+ href: "/portal",
5108
+ className: "er-retry-btn",
5109
+ children: labels[action]
5110
+ }) : /* @__PURE__ */ jsx("button", {
4845
5111
  type: "button",
4846
5112
  onClick: onRetry,
4847
5113
  className: "er-retry-btn",
4848
- children: "Try Again"
5114
+ children: offerWaitlist ? "Join the waiting list" : labels[action]
4849
5115
  })
4850
5116
  });
4851
5117
  };
@@ -4857,7 +5123,7 @@ const ErrorStep = ({ message, onRetry }) => {
4857
5123
  * Displays when the payment was definitively declined/failed.
4858
5124
  * Provides a retry option that returns to payment selection with form state preserved.
4859
5125
  */
4860
- const PaymentFailedStep = ({ message, onRetry }) => {
5126
+ const PaymentFailedStep = ({ onRetry }) => {
4861
5127
  return /* @__PURE__ */ jsx(TerminalPanel, {
4862
5128
  tone: "error",
4863
5129
  className: "er-center",
@@ -4866,16 +5132,16 @@ const PaymentFailedStep = ({ message, onRetry }) => {
4866
5132
  variant: "error",
4867
5133
  children: /* @__PURE__ */ jsx(CrossIcon, {})
4868
5134
  }),
4869
- title: "Payment Failed",
5135
+ title: "Your payment didn’t go through",
4870
5136
  body: /* @__PURE__ */ jsx("p", {
4871
5137
  className: "er-text--muted er-message",
4872
- children: message
5138
+ children: "Please try again or choose another payment option. If this keeps happening, contact the organiser for help."
4873
5139
  }),
4874
5140
  actions: /* @__PURE__ */ jsx("button", {
4875
5141
  type: "button",
4876
5142
  onClick: onRetry,
4877
5143
  className: "er-retry-btn",
4878
- children: "Try Again"
5144
+ children: "View payment options"
4879
5145
  })
4880
5146
  });
4881
5147
  };
@@ -4970,7 +5236,7 @@ const VerifyingPaymentStep = () => {
4970
5236
  * - "Check Again" button to restart polling
4971
5237
  * - Contact support option for user reassurance
4972
5238
  */
4973
- const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "Your registration is saved", message = "We're still confirming your payment. This usually resolves within a few minutes. You'll receive a confirmation email once everything is verified." }) => {
5239
+ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "We’re checking your payment", message = "Your booking is not confirmed yet. Please don’t make another booking while we check. You can check your payment status again in a moment." }) => {
4974
5240
  return /* @__PURE__ */ jsx(TerminalPanel, {
4975
5241
  tone: "warning",
4976
5242
  className: "er-center",
@@ -4989,8 +5255,12 @@ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "Your regist
4989
5255
  type: "button",
4990
5256
  onClick: onCheckAgain,
4991
5257
  className: "er-btn er-btn--primary",
4992
- children: "Check Again"
4993
- }) : void 0,
5258
+ children: "Check payment status"
5259
+ }) : /* @__PURE__ */ jsx("a", {
5260
+ href: "/portal",
5261
+ className: "er-btn er-btn--primary",
5262
+ children: "Check payment status"
5263
+ }),
4994
5264
  secondaryBody: supportEmail ? /* @__PURE__ */ jsxs("p", {
4995
5265
  className: "er-text--sm er-text--muted er-message",
4996
5266
  children: [
@@ -5086,7 +5356,7 @@ function EventRegistrationTerminalState(props) {
5086
5356
  onRetry: () => wizard.setStep("select-payment")
5087
5357
  });
5088
5358
  case "verifying-timeout": return /* @__PURE__ */ jsx(VerifyingTimeoutStep, {
5089
- onCheckAgain: wizard.verification.status === "timeout" ? wizard.verification.retry : void 0,
5359
+ onCheckAgain: wizard.verification.status === "timeout" ? wizard.verification.retry : wizard.verification.retryReturn,
5090
5360
  title: wizard.verification.status === "timeout" ? void 0 : "We’re checking your payment",
5091
5361
  message: wizard.verification.status === "timeout" ? void 0 : wizard.errorMessage,
5092
5362
  supportEmail: props.supportEmail
@@ -5098,6 +5368,8 @@ function EventRegistrationTerminalState(props) {
5098
5368
  }) : null;
5099
5369
  case "error": return /* @__PURE__ */ jsx(ErrorStep, {
5100
5370
  message: wizard.errorMessage,
5371
+ action: wizard.errorAction,
5372
+ canJoinWaitlist: wizard.eligibility?.isFull && wizard.eligibility.canJoinWaitlist,
5101
5373
  onRetry: wizard.handleRetry
5102
5374
  });
5103
5375
  case "select-event":
@@ -5173,7 +5445,7 @@ function BookingEntry(props) {
5173
5445
  className: "er-booking-teaser__ticket-name",
5174
5446
  children: ticketType.name
5175
5447
  }), /* @__PURE__ */ jsx("strong", { children: props.formatTicketPrice(ticketType) })]
5176
- }, ticketType.id))
5448
+ }, ticketType.pricingOptionId))
5177
5449
  }) : null
5178
5450
  ]
5179
5451
  }), /* @__PURE__ */ jsx("button", {
@@ -5255,21 +5527,28 @@ const EVENT_PORTAL_AUTH_COPY = {
5255
5527
  buttonText: "Email me a login link →"
5256
5528
  },
5257
5529
  "waitlist-claim": {
5258
- title: "Log in to claim your waitlist spot",
5530
+ title: "Log in to claim your waitlist place",
5259
5531
  buttonText: "Email me a login link to continue →"
5260
5532
  }
5261
5533
  };
5262
5534
  function getActiveTicketTypes(event) {
5263
- return event?.pricing.kind === "ticket_types" ? event.pricing.ticketTypes.filter((ticketType) => ticketType.isActive).sort((left, right) => left.sortOrder - right.sortOrder) : [];
5535
+ return event?.pricing.kind === "tiered" ? event.pricing.options.filter((ticketType) => ticketType.isActive).sort((left, right) => left.sortOrder - right.sortOrder).map(({ id, ...ticketType }) => ({
5536
+ ...ticketType,
5537
+ pricingOptionId: id
5538
+ })) : [];
5264
5539
  }
5265
5540
  function formatTicketTypePrice(ticketType, currency) {
5266
5541
  return formatCurrencyAmount(ticketType.priceAmount, currency, { decimals: "auto" });
5267
5542
  }
5268
- function getAvailabilityLabel(event) {
5269
- const availableSpots = event?.availableSpots;
5270
- if (availableSpots === null || availableSpots === void 0) return null;
5271
- if (availableSpots > 0) return `${availableSpots} spots left`;
5272
- return event?.waitlistEnabled ? "Waitlist available" : "Fully booked";
5543
+ function formatTicketPriceIfKnown(ticketType, currency) {
5544
+ return currency ? formatTicketTypePrice(ticketType, currency) : "";
5545
+ }
5546
+ function getAvailabilityLabel(event, attendanceMode) {
5547
+ if (!event) return null;
5548
+ return formatRemainingPlaces(selectEventCapacitySource(event, attendanceMode), {
5549
+ full: "Fully booked",
5550
+ waitlist: "Waitlist available"
5551
+ });
5273
5552
  }
5274
5553
  function getWizardStepLabel(input) {
5275
5554
  switch (input.step) {
@@ -5287,7 +5566,7 @@ function getWizardStepLabel(input) {
5287
5566
  }
5288
5567
  }
5289
5568
  function EventRegistrationWizard(props) {
5290
- const { occurrenceContext, subject, events, wholeSeriesOfferings, maxTickets = "5", showVenue = true, showCapacity = true, successMessage = "Thank you for registering! Check your email for confirmation details.", buttonText = "Complete Registration", buttonVariant = "primary", className, spamProtectionEnabled, supportEmail } = props;
5569
+ const { occurrenceContext, subject, events, maxTickets = "5", showVenue = true, showCapacity = true, successMessage = "Thank you for registering! Check your email for confirmation details.", buttonText = "Complete Registration", buttonVariant = "primary", className, spamProtectionEnabled, supportEmail } = props;
5291
5570
  const maxTicketsNum = normalizeEventRegistrationMaxTickets(maxTickets);
5292
5571
  const showSpamProtection = spamProtectionEnabled ?? isSpamProtectionEnabled();
5293
5572
  const buttonClass = themeButtonClassName({
@@ -5301,7 +5580,6 @@ function EventRegistrationWizard(props) {
5301
5580
  occurrenceContext: occurrenceContext ?? null,
5302
5581
  subject: subject ?? null,
5303
5582
  events: events ?? [],
5304
- wholeSeriesOfferings: wholeSeriesOfferings ?? [],
5305
5583
  maxTicketsNum
5306
5584
  });
5307
5585
  const [bookingOverlayOpen, setBookingOverlayOpen] = useState(false);
@@ -5366,7 +5644,7 @@ function EventRegistrationWizard(props) {
5366
5644
  const currentStepNumber = getStepNumber(wizard.step, activeWizardSteps);
5367
5645
  const summaryEventPricingType = summaryEvent ? publicEventPricingType(summaryEvent.pricing) : "free";
5368
5646
  const summaryPriceLabel = summaryEvent ? formatEventPriceDisplay(summaryEvent.pricing) : null;
5369
- const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent);
5647
+ const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent, wizard.selectedAttendanceMode);
5370
5648
  const waitlistClaimRequiresPortalLogin = wizard.waitlistClaim.requiresPortalLogin && wizard.portalAuthState !== "authenticated" && wizard.portalAuthState !== "checking";
5371
5649
  const portalAuthCopy = waitlistClaimRequiresPortalLogin ? {
5372
5650
  ...EVENT_PORTAL_AUTH_COPY["waitlist-claim"],
@@ -5400,7 +5678,7 @@ function EventRegistrationWizard(props) {
5400
5678
  children: renderRegistrationSurface("inline")
5401
5679
  });
5402
5680
  const bookingTicketTypes = getActiveTicketTypes(summaryEvent);
5403
- const bookingTicketTypesCurrency = summaryEvent?.pricing.kind === "ticket_types" ? summaryEvent.pricing.currency : null;
5681
+ const bookingCurrency = summaryEvent?.pricing.kind === "tiered" ? summaryEvent.pricing.currency : null;
5404
5682
  return /* @__PURE__ */ jsxs("div", {
5405
5683
  ref: registrationRootRef,
5406
5684
  id: EVENT_REGISTRATION_ANCHOR_ID,
@@ -5414,9 +5692,9 @@ function EventRegistrationWizard(props) {
5414
5692
  priceLabel: summaryPriceLabel,
5415
5693
  availabilityLabel: summaryAvailabilityLabel,
5416
5694
  ticketTypes: bookingTicketTypes,
5417
- ticketCurrency: bookingTicketTypesCurrency,
5695
+ ticketCurrency: bookingCurrency,
5418
5696
  buttonClass,
5419
- formatTicketPrice: (ticketType) => formatTicketTypePrice(ticketType, bookingTicketTypesCurrency ?? "GBP"),
5697
+ formatTicketPrice: (ticketType) => formatTicketPriceIfKnown(ticketType, bookingCurrency),
5420
5698
  onOpen: () => setBookingOverlayOpen(true),
5421
5699
  onClose: () => setBookingOverlayOpen(false),
5422
5700
  overlaySurface: renderRegistrationSurface("overlay")
@@ -5424,6 +5702,52 @@ function EventRegistrationWizard(props) {
5424
5702
  });
5425
5703
  }
5426
5704
  //#endregion
5705
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationSurface.ts
5706
+ /**
5707
+ * Whether a run agrees with the offering whose page it arrived on.
5708
+ *
5709
+ * Scoping has already restricted the events to the subject's series, so the
5710
+ * series set needs no second check here. What is still worth checking is the
5711
+ * run's own idea of its parent: a run that claims a different offering means
5712
+ * the page and the data disagree about who owns it, and the surface fails
5713
+ * closed rather than taking a booking against an offering it cannot vouch for.
5714
+ */
5715
+ function agreesWithSubjectOffering(run, subject) {
5716
+ if (subject?.kind !== "eventOffering") return true;
5717
+ return run.run.eventOfferingId === subject.eventOfferingId;
5718
+ }
5719
+ /**
5720
+ * Subjects whose page only ever registers for a whole run.
5721
+ *
5722
+ * The subject's own booking scope decides, for an offering exactly as for a
5723
+ * series: an ordinary offering with no dates is a normal empty state, not a
5724
+ * whole-series page with nothing bookable.
5725
+ */
5726
+ function isWholeSeriesSubject(subject) {
5727
+ return (subject?.kind === "eventOffering" || subject?.kind === "eventSeries") && subject.bookingScope === "whole_series";
5728
+ }
5729
+ function planEventRegistrationSurface(input) {
5730
+ const scopedEvents = resolveEventRegistrationScopedEvents(input.events, {
5731
+ occurrenceContext: input.occurrenceContext,
5732
+ subject: input.subject
5733
+ });
5734
+ const bookingScopes = new Set(scopedEvents.map((event) => event.bookingScope));
5735
+ if (scopedEvents.length === 0 && isWholeSeriesSubject(input.subject)) return { kind: "unavailableWholeSeries" };
5736
+ if (!input.occurrenceContext && !input.subject && bookingScopes.has("single_occurrence")) return {
5737
+ kind: "occurrence",
5738
+ events: scopedEvents.filter((event) => event.bookingScope === "single_occurrence")
5739
+ };
5740
+ if (bookingScopes.size === 0 || bookingScopes.has("single_occurrence")) return bookingScopes.has("whole_series") ? { kind: "mixedBookingScopes" } : {
5741
+ kind: "occurrence",
5742
+ events: scopedEvents
5743
+ };
5744
+ const runs = groupPublicEventsByRun(scopedEvents);
5745
+ return runs.every((run) => agreesWithSubjectOffering(run, input.subject)) && runs.length > 0 ? {
5746
+ kind: "wholeSeries",
5747
+ runs
5748
+ } : { kind: "unavailableWholeSeries" };
5749
+ }
5750
+ //#endregion
5427
5751
  //#region ../blocks/src/system/runtime/nodes/event-registration/PastOccurrenceNotice.tsx
5428
5752
  function isPastOccurrence(startsAt) {
5429
5753
  if (!startsAt) return false;
@@ -5503,15 +5827,10 @@ function toEventRegistrationClientProps(props, display) {
5503
5827
  const EventRegistrationClient = (clientProps) => {
5504
5828
  const surface = planEventRegistrationSurface({
5505
5829
  events: clientProps.events ?? [],
5506
- wholeSeriesOfferings: clientProps.wholeSeriesOfferings ?? [],
5507
- wholeSeriesOffering: clientProps.wholeSeriesOffering,
5508
5830
  occurrenceContext: clientProps.occurrenceContext,
5509
5831
  subject: clientProps.subject
5510
5832
  });
5511
- if (surface.kind === "wholeSeries") return /* @__PURE__ */ jsx(EventRegistrationWizard, {
5512
- ...clientProps,
5513
- wholeSeriesOfferings: [...surface.offerings]
5514
- });
5833
+ if (surface.kind === "wholeSeries") return /* @__PURE__ */ jsx(EventRegistrationWizard, { ...clientProps });
5515
5834
  if (surface.kind === "unavailableWholeSeries" || surface.kind === "mixedBookingScopes") return /* @__PURE__ */ jsx("div", {
5516
5835
  className: clientProps.className,
5517
5836
  role: "status",