@riverbankcms/sdk 0.135.1 → 0.137.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 (220) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +4 -8
  3. package/dist/{PreviewEditorSidebar-D4p_5TdY.mjs → PreviewEditorSidebar-Bn6cONPa.mjs} +8 -8
  4. package/dist/{PreviewEditorUI-D0cRoTX1.mjs → PreviewEditorUI-LAwXZPSx.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-DcgaESkm.mjs → PreviewQueryProvider-ztQUdsNo.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-BKLtM3Nz.mjs → SdkPreviewModeRuntime-BbbGsI_v.mjs} +621 -238
  7. package/dist/{SiteChromeCustomizeContext-61X2m7QV.mjs → SiteChromeCustomizeContext-BbkNSt5a.mjs} +5041 -2407
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +18 -850
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +412 -2316
  10. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  11. package/dist/_dts/api/src/accessAdmin.d.ts +1 -9
  12. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  13. package/dist/_dts/api/src/contentRuntime.d.ts +7 -4
  14. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  15. package/dist/_dts/api/src/endpoints.d.ts +201 -181
  16. package/dist/_dts/api/src/index.d.ts +5 -3
  17. package/dist/_dts/api/src/media.d.ts +19 -2
  18. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  19. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +26 -0
  20. package/dist/_dts/api/src/sdkBlockContentAdmission.d.ts +5 -0
  21. package/dist/_dts/api/src/sdkContracts.d.ts +2 -15
  22. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  23. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +61 -0
  24. package/dist/_dts/api/src/sdkThemeWire.d.ts +21 -436
  25. package/dist/_dts/api/src/siteInfrastructure.d.ts +17 -23
  26. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +7 -21
  27. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +27 -3
  28. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  29. package/dist/_dts/api/src/types.d.ts +1 -1
  30. package/dist/_dts/block-form/src/blockDraftSaveability.d.ts +2 -3
  31. package/dist/_dts/block-form/src/utils/slug.d.ts +6 -0
  32. package/dist/_dts/blocks/src/PageRenderer.d.ts +13 -7
  33. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +48 -0
  34. package/dist/_dts/blocks/src/definitions.d.ts +14 -0
  35. package/dist/_dts/blocks/src/index.d.ts +4 -3
  36. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +49 -0
  37. package/dist/_dts/blocks/src/pageRendererBlockSupport.d.ts +75 -0
  38. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +2 -0
  39. package/dist/_dts/blocks/src/server.d.ts +2 -2
  40. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -1
  41. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -0
  42. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +78 -0
  43. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +2 -18
  44. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +103 -16
  45. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +1 -1
  46. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +4 -2
  47. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  48. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  49. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +19 -0
  50. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +52 -0
  51. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -0
  52. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +47 -21
  53. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +38 -9
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  55. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +3 -1
  56. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -0
  57. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  58. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  59. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  60. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  61. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  62. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +8 -3
  63. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -0
  64. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +5 -2
  65. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  66. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.client.d.ts +2 -2
  67. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +40 -0
  68. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +2 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +43 -0
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  74. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +31 -0
  76. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayCell.d.ts +3 -1
  77. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.d.ts +4 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/DayEventsModal.d.ts +2 -1
  79. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModalContext.d.ts +3 -2
  80. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  81. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +4 -1
  82. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +31 -3
  83. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekTimetableView.d.ts +3 -1
  84. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +9 -1
  85. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  87. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/timetableModel.d.ts +4 -3
  88. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +0 -1
  89. package/dist/_dts/calendar/src/grid.d.ts +24 -66
  90. package/dist/_dts/calendar/src/index.d.ts +2 -2
  91. package/dist/_dts/calendar/src/timetable.d.ts +5 -9
  92. package/dist/_dts/commerce/src/purchase/allocation.d.ts +11 -0
  93. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  94. package/dist/_dts/commerce/src/purchase/index.d.ts +3 -0
  95. package/dist/_dts/commerce/src/purchase/settlement.d.ts +33 -0
  96. package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +8 -0
  97. package/dist/_dts/content-model/src/templates/defaults.d.ts +6 -0
  98. package/dist/_dts/core/src/slug.d.ts +20 -5
  99. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +8 -40
  100. package/dist/_dts/db/src/index.d.ts +1 -0
  101. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  102. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  103. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  104. package/dist/_dts/preview-next/src/client/blocks/EditablePageRenderer.d.ts +1 -1
  105. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  106. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  107. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  108. package/dist/_dts/preview-next/src/client/preview/headerInteractionPaletteSurfaceState.d.ts +2 -1
  109. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  110. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  111. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  112. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  113. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  114. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  115. package/dist/_dts/sdk/src/client/publicLoaderCacheAccess.d.ts +6 -0
  116. package/dist/_dts/sdk/src/public-api/contracts.d.ts +7 -2
  117. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +10 -6
  118. package/dist/_dts/sdk/src/rendering/components/Block.shared.d.ts +0 -1
  119. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +4 -4
  120. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +3 -0
  121. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +3 -0
  122. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +1 -1
  123. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +17 -0
  124. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +9 -3
  125. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +1 -1
  126. package/dist/_dts/sdk/src/rendering/index.d.ts +2 -2
  127. package/dist/_dts/sdk/src/rendering/overrideResolution.d.ts +7 -4
  128. package/dist/_dts/sdk/src/version.d.ts +1 -1
  129. package/dist/_dts/theme-core/src/cards/types.d.ts +8 -8
  130. package/dist/_dts/theme-core/src/data.d.ts +12 -0
  131. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  132. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  133. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +25 -2
  134. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +2 -6
  135. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +9 -810
  136. package/dist/_dts/theme-core/src/schema.d.ts +460 -1285
  137. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  138. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +223 -1
  139. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  140. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  141. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  142. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -38
  143. package/dist/_dts/theme-core/src/site-styles/headerLayoutStyles.d.ts +10 -0
  144. package/dist/_dts/theme-core/src/site-styles/headerLayoutTypes.d.ts +8 -5
  145. package/dist/_dts/theme-core/src/site-styles/headerOpeningForeground.d.ts +4 -4
  146. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  147. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  148. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  149. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +25 -2
  150. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +465 -0
  151. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +168 -0
  152. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  153. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  154. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  155. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  156. package/dist/client/{BookingConsentAgreement-Bz9znbpn.mjs → BookingConsentAgreement--_fUCZe-.mjs} +2 -2
  157. package/dist/client/{DiscountCodeDisclosure-35Ru8yB9.mjs → DiscountCodeDisclosure-BMipSoDJ.mjs} +1 -1
  158. package/dist/client/{EventCombined.client-BKB0ZxRR.mjs → EventCombined.client-CwJOxn7A.mjs} +274 -278
  159. package/dist/client/{EventListing.client-Dx8I21ok.mjs → EventListing.client-eVwiylPW.mjs} +2 -2
  160. package/dist/client/{LeafletStaticMap-CEWx-Bhe.mjs → LeafletStaticMap-IKenna5u.mjs} +1 -1
  161. package/dist/client/{accordion-KIDsKTwS.mjs → accordion-MGAO3PuE.mjs} +1 -1
  162. package/dist/client/{bookingForm-D96Rmp-5.mjs → bookingForm-CudlQPUu.mjs} +5 -5
  163. package/dist/client/bookings.mjs +476 -98
  164. package/dist/client/capacity-BW00PO7s.mjs +271 -0
  165. package/dist/client/{carousel-YwUgyL66.mjs → carousel-BrbGJ2Jl.mjs} +1 -1
  166. package/dist/client/{cart-BbwuNrUT.mjs → cart-Bq5eU0xZ.mjs} +3 -3
  167. package/dist/client/{checkout-hp1ucmEY.mjs → checkout-BAFVRdrK.mjs} +3 -3
  168. package/dist/client/client.mjs +4641 -2721
  169. package/dist/client/{context-DljQn8L5.mjs → context-DCkiHVdD.mjs} +20 -5
  170. package/dist/client/dates-2RPXHQy4.mjs +264 -0
  171. package/dist/client/{eventCalendar-DU4iNLpo.mjs → eventCalendar-B0O-hPF7.mjs} +261 -126
  172. package/dist/client/{eventCombined-CZuupW8m.mjs → eventCombined-DCkDaoDI.mjs} +3 -3
  173. package/dist/client/{eventDetails-DPQWwCIi.mjs → eventDetails-D8GG2dnD.mjs} +7 -4
  174. package/dist/client/{eventListing-C7oMidwS.mjs → eventListing-3ssV_bxz.mjs} +3 -3
  175. package/dist/client/{eventRegistration-zEiunlUs.mjs → eventRegistration-bPQo3kVw.mjs} +610 -313
  176. package/dist/client/{form-D2KIyCVJ.mjs → form-qMv8RDkk.mjs} +2 -2
  177. package/dist/client/{headerSection-CK5uChUK.mjs → headerSection-CH0Sc5hX.mjs} +2 -2
  178. package/dist/client/hooks.mjs +2514 -308
  179. package/dist/client/{locationMap-DLQNH1ln.mjs → locationMap-0_xlBnNI.mjs} +2 -2
  180. package/dist/client/{newsletterForm-ZuhIG7fZ.mjs → newsletterForm-NUW7m9RI.mjs} +2 -2
  181. package/dist/client/{productDetail-7hIcGeae.mjs → productDetail-FBEOM7jW.mjs} +3 -3
  182. package/dist/client/{productList-T4Cii1ot.mjs → productList-R0OSGhnn.mjs} +3 -3
  183. package/dist/client/{propsDecoder-mB0aC_Fi.mjs → propsDecoder-pYvx_Srl.mjs} +179 -401
  184. package/dist/client/rendering/client.mjs +3496 -2223
  185. package/dist/client/rendering/islands.mjs +17 -17
  186. package/dist/client/rendering.mjs +4400 -2510
  187. package/dist/client/{scrollToBlock-Du5mHgvL.mjs → scrollToBlock-ByQv96M2.mjs} +1 -1
  188. package/dist/client/{shop-B5zAcKZn.mjs → shop-RUG9DmRE.mjs} +6 -6
  189. package/dist/client/{shop-commerce-BKBZB6l5.mjs → shop-commerce-B76Jz23B.mjs} +5 -5
  190. package/dist/client/{shop-commerce-store-Cq4qlNtQ.mjs → shop-commerce-store-QuQNdDsD.mjs} +1 -1
  191. package/dist/client/{shopShared-DYGzEs9D.mjs → shopShared-vXU1omwJ.mjs} +3 -3
  192. package/dist/client/{useEventFilters-Bphkda8m.mjs → useEventFilters-ByuU6HLA.mjs} +33 -18
  193. package/dist/preview-next/before-render.mjs +86 -51
  194. package/dist/preview-next/client/runtime.mjs +4 -4
  195. package/dist/preview-next/keepAlive.mjs +86 -51
  196. package/dist/preview-next/middleware.mjs +86 -51
  197. package/dist/{previewAuthStore-C7ksxt5b.mjs → previewAuthStore-L_cKW8Sb.mjs} +86 -51
  198. package/dist/{sdk-runtime-BbIC8G4J.mjs → sdk-runtime-sUMr5d70.mjs} +2 -2
  199. package/dist/{separator-CadhAtI7.mjs → separator-BIHkie5i.mjs} +1 -1
  200. package/dist/server/components.mjs +4397 -2960
  201. package/dist/server/config-validation.mjs +354 -145
  202. package/dist/server/config.mjs +354 -145
  203. package/dist/server/data.mjs +354 -144
  204. package/dist/server/index.mjs +201 -55
  205. package/dist/server/manifest.mjs +0 -1
  206. package/dist/server/next.mjs +4046 -2611
  207. package/dist/server/page-converter.mjs +225 -122
  208. package/dist/server/prebuild.mjs +1 -1
  209. package/dist/server/rendering/server.mjs +4105 -2636
  210. package/dist/server/rendering.mjs +4522 -3087
  211. package/dist/server/routing.mjs +3289 -1637
  212. package/dist/server/server.mjs +23175 -21079
  213. package/dist/server/theme-bridge.mjs +708 -272
  214. package/dist/server/theme.mjs +29 -25
  215. package/dist/styles/index.css +624 -181
  216. package/package.json +1 -1
  217. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  218. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  219. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  220. /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-Bz9znbpn.mjs";
3
- import { $t as formatCurrencyAmount, At as assertNever, C as getStepNumber, Jt as publicEventPricingCurrency, Kt as toBookingTimeZone, O as resolveEventRegistrationScopedEvents, Qt as publicEventTicketTypes, Rt as formatBookingDate, S as WIZARD_STEPS, T as planEventRegistrationSurface, V as formatEventPriceDisplay, Vt as formatBookingTime, Xt as publicEventPricingType, Y as resolveRuntimeEventPricing, Yt as publicEventPricingMode, Zt as publicEventScalarPrice, _ as findEventByOccurrenceId, b as resolveEventRequiresPayment, g as toWholeSeriesRegistrationEvents, h as toRegistrationEvents, qt as toScheduledBookingDateTime, st as themeButtonClassName, t as defineIslandPropsDecoder, v as findPreSelectedEvent, w as getWizardStepsForContext, x as resolveRegistrationTarget, y as resolveDiscountContext, zt as formatBookingDateTime } from "./propsDecoder-mB0aC_Fi.mjs";
4
- import { n as useBlockApi, t as BlockApiProvider } from "./context-DljQn8L5.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--_fUCZe-.mjs";
3
+ import { At as formatBookingDateTime, B as formatEventPriceDisplay, Bt as toScheduledBookingDateTime, C as getStepNumber, Gt as publicEventTicketTypes, Ht as publicEventPricingMode, Kt as formatCurrencyAmount, Mt as formatBookingTime, O as resolveEventRegistrationScopedEvents, Ot as formatBookingDate, Rt as toBookingTimeZone, S as WIZARD_STEPS, T as planEventRegistrationSurface, Ut as publicEventPricingType, V as resolveRuntimeEventPricing, Vt as publicEventPricingCurrency, Wt as publicEventScalarPrice, X as themeButtonClassName, _ as findEventByOccurrenceId, b as resolveEventRequiresPayment, g as toWholeSeriesRegistrationEvents, h as toRegistrationEvents, t as defineIslandPropsDecoder, v as findPreSelectedEvent, w as getWizardStepsForContext, x as resolveRegistrationTarget, y as resolveDiscountContext, yt as assertNever } from "./propsDecoder-pYvx_Srl.mjs";
4
+ import { a as formatTicketTypeRemaining, d as selectEventCapacitySource, f as selectMostOpenCapacitySource, i as formatRemainingPlaces, l as getRemainingPlaces, n as canJoinEventWaitlistOnAnyMode, r as formatEventRemainingPlaces, s as getEventAvailability, t as canJoinEventWaitlist, u as isEventFull } from "./capacity-BW00PO7s.mjs";
5
+ import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-DCkiHVdD.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-Du5mHgvL.mjs";
7
- import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-35Ru8yB9.mjs";
7
+ import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-ByQv96M2.mjs";
8
+ import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-BMipSoDJ.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";
@@ -713,10 +714,12 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
713
714
  className: "er-event-grid",
714
715
  children: events.map((event) => {
715
716
  const isSelected = selectedEvent?.selectionId === event.selectionId;
716
- const isSoldOut = event.capacity !== null && event.availableSpots === 0;
717
- const canJoinWaitlist = isSoldOut && event.waitlistEnabled === true;
717
+ const remaining = getRemainingPlaces(selectMostOpenCapacitySource(event));
718
+ const isSoldOut = isEventFull(event);
719
+ const canJoinWaitlist = canJoinEventWaitlistOnAnyMode(event);
718
720
  const isUnavailable = isSoldOut && !canJoinWaitlist;
719
- const isLowCapacity = event.availableSpots !== null && event.availableSpots <= 5 && !isSoldOut;
721
+ const isLowCapacity = remaining.kind === "low";
722
+ const capacityLabel = formatEventRemainingPlaces(event, { full: "Sold out" });
720
723
  const timeZone = event.timeZone ?? null;
721
724
  const cardClasses = [
722
725
  "er-event-card",
@@ -748,9 +751,9 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
748
751
  children: " · "
749
752
  }), event.venue.name] })]
750
753
  })]
751
- }), showCapacity && event.capacity !== null && /* @__PURE__ */ jsx("span", {
754
+ }), showCapacity && capacityLabel && /* @__PURE__ */ jsx("span", {
752
755
  className: badgeClasses,
753
- children: canJoinWaitlist ? "Waitlist" : isUnavailable ? "Sold Out" : `${event.availableSpots ?? 0} spots left`
756
+ children: capacityLabel
754
757
  })]
755
758
  })
756
759
  }, event.selectionId);
@@ -759,6 +762,61 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
759
762
  });
760
763
  };
761
764
  //#endregion
765
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
766
+ function resolveRegistrationRecoveryAction(step, action) {
767
+ if (step === "payment-failed") return "payment-options";
768
+ if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
769
+ return action;
770
+ }
771
+ function canRecoverToWaitlist(eligibility) {
772
+ return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
773
+ }
774
+ function shouldRefreshAfterRegistrationError(step, action, enabled) {
775
+ return enabled || step === "error" && action === "places";
776
+ }
777
+ function resetRegistrationAfterExpiry(input) {
778
+ input.dispatchFlow({
779
+ type: "reset",
780
+ hasPreSelectedContext: input.hasPreSelectedContext
781
+ });
782
+ input.setSelectedTicketLines([]);
783
+ input.setSelectedSlidingAmountInput("");
784
+ const mode = input.preSelectedEvent?.deliveryMode;
785
+ input.setSelectedAttendanceMode(mode === "online" || mode === "in_person" ? mode : null);
786
+ input.setErrorMessage("");
787
+ input.setPendingRegistrationReference(null);
788
+ }
789
+ /** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
790
+ function recoverRegistrationError(action, handlers) {
791
+ switch (action) {
792
+ case "none": return;
793
+ case "portal":
794
+ case "payment-status":
795
+ window.location.assign("/portal");
796
+ return;
797
+ case "event":
798
+ window.location.assign(window.location.pathname);
799
+ return;
800
+ case "retry":
801
+ window.location.reload();
802
+ return;
803
+ case "details":
804
+ handlers.clearError();
805
+ handlers.setStep("enter-details");
806
+ return;
807
+ case "payment-options":
808
+ case "places":
809
+ handlers.refreshOptions();
810
+ handlers.clearError();
811
+ handlers.setStep(action === "places" ? handlers.canJoinWaitlist ? "enter-details" : "select-tickets" : "select-payment");
812
+ return;
813
+ case "start":
814
+ handlers.refreshOptions();
815
+ handlers.reset();
816
+ return;
817
+ }
818
+ }
819
+ //#endregion
762
820
  //#region ../blocks/src/system/runtime/nodes/event-registration/validation.ts
763
821
  function createInitialRegistrationFormData() {
764
822
  return {
@@ -1002,11 +1060,11 @@ function useEligibilityWithDeps(params, deps) {
1002
1060
  type: "resolved",
1003
1061
  eligibility: transformRegistrationOptionsResponse(result)
1004
1062
  });
1005
- } catch (err) {
1063
+ } catch {
1006
1064
  if (cancelled || requestIdRef.current !== requestId) return;
1007
1065
  dispatch({
1008
1066
  type: "failed",
1009
- message: err instanceof Error ? err.message : "Failed to check eligibility"
1067
+ 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
1068
  });
1011
1069
  }
1012
1070
  }
@@ -1018,6 +1076,7 @@ function useEligibilityWithDeps(params, deps) {
1018
1076
  }, [
1019
1077
  api,
1020
1078
  params.enabled,
1079
+ params.refreshKey,
1021
1080
  params.subject?.kind,
1022
1081
  params.subject?.kind === "occurrence" ? params.subject.occurrenceId : params.subject?.eventSeriesId,
1023
1082
  params.attendanceMode,
@@ -1127,8 +1186,252 @@ function logoutPortal(anchorId) {
1127
1186
  form.submit();
1128
1187
  }
1129
1188
  //#endregion
1189
+ //#region ../commerce/src/purchase/checkoutPaymentFlow.ts
1190
+ function planCheckoutPaymentFlow(options) {
1191
+ const amountDueCents = toNonNegativeCents(options.amountDueCents);
1192
+ const discountCents = toNonNegativeCents(options.discountCents);
1193
+ const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
1194
+ const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
1195
+ if (amountDueCents <= 0) return {
1196
+ kind: "bypass",
1197
+ bypassReason: discountCents > 0 ? "discount_free" : "free",
1198
+ reserveCreditCents: 0
1199
+ };
1200
+ if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
1201
+ kind: "stripe",
1202
+ reserveCreditCents: toNonNegativeCents(0)
1203
+ };
1204
+ if (availableCreditCents >= amountDueCents) return {
1205
+ kind: "store_credit_only",
1206
+ reserveCreditCents: amountDueCents
1207
+ };
1208
+ return {
1209
+ kind: "stripe",
1210
+ reserveCreditCents: planMixedPaymentCreditReservation({
1211
+ amountDueCents,
1212
+ availableCreditCents,
1213
+ minimumStripeChargeCents
1214
+ })
1215
+ };
1216
+ }
1217
+ function planMixedPaymentCreditReservation(input) {
1218
+ const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
1219
+ if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
1220
+ if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
1221
+ return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
1222
+ }
1223
+ /**
1224
+ * Apply site credit to a flexible-balance booking before card payment.
1225
+ *
1226
+ * Credit first reduces the configured payment due now. Once that payment is
1227
+ * covered, any remaining credit reduces the later balance. If a partial credit
1228
+ * application would leave a positive card charge below Stripe's minimum, less
1229
+ * credit is used so the configured payment can still be collected.
1230
+ */
1231
+ function planFlexibleBalanceCreditFirst(input) {
1232
+ const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
1233
+ const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
1234
+ const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
1235
+ const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
1236
+ if (availableCreditCents >= totalCents && totalCents > 0) return {
1237
+ kind: "credit_only",
1238
+ reserveCreditCents: totalCents,
1239
+ cardDueNowCents: toNonNegativeCents(0),
1240
+ laterBalanceCents: toNonNegativeCents(0)
1241
+ };
1242
+ let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
1243
+ if (reserveCreditCents >= configuredDueNowCents) return {
1244
+ kind: "flexible_balance",
1245
+ reserveCreditCents,
1246
+ cardDueNowCents: toNonNegativeCents(0),
1247
+ laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
1248
+ };
1249
+ const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
1250
+ if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
1251
+ if (configuredDueNowCents < minimumStripeChargeCents) return {
1252
+ kind: "card_below_minimum",
1253
+ reserveCreditCents: toNonNegativeCents(0),
1254
+ cardDueNowCents: configuredDueNowCents,
1255
+ laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
1256
+ };
1257
+ reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
1258
+ }
1259
+ const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
1260
+ return {
1261
+ kind: "flexible_balance",
1262
+ reserveCreditCents,
1263
+ cardDueNowCents,
1264
+ laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
1265
+ };
1266
+ }
1267
+ //#endregion
1268
+ //#region ../commerce/src/purchase/settlement.ts
1269
+ function normalizeCurrencyCode(raw) {
1270
+ const normalized = raw.trim().toUpperCase();
1271
+ if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
1272
+ return normalized;
1273
+ }
1274
+ //#endregion
1275
+ //#region ../commerce/src/purchase/checkoutSessionMetadata.ts
1276
+ const datedOfferingSharedEngineMetadataShape = {
1277
+ datedOfferingEngineContractVersion: z.literal("shared_v1"),
1278
+ datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
1279
+ datedOfferingCapacityHoldId: z.string().uuid(),
1280
+ datedOfferingCapacityHoldAuditRef: z.string().uuid(),
1281
+ customerId: z.string().uuid(),
1282
+ eventSeriesId: z.string().uuid()
1283
+ };
1284
+ z.object(datedOfferingSharedEngineMetadataShape);
1285
+ const orderCheckoutSessionMetadataBaseShape = {
1286
+ type: z.literal("order"),
1287
+ site_id: z.string().uuid(),
1288
+ order_id: z.string().uuid().optional(),
1289
+ customer_email: z.string().email(),
1290
+ customer_name: z.string().optional(),
1291
+ product_id: z.string().uuid().optional(),
1292
+ attendee_id: z.string().uuid().optional(),
1293
+ occurrence_id: z.string().uuid().optional(),
1294
+ tickets_count: z.string().optional(),
1295
+ notes: z.string().optional(),
1296
+ discount_redemption_id: z.string().uuid().optional(),
1297
+ discount_code_id: z.string().uuid().optional(),
1298
+ discount_code: z.string().optional(),
1299
+ discount_public_label: z.string().optional(),
1300
+ credit_reservation_id: z.string().uuid().optional(),
1301
+ credit_reserved_cents: z.string().optional(),
1302
+ discount_code_amount_cents: z.string().optional(),
1303
+ order_payment_reason: z.enum([
1304
+ "instalment_payoff",
1305
+ "flexible_balance_initial",
1306
+ "flexible_balance_paydown"
1307
+ ]).optional(),
1308
+ order_payment_plan_id: z.string().uuid().optional(),
1309
+ order_flexible_balance_contract_id: z.string().uuid().optional()
1310
+ };
1311
+ const absentDatedOfferingSharedEngineMetadataShape = {
1312
+ datedOfferingEngineContractVersion: z.never().optional(),
1313
+ datedOfferingEngineRouteKind: z.never().optional(),
1314
+ datedOfferingCapacityHoldId: z.never().optional(),
1315
+ datedOfferingCapacityHoldAuditRef: z.never().optional(),
1316
+ customerId: z.never().optional(),
1317
+ eventSeriesId: z.never().optional()
1318
+ };
1319
+ const LegacyOrderCheckoutSessionMetadataSchema = z.object({
1320
+ ...orderCheckoutSessionMetadataBaseShape,
1321
+ ...absentDatedOfferingSharedEngineMetadataShape
1322
+ });
1323
+ const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
1324
+ ...orderCheckoutSessionMetadataBaseShape,
1325
+ ...datedOfferingSharedEngineMetadataShape
1326
+ });
1327
+ z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
1328
+ toNonNegativeCents(0);
1329
+ //#endregion
1330
+ //#region ../commerce/src/purchase/stripeMinimumCharge.ts
1331
+ const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
1332
+ GBP: 30,
1333
+ USD: 50,
1334
+ EUR: 50,
1335
+ AED: 200,
1336
+ AUD: 50,
1337
+ BGN: 100,
1338
+ BRL: 50,
1339
+ CAD: 50,
1340
+ CHF: 50,
1341
+ CZK: 1500,
1342
+ DKK: 250,
1343
+ HKD: 400,
1344
+ HUF: 17500,
1345
+ JPY: 50,
1346
+ MXN: 1e3,
1347
+ MYR: 200,
1348
+ NOK: 300,
1349
+ NZD: 50,
1350
+ PLN: 200,
1351
+ RON: 200,
1352
+ SEK: 300,
1353
+ SGD: 50,
1354
+ THB: 1e3
1355
+ };
1356
+ function isStripeMinimumChargeCurrency(currency) {
1357
+ return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
1358
+ }
1359
+ function getStripeMinimumChargeRequirement(rawCurrency) {
1360
+ let currency;
1361
+ try {
1362
+ currency = normalizeCurrencyCode(rawCurrency);
1363
+ } catch {
1364
+ return null;
1365
+ }
1366
+ const currencyKey = currency;
1367
+ if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
1368
+ return {
1369
+ currency,
1370
+ minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
1371
+ };
1372
+ }
1373
+ //#endregion
1374
+ //#region ../commerce/src/purchase/event-registration-errors.ts
1375
+ const eventRegistrationErrors = {
1376
+ registrationClosed: {
1377
+ message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
1378
+ action: "none"
1379
+ },
1380
+ paymentUncertain: {
1381
+ 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.",
1382
+ action: "payment-status"
1383
+ },
1384
+ paymentPageUnavailable: {
1385
+ message: "We couldn’t open the payment page. Please refresh this page and try again. If this keeps happening, contact the organiser for help.",
1386
+ action: "retry"
1387
+ },
1388
+ paymentUnavailable: {
1389
+ message: "Online payment is not available for this event right now. Please contact the organiser for help with booking.",
1390
+ action: "none"
1391
+ },
1392
+ capacityChanged: {
1393
+ message: "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.",
1394
+ action: "places"
1395
+ },
1396
+ selectionChanged: {
1397
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
1398
+ action: "places"
1399
+ },
1400
+ registrationPreparationChanged: {
1401
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
1402
+ action: "places"
1403
+ },
1404
+ registrationAlreadyExists: {
1405
+ message: "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser.",
1406
+ action: "portal"
1407
+ },
1408
+ holdExpired: {
1409
+ message: "Your reservation has timed out. Please check availability and choose your places again.",
1410
+ action: "start"
1411
+ },
1412
+ waitlistUnavailable: {
1413
+ 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.",
1414
+ action: "event"
1415
+ },
1416
+ paymentContextMismatch: {
1417
+ message: "The payment options for this event have changed. Please check the latest options before continuing.",
1418
+ action: "payment-options"
1419
+ },
1420
+ idempotencyConflict: {
1421
+ 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.",
1422
+ action: "portal"
1423
+ },
1424
+ invalidCommitInput: {
1425
+ 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.",
1426
+ action: "details"
1427
+ }
1428
+ };
1429
+ function parseEventRegistrationErrorReason(value) {
1430
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(eventRegistrationErrors, value) ? value : null;
1431
+ }
1432
+ //#endregion
1130
1433
  //#region ../blocks/src/system/runtime/shared/useWaitlistClaim.state.ts
1131
- const DEFAULT_WAITLIST_CLAIM_ERROR = "Failed to resolve waitlist claim";
1434
+ const DEFAULT_WAITLIST_CLAIM_ERROR = "Online booking is unavailable right now. Please contact the organiser for help with booking.";
1132
1435
  function createInitialWaitlistClaimState(hasToken) {
1133
1436
  return hasToken ? { kind: "resolving" } : { kind: "idle" };
1134
1437
  }
@@ -1144,14 +1447,13 @@ function waitlistClaimReducer(_state, action) {
1144
1447
  }
1145
1448
  }
1146
1449
  function toWaitlistClaimResolveFailure(error) {
1147
- const message = error instanceof Error ? error.message : DEFAULT_WAITLIST_CLAIM_ERROR;
1148
1450
  if (isUnauthorizedBlockApiError(error)) return {
1149
1451
  kind: "portal-login-required",
1150
- message
1452
+ message: "Please log in to continue with your waiting list invitation."
1151
1453
  };
1152
1454
  return {
1153
1455
  kind: "error",
1154
- message
1456
+ message: hasStatus(error) && (error.status === 404 || error.status === 409 || error.status === 410) ? eventRegistrationErrors.waitlistUnavailable.message : DEFAULT_WAITLIST_CLAIM_ERROR
1155
1457
  };
1156
1458
  }
1157
1459
  function isUnauthorizedBlockApiError(error) {
@@ -1512,47 +1814,136 @@ function cleanupStaleRegistrations() {
1512
1814
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.ts
1513
1815
  const eventRegistrationStripeReturnDeps = {
1514
1816
  usePaymentVerification,
1515
- consumeReturnParams: () => consumeSearchParams(["registration", EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]),
1817
+ readReturnParams: () => {
1818
+ const params = new URLSearchParams(window.location.search);
1819
+ return {
1820
+ registration: params.get("registration"),
1821
+ registration_attempt: params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM),
1822
+ session_id: params.get("session_id")
1823
+ };
1824
+ },
1825
+ clearReturnParams: () => {
1826
+ consumeSearchParams([
1827
+ "registration",
1828
+ EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
1829
+ "session_id"
1830
+ ]);
1831
+ },
1516
1832
  scrollToBlock,
1517
1833
  cleanupStaleRegistrations,
1518
1834
  clearPendingRegistration,
1519
1835
  getPendingRegistration
1520
1836
  };
1837
+ function restorePendingPresentation(options, pending) {
1838
+ options.setFormData(pending.formData);
1839
+ options.setTicketSelections(pending.ticketSelections);
1840
+ options.setTicketsCount(getTicketSelectionTotal(pending.ticketSelections));
1841
+ if (pending.selectedEventId) {
1842
+ const event = options.events.find((item) => item.selectionId === pending.selectedEventId) ?? null;
1843
+ if (event) options.setSelectedEvent(event);
1844
+ }
1845
+ options.setSelectedAttendanceMode(pending.selectedAttendanceMode ?? null);
1846
+ options.setPaymentSelection(pending.paymentSelection);
1847
+ }
1848
+ function showReturnReconciliation(options, message) {
1849
+ options.setErrorMessage(message);
1850
+ options.setStep("verifying-timeout");
1851
+ }
1852
+ function checkoutReturnCommand(params) {
1853
+ const checkoutAttemptId = params.registration_attempt;
1854
+ if (!checkoutAttemptId) return null;
1855
+ if (params.registration === "cancelled") return {
1856
+ kind: "abandon",
1857
+ checkoutAttemptId
1858
+ };
1859
+ if (params.registration === "success" && params.session_id) return {
1860
+ kind: "resolveSuccess",
1861
+ checkoutAttemptId,
1862
+ sessionId: params.session_id
1863
+ };
1864
+ return null;
1865
+ }
1866
+ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
1867
+ deps.cleanupStaleRegistrations();
1868
+ const params = deps.readReturnParams();
1869
+ if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
1870
+ deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
1871
+ const siteId = options.api.config.siteId;
1872
+ const command = checkoutReturnCommand(params);
1873
+ if (!siteId) return "settled";
1874
+ if (!command) {
1875
+ 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.");
1876
+ return "settled";
1877
+ }
1878
+ const { checkoutAttemptId } = command;
1879
+ const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
1880
+ checkoutAttemptIdRef.current = checkoutAttemptId;
1881
+ if (pending) restorePendingPresentation(options, pending);
1882
+ options.setStep("verifying-payment");
1883
+ try {
1884
+ const outcome = await options.api.resolveEventCheckoutReturn(command);
1885
+ switch (outcome.kind) {
1886
+ case "resolved":
1887
+ deps.clearReturnParams();
1888
+ options.setPendingRegistrationReference(outcome.reference);
1889
+ options.setStep("verifying-payment");
1890
+ return "settled";
1891
+ case "abandoned":
1892
+ deps.clearReturnParams();
1893
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1894
+ options.setStep("select-payment");
1895
+ return "settled";
1896
+ case "notFound":
1897
+ if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
1898
+ deps.clearReturnParams();
1899
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1900
+ options.setStep("select-payment");
1901
+ return "settled";
1902
+ }
1903
+ 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.");
1904
+ return "settled";
1905
+ case "reconciliationRequired": break;
1906
+ }
1907
+ 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.");
1908
+ } catch {
1909
+ 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.");
1910
+ }
1911
+ return "retryable";
1912
+ }
1521
1913
  function useEventRegistrationStripeReturn(options) {
1522
1914
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
1523
1915
  }
1524
1916
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
1525
- const { api, events, pendingRegistrationReference, setErrorMessage, setFormData, setPaymentSelection, setPendingRegistrationReference, setRegistrationResult, setSelectedEvent, setSelectedAttendanceMode, setTicketSelections, setStep, setTicketsCount, step } = options;
1917
+ const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
1526
1918
  const checkoutAttemptIdRef = useRef(null);
1919
+ const lookupInFlightRef = useRef(false);
1920
+ const [lookupState, setLookupState] = useState("settled");
1921
+ const resolveReturn = useCallback(async () => {
1922
+ if (lookupInFlightRef.current) return;
1923
+ lookupInFlightRef.current = true;
1924
+ setLookupState("loading");
1925
+ try {
1926
+ setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
1927
+ } finally {
1928
+ lookupInFlightRef.current = false;
1929
+ }
1930
+ }, [options, deps]);
1527
1931
  const verificationConfig = useMemo(() => step === "verifying-payment" && pendingRegistrationReference ? { reference: pendingRegistrationReference } : null, [step, pendingRegistrationReference]);
1528
1932
  const verification = deps.usePaymentVerification(verificationConfig);
1529
- useEffect(() => {
1530
- deps.cleanupStaleRegistrations();
1531
- const returnParams = deps.consumeReturnParams();
1532
- const registrationStatus = returnParams.registration;
1533
- if (registrationStatus !== "cancelled" && registrationStatus !== "success") return;
1534
- deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
1535
- const siteId = api.config.siteId;
1536
- if (!siteId) return;
1537
- const checkoutAttemptId = returnParams[EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM];
1538
- if (!checkoutAttemptId) return;
1539
- const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
1540
- if (!pending) return;
1541
- checkoutAttemptIdRef.current = checkoutAttemptId;
1542
- setFormData(pending.formData);
1543
- setTicketSelections(pending.ticketSelections);
1544
- setTicketsCount(getTicketSelectionTotal(pending.ticketSelections));
1545
- if (pending.selectedEventId) {
1546
- const event = events.find((e) => e.selectionId === pending.selectedEventId) ?? null;
1547
- if (event) setSelectedEvent(event);
1548
- }
1549
- setSelectedAttendanceMode(pending.selectedAttendanceMode ?? null);
1550
- setPaymentSelection(pending.paymentSelection);
1551
- if (registrationStatus === "cancelled") setStep("select-payment");
1552
- else if (registrationStatus === "success") {
1553
- setPendingRegistrationReference(pending.reference);
1933
+ const hasVerificationTimeout = step === "verifying-timeout" && pendingRegistrationReference !== null;
1934
+ const retry = useCallback(() => {
1935
+ if (hasVerificationTimeout) {
1554
1936
  setStep("verifying-payment");
1937
+ return;
1555
1938
  }
1939
+ verification.retry();
1940
+ }, [
1941
+ hasVerificationTimeout,
1942
+ setStep,
1943
+ verification.retry
1944
+ ]);
1945
+ useEffect(() => {
1946
+ resolveReturn();
1556
1947
  }, []);
1557
1948
  useEffect(() => {
1558
1949
  if (verification.status === "completed") {
@@ -1562,7 +1953,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
1562
1953
  setRegistrationResult({ status: "confirmed" });
1563
1954
  setStep("success");
1564
1955
  } else if (verification.status === "failed") {
1565
- setErrorMessage(verification.error ?? "Payment failed. Please try again.");
1956
+ setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
1566
1957
  setStep("payment-failed");
1567
1958
  } else if (verification.status === "timeout") setStep("verifying-timeout");
1568
1959
  }, [
@@ -1575,7 +1966,22 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
1575
1966
  setRegistrationResult,
1576
1967
  setStep
1577
1968
  ]);
1578
- return verification;
1969
+ return {
1970
+ ...verification,
1971
+ status: hasVerificationTimeout ? "timeout" : verification.status,
1972
+ retry,
1973
+ retryReturn: lookupState === "retryable" ? () => {
1974
+ resolveReturn();
1975
+ } : void 0
1976
+ };
1977
+ }
1978
+ //#endregion
1979
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.ts
1980
+ function registrationErrorPresentation(error) {
1981
+ const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
1982
+ if (reason) return eventRegistrationErrors[reason];
1983
+ if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
1984
+ return eventRegistrationErrors.paymentUncertain;
1579
1985
  }
1580
1986
  //#endregion
1581
1987
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
@@ -1635,7 +2041,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1635
2041
  options.setStep("submitting");
1636
2042
  try {
1637
2043
  const prepared = prepareSubmitContext(options, target, method, passId, membershipId, selection);
1638
- if (prepared.subject.kind === "wholeSeries" && method === "stripe") return submitWholeSeriesCheckout(options, deps, {
2044
+ if (prepared.subject.kind === "wholeSeries" && method === "stripe") return await submitWholeSeriesCheckout(options, deps, {
1639
2045
  ...prepared,
1640
2046
  subject: prepared.subject
1641
2047
  }, submitOptions);
@@ -1656,7 +2062,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1656
2062
  claimToken: options.claimToken ?? void 0,
1657
2063
  guests: prepared.guestsPayload
1658
2064
  });
1659
- if (needsOccurrenceCheckout(result, method)) return submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
2065
+ if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
1660
2066
  options.setRegistrationResult(toRegistrationResult(result));
1661
2067
  options.setStep("success");
1662
2068
  return {
@@ -1664,7 +2070,8 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
1664
2070
  outcome: { kind: "completed" }
1665
2071
  };
1666
2072
  } catch (error) {
1667
- const message = error instanceof Error ? error.message : "Registration failed";
2073
+ const { message, action } = registrationErrorPresentation(error);
2074
+ options.setErrorAction?.(action);
1668
2075
  options.setErrorMessage(message);
1669
2076
  options.setStep("error");
1670
2077
  return {
@@ -1739,6 +2146,7 @@ async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions)
1739
2146
  claimToken: options.claimToken ?? void 0,
1740
2147
  discountCode: options.discountCode.trim() || void 0,
1741
2148
  collectionOptionKind: prepared.selectionSnapshot.collectionOptionKind,
2149
+ checkoutAttemptId,
1742
2150
  ...urls
1743
2151
  });
1744
2152
  if (result.scope !== "wholeSeries") throw new Error("Unexpected registration scope for event checkout.");
@@ -1828,6 +2236,13 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
1828
2236
  }
1829
2237
  //#endregion
1830
2238
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.ts
2239
+ function getEventSelectionDefaults(event) {
2240
+ const suggestedAmount = getSlidingScaleForEvent(event)?.suggestedAmount;
2241
+ return {
2242
+ slidingAmountInput: suggestedAmount == null ? "" : formatCentsAsCurrencyInput(suggestedAmount),
2243
+ attendanceMode: event.deliveryMode === "hybrid" ? null : event.deliveryMode ?? "in_person"
2244
+ };
2245
+ }
1831
2246
  function parseCurrencyInputToCents(value) {
1832
2247
  const trimmed = value.trim();
1833
2248
  if (!trimmed) return null;
@@ -1875,7 +2290,7 @@ function resolveWaitlistClaimRestoration(input) {
1875
2290
  const event = findEventByOccurrenceId(input.scopedEvents, subject.occurrenceId);
1876
2291
  if (!event && input.occurrenceContextId !== subject.occurrenceId) return {
1877
2292
  ok: false,
1878
- message: "This waitlist claim is for a different event."
2293
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
1879
2294
  };
1880
2295
  return {
1881
2296
  ok: true,
@@ -1892,7 +2307,7 @@ function resolveWaitlistClaimRestoration(input) {
1892
2307
  const event = input.scopedEvents.find((candidate) => candidate.registrationSubject.kind === "wholeSeries" && candidate.registrationSubject.eventSeriesId === subject.eventSeriesId);
1893
2308
  if (!event) return {
1894
2309
  ok: false,
1895
- message: "This waitlist claim is for a different event run."
2310
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
1896
2311
  };
1897
2312
  return {
1898
2313
  ok: true,
@@ -1925,6 +2340,7 @@ function useWaitlistClaimRestoration(input) {
1925
2340
  occurrenceContextId: input.occurrenceContextId
1926
2341
  });
1927
2342
  if (!restoration.ok) {
2343
+ input.setters.setErrorAction?.("none");
1928
2344
  input.setters.setErrorMessage(restoration.message);
1929
2345
  input.setters.setStep("error");
1930
2346
  return;
@@ -2003,6 +2419,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2003
2419
  });
2004
2420
  const [registrationResult, setRegistrationResult] = useState(null);
2005
2421
  const [errorMessage, setErrorMessage] = useState("");
2422
+ const [errorAction, setErrorAction] = useState("retry");
2423
+ const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
2006
2424
  const [altchaSolution, setAltchaSolution] = useState(null);
2007
2425
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
2008
2426
  const waitlistClaim = deps.useWaitlistClaim();
@@ -2032,10 +2450,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2032
2450
  const currentEvent = selectedEvent ?? preSelectedEvent;
2033
2451
  const currentEventPricing = currentEvent ? resolveRuntimeEventPricing(currentEvent) : null;
2034
2452
  const currentEventUsesTicketTypes = currentEventPricing !== null && publicEventPricingMode(currentEventPricing) === "ticket_types";
2035
- const selectedModeAvailability = selectedAttendanceMode ? (currentEvent?.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
2036
- const currentAvailableSpots = selectedModeAvailability?.availableSpots ?? currentEvent?.availableSpots;
2037
- const currentWaitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? currentEvent?.waitlistEnabled ?? false;
2038
- const usesTicketTypeSelections = currentEventUsesTicketTypes && !(currentAvailableSpots === 0 && currentWaitlistEnabled === true);
2453
+ const currentCanJoinWaitlist = currentEvent ? canJoinEventWaitlist(currentEvent, { attendanceMode: selectedAttendanceMode }) : false;
2454
+ const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
2039
2455
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
2040
2456
  event: currentEvent,
2041
2457
  eligibility: null,
@@ -2066,7 +2482,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2066
2482
  });
2067
2483
  }, [flowState.ticketsCount, ticketsCount]);
2068
2484
  const eligibilityResult = deps.useEligibility({
2069
- enabled: shouldLoadRegistrationOptions(portalAuthState, step),
2485
+ refreshKey: optionsRefreshKey,
2486
+ enabled: shouldRefreshAfterRegistrationError(step, errorAction, shouldLoadRegistrationOptions(portalAuthState, step)),
2070
2487
  subject: registrationTarget?.subject ?? null,
2071
2488
  attendanceMode: selectedAttendanceMode,
2072
2489
  ticketSelections,
@@ -2095,6 +2512,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2095
2512
  });
2096
2513
  }, [waitlistClaim.claim]);
2097
2514
  const waitlistRestorationSetters = useMemo(() => ({
2515
+ setErrorAction,
2098
2516
  setErrorMessage,
2099
2517
  setStep,
2100
2518
  setSelectedEvent,
@@ -2118,6 +2536,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2118
2536
  if (waitlistClaim.requiresPortalLogin) return;
2119
2537
  if (!waitlistClaim.error) return;
2120
2538
  setErrorMessage(waitlistClaim.error);
2539
+ setErrorAction("event");
2121
2540
  setStep("error");
2122
2541
  }, [
2123
2542
  waitlistClaim.error,
@@ -2220,8 +2639,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2220
2639
  const event = currentEvent;
2221
2640
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") return false;
2222
2641
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
2223
- const selectedModeAvailability = selectedAttendanceMode ? (event.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
2224
- return !((selectedModeAvailability?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability?.waitlistEnabled ?? event.waitlistEnabled) === true);
2642
+ return !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
2225
2643
  }, [currentEvent, selectedAttendanceMode]);
2226
2644
  const hasValidTicketSelection = !ticketTypeSelectionRequired || normalizedSelectedTicketLines.length > 0 && normalizedSelectedTicketLines.every((line) => {
2227
2645
  if (!line.ticketTypeId) return false;
@@ -2302,6 +2720,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2302
2720
  ]);
2303
2721
  const handleDetailsComplete = async () => {
2304
2722
  if (ticketTypeSelectionRequired && !hasValidTicketSelection) {
2723
+ setErrorAction("places");
2305
2724
  const message = "Choose at least one available ticket to continue.";
2306
2725
  setErrorMessage(message);
2307
2726
  setStep("error");
@@ -2312,6 +2731,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2312
2731
  };
2313
2732
  }
2314
2733
  if (slidingScaleSelectionMessage) {
2734
+ setErrorAction("places");
2315
2735
  setErrorMessage(slidingScaleSelectionMessage);
2316
2736
  setStep("error");
2317
2737
  return {
@@ -2329,7 +2749,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2329
2749
  };
2330
2750
  }
2331
2751
  if (waitlistClaim.claim === null && eligibility?.isFull && !eligibility.canJoinWaitlist) {
2332
- const message = "This event is sold out.";
2752
+ const message = "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.";
2753
+ setErrorAction("places");
2333
2754
  setErrorMessage(message);
2334
2755
  setStep("error");
2335
2756
  return {
@@ -2445,6 +2866,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2445
2866
  const selectionMethod = paymentSelection.method;
2446
2867
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
2447
2868
  const handleSubmit = deps.useEventRegistrationSubmit({
2869
+ setErrorAction,
2448
2870
  api,
2449
2871
  registrationTarget,
2450
2872
  selectedAttendanceMode,
@@ -2467,26 +2889,31 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2467
2889
  ...current,
2468
2890
  consentAccepted: false
2469
2891
  }));
2470
- const slidingScale = getSlidingScaleForEvent(event);
2471
- setSelectedSlidingAmountInput(slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "");
2472
- setSelectedAttendanceMode((event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person");
2892
+ const defaults = getEventSelectionDefaults(event);
2893
+ setSelectedSlidingAmountInput(defaults.slidingAmountInput);
2894
+ setSelectedAttendanceMode(defaults.attendanceMode);
2473
2895
  setSelectedTicketLines([]);
2474
2896
  dispatchFlow({
2475
2897
  type: "event-changed",
2476
2898
  hasPreSelectedContext: true
2477
2899
  });
2478
2900
  };
2479
- const handleRetry = () => {
2480
- dispatchFlow({
2481
- type: "reset",
2482
- hasPreSelectedContext
2483
- });
2484
- setSelectedTicketLines([]);
2485
- setSelectedSlidingAmountInput("");
2486
- setSelectedAttendanceMode(preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null);
2487
- setErrorMessage("");
2488
- setPendingRegistrationReference(null);
2489
- };
2901
+ const handleRetry = () => recoverRegistrationError(resolveRegistrationRecoveryAction(step, errorAction), {
2902
+ canJoinWaitlist: canRecoverToWaitlist(eligibility),
2903
+ refreshOptions: () => setOptionsRefreshKey((value) => value + 1),
2904
+ clearError: () => setErrorMessage(""),
2905
+ setStep,
2906
+ reset: () => resetRegistrationAfterExpiry({
2907
+ hasPreSelectedContext,
2908
+ preSelectedEvent,
2909
+ dispatchFlow,
2910
+ setSelectedTicketLines,
2911
+ setSelectedSlidingAmountInput,
2912
+ setSelectedAttendanceMode,
2913
+ setErrorMessage,
2914
+ setPendingRegistrationReference
2915
+ })
2916
+ });
2490
2917
  const handlePaymentStepStateChange = useCallback((state) => {
2491
2918
  setPaymentStepState(state);
2492
2919
  }, []);
@@ -2549,6 +2976,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2549
2976
  handleGuestShakeEnd,
2550
2977
  registrationResult,
2551
2978
  errorMessage,
2979
+ errorAction,
2552
2980
  altchaSolution,
2553
2981
  setAltchaSolution,
2554
2982
  portalAuthState,
@@ -2955,11 +3383,11 @@ function EventRegistrationSummaryBar(props) {
2955
3383
  */
2956
3384
  function resolveDetailsButtonText(input) {
2957
3385
  const { wizard } = input;
2958
- if (wizard.eligibility?.canJoinWaitlist) return "Join Waitlist";
3386
+ if (wizard.eligibility?.canJoinWaitlist) return "Join waitlist";
2959
3387
  if (!input.hasWaitlistClaim && wizard.eligibility?.isFull) return "Sold out";
2960
3388
  if (wizard.paymentSelection.method === "pass") return "Book using class pass";
2961
3389
  if (wizard.paymentSelection.method === "membership") return input.fallbackText;
2962
- return wizard.eventRequiresPayment ? "Continue to Payment" : input.fallbackText;
3390
+ return wizard.eventRequiresPayment ? "Continue to payment" : input.fallbackText;
2963
3391
  }
2964
3392
  function resolveExistingRegistrationLabel(wizard) {
2965
3393
  return wizard.registrationState.kind === "waitlisted" ? "Already on waitlist" : "Already registered";
@@ -2971,13 +3399,24 @@ function isExistingRegistrationBlocking$1(wizard) {
2971
3399
  hasWaitlistClaim: wizard.waitlistClaim.claim !== null
2972
3400
  });
2973
3401
  }
3402
+ /**
3403
+ * Full for the visitor's selected attendance mode (or the whole event when no
3404
+ * mode is chosen) with no waitlist to fall back on. Reads the same capacity
3405
+ * source as the steps above the action bar so the button never disagrees
3406
+ * with them on a hybrid event.
3407
+ */
3408
+ function isFullWithNoWaitlist(event, attendanceMode) {
3409
+ if (!event) return false;
3410
+ const source = selectEventCapacitySource(event, attendanceMode);
3411
+ return getEventAvailability(source).isFull && !source.waitlistEnabled;
3412
+ }
2974
3413
  function EventRegistrationWizardActions(props) {
2975
3414
  const { wizard } = props;
2976
3415
  const nextStepAfterTickets = "enter-details";
2977
3416
  const prevStepForDetails = "select-tickets";
2978
3417
  const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
2979
3418
  const currentEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
2980
- const isSoldOutWithoutWaitlist = !hasWaitlistClaim && currentEvent?.availableSpots === 0 && currentEvent.waitlistEnabled !== true;
3419
+ const isSoldOutWithoutWaitlist = !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
2981
3420
  const getDetailsButtonText = () => resolveDetailsButtonText({
2982
3421
  wizard,
2983
3422
  hasWaitlistClaim,
@@ -3069,191 +3508,6 @@ function EventRegistrationWizardActions(props) {
3069
3508
  }
3070
3509
  }
3071
3510
  //#endregion
3072
- //#region ../commerce/src/purchase/checkoutPaymentFlow.ts
3073
- function planCheckoutPaymentFlow(options) {
3074
- const amountDueCents = toNonNegativeCents(options.amountDueCents);
3075
- const discountCents = toNonNegativeCents(options.discountCents);
3076
- const availableCreditCents = toNonNegativeCents(options.availableCreditCents);
3077
- const minimumStripeChargeCents = toNonNegativeCents(options.minimumStripeChargeCents ?? 0);
3078
- if (amountDueCents <= 0) return {
3079
- kind: "bypass",
3080
- bypassReason: discountCents > 0 ? "discount_free" : "free",
3081
- reserveCreditCents: 0
3082
- };
3083
- if (!options.creditCurrencyMatches || availableCreditCents <= 0) return {
3084
- kind: "stripe",
3085
- reserveCreditCents: toNonNegativeCents(0)
3086
- };
3087
- if (availableCreditCents >= amountDueCents) return {
3088
- kind: "store_credit_only",
3089
- reserveCreditCents: amountDueCents
3090
- };
3091
- return {
3092
- kind: "stripe",
3093
- reserveCreditCents: planMixedPaymentCreditReservation({
3094
- amountDueCents,
3095
- availableCreditCents,
3096
- minimumStripeChargeCents
3097
- })
3098
- };
3099
- }
3100
- function planMixedPaymentCreditReservation(input) {
3101
- const chargeAfterFullCredit = input.amountDueCents - input.availableCreditCents;
3102
- if (input.minimumStripeChargeCents <= 0 || chargeAfterFullCredit <= 0 || chargeAfterFullCredit >= input.minimumStripeChargeCents) return input.availableCreditCents;
3103
- if (input.amountDueCents < input.minimumStripeChargeCents) return toNonNegativeCents(0);
3104
- return toNonNegativeCents(input.amountDueCents - input.minimumStripeChargeCents);
3105
- }
3106
- /**
3107
- * Apply site credit to a flexible-balance booking before card payment.
3108
- *
3109
- * Credit first reduces the configured payment due now. Once that payment is
3110
- * covered, any remaining credit reduces the later balance. If a partial credit
3111
- * application would leave a positive card charge below Stripe's minimum, less
3112
- * credit is used so the configured payment can still be collected.
3113
- */
3114
- function planFlexibleBalanceCreditFirst(input) {
3115
- const totalCents = toNonNegativeCents(input.totalAfterDiscountCents);
3116
- const configuredDueNowCents = toNonNegativeCents(Math.min(input.configuredPaymentDueNowCents, totalCents));
3117
- const availableCreditCents = toNonNegativeCents(input.creditCurrencyMatches ? input.availableCreditCents : 0);
3118
- const minimumStripeChargeCents = toNonNegativeCents(input.minimumStripeChargeCents ?? 0);
3119
- if (availableCreditCents >= totalCents && totalCents > 0) return {
3120
- kind: "credit_only",
3121
- reserveCreditCents: totalCents,
3122
- cardDueNowCents: toNonNegativeCents(0),
3123
- laterBalanceCents: toNonNegativeCents(0)
3124
- };
3125
- let reserveCreditCents = toNonNegativeCents(Math.min(availableCreditCents, totalCents));
3126
- if (reserveCreditCents >= configuredDueNowCents) return {
3127
- kind: "flexible_balance",
3128
- reserveCreditCents,
3129
- cardDueNowCents: toNonNegativeCents(0),
3130
- laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents)
3131
- };
3132
- const plannedCardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
3133
- if (plannedCardDueNowCents > 0 && plannedCardDueNowCents < minimumStripeChargeCents) {
3134
- if (configuredDueNowCents < minimumStripeChargeCents) return {
3135
- kind: "card_below_minimum",
3136
- reserveCreditCents: toNonNegativeCents(0),
3137
- cardDueNowCents: configuredDueNowCents,
3138
- laterBalanceCents: toNonNegativeCents(totalCents - configuredDueNowCents)
3139
- };
3140
- reserveCreditCents = toNonNegativeCents(configuredDueNowCents - minimumStripeChargeCents);
3141
- }
3142
- const cardDueNowCents = toNonNegativeCents(configuredDueNowCents - reserveCreditCents);
3143
- return {
3144
- kind: "flexible_balance",
3145
- reserveCreditCents,
3146
- cardDueNowCents,
3147
- laterBalanceCents: toNonNegativeCents(totalCents - reserveCreditCents - cardDueNowCents)
3148
- };
3149
- }
3150
- //#endregion
3151
- //#region ../commerce/src/purchase/settlement.ts
3152
- function normalizeCurrencyCode(raw) {
3153
- const normalized = raw.trim().toUpperCase();
3154
- if (!/^[A-Z]{3}$/.test(normalized)) throw new Error(`Invalid currency code: ${raw}`);
3155
- return normalized;
3156
- }
3157
- //#endregion
3158
- //#region ../commerce/src/purchase/checkoutSessionMetadata.ts
3159
- const datedOfferingSharedEngineMetadataShape = {
3160
- datedOfferingEngineContractVersion: z.literal("shared_v1"),
3161
- datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
3162
- datedOfferingCapacityHoldId: z.string().uuid(),
3163
- datedOfferingCapacityHoldAuditRef: z.string().uuid(),
3164
- customerId: z.string().uuid(),
3165
- eventSeriesId: z.string().uuid()
3166
- };
3167
- z.object(datedOfferingSharedEngineMetadataShape);
3168
- const orderCheckoutSessionMetadataBaseShape = {
3169
- type: z.literal("order"),
3170
- site_id: z.string().uuid(),
3171
- order_id: z.string().uuid().optional(),
3172
- customer_email: z.string().email(),
3173
- customer_name: z.string().optional(),
3174
- product_id: z.string().uuid().optional(),
3175
- attendee_id: z.string().uuid().optional(),
3176
- occurrence_id: z.string().uuid().optional(),
3177
- tickets_count: z.string().optional(),
3178
- notes: z.string().optional(),
3179
- discount_redemption_id: z.string().uuid().optional(),
3180
- discount_code_id: z.string().uuid().optional(),
3181
- discount_code: z.string().optional(),
3182
- discount_public_label: z.string().optional(),
3183
- credit_reservation_id: z.string().uuid().optional(),
3184
- credit_reserved_cents: z.string().optional(),
3185
- discount_code_amount_cents: z.string().optional(),
3186
- order_payment_reason: z.enum([
3187
- "instalment_payoff",
3188
- "flexible_balance_initial",
3189
- "flexible_balance_paydown"
3190
- ]).optional(),
3191
- order_payment_plan_id: z.string().uuid().optional(),
3192
- order_flexible_balance_contract_id: z.string().uuid().optional()
3193
- };
3194
- const absentDatedOfferingSharedEngineMetadataShape = {
3195
- datedOfferingEngineContractVersion: z.never().optional(),
3196
- datedOfferingEngineRouteKind: z.never().optional(),
3197
- datedOfferingCapacityHoldId: z.never().optional(),
3198
- datedOfferingCapacityHoldAuditRef: z.never().optional(),
3199
- customerId: z.never().optional(),
3200
- eventSeriesId: z.never().optional()
3201
- };
3202
- const LegacyOrderCheckoutSessionMetadataSchema = z.object({
3203
- ...orderCheckoutSessionMetadataBaseShape,
3204
- ...absentDatedOfferingSharedEngineMetadataShape
3205
- });
3206
- const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
3207
- ...orderCheckoutSessionMetadataBaseShape,
3208
- ...datedOfferingSharedEngineMetadataShape
3209
- });
3210
- z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
3211
- toNonNegativeCents(0);
3212
- //#endregion
3213
- //#region ../commerce/src/purchase/stripeMinimumCharge.ts
3214
- const STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY = {
3215
- GBP: 30,
3216
- USD: 50,
3217
- EUR: 50,
3218
- AED: 200,
3219
- AUD: 50,
3220
- BGN: 100,
3221
- BRL: 50,
3222
- CAD: 50,
3223
- CHF: 50,
3224
- CZK: 1500,
3225
- DKK: 250,
3226
- HKD: 400,
3227
- HUF: 17500,
3228
- JPY: 50,
3229
- MXN: 1e3,
3230
- MYR: 200,
3231
- NOK: 300,
3232
- NZD: 50,
3233
- PLN: 200,
3234
- RON: 200,
3235
- SEK: 300,
3236
- SGD: 50,
3237
- THB: 1e3
3238
- };
3239
- function isStripeMinimumChargeCurrency(currency) {
3240
- return currency in STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY;
3241
- }
3242
- function getStripeMinimumChargeRequirement(rawCurrency) {
3243
- let currency;
3244
- try {
3245
- currency = normalizeCurrencyCode(rawCurrency);
3246
- } catch {
3247
- return null;
3248
- }
3249
- const currencyKey = currency;
3250
- if (!isStripeMinimumChargeCurrency(currencyKey)) return null;
3251
- return {
3252
- currency,
3253
- minimumAmountCents: STRIPE_MINIMUM_CHARGE_CENTS_BY_CURRENCY[currencyKey]
3254
- };
3255
- }
3256
- //#endregion
3257
3511
  //#region ../blocks/src/system/runtime/nodes/event-registration/siteCreditPreview.ts
3258
3512
  function calculateEventRegistrationPreviewTotal(input) {
3259
3513
  const discountCents = input.manualDiscountCode.trim() ? 0 : input.automaticDiscount?.computedAmountCents ?? 0;
@@ -3367,9 +3621,16 @@ function TicketCounter(props) {
3367
3621
  });
3368
3622
  }
3369
3623
  function TicketCounterLimit(props) {
3624
+ const showBookingLimit = !props.availabilityLabel || props.maxAllowed < (props.availableSpots ?? Infinity);
3370
3625
  return /* @__PURE__ */ jsxs("p", {
3371
3626
  className: "er-ticket-counter-limit",
3372
- children: [/* @__PURE__ */ jsx("span", { children: props.maximumLabel }), props.availabilityLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("br", {}), /* @__PURE__ */ jsx("span", { children: props.availabilityLabel })] }) : null]
3627
+ 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: [
3628
+ "Up to ",
3629
+ props.maxAllowed,
3630
+ " ",
3631
+ props.quantityLabel,
3632
+ " per booking."
3633
+ ] })] }) : null]
3373
3634
  });
3374
3635
  }
3375
3636
  function BookingUnitSessionHint(props) {
@@ -3385,7 +3646,7 @@ function BookingUnitSessionHint(props) {
3385
3646
  }
3386
3647
  function bookingQuantityLabel(unit, waitlist) {
3387
3648
  if (!waitlist) return pluralizeBookingUnit(unit, 2);
3388
- return unit === "place" ? "waitlist places" : "waitlist spots";
3649
+ return "waitlist places";
3389
3650
  }
3390
3651
  function BookingQuantityPrompt(props) {
3391
3652
  return /* @__PURE__ */ jsxs("h3", {
@@ -3444,6 +3705,7 @@ function SlidingScaleAmountField(props) {
3444
3705
  }
3445
3706
  function TicketTypeQuantityRows(props) {
3446
3707
  const totalSelectedQuantity = getTicketSelectionTotal(props.ticketSelections);
3708
+ const effectiveMaxTickets = props.availableSpots == null ? props.maxTickets : Math.min(props.maxTickets, props.availableSpots);
3447
3709
  return /* @__PURE__ */ jsxs("div", {
3448
3710
  className: "er-ticket-lines",
3449
3711
  children: [
@@ -3455,6 +3717,7 @@ function TicketTypeQuantityRows(props) {
3455
3717
  const quantity = getTicketLineQuantity(props.ticketSelections, ticketType.id);
3456
3718
  const disabled = ticketType.availability.kind === "unavailable";
3457
3719
  const availabilityLabel = getTicketTypeAvailabilityLabel(ticketType);
3720
+ const ticketTypeRemainingLabel = formatTicketTypeRemaining(ticketType.availability.remainingCapacity);
3458
3721
  const priceLabel = ticketType.priceAmount > 0 ? formatPrice(ticketType.priceAmount, props.currency) : "Free";
3459
3722
  const maxAllowed = disabled ? 0 : getTicketTypeLineMax({
3460
3723
  currentQuantity: quantity,
@@ -3486,9 +3749,9 @@ function TicketTypeQuantityRows(props) {
3486
3749
  availabilityLabel ? /* @__PURE__ */ jsx("p", {
3487
3750
  className: "er-ticket-line-detail",
3488
3751
  children: availabilityLabel
3489
- }) : ticketType.availability.remainingCapacity !== null ? /* @__PURE__ */ jsxs("p", {
3752
+ }) : ticketTypeRemainingLabel ? /* @__PURE__ */ jsx("p", {
3490
3753
  className: "er-ticket-line-detail",
3491
- children: [ticketType.availability.remainingCapacity, " remaining for this ticket type"]
3754
+ children: ticketTypeRemainingLabel
3492
3755
  }) : null
3493
3756
  ]
3494
3757
  }), /* @__PURE__ */ jsx(TicketCounter, {
@@ -3508,8 +3771,10 @@ function TicketTypeQuantityRows(props) {
3508
3771
  })
3509
3772
  }),
3510
3773
  /* @__PURE__ */ jsx(TicketCounterLimit, {
3511
- maximumLabel: `${props.maxTickets} ${pluralizeBookingUnit(props.bookingUnit, props.maxTickets)} maximum`,
3512
- availabilityLabel: props.availableSpots != null ? `${props.availableSpots} spots available` : null
3774
+ maxAllowed: effectiveMaxTickets,
3775
+ quantityLabel: pluralizeBookingUnit(props.bookingUnit, effectiveMaxTickets),
3776
+ availableSpots: props.availableSpots,
3777
+ availabilityLabel: props.availabilityLabel
3513
3778
  }),
3514
3779
  totalSelectedQuantity === 0 && props.hasAvailableTicketTypes ? /* @__PURE__ */ jsxs("p", {
3515
3780
  className: "er-text er-text--muted",
@@ -3530,9 +3795,13 @@ function TicketTypeQuantityRows(props) {
3530
3795
  });
3531
3796
  }
3532
3797
  function getAttendanceModeDetail(attendanceMode) {
3533
- if (attendanceMode.availableSpots === null) return "Unlimited spots";
3534
- if (attendanceMode.availableSpots === 0) return attendanceMode.waitlistEnabled ? "Currently full, waitlist available" : "Currently full";
3535
- return `${attendanceMode.availableSpots} spots available`;
3798
+ const remaining = getRemainingPlaces(attendanceMode);
3799
+ switch (remaining.kind) {
3800
+ case "unlimited": return "Unlimited places";
3801
+ case "open": return "Places available";
3802
+ case "low": return formatRemainingPlaces(attendanceMode);
3803
+ case "full": return remaining.waitlist ? "Currently full, waitlist available" : "Currently full";
3804
+ }
3536
3805
  }
3537
3806
  function AttendanceModeTiles(props) {
3538
3807
  return /* @__PURE__ */ jsx("div", {
@@ -3578,11 +3847,11 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3578
3847
  const unit = bookingUnit.kind;
3579
3848
  const deliveryMode = event?.deliveryMode ?? "in_person";
3580
3849
  const attendanceModes = event?.attendanceModes ?? [];
3581
- const selectedModeAvailability = selectedAttendanceMode ? attendanceModes.find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
3582
- const availableSpots = selectedModeAvailability?.availableSpots ?? event?.availableSpots;
3583
- const waitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? event?.waitlistEnabled ?? false;
3584
- const isSoldOut = availableSpots === 0;
3585
- const canJoinWaitlist = isSoldOut && waitlistEnabled === true;
3850
+ const capacitySource = event ? selectEventCapacitySource(event, selectedAttendanceMode) : null;
3851
+ const availability = capacitySource ? getEventAvailability(capacitySource) : null;
3852
+ const availableSpots = availability?.available;
3853
+ const isSoldOut = availability?.isFull ?? false;
3854
+ const canJoinWaitlist = capacitySource ? canJoinEventWaitlist(capacitySource) : false;
3586
3855
  const eventPricing = event ? resolveRuntimeEventPricing(event) : null;
3587
3856
  const eventPricingMode = eventPricing ? publicEventPricingMode(eventPricing) : void 0;
3588
3857
  const eventPricingCurrency = eventPricing ? publicEventPricingCurrency(eventPricing) : null;
@@ -3603,7 +3872,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3603
3872
  selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingCapacity ?? null,
3604
3873
  hasSelectedTicketType: implicitTicketType !== null
3605
3874
  });
3606
- const eventAvailabilityLabel = !canJoinWaitlist && event?.availableSpots != null ? `${event.availableSpots} spots available` : null;
3875
+ const eventAvailabilityLabel = !canJoinWaitlist && capacitySource ? formatRemainingPlaces(capacitySource) : null;
3607
3876
  if (deliveryMode === "hybrid" && !selectedAttendanceMode) return /* @__PURE__ */ jsxs("div", {
3608
3877
  className: "rb-reg-stack er-stack",
3609
3878
  children: [
@@ -3626,7 +3895,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3626
3895
  className: "rb-reg-stack er-stack",
3627
3896
  children: [/* @__PURE__ */ jsx("h3", {
3628
3897
  className: "er-title",
3629
- children: "Sold Out"
3898
+ children: "Sold out"
3630
3899
  }), /* @__PURE__ */ jsx("p", {
3631
3900
  className: "er-text er-text--muted",
3632
3901
  children: "Sorry you missed out! This event is fully booked."
@@ -3657,6 +3926,7 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3657
3926
  ticketSelections,
3658
3927
  maxTickets,
3659
3928
  availableSpots,
3929
+ availabilityLabel: eventAvailabilityLabel,
3660
3930
  currency: eventPricingCurrency ?? "usd",
3661
3931
  hasAvailableTicketTypes,
3662
3932
  onTicketSelectionsChange,
@@ -3683,14 +3953,16 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
3683
3953
  label: bookingQuantityLabel(unit, canJoinWaitlist),
3684
3954
  onTicketsChange
3685
3955
  }), /* @__PURE__ */ jsx(TicketCounterLimit, {
3686
- maximumLabel: `${maxAllowed} ${canJoinWaitlist ? bookingQuantityLabel(unit, true) : pluralizeBookingUnit(unit, maxAllowed)} maximum`,
3956
+ maxAllowed,
3957
+ quantityLabel: canJoinWaitlist ? bookingQuantityLabel(unit, true) : pluralizeBookingUnit(unit, maxAllowed),
3958
+ availableSpots: canJoinWaitlist ? null : availableSpots,
3687
3959
  availabilityLabel: eventAvailabilityLabel
3688
3960
  })]
3689
3961
  })
3690
3962
  ] }),
3691
3963
  canJoinWaitlist && /* @__PURE__ */ jsx("p", {
3692
3964
  className: "er-text er-text--muted",
3693
- children: "This event is currently full. Continue to join the waitlist if a spot opens up."
3965
+ children: "This event is currently full. Continue to join the waitlist if a place opens up."
3694
3966
  })
3695
3967
  ]
3696
3968
  });
@@ -4172,15 +4444,14 @@ function resolvePaymentStepStatus(input) {
4172
4444
  function shouldPromptForLogin(status, isAuthenticated, eligibility) {
4173
4445
  return Boolean(status === "not-eligible" && !isAuthenticated && eligibility && (eligibility.requiresAuthForPasses || eligibility.requiresAuthForMemberships));
4174
4446
  }
4175
- function MissingEligibilityPanel(props) {
4176
- const missing = [!props.occurrenceId && "occurrenceId", !props.email && "email"].filter((part) => Boolean(part));
4447
+ function MissingEligibilityPanel() {
4177
4448
  return /* @__PURE__ */ jsx("div", {
4178
4449
  className: "rb-reg-stack er-stack",
4179
4450
  children: /* @__PURE__ */ jsx("div", {
4180
4451
  className: "er-center",
4181
- children: /* @__PURE__ */ jsxs("p", {
4452
+ children: /* @__PURE__ */ jsx("p", {
4182
4453
  className: "er-text er-text--error",
4183
- children: ["Unable to determine payment options", missing.length > 0 && ` (missing: ${missing.join(", ")})`]
4454
+ 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."
4184
4455
  })
4185
4456
  })
4186
4457
  });
@@ -4270,10 +4541,7 @@ function PaymentBlockingPanel(props) {
4270
4541
  })
4271
4542
  })
4272
4543
  });
4273
- if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {
4274
- occurrenceId: props.occurrenceId,
4275
- email: props.email
4276
- });
4544
+ if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {});
4277
4545
  if (props.eligibility.registration.kind !== "none") {
4278
4546
  const isBooked = props.eligibility.registration.kind === "booked";
4279
4547
  return /* @__PURE__ */ jsxs("div", {
@@ -4281,12 +4549,16 @@ function PaymentBlockingPanel(props) {
4281
4549
  children: [/* @__PURE__ */ jsx("h3", {
4282
4550
  className: "er-title",
4283
4551
  children: isBooked ? "Already booked" : "Already on the waitlist"
4284
- }), /* @__PURE__ */ jsx("div", {
4552
+ }), /* @__PURE__ */ jsxs("div", {
4285
4553
  className: "er-center",
4286
- children: /* @__PURE__ */ jsx("p", {
4554
+ children: [/* @__PURE__ */ jsx("p", {
4287
4555
  className: "er-text er-text--muted",
4288
- children: isBooked ? "You're already booked on this event." : "You're already on the waitlist for this event."
4289
- })
4556
+ 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."
4557
+ }), /* @__PURE__ */ jsx("a", {
4558
+ href: "/portal",
4559
+ className: "er-retry-btn",
4560
+ children: "View my bookings"
4561
+ })]
4290
4562
  })]
4291
4563
  });
4292
4564
  }
@@ -4409,7 +4681,7 @@ function resolvePaymentButtonText(method, siteCreditPreview, eligibility, select
4409
4681
  const currency = eligibility?.currency ?? "GBP";
4410
4682
  switch (method) {
4411
4683
  case "stripe":
4412
- if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to Payment";
4684
+ if (siteCreditPreview.kind !== "applied") return selectedDirectOption?.ctaLabel ?? "Continue to payment";
4413
4685
  if (siteCreditPreview.cardDueNowCents > 0) return `Pay ${formatPrice(siteCreditPreview.cardDueNowCents, currency)} by card`;
4414
4686
  return `Book with ${formatPrice(siteCreditPreview.appliedCreditCents, currency)} site credit`;
4415
4687
  case "pass": return "Book using class pass";
@@ -4785,7 +5057,19 @@ function StepIndicator(props) {
4785
5057
  *
4786
5058
  * Displays an error message with a retry button when registration fails.
4787
5059
  */
4788
- const ErrorStep = ({ message, onRetry }) => {
5060
+ const labels = {
5061
+ none: "",
5062
+ retry: "Try again",
5063
+ places: "Choose places",
5064
+ start: "Start again",
5065
+ details: "Check my details",
5066
+ "payment-options": "View payment options",
5067
+ "payment-status": "Check payment status",
5068
+ portal: "View my bookings",
5069
+ event: "View event"
5070
+ };
5071
+ const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false }) => {
5072
+ const offerWaitlist = action === "places" && canJoinWaitlist;
4789
5073
  return /* @__PURE__ */ jsx(TerminalPanel, {
4790
5074
  tone: "error",
4791
5075
  className: "er-center",
@@ -4794,16 +5078,20 @@ const ErrorStep = ({ message, onRetry }) => {
4794
5078
  variant: "error",
4795
5079
  children: /* @__PURE__ */ jsx(CrossIcon, {})
4796
5080
  }),
4797
- title: "Registration Failed",
5081
+ title: "We couldn’t complete your booking",
4798
5082
  body: /* @__PURE__ */ jsx("p", {
4799
5083
  className: "er-text--muted er-message",
4800
- children: message
5084
+ 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
4801
5085
  }),
4802
- actions: /* @__PURE__ */ jsx("button", {
5086
+ actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
5087
+ href: "/portal",
5088
+ className: "er-retry-btn",
5089
+ children: labels[action]
5090
+ }) : /* @__PURE__ */ jsx("button", {
4803
5091
  type: "button",
4804
5092
  onClick: onRetry,
4805
5093
  className: "er-retry-btn",
4806
- children: "Try Again"
5094
+ children: offerWaitlist ? "Join the waiting list" : labels[action]
4807
5095
  })
4808
5096
  });
4809
5097
  };
@@ -4815,7 +5103,7 @@ const ErrorStep = ({ message, onRetry }) => {
4815
5103
  * Displays when the payment was definitively declined/failed.
4816
5104
  * Provides a retry option that returns to payment selection with form state preserved.
4817
5105
  */
4818
- const PaymentFailedStep = ({ message, onRetry }) => {
5106
+ const PaymentFailedStep = ({ onRetry }) => {
4819
5107
  return /* @__PURE__ */ jsx(TerminalPanel, {
4820
5108
  tone: "error",
4821
5109
  className: "er-center",
@@ -4824,16 +5112,16 @@ const PaymentFailedStep = ({ message, onRetry }) => {
4824
5112
  variant: "error",
4825
5113
  children: /* @__PURE__ */ jsx(CrossIcon, {})
4826
5114
  }),
4827
- title: "Payment Failed",
5115
+ title: "Your payment didn’t go through",
4828
5116
  body: /* @__PURE__ */ jsx("p", {
4829
5117
  className: "er-text--muted er-message",
4830
- children: message
5118
+ children: "Please try again or choose another payment option. If this keeps happening, contact the organiser for help."
4831
5119
  }),
4832
5120
  actions: /* @__PURE__ */ jsx("button", {
4833
5121
  type: "button",
4834
5122
  onClick: onRetry,
4835
5123
  className: "er-retry-btn",
4836
- children: "Try Again"
5124
+ children: "View payment options"
4837
5125
  })
4838
5126
  });
4839
5127
  };
@@ -4928,7 +5216,7 @@ const VerifyingPaymentStep = () => {
4928
5216
  * - "Check Again" button to restart polling
4929
5217
  * - Contact support option for user reassurance
4930
5218
  */
4931
- const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail }) => {
5219
+ 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." }) => {
4932
5220
  return /* @__PURE__ */ jsx(TerminalPanel, {
4933
5221
  tone: "warning",
4934
5222
  className: "er-center",
@@ -4937,17 +5225,21 @@ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail }) => {
4937
5225
  variant: "warning",
4938
5226
  children: /* @__PURE__ */ jsx(ClockIcon, {})
4939
5227
  }),
4940
- title: "Your registration is saved",
5228
+ title,
4941
5229
  body: /* @__PURE__ */ jsx("p", {
4942
5230
  className: "er-text--muted er-message",
4943
- children: "We're still confirming your payment. This usually resolves within a few minutes. You'll receive a confirmation email once everything is verified."
5231
+ children: message
4944
5232
  }),
4945
5233
  actionsClassName: "er-button-group",
4946
- actions: /* @__PURE__ */ jsx("button", {
5234
+ actions: onCheckAgain ? /* @__PURE__ */ jsx("button", {
4947
5235
  type: "button",
4948
5236
  onClick: onCheckAgain,
4949
5237
  className: "er-btn er-btn--primary",
4950
- children: "Check Again"
5238
+ children: "Check payment status"
5239
+ }) : /* @__PURE__ */ jsx("a", {
5240
+ href: "/portal",
5241
+ className: "er-btn er-btn--primary",
5242
+ children: "Check payment status"
4951
5243
  }),
4952
5244
  secondaryBody: supportEmail ? /* @__PURE__ */ jsxs("p", {
4953
5245
  className: "er-text--sm er-text--muted er-message",
@@ -5044,7 +5336,9 @@ function EventRegistrationTerminalState(props) {
5044
5336
  onRetry: () => wizard.setStep("select-payment")
5045
5337
  });
5046
5338
  case "verifying-timeout": return /* @__PURE__ */ jsx(VerifyingTimeoutStep, {
5047
- onCheckAgain: wizard.verification.retry,
5339
+ onCheckAgain: wizard.verification.status === "timeout" ? wizard.verification.retry : wizard.verification.retryReturn,
5340
+ title: wizard.verification.status === "timeout" ? void 0 : "We’re checking your payment",
5341
+ message: wizard.verification.status === "timeout" ? void 0 : wizard.errorMessage,
5048
5342
  supportEmail: props.supportEmail
5049
5343
  });
5050
5344
  case "success": return wizard.registrationResult ? /* @__PURE__ */ jsx(SuccessStep, {
@@ -5054,6 +5348,8 @@ function EventRegistrationTerminalState(props) {
5054
5348
  }) : null;
5055
5349
  case "error": return /* @__PURE__ */ jsx(ErrorStep, {
5056
5350
  message: wizard.errorMessage,
5351
+ action: wizard.errorAction,
5352
+ canJoinWaitlist: wizard.eligibility?.isFull && wizard.eligibility.canJoinWaitlist,
5057
5353
  onRetry: wizard.handleRetry
5058
5354
  });
5059
5355
  case "select-event":
@@ -5211,7 +5507,7 @@ const EVENT_PORTAL_AUTH_COPY = {
5211
5507
  buttonText: "Email me a login link →"
5212
5508
  },
5213
5509
  "waitlist-claim": {
5214
- title: "Log in to claim your waitlist spot",
5510
+ title: "Log in to claim your waitlist place",
5215
5511
  buttonText: "Email me a login link to continue →"
5216
5512
  }
5217
5513
  };
@@ -5221,11 +5517,12 @@ function getActiveTicketTypes(event) {
5221
5517
  function formatTicketTypePrice(ticketType, currency) {
5222
5518
  return formatCurrencyAmount(ticketType.priceAmount, currency, { decimals: "auto" });
5223
5519
  }
5224
- function getAvailabilityLabel(event) {
5225
- const availableSpots = event?.availableSpots;
5226
- if (availableSpots === null || availableSpots === void 0) return null;
5227
- if (availableSpots > 0) return `${availableSpots} spots left`;
5228
- return event?.waitlistEnabled ? "Waitlist available" : "Fully booked";
5520
+ function getAvailabilityLabel(event, attendanceMode) {
5521
+ if (!event) return null;
5522
+ return formatRemainingPlaces(selectEventCapacitySource(event, attendanceMode), {
5523
+ full: "Fully booked",
5524
+ waitlist: "Waitlist available"
5525
+ });
5229
5526
  }
5230
5527
  function getWizardStepLabel(input) {
5231
5528
  switch (input.step) {
@@ -5322,7 +5619,7 @@ function EventRegistrationWizard(props) {
5322
5619
  const currentStepNumber = getStepNumber(wizard.step, activeWizardSteps);
5323
5620
  const summaryEventPricingType = summaryEvent ? publicEventPricingType(summaryEvent.pricing) : "free";
5324
5621
  const summaryPriceLabel = summaryEvent ? formatEventPriceDisplay(summaryEvent.pricing) : null;
5325
- const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent);
5622
+ const summaryAvailabilityLabel = getAvailabilityLabel(summaryEvent, wizard.selectedAttendanceMode);
5326
5623
  const waitlistClaimRequiresPortalLogin = wizard.waitlistClaim.requiresPortalLogin && wizard.portalAuthState !== "authenticated" && wizard.portalAuthState !== "checking";
5327
5624
  const portalAuthCopy = waitlistClaimRequiresPortalLogin ? {
5328
5625
  ...EVENT_PORTAL_AUTH_COPY["waitlist-claim"],