@riverbankcms/sdk 0.136.0 → 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 (160) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/dist/{PreviewEditorSidebar-BU5ZlhQR.mjs → PreviewEditorSidebar-Bn6cONPa.mjs} +8 -8
  3. package/dist/{PreviewEditorUI-9XkHL-bC.mjs → PreviewEditorUI-LAwXZPSx.mjs} +3 -3
  4. package/dist/{PreviewQueryProvider-wbEpXryy.mjs → PreviewQueryProvider-ztQUdsNo.mjs} +1 -1
  5. package/dist/{SdkPreviewModeRuntime-CnWmj-2B.mjs → SdkPreviewModeRuntime-BbbGsI_v.mjs} +596 -164
  6. package/dist/{SiteChromeCustomizeContext-B6d5zkog.mjs → SiteChromeCustomizeContext-BbkNSt5a.mjs} +2888 -1404
  7. package/dist/_dts/ai/src/contracts/proposals.d.ts +12 -298
  8. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +21 -14
  9. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  10. package/dist/_dts/api/src/accessAdmin.d.ts +1 -9
  11. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  12. package/dist/_dts/api/src/contentRuntime.d.ts +1 -4
  13. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  14. package/dist/_dts/api/src/endpoints.d.ts +194 -181
  15. package/dist/_dts/api/src/index.d.ts +4 -3
  16. package/dist/_dts/api/src/media.d.ts +19 -2
  17. package/dist/_dts/api/src/sdkContracts.d.ts +2 -15
  18. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  19. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +61 -0
  20. package/dist/_dts/api/src/sdkThemeWire.d.ts +15 -4
  21. package/dist/_dts/api/src/siteInfrastructure.d.ts +17 -23
  22. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +7 -21
  23. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +27 -3
  24. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  25. package/dist/_dts/api/src/types.d.ts +1 -1
  26. package/dist/_dts/blocks/src/PageRenderer.d.ts +2 -2
  27. package/dist/_dts/blocks/src/index.d.ts +1 -1
  28. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +9 -0
  29. package/dist/_dts/blocks/src/pageRendererBlockSupport.d.ts +75 -0
  30. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +2 -0
  31. package/dist/_dts/blocks/src/server.d.ts +1 -1
  32. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -1
  33. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +2 -1
  34. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +3 -3
  35. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +34 -22
  36. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  37. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -0
  38. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  39. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  40. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  41. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  42. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  43. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +7 -3
  44. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -0
  45. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +5 -2
  46. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  47. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  48. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  49. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  50. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  51. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  52. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +31 -0
  53. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  54. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +1 -1
  55. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +1 -1
  56. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  57. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  58. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  59. package/dist/_dts/commerce/src/purchase/index.d.ts +1 -0
  60. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +8 -40
  61. package/dist/_dts/db/src/index.d.ts +1 -0
  62. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  63. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  64. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  65. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  66. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  67. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  68. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  69. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  70. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  71. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  72. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  73. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  74. package/dist/_dts/sdk/src/public-api/contracts.d.ts +2 -1
  75. package/dist/_dts/sdk/src/version.d.ts +1 -1
  76. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  77. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  78. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +25 -2
  79. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +3 -3
  80. package/dist/_dts/theme-core/src/schema.d.ts +12 -11
  81. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  82. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +10 -0
  83. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  84. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  85. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  86. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -9
  87. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  88. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  89. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  90. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +10 -1
  91. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +1 -1
  92. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +7 -0
  93. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  94. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  95. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  96. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  97. package/dist/client/{BookingConsentAgreement-DCch21a6.mjs → BookingConsentAgreement--_fUCZe-.mjs} +2 -2
  98. package/dist/client/{DiscountCodeDisclosure-DIj1T59K.mjs → DiscountCodeDisclosure-BMipSoDJ.mjs} +1 -1
  99. package/dist/client/{EventCombined.client-BWVNlXTx.mjs → EventCombined.client-CwJOxn7A.mjs} +154 -178
  100. package/dist/client/{EventListing.client-DVFCDe3i.mjs → EventListing.client-eVwiylPW.mjs} +2 -2
  101. package/dist/client/{LeafletStaticMap-BY5mIHw9.mjs → LeafletStaticMap-IKenna5u.mjs} +1 -1
  102. package/dist/client/{accordion-CK29E8rG.mjs → accordion-MGAO3PuE.mjs} +1 -1
  103. package/dist/client/{bookingForm-DFvnet-N.mjs → bookingForm-CudlQPUu.mjs} +5 -5
  104. package/dist/client/bookings.mjs +418 -79
  105. package/dist/client/capacity-BW00PO7s.mjs +271 -0
  106. package/dist/client/{carousel-BVU5NQlb.mjs → carousel-BrbGJ2Jl.mjs} +1 -1
  107. package/dist/client/{cart-BNItcZVa.mjs → cart-Bq5eU0xZ.mjs} +3 -3
  108. package/dist/client/{checkout-Bu6iCdkP.mjs → checkout-BAFVRdrK.mjs} +3 -3
  109. package/dist/client/client.mjs +2919 -1918
  110. package/dist/client/{context-BImarOAq.mjs → context-DCkiHVdD.mjs} +8 -5
  111. package/dist/client/dates-2RPXHQy4.mjs +264 -0
  112. package/dist/client/{eventCalendar-DS2h-UXD.mjs → eventCalendar-B0O-hPF7.mjs} +10 -17
  113. package/dist/client/{eventCombined-Drt4Oq9p.mjs → eventCombined-DCkDaoDI.mjs} +3 -3
  114. package/dist/client/{eventDetails-Bl2vWgTX.mjs → eventDetails-D8GG2dnD.mjs} +5 -3
  115. package/dist/client/{eventListing-CS7PHY7v.mjs → eventListing-3ssV_bxz.mjs} +3 -3
  116. package/dist/client/{eventRegistration-D-j8eNy2.mjs → eventRegistration-bPQo3kVw.mjs} +562 -309
  117. package/dist/client/{form-RMpoLTyr.mjs → form-qMv8RDkk.mjs} +2 -2
  118. package/dist/client/{headerSection-BYE3Lb3k.mjs → headerSection-CH0Sc5hX.mjs} +2 -2
  119. package/dist/client/hooks.mjs +289 -32
  120. package/dist/client/{locationMap-BxSr7Kkh.mjs → locationMap-0_xlBnNI.mjs} +2 -2
  121. package/dist/client/{newsletterForm-DerbXwFC.mjs → newsletterForm-NUW7m9RI.mjs} +2 -2
  122. package/dist/client/{productDetail-4SZCLhAf.mjs → productDetail-FBEOM7jW.mjs} +3 -3
  123. package/dist/client/{productList-D7AP4GNq.mjs → productList-R0OSGhnn.mjs} +3 -3
  124. package/dist/client/{propsDecoder-pzBeCh14.mjs → propsDecoder-pYvx_Srl.mjs} +88 -380
  125. package/dist/client/rendering/client.mjs +2616 -1637
  126. package/dist/client/rendering/islands.mjs +17 -17
  127. package/dist/client/rendering.mjs +2710 -1702
  128. package/dist/client/{scrollToBlock-CduAKf8T.mjs → scrollToBlock-ByQv96M2.mjs} +1 -1
  129. package/dist/client/{shop-Cp5p_kGe.mjs → shop-RUG9DmRE.mjs} +6 -6
  130. package/dist/client/{shop-commerce-DicXYaYX.mjs → shop-commerce-B76Jz23B.mjs} +5 -5
  131. package/dist/client/{shop-commerce-store-CGWnpY4K.mjs → shop-commerce-store-QuQNdDsD.mjs} +1 -1
  132. package/dist/client/{shopShared-BDf9ljkm.mjs → shopShared-vXU1omwJ.mjs} +3 -3
  133. package/dist/client/{useEventFilters-DeeyGYuk.mjs → useEventFilters-ByuU6HLA.mjs} +33 -18
  134. package/dist/preview-next/before-render.mjs +79 -51
  135. package/dist/preview-next/client/runtime.mjs +4 -4
  136. package/dist/preview-next/keepAlive.mjs +79 -51
  137. package/dist/preview-next/middleware.mjs +79 -51
  138. package/dist/{previewAuthStore-DbqBol0V.mjs → previewAuthStore-L_cKW8Sb.mjs} +79 -51
  139. package/dist/{sdk-runtime-19MO27F0.mjs → sdk-runtime-sUMr5d70.mjs} +2 -2
  140. package/dist/{separator-DaPLtJ1f.mjs → separator-BIHkie5i.mjs} +1 -1
  141. package/dist/server/components.mjs +2077 -1274
  142. package/dist/server/config-validation.mjs +289 -32
  143. package/dist/server/config.mjs +289 -32
  144. package/dist/server/data.mjs +289 -32
  145. package/dist/server/index.mjs +80 -52
  146. package/dist/server/next.mjs +1822 -1111
  147. package/dist/server/page-converter.mjs +167 -31
  148. package/dist/server/prebuild.mjs +1 -1
  149. package/dist/server/rendering/server.mjs +1988 -1286
  150. package/dist/server/rendering.mjs +1943 -1182
  151. package/dist/server/routing.mjs +351 -86
  152. package/dist/server/server.mjs +293 -128
  153. package/dist/server/theme-bridge.mjs +52 -8
  154. package/dist/server/theme.mjs +27 -8
  155. package/dist/styles/index.css +15 -0
  156. package/package.json +1 -1
  157. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  158. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  159. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  160. /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
@@ -2838,10 +2838,11 @@ function encodePublicProductCategorySelector(selector) {
2838
2838
  * Error thrown when an API request fails
2839
2839
  */
2840
2840
  var BlockApiError = class extends Error {
2841
- constructor(message, status, code) {
2841
+ constructor(message, status, code, tag) {
2842
2842
  super(message);
2843
2843
  this.status = status;
2844
2844
  this.code = code;
2845
+ this.tag = tag;
2845
2846
  this.name = "BlockApiError";
2846
2847
  }
2847
2848
  };
@@ -2873,7 +2874,8 @@ function getBlockApiErrorDetails(data) {
2873
2874
  const nestedError = isRecord$7(data.error) ? data.error : void 0;
2874
2875
  return {
2875
2876
  message: typeof nestedError?.message === "string" ? nestedError.message : typeof data.message === "string" ? data.message : void 0,
2876
- code: typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0
2877
+ code: typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0,
2878
+ tag: typeof nestedError?.tag === "string" ? nestedError.tag : void 0
2877
2879
  };
2878
2880
  }
2879
2881
  function buildUnexpectedJsonResponseMessage(parseResult) {
@@ -2892,7 +2894,8 @@ async function extractErrorDetails(response) {
2892
2894
  const details = getBlockApiErrorDetails(parseResult.value);
2893
2895
  return {
2894
2896
  message: details.message ?? fallbackMessage,
2895
- code: details.code
2897
+ code: details.code,
2898
+ tag: details.tag
2896
2899
  };
2897
2900
  }
2898
2901
  case "invalid-json": return { message: `${fallbackMessage} (received invalid JSON)` };
@@ -2989,7 +2992,7 @@ function createBlockApi(config) {
2989
2992
  const response = await fetch(url, fetchOptions);
2990
2993
  if (!response.ok) {
2991
2994
  const details = await extractErrorDetails(response);
2992
- throw new BlockApiError(details.message, response.status, details.code);
2995
+ throw new BlockApiError(details.message, response.status, details.code, details.tag);
2993
2996
  }
2994
2997
  const json = await parseJsonApiResponse(response);
2995
2998
  if (isApiSuccessResponse(json)) return json.data;
@@ -3823,6 +3826,18 @@ function isRecord$6(value) {
3823
3826
  return typeof value === "object" && value !== null && !Array.isArray(value);
3824
3827
  }
3825
3828
  //#endregion
3829
+ //#region ../core/src/money-input.ts
3830
+ function unsafeAsNonNegativeCents(value) {
3831
+ return value;
3832
+ }
3833
+ /**
3834
+ * Normalizes a derived amount to non-negative whole cents. This intentionally
3835
+ * clamps negatives to zero; use parseNonNegativeCents for validation.
3836
+ */
3837
+ function toNonNegativeCents(value) {
3838
+ return unsafeAsNonNegativeCents(Math.max(0, Math.trunc(value)));
3839
+ }
3840
+ //#endregion
3826
3841
  //#region ../core/src/booking-flexible-balance.ts
3827
3842
  function clampFlexibleBalanceConfiguredAmount(amountCents, totalAmountCents) {
3828
3843
  return Math.max(0, Math.min(amountCents, totalAmountCents));
@@ -8783,6 +8798,11 @@ const gradientConfigSchema = z.object({
8783
8798
  position: z.string().optional(),
8784
8799
  stops: z.array(gradientStopSchema).min(2)
8785
8800
  });
8801
+ const gradientsSchema = z.object({
8802
+ button: z.string().min(1).nullable().optional(),
8803
+ hero: z.string().min(1).nullable().optional(),
8804
+ background: z.string().min(1).nullable().optional()
8805
+ });
8786
8806
  //#endregion
8787
8807
  //#region ../theme-core/src/interactive/baseSchema.ts
8788
8808
  /**
@@ -9448,11 +9468,30 @@ const headerContainerSchema = z.object({
9448
9468
  tint: z.string().nullable().optional(),
9449
9469
  opacity: z.number().min(0).max(1).default(.12)
9450
9470
  }).optional();
9451
- const headerInteractionPaletteSchema = z.enum([
9471
+ //#endregion
9472
+ //#region ../theme-core/src/header/interactionPalette.ts
9473
+ /**
9474
+ * The palettes that fill the nav link with a colour on hover and focus. Each
9475
+ * one names a background/foreground token pair.
9476
+ */
9477
+ const headerInteractionPillPaletteValues = [
9452
9478
  "primary",
9453
9479
  "secondary",
9454
9480
  "accent"
9455
- ]);
9481
+ ];
9482
+ /**
9483
+ * "No pill": the nav link keeps whatever hover treatment its recipe compiled,
9484
+ * and the palette step writes nothing at all (#1318 Phase 4c decision 2).
9485
+ *
9486
+ * It exists because "a colour change on hover, no filled background" was not
9487
+ * expressible in the vocabulary, and it is what
9488
+ * `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
9489
+ * the palette existed. Without it those two rows can only be classified as a
9490
+ * conscious visual change.
9491
+ */
9492
+ const headerInteractionQuietPalette = "quiet";
9493
+ const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
9494
+ const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
9456
9495
  //#endregion
9457
9496
  //#region ../theme-core/src/boxBackgroundSchema.ts
9458
9497
  const boxBackgroundOverlaySchema = z.preprocess(normalizeAuthoringOpacity, z.discriminatedUnion("type", [
@@ -9973,7 +10012,7 @@ const headerSchema = z.object({
9973
10012
  variant: headerVariantWithLegacyAliasSchema,
9974
10013
  positioning: headerPositioning,
9975
10014
  shrinkOnScroll: z.boolean(),
9976
- maxWidth: headerMaxWidth,
10015
+ maxWidth: headerMaxWidth.default("container"),
9977
10016
  scrollTreatment: headerScrollTreatmentSchema,
9978
10017
  background: headerBackgroundSchema.default({
9979
10018
  type: "color",
@@ -10172,11 +10211,6 @@ const bodyTextSchema = z.object({ intro: z.object({
10172
10211
  paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
10173
10212
  headingSize: bodyTextIntroTypographySizeSchema.optional()
10174
10213
  }).optional() }).optional();
10175
- const gradientsSchema = z.object({
10176
- button: z.string().min(1).nullable().optional(),
10177
- hero: z.string().min(1).nullable().optional(),
10178
- background: z.string().min(1).nullable().optional()
10179
- });
10180
10214
  /**
10181
10215
  * Testimonial quote-mark treatment, owned by the site style (analogous to the
10182
10216
  * accordion icon). `style` is the coarse selector; the optional fields fine-tune
@@ -14300,7 +14334,113 @@ const BookingFormClient$1 = ({ siteId: siteIdProp, render }) => {
14300
14334
  });
14301
14335
  };
14302
14336
  //#endregion
14303
- //#region ../blocks/src/system/runtime/nodes/events/shared/destination.ts
14337
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
14338
+ function resolveRegistrationRecoveryAction(step, action) {
14339
+ if (step === "payment-failed") return "payment-options";
14340
+ if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
14341
+ return action;
14342
+ }
14343
+ function canRecoverToWaitlist(eligibility) {
14344
+ return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
14345
+ }
14346
+ function shouldRefreshAfterRegistrationError(step, action, enabled) {
14347
+ return enabled || step === "error" && action === "places";
14348
+ }
14349
+ function resetRegistrationAfterExpiry(input) {
14350
+ input.dispatchFlow({
14351
+ type: "reset",
14352
+ hasPreSelectedContext: input.hasPreSelectedContext
14353
+ });
14354
+ input.setSelectedTicketLines([]);
14355
+ input.setSelectedSlidingAmountInput("");
14356
+ const mode = input.preSelectedEvent?.deliveryMode;
14357
+ input.setSelectedAttendanceMode(mode === "online" || mode === "in_person" ? mode : null);
14358
+ input.setErrorMessage("");
14359
+ input.setPendingRegistrationReference(null);
14360
+ }
14361
+ /** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
14362
+ function recoverRegistrationError(action, handlers) {
14363
+ switch (action) {
14364
+ case "none": return;
14365
+ case "portal":
14366
+ case "payment-status":
14367
+ window.location.assign("/portal");
14368
+ return;
14369
+ case "event":
14370
+ window.location.assign(window.location.pathname);
14371
+ return;
14372
+ case "retry":
14373
+ window.location.reload();
14374
+ return;
14375
+ case "details":
14376
+ handlers.clearError();
14377
+ handlers.setStep("enter-details");
14378
+ return;
14379
+ case "payment-options":
14380
+ case "places":
14381
+ handlers.refreshOptions();
14382
+ handlers.clearError();
14383
+ handlers.setStep(action === "places" ? handlers.canJoinWaitlist ? "enter-details" : "select-tickets" : "select-payment");
14384
+ return;
14385
+ case "start":
14386
+ handlers.refreshOptions();
14387
+ handlers.reset();
14388
+ return;
14389
+ }
14390
+ }
14391
+ //#endregion
14392
+ //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
14393
+ /**
14394
+ * Compute remaining spots for an event from `availableSpots` with a fallback
14395
+ * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
14396
+ * for uncapped events so call sites can render no capacity indicator at all.
14397
+ *
14398
+ * The single source of truth for capacity math across event cards, agenda
14399
+ * rows, and timetable cells — keep wording variations in the call sites.
14400
+ */
14401
+ function getEventAvailability(event) {
14402
+ if (event.capacity == null) return {
14403
+ available: null,
14404
+ isFull: false
14405
+ };
14406
+ const available = event.availableSpots ?? event.capacity - event.registeredCount;
14407
+ if (available <= 0) return {
14408
+ available: 0,
14409
+ isFull: true
14410
+ };
14411
+ return {
14412
+ available,
14413
+ isFull: false
14414
+ };
14415
+ }
14416
+ /**
14417
+ * Pick the capacity row a booking decision should read: the selected
14418
+ * attendance mode's availability when the visitor has chosen one and the
14419
+ * event publishes it, otherwise the event-level aggregate.
14420
+ *
14421
+ * The mode row is used wholesale. A mode with `capacity: null` on a capped
14422
+ * event is uncapped *by design*: the registration write path applies the
14423
+ * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
14424
+ * back to the event's `availableSpots` here would block bookings the server
14425
+ * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
14426
+ */
14427
+ function selectEventCapacitySource(event, attendanceMode) {
14428
+ if (!attendanceMode) return event;
14429
+ return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
14430
+ }
14431
+ /**
14432
+ * Whether the visitor's next step is the waitlist: the source is full *and*
14433
+ * a waitlist is open. The one place this is decided — cards, rows, the
14434
+ * wizard's steps and its action bar all route through here so they agree,
14435
+ * including for producers that omit `availableSpots` and rely on the
14436
+ * `capacity - registeredCount` fallback.
14437
+ */
14438
+ function canJoinEventWaitlist(event, options) {
14439
+ const source = selectEventCapacitySource(event, options?.attendanceMode);
14440
+ return getEventAvailability(source).isFull && source.waitlistEnabled === true;
14441
+ }
14442
+ //#endregion
14443
+ //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
14304
14444
  function isRecord$2(value) {
14305
14445
  return typeof value === "object" && value !== null && !Array.isArray(value);
14306
14446
  }
@@ -14318,7 +14458,7 @@ function resolveRuntimeEventDestination(event) {
14318
14458
  };
14319
14459
  }
14320
14460
  //#endregion
14321
- //#region ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
14461
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
14322
14462
  const DEFAULT_EVENT_CURRENCY = "USD";
14323
14463
  function resolveRuntimeEventPricing(event) {
14324
14464
  if (isPublicEventPricing(event.pricing)) return event.pricing;
@@ -14828,11 +14968,11 @@ function useEligibilityWithDeps(params, deps) {
14828
14968
  type: "resolved",
14829
14969
  eligibility: transformRegistrationOptionsResponse(result)
14830
14970
  });
14831
- } catch (err) {
14971
+ } catch {
14832
14972
  if (cancelled || requestIdRef.current !== requestId) return;
14833
14973
  dispatch({
14834
14974
  type: "failed",
14835
- message: err instanceof Error ? err.message : "Failed to check eligibility"
14975
+ 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."
14836
14976
  });
14837
14977
  }
14838
14978
  }
@@ -14844,6 +14984,7 @@ function useEligibilityWithDeps(params, deps) {
14844
14984
  }, [
14845
14985
  api,
14846
14986
  params.enabled,
14987
+ params.refreshKey,
14847
14988
  params.subject?.kind,
14848
14989
  params.subject?.kind === "occurrence" ? params.subject.occurrenceId : params.subject?.eventSeriesId,
14849
14990
  params.attendanceMode,
@@ -14953,8 +15094,122 @@ function logoutPortal(anchorId) {
14953
15094
  form.submit();
14954
15095
  }
14955
15096
  //#endregion
15097
+ //#region ../commerce/src/purchase/checkoutSessionMetadata.ts
15098
+ const datedOfferingSharedEngineMetadataShape = {
15099
+ datedOfferingEngineContractVersion: z.literal("shared_v1"),
15100
+ datedOfferingEngineRouteKind: z.literal("shared_whole_series_registration"),
15101
+ datedOfferingCapacityHoldId: z.string().uuid(),
15102
+ datedOfferingCapacityHoldAuditRef: z.string().uuid(),
15103
+ customerId: z.string().uuid(),
15104
+ eventSeriesId: z.string().uuid()
15105
+ };
15106
+ z.object(datedOfferingSharedEngineMetadataShape);
15107
+ const orderCheckoutSessionMetadataBaseShape = {
15108
+ type: z.literal("order"),
15109
+ site_id: z.string().uuid(),
15110
+ order_id: z.string().uuid().optional(),
15111
+ customer_email: z.string().email(),
15112
+ customer_name: z.string().optional(),
15113
+ product_id: z.string().uuid().optional(),
15114
+ attendee_id: z.string().uuid().optional(),
15115
+ occurrence_id: z.string().uuid().optional(),
15116
+ tickets_count: z.string().optional(),
15117
+ notes: z.string().optional(),
15118
+ discount_redemption_id: z.string().uuid().optional(),
15119
+ discount_code_id: z.string().uuid().optional(),
15120
+ discount_code: z.string().optional(),
15121
+ discount_public_label: z.string().optional(),
15122
+ credit_reservation_id: z.string().uuid().optional(),
15123
+ credit_reserved_cents: z.string().optional(),
15124
+ discount_code_amount_cents: z.string().optional(),
15125
+ order_payment_reason: z.enum([
15126
+ "instalment_payoff",
15127
+ "flexible_balance_initial",
15128
+ "flexible_balance_paydown"
15129
+ ]).optional(),
15130
+ order_payment_plan_id: z.string().uuid().optional(),
15131
+ order_flexible_balance_contract_id: z.string().uuid().optional()
15132
+ };
15133
+ const absentDatedOfferingSharedEngineMetadataShape = {
15134
+ datedOfferingEngineContractVersion: z.never().optional(),
15135
+ datedOfferingEngineRouteKind: z.never().optional(),
15136
+ datedOfferingCapacityHoldId: z.never().optional(),
15137
+ datedOfferingCapacityHoldAuditRef: z.never().optional(),
15138
+ customerId: z.never().optional(),
15139
+ eventSeriesId: z.never().optional()
15140
+ };
15141
+ const LegacyOrderCheckoutSessionMetadataSchema = z.object({
15142
+ ...orderCheckoutSessionMetadataBaseShape,
15143
+ ...absentDatedOfferingSharedEngineMetadataShape
15144
+ });
15145
+ const SharedDatedOfferingOrderCheckoutSessionMetadataSchema = z.object({
15146
+ ...orderCheckoutSessionMetadataBaseShape,
15147
+ ...datedOfferingSharedEngineMetadataShape
15148
+ });
15149
+ z.union([SharedDatedOfferingOrderCheckoutSessionMetadataSchema, LegacyOrderCheckoutSessionMetadataSchema]);
15150
+ toNonNegativeCents(0);
15151
+ //#endregion
15152
+ //#region ../commerce/src/purchase/event-registration-errors.ts
15153
+ const eventRegistrationErrors = {
15154
+ registrationClosed: {
15155
+ message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
15156
+ action: "none"
15157
+ },
15158
+ paymentUncertain: {
15159
+ 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.",
15160
+ action: "payment-status"
15161
+ },
15162
+ paymentPageUnavailable: {
15163
+ message: "We couldn’t open the payment page. Please refresh this page and try again. If this keeps happening, contact the organiser for help.",
15164
+ action: "retry"
15165
+ },
15166
+ paymentUnavailable: {
15167
+ message: "Online payment is not available for this event right now. Please contact the organiser for help with booking.",
15168
+ action: "none"
15169
+ },
15170
+ capacityChanged: {
15171
+ message: "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.",
15172
+ action: "places"
15173
+ },
15174
+ selectionChanged: {
15175
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
15176
+ action: "places"
15177
+ },
15178
+ registrationPreparationChanged: {
15179
+ message: "The booking options for this event have changed. Please check the latest options and choose your places again.",
15180
+ action: "places"
15181
+ },
15182
+ registrationAlreadyExists: {
15183
+ message: "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser.",
15184
+ action: "portal"
15185
+ },
15186
+ holdExpired: {
15187
+ message: "Your reservation has timed out. Please check availability and choose your places again.",
15188
+ action: "start"
15189
+ },
15190
+ waitlistUnavailable: {
15191
+ 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.",
15192
+ action: "event"
15193
+ },
15194
+ paymentContextMismatch: {
15195
+ message: "The payment options for this event have changed. Please check the latest options before continuing.",
15196
+ action: "payment-options"
15197
+ },
15198
+ idempotencyConflict: {
15199
+ 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.",
15200
+ action: "portal"
15201
+ },
15202
+ invalidCommitInput: {
15203
+ 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.",
15204
+ action: "details"
15205
+ }
15206
+ };
15207
+ function parseEventRegistrationErrorReason(value) {
15208
+ return typeof value === "string" && Object.prototype.hasOwnProperty.call(eventRegistrationErrors, value) ? value : null;
15209
+ }
15210
+ //#endregion
14956
15211
  //#region ../blocks/src/system/runtime/shared/useWaitlistClaim.state.ts
14957
- const DEFAULT_WAITLIST_CLAIM_ERROR = "Failed to resolve waitlist claim";
15212
+ const DEFAULT_WAITLIST_CLAIM_ERROR = "Online booking is unavailable right now. Please contact the organiser for help with booking.";
14958
15213
  function createInitialWaitlistClaimState(hasToken) {
14959
15214
  return hasToken ? { kind: "resolving" } : { kind: "idle" };
14960
15215
  }
@@ -14970,14 +15225,13 @@ function waitlistClaimReducer(_state, action) {
14970
15225
  }
14971
15226
  }
14972
15227
  function toWaitlistClaimResolveFailure(error) {
14973
- const message = error instanceof Error ? error.message : DEFAULT_WAITLIST_CLAIM_ERROR;
14974
15228
  if (isUnauthorizedBlockApiError(error)) return {
14975
15229
  kind: "portal-login-required",
14976
- message
15230
+ message: "Please log in to continue with your waiting list invitation."
14977
15231
  };
14978
15232
  return {
14979
15233
  kind: "error",
14980
- message
15234
+ message: hasStatus(error) && (error.status === 404 || error.status === 409 || error.status === 410) ? eventRegistrationErrors.waitlistUnavailable.message : DEFAULT_WAITLIST_CLAIM_ERROR
14981
15235
  };
14982
15236
  }
14983
15237
  function isUnauthorizedBlockApiError(error) {
@@ -15338,11 +15592,21 @@ function cleanupStaleRegistrations() {
15338
15592
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.ts
15339
15593
  const eventRegistrationStripeReturnDeps = {
15340
15594
  usePaymentVerification,
15341
- consumeReturnParams: () => consumeSearchParams([
15342
- "registration",
15343
- EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
15344
- "session_id"
15345
- ]),
15595
+ readReturnParams: () => {
15596
+ const params = new URLSearchParams(window.location.search);
15597
+ return {
15598
+ registration: params.get("registration"),
15599
+ registration_attempt: params.get(EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM),
15600
+ session_id: params.get("session_id")
15601
+ };
15602
+ },
15603
+ clearReturnParams: () => {
15604
+ consumeSearchParams([
15605
+ "registration",
15606
+ EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM,
15607
+ "session_id"
15608
+ ]);
15609
+ },
15346
15610
  scrollToBlock,
15347
15611
  cleanupStaleRegistrations,
15348
15612
  clearPendingRegistration,
@@ -15363,52 +15627,66 @@ function showReturnReconciliation(options, message) {
15363
15627
  options.setErrorMessage(message);
15364
15628
  options.setStep("verifying-timeout");
15365
15629
  }
15630
+ function checkoutReturnCommand(params) {
15631
+ const checkoutAttemptId = params.registration_attempt;
15632
+ if (!checkoutAttemptId) return null;
15633
+ if (params.registration === "cancelled") return {
15634
+ kind: "abandon",
15635
+ checkoutAttemptId
15636
+ };
15637
+ if (params.registration === "success" && params.session_id) return {
15638
+ kind: "resolveSuccess",
15639
+ checkoutAttemptId,
15640
+ sessionId: params.session_id
15641
+ };
15642
+ return null;
15643
+ }
15366
15644
  async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
15367
15645
  deps.cleanupStaleRegistrations();
15368
- const params = deps.consumeReturnParams();
15369
- if (params.registration !== "cancelled" && params.registration !== "success") return;
15646
+ const params = deps.readReturnParams();
15647
+ if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
15370
15648
  deps.scrollToBlock(EVENT_REGISTRATION_ANCHOR_ID);
15371
15649
  const siteId = options.api.config.siteId;
15372
- const checkoutAttemptId = params.registration_attempt;
15373
- if (!siteId) return;
15374
- if (!checkoutAttemptId) {
15375
- showReturnReconciliation(options, "We could not identify this payment attempt. Please contact us before trying again.");
15376
- return;
15650
+ const command = checkoutReturnCommand(params);
15651
+ if (!siteId) return "settled";
15652
+ if (!command) {
15653
+ 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.");
15654
+ return "settled";
15377
15655
  }
15656
+ const { checkoutAttemptId } = command;
15378
15657
  const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
15379
15658
  checkoutAttemptIdRef.current = checkoutAttemptId;
15380
15659
  if (pending) restorePendingPresentation(options, pending);
15660
+ options.setStep("verifying-payment");
15381
15661
  try {
15382
- const outcome = params.registration === "success" ? params.session_id ? await options.api.resolveEventCheckoutReturn({
15383
- kind: "resolveSuccess",
15384
- checkoutAttemptId,
15385
- sessionId: params.session_id
15386
- }) : { kind: "notFound" } : await options.api.resolveEventCheckoutReturn({
15387
- kind: "abandon",
15388
- checkoutAttemptId
15389
- });
15662
+ const outcome = await options.api.resolveEventCheckoutReturn(command);
15390
15663
  switch (outcome.kind) {
15391
15664
  case "resolved":
15665
+ deps.clearReturnParams();
15392
15666
  options.setPendingRegistrationReference(outcome.reference);
15393
15667
  options.setStep("verifying-payment");
15394
- return;
15668
+ return "settled";
15395
15669
  case "abandoned":
15670
+ deps.clearReturnParams();
15396
15671
  options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
15397
15672
  options.setStep("select-payment");
15398
- return;
15673
+ return "settled";
15399
15674
  case "notFound":
15400
15675
  if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
15676
+ deps.clearReturnParams();
15401
15677
  options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
15402
15678
  options.setStep("select-payment");
15403
- return;
15679
+ return "settled";
15404
15680
  }
15405
- break;
15681
+ 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.");
15682
+ return "settled";
15406
15683
  case "reconciliationRequired": break;
15407
15684
  }
15408
- showReturnReconciliation(options, "We’re checking your payment. Please wait a moment before trying again.");
15685
+ 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.");
15409
15686
  } catch {
15410
- showReturnReconciliation(options, "We could not check your payment yet. Please try again shortly.");
15687
+ 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.");
15411
15688
  }
15689
+ return "retryable";
15412
15690
  }
15413
15691
  function useEventRegistrationStripeReturn(options) {
15414
15692
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
@@ -15416,10 +15694,34 @@ function useEventRegistrationStripeReturn(options) {
15416
15694
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
15417
15695
  const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
15418
15696
  const checkoutAttemptIdRef = useRef(null);
15697
+ const lookupInFlightRef = useRef(false);
15698
+ const [lookupState, setLookupState] = useState("settled");
15699
+ const resolveReturn = useCallback(async () => {
15700
+ if (lookupInFlightRef.current) return;
15701
+ lookupInFlightRef.current = true;
15702
+ setLookupState("loading");
15703
+ try {
15704
+ setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
15705
+ } finally {
15706
+ lookupInFlightRef.current = false;
15707
+ }
15708
+ }, [options, deps]);
15419
15709
  const verificationConfig = useMemo(() => step === "verifying-payment" && pendingRegistrationReference ? { reference: pendingRegistrationReference } : null, [step, pendingRegistrationReference]);
15420
15710
  const verification = deps.usePaymentVerification(verificationConfig);
15711
+ const hasVerificationTimeout = step === "verifying-timeout" && pendingRegistrationReference !== null;
15712
+ const retry = useCallback(() => {
15713
+ if (hasVerificationTimeout) {
15714
+ setStep("verifying-payment");
15715
+ return;
15716
+ }
15717
+ verification.retry();
15718
+ }, [
15719
+ hasVerificationTimeout,
15720
+ setStep,
15721
+ verification.retry
15722
+ ]);
15421
15723
  useEffect(() => {
15422
- handleCheckoutReturn(options, deps, checkoutAttemptIdRef);
15724
+ resolveReturn();
15423
15725
  }, []);
15424
15726
  useEffect(() => {
15425
15727
  if (verification.status === "completed") {
@@ -15429,7 +15731,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
15429
15731
  setRegistrationResult({ status: "confirmed" });
15430
15732
  setStep("success");
15431
15733
  } else if (verification.status === "failed") {
15432
- setErrorMessage(verification.error ?? "Payment failed. Please try again.");
15734
+ setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
15433
15735
  setStep("payment-failed");
15434
15736
  } else if (verification.status === "timeout") setStep("verifying-timeout");
15435
15737
  }, [
@@ -15442,7 +15744,22 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
15442
15744
  setRegistrationResult,
15443
15745
  setStep
15444
15746
  ]);
15445
- return verification;
15747
+ return {
15748
+ ...verification,
15749
+ status: hasVerificationTimeout ? "timeout" : verification.status,
15750
+ retry,
15751
+ retryReturn: lookupState === "retryable" ? () => {
15752
+ resolveReturn();
15753
+ } : void 0
15754
+ };
15755
+ }
15756
+ //#endregion
15757
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.ts
15758
+ function registrationErrorPresentation(error) {
15759
+ const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
15760
+ if (reason) return eventRegistrationErrors[reason];
15761
+ if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
15762
+ return eventRegistrationErrors.paymentUncertain;
15446
15763
  }
15447
15764
  //#endregion
15448
15765
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
@@ -15531,7 +15848,8 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
15531
15848
  outcome: { kind: "completed" }
15532
15849
  };
15533
15850
  } catch (error) {
15534
- const message = error instanceof Error ? error.message : "Registration failed";
15851
+ const { message, action } = registrationErrorPresentation(error);
15852
+ options.setErrorAction?.(action);
15535
15853
  options.setErrorMessage(message);
15536
15854
  options.setStep("error");
15537
15855
  return {
@@ -16616,12 +16934,12 @@ function getEventRegistrationOverrideInput(props) {
16616
16934
  if (!props.siteId) return {
16617
16935
  ok: false,
16618
16936
  reason: "missing-site-id",
16619
- message: "Event registration is missing site context."
16937
+ message: "Online booking is unavailable right now. Please contact the organiser for help with booking."
16620
16938
  };
16621
16939
  if (!props.apiBaseUrl) return {
16622
16940
  ok: false,
16623
16941
  reason: "missing-api-base-url",
16624
- message: "Event registration is missing API configuration."
16942
+ message: "Online booking is unavailable right now. Please contact the organiser for help with booking."
16625
16943
  };
16626
16944
  const content = props.content ?? {};
16627
16945
  const data = isRecord(props.data) ? props.data : {};
@@ -16808,7 +17126,8 @@ function resolveHeadlessEventRegistrationStatus(wizard) {
16808
17126
  } : { tag: "ready" };
16809
17127
  case "error": return {
16810
17128
  tag: "error",
16811
- message: wizard.errorMessage
17129
+ message: wizard.errorMessage,
17130
+ ...wizard.errorAction ? { action: wizard.errorAction } : {}
16812
17131
  };
16813
17132
  case "select-event":
16814
17133
  case "select-tickets":
@@ -16823,7 +17142,7 @@ function resolveHeadlessEventRegistrationView(wizard) {
16823
17142
  };
16824
17143
  if (!wizard.scopedEvents.some(isEventRegistrationAvailable) && !wizard.occurrenceContext) return {
16825
17144
  kind: "unavailable",
16826
- message: "Registration is not currently available for this event."
17145
+ message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more."
16827
17146
  };
16828
17147
  switch (wizard.step) {
16829
17148
  case "verifying-payment": return {
@@ -16841,7 +17160,7 @@ function resolveHeadlessEventRegistrationView(wizard) {
16841
17160
  };
16842
17161
  case "verifying-timeout": return {
16843
17162
  kind: "verifying",
16844
- message: "Your registration is saved."
17163
+ 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."
16845
17164
  };
16846
17165
  case "select-event":
16847
17166
  case "select-tickets":
@@ -17078,6 +17397,13 @@ function toWholeSeriesRegistrationEvents(offerings) {
17078
17397
  }
17079
17398
  //#endregion
17080
17399
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.ts
17400
+ function getEventSelectionDefaults(event) {
17401
+ const suggestedAmount = getSlidingScaleForEvent(event)?.suggestedAmount;
17402
+ return {
17403
+ slidingAmountInput: suggestedAmount == null ? "" : formatCentsAsCurrencyInput(suggestedAmount),
17404
+ attendanceMode: event.deliveryMode === "hybrid" ? null : event.deliveryMode ?? "in_person"
17405
+ };
17406
+ }
17081
17407
  function parseCurrencyInputToCents(value) {
17082
17408
  const trimmed = value.trim();
17083
17409
  if (!trimmed) return null;
@@ -17125,7 +17451,7 @@ function resolveWaitlistClaimRestoration(input) {
17125
17451
  const event = findEventByOccurrenceId(input.scopedEvents, subject.occurrenceId);
17126
17452
  if (!event && input.occurrenceContextId !== subject.occurrenceId) return {
17127
17453
  ok: false,
17128
- message: "This waitlist claim is for a different event."
17454
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
17129
17455
  };
17130
17456
  return {
17131
17457
  ok: true,
@@ -17142,7 +17468,7 @@ function resolveWaitlistClaimRestoration(input) {
17142
17468
  const event = input.scopedEvents.find((candidate) => candidate.registrationSubject.kind === "wholeSeries" && candidate.registrationSubject.eventSeriesId === subject.eventSeriesId);
17143
17469
  if (!event) return {
17144
17470
  ok: false,
17145
- message: "This waitlist claim is for a different event run."
17471
+ message: "This waiting list invitation is for a different event. Please open the link in your invitation email."
17146
17472
  };
17147
17473
  return {
17148
17474
  ok: true,
@@ -17175,6 +17501,7 @@ function useWaitlistClaimRestoration(input) {
17175
17501
  occurrenceContextId: input.occurrenceContextId
17176
17502
  });
17177
17503
  if (!restoration.ok) {
17504
+ input.setters.setErrorAction?.("none");
17178
17505
  input.setters.setErrorMessage(restoration.message);
17179
17506
  input.setters.setStep("error");
17180
17507
  return;
@@ -17253,6 +17580,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17253
17580
  });
17254
17581
  const [registrationResult, setRegistrationResult] = useState(null);
17255
17582
  const [errorMessage, setErrorMessage] = useState("");
17583
+ const [errorAction, setErrorAction] = useState("retry");
17584
+ const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
17256
17585
  const [altchaSolution, setAltchaSolution] = useState(null);
17257
17586
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
17258
17587
  const waitlistClaim = deps.useWaitlistClaim();
@@ -17282,10 +17611,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17282
17611
  const currentEvent = selectedEvent ?? preSelectedEvent;
17283
17612
  const currentEventPricing = currentEvent ? resolveRuntimeEventPricing(currentEvent) : null;
17284
17613
  const currentEventUsesTicketTypes = currentEventPricing !== null && publicEventPricingMode(currentEventPricing) === "ticket_types";
17285
- const selectedModeAvailability = selectedAttendanceMode ? (currentEvent?.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
17286
- const currentAvailableSpots = selectedModeAvailability?.availableSpots ?? currentEvent?.availableSpots;
17287
- const currentWaitlistEnabled = selectedModeAvailability?.waitlistEnabled ?? currentEvent?.waitlistEnabled ?? false;
17288
- const usesTicketTypeSelections = currentEventUsesTicketTypes && !(currentAvailableSpots === 0 && currentWaitlistEnabled === true);
17614
+ const currentCanJoinWaitlist = currentEvent ? canJoinEventWaitlist(currentEvent, { attendanceMode: selectedAttendanceMode }) : false;
17615
+ const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
17289
17616
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
17290
17617
  event: currentEvent,
17291
17618
  eligibility: null,
@@ -17316,7 +17643,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17316
17643
  });
17317
17644
  }, [flowState.ticketsCount, ticketsCount]);
17318
17645
  const eligibilityResult = deps.useEligibility({
17319
- enabled: shouldLoadRegistrationOptions(portalAuthState, step),
17646
+ refreshKey: optionsRefreshKey,
17647
+ enabled: shouldRefreshAfterRegistrationError(step, errorAction, shouldLoadRegistrationOptions(portalAuthState, step)),
17320
17648
  subject: registrationTarget?.subject ?? null,
17321
17649
  attendanceMode: selectedAttendanceMode,
17322
17650
  ticketSelections,
@@ -17345,6 +17673,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17345
17673
  });
17346
17674
  }, [waitlistClaim.claim]);
17347
17675
  const waitlistRestorationSetters = useMemo(() => ({
17676
+ setErrorAction,
17348
17677
  setErrorMessage,
17349
17678
  setStep,
17350
17679
  setSelectedEvent,
@@ -17368,6 +17697,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17368
17697
  if (waitlistClaim.requiresPortalLogin) return;
17369
17698
  if (!waitlistClaim.error) return;
17370
17699
  setErrorMessage(waitlistClaim.error);
17700
+ setErrorAction("event");
17371
17701
  setStep("error");
17372
17702
  }, [
17373
17703
  waitlistClaim.error,
@@ -17470,8 +17800,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17470
17800
  const event = currentEvent;
17471
17801
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") return false;
17472
17802
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
17473
- const selectedModeAvailability = selectedAttendanceMode ? (event.attendanceModes ?? []).find((mode) => mode.mode === selectedAttendanceMode) ?? null : null;
17474
- return !((selectedModeAvailability?.availableSpots ?? event.availableSpots) === 0 && (selectedModeAvailability?.waitlistEnabled ?? event.waitlistEnabled) === true);
17803
+ return !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
17475
17804
  }, [currentEvent, selectedAttendanceMode]);
17476
17805
  const hasValidTicketSelection = !ticketTypeSelectionRequired || normalizedSelectedTicketLines.length > 0 && normalizedSelectedTicketLines.every((line) => {
17477
17806
  if (!line.ticketTypeId) return false;
@@ -17552,6 +17881,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17552
17881
  ]);
17553
17882
  const handleDetailsComplete = async () => {
17554
17883
  if (ticketTypeSelectionRequired && !hasValidTicketSelection) {
17884
+ setErrorAction("places");
17555
17885
  const message = "Choose at least one available ticket to continue.";
17556
17886
  setErrorMessage(message);
17557
17887
  setStep("error");
@@ -17562,6 +17892,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17562
17892
  };
17563
17893
  }
17564
17894
  if (slidingScaleSelectionMessage) {
17895
+ setErrorAction("places");
17565
17896
  setErrorMessage(slidingScaleSelectionMessage);
17566
17897
  setStep("error");
17567
17898
  return {
@@ -17579,7 +17910,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17579
17910
  };
17580
17911
  }
17581
17912
  if (waitlistClaim.claim === null && eligibility?.isFull && !eligibility.canJoinWaitlist) {
17582
- const message = "This event is sold out.";
17913
+ const message = "Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.";
17914
+ setErrorAction("places");
17583
17915
  setErrorMessage(message);
17584
17916
  setStep("error");
17585
17917
  return {
@@ -17695,6 +18027,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17695
18027
  const selectionMethod = paymentSelection.method;
17696
18028
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
17697
18029
  const handleSubmit = deps.useEventRegistrationSubmit({
18030
+ setErrorAction,
17698
18031
  api,
17699
18032
  registrationTarget,
17700
18033
  selectedAttendanceMode,
@@ -17717,26 +18050,31 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17717
18050
  ...current,
17718
18051
  consentAccepted: false
17719
18052
  }));
17720
- const slidingScale = getSlidingScaleForEvent(event);
17721
- setSelectedSlidingAmountInput(slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "");
17722
- setSelectedAttendanceMode((event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person");
18053
+ const defaults = getEventSelectionDefaults(event);
18054
+ setSelectedSlidingAmountInput(defaults.slidingAmountInput);
18055
+ setSelectedAttendanceMode(defaults.attendanceMode);
17723
18056
  setSelectedTicketLines([]);
17724
18057
  dispatchFlow({
17725
18058
  type: "event-changed",
17726
18059
  hasPreSelectedContext: true
17727
18060
  });
17728
18061
  };
17729
- const handleRetry = () => {
17730
- dispatchFlow({
17731
- type: "reset",
17732
- hasPreSelectedContext
17733
- });
17734
- setSelectedTicketLines([]);
17735
- setSelectedSlidingAmountInput("");
17736
- setSelectedAttendanceMode(preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null);
17737
- setErrorMessage("");
17738
- setPendingRegistrationReference(null);
17739
- };
18062
+ const handleRetry = () => recoverRegistrationError(resolveRegistrationRecoveryAction(step, errorAction), {
18063
+ canJoinWaitlist: canRecoverToWaitlist(eligibility),
18064
+ refreshOptions: () => setOptionsRefreshKey((value) => value + 1),
18065
+ clearError: () => setErrorMessage(""),
18066
+ setStep,
18067
+ reset: () => resetRegistrationAfterExpiry({
18068
+ hasPreSelectedContext,
18069
+ preSelectedEvent,
18070
+ dispatchFlow,
18071
+ setSelectedTicketLines,
18072
+ setSelectedSlidingAmountInput,
18073
+ setSelectedAttendanceMode,
18074
+ setErrorMessage,
18075
+ setPendingRegistrationReference
18076
+ })
18077
+ });
17740
18078
  const handlePaymentStepStateChange = useCallback((state) => {
17741
18079
  setPaymentStepState(state);
17742
18080
  }, []);
@@ -17799,6 +18137,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17799
18137
  handleGuestShakeEnd,
17800
18138
  registrationResult,
17801
18139
  errorMessage,
18140
+ errorAction,
17802
18141
  altchaSolution,
17803
18142
  setAltchaSolution,
17804
18143
  portalAuthState,