@riverbankcms/sdk 0.141.0 → 0.143.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 (123) hide show
  1. package/dist/{PreviewEditorSidebar-Q-fy9GYw.mjs → PreviewEditorSidebar-DROGfMKi.mjs} +24 -15
  2. package/dist/{PreviewEditorUI-LYGzgk85.mjs → PreviewEditorUI-BaRjhFu-.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-CN0ewic5.mjs → PreviewQueryProvider-BCYICImX.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-CoPYI5Jq.mjs → SdkPreviewModeRuntime-OIABYLES.mjs} +16 -11
  5. package/dist/{SiteChromeCustomizeContext-D8LePX8x.mjs → SiteChromeCustomizeContext-ohjTvZbc.mjs} +607 -545
  6. package/dist/_dts/api/src/billing.d.ts +6 -1
  7. package/dist/_dts/api/src/bookingConfig.d.ts +1 -1
  8. package/dist/_dts/api/src/commercePortalEndpoints.d.ts +2 -2
  9. package/dist/_dts/api/src/endpoints.d.ts +7 -27
  10. package/dist/_dts/api/src/index.d.ts +1 -1
  11. package/dist/_dts/api/src/publicBookingService.d.ts +1 -0
  12. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  13. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +48 -41
  14. package/dist/_dts/api/src/sdkConfig/layout.d.ts +13 -0
  15. package/dist/_dts/api/src/sdkContracts.d.ts +3 -8
  16. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +1 -31
  17. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +6 -1
  18. package/dist/_dts/billing/src/site-arrangements.d.ts +9 -5
  19. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +56 -0
  20. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +8 -0
  21. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +2 -1
  22. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  23. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +95 -35
  24. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentStatusStep.d.ts +10 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +0 -2
  26. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +12 -1
  27. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +2 -2
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +1 -4
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +12 -0
  30. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -1
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/sessionStorage.d.ts +1 -0
  32. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.d.ts +4 -5
  33. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +9 -11
  34. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +56 -2
  35. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +10 -4
  36. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +23 -0
  37. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +1 -1
  38. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +56 -0
  39. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +4 -4
  40. package/dist/_dts/commerce/src/purchase/eventCheckoutRecoveryWindow.d.ts +8 -0
  41. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -1
  42. package/dist/_dts/commerce/src/purchase/stateTransitions.d.ts +1 -13
  43. package/dist/_dts/commerce/src/purchase/stripeCommissionMetadata.d.ts +1 -2
  44. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +58 -45
  45. package/dist/_dts/editor-blocks/src/block-editor/ColourFieldOptionsProvider.d.ts +7 -0
  46. package/dist/_dts/editor-blocks/src/block-editor/colourFieldVisibility.d.ts +7 -0
  47. package/dist/_dts/editor-blocks/src/block-editor/preparedManifest.d.ts +2 -0
  48. package/dist/_dts/editor-blocks/src/index.d.ts +1 -0
  49. package/dist/_dts/preview-next/src/client/preview/PreviewEditorSidebar.d.ts +2 -2
  50. package/dist/_dts/preview-next/src/client/preview/PreviewFooterEditorPanel.d.ts +3 -6
  51. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +2 -2
  52. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +2 -2
  53. package/dist/_dts/preview-next/src/client/preview/footerInspection.d.ts +16 -0
  54. package/dist/_dts/preview-next/src/client/preview/sdkConfig.d.ts +6 -0
  55. package/dist/_dts/sdk/src/config/types.d.ts +2 -18
  56. package/dist/_dts/sdk/src/config/validation.d.ts +6 -0
  57. package/dist/_dts/sdk/src/public-api/contracts.d.ts +3 -0
  58. package/dist/_dts/sdk/src/version.d.ts +1 -1
  59. package/dist/_dts/theme-core/src/footer/mode.d.ts +10 -1
  60. package/dist/_dts/theme-core/src/schema.d.ts +1 -7
  61. package/dist/_dts/theme-core/src/site-styles/effectivePageDesign.d.ts +32 -0
  62. package/dist/_dts/theme-core/src/site-styles/index.d.ts +1 -0
  63. package/dist/client/{BookingConsentAgreement-xJeYNdnb.mjs → BookingConsentAgreement-BoCtvzzu.mjs} +2 -2
  64. package/dist/client/{DiscountCodeDisclosure-DdT6uzl9.mjs → DiscountCodeDisclosure-B5S30nZk.mjs} +1 -1
  65. package/dist/client/{EventCombined.client-Cp28uYuM.mjs → EventCombined.client-CeZ2-zSt.mjs} +5 -5
  66. package/dist/client/{EventListing.client-CD6AJsMr.mjs → EventListing.client-DJN3j8B3.mjs} +2 -2
  67. package/dist/client/{LeafletStaticMap-DBsAi9-e.mjs → LeafletStaticMap-RjtGzDQT.mjs} +1 -1
  68. package/dist/client/{accordion-6dwbOKYR.mjs → accordion-DOmnDoyg.mjs} +1 -1
  69. package/dist/client/{bookingForm-CZE9_qPQ.mjs → bookingForm-C0vk7pgS.mjs} +5 -5
  70. package/dist/client/bookings.mjs +468 -534
  71. package/dist/client/{carousel-7VHGQARz.mjs → carousel-BZJ7pno3.mjs} +1 -1
  72. package/dist/client/{cart-Qg_mvQhh.mjs → cart-CaeVAHJl.mjs} +3 -3
  73. package/dist/client/{checkout-D84rpgdr.mjs → checkout-7wmoNDtG.mjs} +3 -3
  74. package/dist/client/client.mjs +559 -593
  75. package/dist/client/{context-Ctp3T0Wr.mjs → context-BOimxE3U.mjs} +12 -1
  76. package/dist/client/{dates-DJKFcI_N.mjs → dates-BnzVksi4.mjs} +2 -2
  77. package/dist/client/{eventCalendar-CK31z3vs.mjs → eventCalendar-l-fpaslC.mjs} +7 -7
  78. package/dist/client/{eventCombined-DiXd-ewn.mjs → eventCombined-DGGvssK6.mjs} +3 -3
  79. package/dist/client/{eventDetails-CTRgt2DK.mjs → eventDetails-DRiCm_hL.mjs} +4 -4
  80. package/dist/client/{eventListing-Dy9vi_j_.mjs → eventListing-dph8loYX.mjs} +3 -3
  81. package/dist/client/{eventRegistration-CnalYMr2.mjs → eventRegistration-ClBohmbY.mjs} +472 -531
  82. package/dist/client/{form-eUiMveTT.mjs → form-BLVVAnd3.mjs} +2 -2
  83. package/dist/client/{headerSection-Dy9kQpB1.mjs → headerSection-DHy4C8Ix.mjs} +2 -2
  84. package/dist/client/hooks.mjs +39 -13
  85. package/dist/client/{locationMap-DalCmWU0.mjs → locationMap-6HuS28ck.mjs} +2 -2
  86. package/dist/client/{newsletterForm-SDDPvhS0.mjs → newsletterForm-xOdcOhZP.mjs} +2 -2
  87. package/dist/client/{pricing-OcMz4SnX.mjs → pricing-ChinRYFt.mjs} +1 -1
  88. package/dist/client/{productDetail-BQrgHElM.mjs → productDetail-0chA5gbK.mjs} +3 -3
  89. package/dist/client/{productList-OnPn_UJj.mjs → productList-OoINA0h1.mjs} +3 -3
  90. package/dist/client/{propsDecoder-zBdsnS-N.mjs → propsDecoder-CIZ-hbIG.mjs} +33 -9
  91. package/dist/client/rendering/client.mjs +536 -550
  92. package/dist/client/rendering/islands.mjs +17 -17
  93. package/dist/client/rendering.mjs +602 -596
  94. package/dist/client/{scrollToBlock-BNM4Ob2Y.mjs → scrollToBlock-BPZl0gPZ.mjs} +1 -1
  95. package/dist/client/{shop-Bv1fPTwQ.mjs → shop-CLJFqNIq.mjs} +6 -6
  96. package/dist/client/{shop-commerce-D4KNGaW8.mjs → shop-commerce-PpANg5wB.mjs} +5 -5
  97. package/dist/client/{shop-commerce-store-2N5EV0Rc.mjs → shop-commerce-store-TA_Kz2ku.mjs} +1 -1
  98. package/dist/client/{shopShared-DvJGdHTV.mjs → shopShared-DzcFcA5g.mjs} +3 -3
  99. package/dist/client/{useEventFilters-BBBvGDHQ.mjs → useEventFilters-CFU21QZo.mjs} +5 -5
  100. package/dist/preview-next/before-render.mjs +11 -37
  101. package/dist/preview-next/client/runtime.mjs +4 -4
  102. package/dist/preview-next/keepAlive.mjs +11 -37
  103. package/dist/preview-next/middleware.mjs +11 -37
  104. package/dist/{previewAuthStore-Gjb6r6vz.mjs → previewAuthStore-JA6-I_sL.mjs} +11 -37
  105. package/dist/{sdk-runtime-Defnbfzw.mjs → sdk-runtime-CDKl0Fr7.mjs} +2 -2
  106. package/dist/{separator-CZXO3Sb0.mjs → separator-BTp96X32.mjs} +1 -1
  107. package/dist/server/components.mjs +80 -51
  108. package/dist/server/config-validation.mjs +43 -14
  109. package/dist/server/config.mjs +43 -14
  110. package/dist/server/data.mjs +39 -13
  111. package/dist/server/index.mjs +12 -38
  112. package/dist/server/next.mjs +40 -53
  113. package/dist/server/page-converter.mjs +20 -8
  114. package/dist/server/prebuild.mjs +1 -1
  115. package/dist/server/rendering/server.mjs +80 -51
  116. package/dist/server/rendering.mjs +80 -51
  117. package/dist/server/routing.mjs +52 -17
  118. package/dist/server/server.mjs +40 -51
  119. package/dist/server/theme-bridge.mjs +10 -8
  120. package/dist/server/theme.mjs +10 -8
  121. package/package.json +1 -1
  122. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.d.ts +0 -14
  123. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.d.ts +0 -8
@@ -3114,6 +3114,14 @@ function orderedLiveRunSessions(sessions) {
3114
3114
  return sessions.filter((session) => session.status !== "cancelled" && Number.isFinite(sessionStartMs(session))).sort((left, right) => sessionStartMs(left) - sessionStartMs(right));
3115
3115
  }
3116
3116
  //#endregion
3117
+ //#region ../theme-core/src/footer/mode.ts
3118
+ const footerModeSchema = z.enum([
3119
+ "default",
3120
+ "blocks",
3121
+ "default+blocks",
3122
+ "none"
3123
+ ]);
3124
+ //#endregion
3117
3125
  //#region ../theme-core/src/lib/media.ts
3118
3126
  const hotspotSchema = z.object({
3119
3127
  x: z.number().min(0).max(1),
@@ -7447,12 +7455,6 @@ const footerVariant = z.enum([
7447
7455
  "split"
7448
7456
  ]);
7449
7457
  const footerMaxWidth = z.enum(["container", "full"]);
7450
- const footerMode = z.enum([
7451
- "default",
7452
- "blocks",
7453
- "default+blocks",
7454
- "none"
7455
- ]);
7456
7458
  const footerNavLayoutMode = z.enum([
7457
7459
  "stack",
7458
7460
  "inline",
@@ -7541,9 +7543,9 @@ const footerSchema = z.object({
7541
7543
  /**
7542
7544
  * SDK-only footer rendering mode.
7543
7545
  *
7544
- * Backwards compatible: missing/undefined behaves as 'default'.
7546
+ * Historical persisted field; SDK rendering mode now lives in layout.footerMode.
7545
7547
  */
7546
- mode: footerMode.optional(),
7548
+ mode: footerModeSchema.optional(),
7547
7549
  variant: footerVariant,
7548
7550
  maxWidth: footerMaxWidth,
7549
7551
  showLogoText: z.boolean().optional(),
@@ -8125,6 +8127,15 @@ const publicEventBaseSchema = z.object({
8125
8127
  timeZone: z.string(),
8126
8128
  pricing: publicEventPricingSchema,
8127
8129
  paymentTerms: blockBookingPaymentTermsSchema,
8130
+ cancellationPolicy: z.object({ refunds: z.object({
8131
+ mode: z.enum([
8132
+ "never",
8133
+ "always",
8134
+ "cutoff"
8135
+ ]),
8136
+ cutoffHours: z.number(),
8137
+ method: z.enum(["original_payment_method", "site_credit"])
8138
+ }) }),
8128
8139
  consent: blockBookingConsentSchema.nullable(),
8129
8140
  accessRestriction: accessRestrictionSchema,
8130
8141
  allowedCategoryIds: z.array(z.string()),
@@ -8349,6 +8360,10 @@ const EVENTS = {
8349
8360
  method: "POST",
8350
8361
  authScope: "portal"
8351
8362
  },
8363
+ registrationNextStep: {
8364
+ path: "/public/sites/{siteId}/events/registration-next-step",
8365
+ method: "POST"
8366
+ },
8352
8367
  registrationStatus: {
8353
8368
  path: "/public/sites/{siteId}/events/registration-status",
8354
8369
  method: "GET"
@@ -8953,6 +8968,13 @@ function createBlockApi(config) {
8953
8968
  body: data
8954
8969
  });
8955
8970
  },
8971
+ async checkRegistrationNextStep(data) {
8972
+ const path = buildPath(EVENTS.registrationNextStep.path, { siteId: requireSiteId() });
8973
+ return request(EVENTS.registrationNextStep, {
8974
+ path,
8975
+ body: data
8976
+ });
8977
+ },
8956
8978
  async getEventRegistrationStatus(params) {
8957
8979
  const sid = requireSiteId();
8958
8980
  const path = buildPath(EVENTS.registrationStatus.path, { siteId: sid });
@@ -14900,188 +14922,15 @@ const BookingFormClient$1 = ({ siteId: siteIdProp, render }) => {
14900
14922
  });
14901
14923
  };
14902
14924
  //#endregion
14903
- //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
14904
- /**
14905
- * Compute remaining spots for an event from `availableSpots` with a fallback
14906
- * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
14907
- * for uncapped events so call sites can render no capacity indicator at all.
14908
- *
14909
- * The single source of truth for capacity math across event cards, agenda
14910
- * rows, and timetable cells — keep wording variations in the call sites.
14911
- */
14912
- function getEventAvailability(event) {
14913
- if (event.capacity == null) return {
14914
- available: null,
14915
- isFull: false
14916
- };
14917
- const available = event.availableSpots ?? event.capacity - event.registeredCount;
14918
- if (available <= 0) return {
14919
- available: 0,
14920
- isFull: true
14921
- };
14922
- return {
14923
- available,
14924
- isFull: false
14925
- };
14926
- }
14927
- /**
14928
- * Pick the capacity row a booking decision should read: the selected
14929
- * attendance mode's availability when the visitor has chosen one and the
14930
- * event publishes it, otherwise the event-level aggregate.
14931
- *
14932
- * The mode row is used wholesale. A mode with `capacity: null` on a capped
14933
- * event is uncapped *by design*: the registration write path applies the
14934
- * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
14935
- * back to the event's `availableSpots` here would block bookings the server
14936
- * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
14937
- */
14938
- function selectEventCapacitySource(event, attendanceMode) {
14939
- if (!attendanceMode) return event;
14940
- return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
14941
- }
14942
- /**
14943
- * Whether the visitor's next step is the waitlist: the source is full *and*
14944
- * a waitlist is open. The one place this is decided — cards, rows, the
14945
- * wizard's steps and its action bar all route through here so they agree,
14946
- * including for producers that omit `availableSpots` and rely on the
14947
- * `capacity - registeredCount` fallback.
14948
- */
14949
- function canJoinEventWaitlist(event, options) {
14950
- const source = selectEventCapacitySource(event, options?.attendanceMode);
14951
- return getEventAvailability(source).isFull && source.waitlistEnabled === true;
14952
- }
14953
- //#endregion
14954
- //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
14955
- function isRecord$1(value) {
14956
- return typeof value === "object" && value !== null && !Array.isArray(value);
14957
- }
14958
- function isRuntimeEventDestination(value) {
14959
- if (!isRecord$1(value) || typeof value.kind !== "string") return false;
14960
- if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
14961
- return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
14962
- }
14963
- function resolveRuntimeEventDestination(event) {
14964
- if (isRuntimeEventDestination(event.destination)) return event.destination;
14965
- return {
14966
- kind: "native",
14967
- mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
14968
- path: typeof event.path === "string" ? event.path : ""
14969
- };
14970
- }
14971
- //#endregion
14972
- //#region ../blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.ts
14973
- /** Recovery may select held places; checkout still owns admission and payment validation. */
14974
- function checkoutRecoveryOptionAvailability(availability) {
14975
- if (availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable") return {
14976
- kind: "available",
14977
- remainingPlaces: null
14978
- };
14979
- return availability;
14980
- }
14981
- /**
14982
- * A form-only projection for the explicit existing-checkout path. It permits
14983
- * collecting the original selection without claiming a new place is available.
14984
- * The public checkout service identifies the customer and remains the admission
14985
- * authority; no reservation/customer data is exposed through this projection.
14986
- */
14987
- function getCheckoutRecoveryFormEligibility(eligibility) {
14988
- if (!eligibility || eligibility.registration.kind !== "none" && eligibility.registration.kind !== "pending_payment" || eligibility.availability.kind === "unavailable" && eligibility.availability.reason !== "full" && eligibility.availability.reason !== "selectionRequired") return eligibility;
14989
- const amountCents = eligibility.price ?? 0;
14990
- if (amountCents <= 0 || !eligibility.currency || eligibility.availability.kind === "unavailable" && eligibility.availability.reason === "selectionRequired") return eligibility.registration.kind === "pending_payment" ? {
14991
- ...eligibility,
14992
- registration: { kind: "none" },
14993
- ticketTypes: eligibility.ticketTypes.map((option) => ({
14994
- ...option,
14995
- availability: checkoutRecoveryOptionAvailability(option.availability)
14996
- }))
14997
- } : eligibility;
14998
- const currency = eligibility.currency;
14999
- const paymentOptions = eligibility.paymentTerms.allowedOptions.map((option) => {
15000
- switch (option.kind) {
15001
- case "upfront": return {
15002
- kind: "payInFull",
15003
- amountCents,
15004
- currency
15005
- };
15006
- case "deferred_manual": return {
15007
- kind: "deferredManual",
15008
- amountCents,
15009
- currency
15010
- };
15011
- case "instalment_plan": return {
15012
- kind: "instalmentPlan",
15013
- amountCents,
15014
- currency
15015
- };
15016
- case "flexible_balance": return {
15017
- kind: "flexibleBalance",
15018
- amountCents,
15019
- currency
15020
- };
15021
- }
15022
- });
15023
- return {
15024
- ...eligibility,
15025
- registration: { kind: "none" },
15026
- availability: {
15027
- kind: "available",
15028
- remainingPlaces: null,
15029
- waitlistEnabled: eligibility.waitlistEnabled
15030
- },
15031
- eligible: paymentOptions.length > 0,
15032
- isFull: false,
15033
- canJoinWaitlist: false,
15034
- isFree: false,
15035
- requiresPayment: true,
15036
- canPayDirectly: paymentOptions.length > 0,
15037
- paymentOptions,
15038
- eligiblePasses: [],
15039
- eligibleMemberships: [],
15040
- ticketTypes: eligibility.ticketTypes.map((option) => ({
15041
- ...option,
15042
- availability: checkoutRecoveryOptionAvailability(option.availability)
15043
- }))
15044
- };
15045
- }
15046
- function canJoinWaitlistOutsideRecovery(event, attendanceMode, checkoutRecovery) {
15047
- return !checkoutRecovery && event !== null && canJoinEventWaitlist(event, { attendanceMode });
15048
- }
15049
- //#endregion
15050
- //#region ../blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.ts
15051
- /** Recovery belongs to the selected booking subject; changing events resets its scope. */
15052
- function useCheckoutRecovery(event, setStep) {
15053
- const [recoveryEventId, setRecoveryEventId] = useState(null);
15054
- return {
15055
- checkoutRecovery: event !== null && recoveryEventId === event.selectionId,
15056
- startCheckoutRecovery: useCallback(() => {
15057
- if (!event) return;
15058
- setRecoveryEventId(event.selectionId);
15059
- setStep("select-tickets");
15060
- }, [event, setStep])
15061
- };
15062
- }
15063
- /** Authenticated pending bookings enter the same form-only recovery path as guests. */
15064
- function useCheckoutRecoveryEligibility(eligibility, step, checkoutRecovery, startCheckoutRecovery) {
15065
- const registrationKind = eligibility?.registration.kind;
15066
- useEffect(() => {
15067
- if ((step === "select-event" || step === "select-tickets" || step === "enter-details" || step === "select-payment") && registrationKind === "pending_payment" && !checkoutRecovery) startCheckoutRecovery();
15068
- }, [
15069
- registrationKind,
15070
- step,
15071
- checkoutRecovery,
15072
- startCheckoutRecovery
15073
- ]);
15074
- return useMemo(() => checkoutRecovery ? getCheckoutRecoveryFormEligibility(eligibility) : eligibility, [checkoutRecovery, eligibility]);
15075
- }
15076
- //#endregion
15077
14925
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
15078
14926
  function resolveRegistrationRecoveryAction(step, action) {
15079
14927
  if (step === "payment-failed") return "payment-options";
15080
- if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
14928
+ if (step === "payment-status") return "none";
14929
+ if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "none";
15081
14930
  return action;
15082
14931
  }
15083
14932
  function canRecoverToWaitlist(eligibility) {
15084
- return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
14933
+ return eligibility?.availability.kind === "waitlist" && eligibility.registration.kind !== "pending_payment";
15085
14934
  }
15086
14935
  function shouldRefreshAfterRegistrationError(step, action, enabled) {
15087
14936
  return enabled || step === "error" && action === "places";
@@ -15098,19 +14947,19 @@ function resetRegistrationAfterExpiry(input) {
15098
14947
  input.setErrorMessage("");
15099
14948
  input.setPendingRegistrationReference(null);
15100
14949
  }
15101
- /** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
14950
+ /** Retry the retained submission in place; explicit payment uncertainty stays status-only. */
15102
14951
  function recoverRegistrationError(action, handlers) {
15103
14952
  switch (action) {
15104
14953
  case "none": return;
15105
14954
  case "portal":
15106
- case "payment-status":
15107
14955
  window.location.assign("/portal");
15108
14956
  return;
15109
14957
  case "event":
15110
14958
  window.location.assign(window.location.pathname);
15111
14959
  return;
15112
14960
  case "retry":
15113
- window.location.reload();
14961
+ handlers.clearError();
14962
+ handlers.setStep("select-payment");
15114
14963
  return;
15115
14964
  case "details":
15116
14965
  handlers.clearError();
@@ -15148,13 +14997,43 @@ function validateFormData(formData, requiresConsent = false) {
15148
14997
  return errors;
15149
14998
  }
15150
14999
  //#endregion
15000
+ //#region ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
15001
+ /** Ticket selection requests a quote; only the server admits a booking. */
15002
+ function canRequestTicketOption(availability) {
15003
+ return availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable";
15004
+ }
15005
+ function normalizeTicketSelectionLines(lines) {
15006
+ const quantitiesByTicketType = /* @__PURE__ */ new Map();
15007
+ for (const line of lines) {
15008
+ const quantity = Math.floor(line.quantity);
15009
+ if (quantity <= 0) continue;
15010
+ const key = line.pricingOptionId ?? null;
15011
+ quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
15012
+ }
15013
+ return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
15014
+ pricingOptionId,
15015
+ quantity
15016
+ }));
15017
+ }
15018
+ function getTicketSelectionTotal(lines) {
15019
+ return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
15020
+ }
15021
+ function getSingleSelectedPricingOptionId(lines) {
15022
+ const normalized = normalizeTicketSelectionLines(lines);
15023
+ return normalized.length === 1 ? normalized[0]?.pricingOptionId ?? null : null;
15024
+ }
15025
+ function hasValidPricingOptionSelection(input) {
15026
+ if (!input.selectionRequired) return true;
15027
+ if (input.lines.length === 0) return false;
15028
+ const availableOptionIds = new Set(input.visibleOptions.filter((option) => canRequestTicketOption(option.availability)).map((option) => option.pricingOptionId));
15029
+ return input.lines.every((line) => line.pricingOptionId !== null && availableOptionIds.has(line.pricingOptionId));
15030
+ }
15031
+ //#endregion
15151
15032
  //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
15152
15033
  function getPortalSubjectEmail(profile) {
15153
15034
  return profile?.email ?? null;
15154
15035
  }
15155
15036
  function transformRegistrationOptionsResponse(response) {
15156
- const canJoinWaitlist = response.availability.kind === "waitlist";
15157
- const isFull = response.availability.remainingPlaces === 0 || response.availability.kind === "unavailable" && response.availability.reason === "full";
15158
15037
  const eligiblePasses = response.entitlements.passes.map((pass) => ({
15159
15038
  id: pass.customerPassId,
15160
15039
  passName: pass.passName,
@@ -15167,7 +15046,6 @@ function transformRegistrationOptionsResponse(response) {
15167
15046
  const eventAcceptsPasses = response.entitlements.requiresLoginForPasses || eligiblePasses.length > 0 || response.entitlements.availableForPurchase.passes.length > 0;
15168
15047
  const eventAcceptsMemberships = response.entitlements.requiresLoginForMemberships || eligibleMemberships.length > 0 || response.entitlements.availableForPurchase.memberships.length > 0;
15169
15048
  const monetaryPaymentAvailable = hasMonetaryPaymentOption(response);
15170
- const registrationAvailable = response.availability.kind === "available" || response.availability.kind === "waitlist";
15171
15049
  return {
15172
15050
  availability: response.availability,
15173
15051
  registration: response.registration,
@@ -15182,7 +15060,6 @@ function transformRegistrationOptionsResponse(response) {
15182
15060
  },
15183
15061
  transactions: []
15184
15062
  } : null,
15185
- eligible: response.registration.kind === "none" && registrationAvailable,
15186
15063
  requiresPayment: monetaryPaymentAvailable,
15187
15064
  pricingStrategy: response.pricing.strategy,
15188
15065
  paymentTerms: response.pricing.paymentTerms,
@@ -15197,16 +15074,12 @@ function transformRegistrationOptionsResponse(response) {
15197
15074
  currency: response.pricing.currency
15198
15075
  })),
15199
15076
  ticketTypes: response.pricing.options,
15200
- isFull,
15201
- waitlistEnabled: response.availability.waitlistEnabled,
15202
- canJoinWaitlist,
15203
15077
  price: response.pricing.subtotalCents,
15204
15078
  currency: response.pricing.currency,
15205
15079
  requiresAuthForPasses: response.entitlements.requiresLoginForPasses,
15206
15080
  requiresAuthForMemberships: response.entitlements.requiresLoginForMemberships,
15207
15081
  eligiblePasses,
15208
15082
  eligibleMemberships,
15209
- canPayDirectly: monetaryPaymentAvailable,
15210
15083
  eventAcceptsPasses,
15211
15084
  eventAcceptsMemberships,
15212
15085
  availablePasses: [...response.entitlements.availableForPurchase.passes],
@@ -15223,8 +15096,8 @@ function getVisibleTicketTypes(input) {
15223
15096
  })) : [];
15224
15097
  return (input.eligibility?.ticketTypes ?? eventTicketTypes).filter((ticketType) => ticketType.isActive && (!input.selectedAttendanceMode || (ticketType.attendanceScope ?? "both") === "both" || (ticketType.attendanceScope ?? "both") === input.selectedAttendanceMode));
15225
15098
  }
15226
- function getImplicitSingleAvailableTicketType(ticketTypes) {
15227
- return ticketTypes.length === 1 && ticketTypes[0]?.availability.kind === "available" ? ticketTypes[0] : null;
15099
+ function getImplicitSingleTicketType(ticketTypes) {
15100
+ return ticketTypes.length === 1 && ticketTypes[0] && canRequestTicketOption(ticketTypes[0].availability) ? ticketTypes[0] : null;
15228
15101
  }
15229
15102
  function findSelectedTicketType(input) {
15230
15103
  if (!input.selectedPricingOptionId) return null;
@@ -15235,32 +15108,74 @@ function findSelectedTicketType(input) {
15235
15108
  }).find((ticketType) => ticketType.pricingOptionId === input.selectedPricingOptionId) ?? null;
15236
15109
  }
15237
15110
  //#endregion
15238
- //#region ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
15239
- function normalizeTicketSelectionLines(lines) {
15240
- const quantitiesByTicketType = /* @__PURE__ */ new Map();
15241
- for (const line of lines) {
15242
- const quantity = Math.floor(line.quantity);
15243
- if (quantity <= 0) continue;
15244
- const key = line.pricingOptionId ?? null;
15245
- quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
15246
- }
15247
- return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
15248
- pricingOptionId,
15249
- quantity
15111
+ //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
15112
+ /** Public requests share subject identity without copying the run's display sessions. */
15113
+ function toRegistrationRequestSubject(subject, attendanceMode) {
15114
+ return subject.kind === "occurrence" ? {
15115
+ kind: "occurrence",
15116
+ occurrenceId: subject.occurrenceId,
15117
+ attendanceMode: attendanceMode ?? void 0
15118
+ } : {
15119
+ kind: "wholeSeries",
15120
+ eventSeriesId: subject.eventSeriesId,
15121
+ attendanceMode: attendanceMode ?? void 0
15122
+ };
15123
+ }
15124
+ function eventMatchesOccurrenceId(event, occurrenceId) {
15125
+ return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
15126
+ }
15127
+ function findEventByOccurrenceId(events, occurrenceId) {
15128
+ return events.find((event) => eventMatchesOccurrenceId(event, occurrenceId)) ?? null;
15129
+ }
15130
+ function findPreSelectedEvent(input) {
15131
+ const occurrenceContext = input.occurrenceContext;
15132
+ if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
15133
+ const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
15134
+ startsAt: occurrenceContext.startsAt,
15135
+ timeZone: occurrenceContext.timeZone
15250
15136
  }));
15137
+ return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
15138
+ startsAt: event.startsAt,
15139
+ timeZone: event.timeZone
15140
+ })) === contextDate) ?? null;
15251
15141
  }
15252
- function getTicketSelectionTotal(lines) {
15253
- return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
15142
+ function resolveRegistrationTarget(input) {
15143
+ const subject = input.occurrenceContext ? {
15144
+ kind: "occurrence",
15145
+ occurrenceId: input.occurrenceContext.id
15146
+ } : input.selectedEvent?.registrationSubject ?? null;
15147
+ if (!subject) return null;
15148
+ return {
15149
+ subject,
15150
+ selectedEventId: input.selectedEvent?.selectionId ?? null
15151
+ };
15254
15152
  }
15255
- function getSingleSelectedPricingOptionId(lines) {
15256
- const normalized = normalizeTicketSelectionLines(lines);
15257
- return normalized.length === 1 ? normalized[0]?.pricingOptionId ?? null : null;
15153
+ function resolveEventRequiresPayment(input) {
15154
+ if (input.eligibility) return input.eligibility.requiresPayment;
15155
+ if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
15156
+ if (!input.event) return false;
15157
+ return publicEventPricingType(input.event.pricing) === "paid";
15258
15158
  }
15259
- function hasValidPricingOptionSelection(input) {
15260
- if (!input.selectionRequired) return true;
15261
- if (input.lines.length === 0) return false;
15262
- const availableOptionIds = new Set(input.visibleOptions.filter((option) => option.availability.kind === "available").map((option) => option.pricingOptionId));
15263
- return input.lines.every((line) => line.pricingOptionId !== null && availableOptionIds.has(line.pricingOptionId));
15159
+ function resolveDiscountContext(input) {
15160
+ const selection = input.paymentSelection.method ? input.paymentSelection : null;
15161
+ if (!input.event) return null;
15162
+ if (selection?.method === "pass_bundle" && selection.bundlePassId) return {
15163
+ type: "pass",
15164
+ passId: selection.bundlePassId
15165
+ };
15166
+ if (selection?.method === "membership_bundle" && selection.bundleMembershipId) return {
15167
+ type: "membership",
15168
+ membershipId: selection.bundleMembershipId
15169
+ };
15170
+ if (!input.eventRequiresPayment) return null;
15171
+ if (input.ticketsCount <= 0) return null;
15172
+ if (selection?.method && selection.method !== "stripe") return null;
15173
+ return {
15174
+ type: "event",
15175
+ eventSeriesId: input.event.seriesId,
15176
+ eventCategoryId: input.event.eventCategory?.id ?? null,
15177
+ ticketsCount: input.ticketsCount
15178
+ };
15264
15179
  }
15265
15180
  //#endregion
15266
15181
  //#region ../blocks/src/system/runtime/nodes/event-registration/hooks.state.ts
@@ -15380,17 +15295,6 @@ function usePortalAuthWithDeps(deps) {
15380
15295
  logout
15381
15296
  };
15382
15297
  }
15383
- function toRegistrationOptionsSubject(subject, attendanceMode) {
15384
- return subject.kind === "occurrence" ? {
15385
- kind: "occurrence",
15386
- occurrenceId: subject.occurrenceId,
15387
- attendanceMode: attendanceMode ?? void 0
15388
- } : {
15389
- kind: "wholeSeries",
15390
- eventSeriesId: subject.eventSeriesId,
15391
- attendanceMode: attendanceMode ?? void 0
15392
- };
15393
- }
15394
15298
  function useEligibility(params) {
15395
15299
  return useEligibilityWithDeps(params, eventRegistrationHooksDeps);
15396
15300
  }
@@ -15431,7 +15335,7 @@ function useEligibilityWithDeps(params, deps) {
15431
15335
  requestIdRef.current = requestId;
15432
15336
  dispatch({ type: "loading-started" });
15433
15337
  try {
15434
- const subject = toRegistrationOptionsSubject(params.subject, params.attendanceMode);
15338
+ const subject = toRegistrationRequestSubject(params.subject, params.attendanceMode);
15435
15339
  const result = await api.getRegistrationOptions({
15436
15340
  subject,
15437
15341
  selections: ticketSelections.map((selection) => ({
@@ -15635,7 +15539,6 @@ const STRIPE_COMMISSION_FREE_REASONS = [
15635
15539
  "no_commission_plan",
15636
15540
  "billing_evidence_invalid",
15637
15541
  "fee_rounds_to_zero",
15638
- "non_commissionable_scope",
15639
15542
  "payment_not_stripe"
15640
15543
  ];
15641
15544
  const historicalContext = {
@@ -15669,9 +15572,9 @@ const eventRegistrationErrors = {
15669
15572
  message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
15670
15573
  action: "none"
15671
15574
  },
15672
- paymentUncertain: {
15673
- 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.",
15674
- action: "payment-status"
15575
+ paymentInvestigationRequired: {
15576
+ message: "We still can’t confirm your payment. Please don’t make another payment. Contact the organiser so they can check your booking.",
15577
+ action: "none"
15675
15578
  },
15676
15579
  paymentProviderUnavailable: {
15677
15580
  message: "We couldn’t reach the payment provider. Please try again in a minute.",
@@ -16169,7 +16072,30 @@ function checkoutReturnCommand(params) {
16169
16072
  };
16170
16073
  return null;
16171
16074
  }
16172
- async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
16075
+ function handleCheckoutReturnOutcome(options, deps, context, outcome) {
16076
+ const { pending, params } = context;
16077
+ if (outcome.kind === "resolved") {
16078
+ deps.clearReturnParams();
16079
+ options.setPendingRegistrationReference(outcome.reference);
16080
+ options.setStep("verifying-payment");
16081
+ return "settled";
16082
+ }
16083
+ if (outcome.kind === "abandoned") {
16084
+ deps.clearReturnParams();
16085
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16086
+ options.setStep("select-payment");
16087
+ return "settled";
16088
+ }
16089
+ if (outcome.kind === "notFound" && params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
16090
+ deps.clearReturnParams();
16091
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16092
+ options.setStep("select-payment");
16093
+ return "settled";
16094
+ }
16095
+ 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.");
16096
+ return outcome.kind === "notFound" ? "settled" : "retryable";
16097
+ }
16098
+ async function handleCheckoutReturn(options, deps, returnContextRef) {
16173
16099
  deps.cleanupStaleRegistrations();
16174
16100
  const params = deps.readReturnParams();
16175
16101
  if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
@@ -16183,34 +16109,15 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
16183
16109
  }
16184
16110
  const { checkoutAttemptId } = command;
16185
16111
  const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
16186
- checkoutAttemptIdRef.current = checkoutAttemptId;
16112
+ returnContextRef.current = { checkoutAttemptId };
16187
16113
  if (pending) restorePendingPresentation(options, pending);
16188
16114
  options.setStep("verifying-payment");
16189
16115
  try {
16190
16116
  const outcome = await options.api.resolveEventCheckoutReturn(command);
16191
- switch (outcome.kind) {
16192
- case "resolved":
16193
- deps.clearReturnParams();
16194
- options.setPendingRegistrationReference(outcome.reference);
16195
- options.setStep("verifying-payment");
16196
- return "settled";
16197
- case "abandoned":
16198
- deps.clearReturnParams();
16199
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16200
- options.setStep("select-payment");
16201
- return "settled";
16202
- case "notFound":
16203
- if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
16204
- deps.clearReturnParams();
16205
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16206
- options.setStep("select-payment");
16207
- return "settled";
16208
- }
16209
- 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.");
16210
- return "settled";
16211
- case "reconciliationRequired": break;
16212
- }
16213
- 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.");
16117
+ return handleCheckoutReturnOutcome(options, deps, {
16118
+ pending,
16119
+ params
16120
+ }, outcome);
16214
16121
  } catch {
16215
16122
  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.");
16216
16123
  }
@@ -16219,9 +16126,16 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
16219
16126
  function useEventRegistrationStripeReturn(options) {
16220
16127
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
16221
16128
  }
16129
+ function completeVerifiedRegistration(options, deps, returnContext) {
16130
+ const siteId = options.api.config.siteId;
16131
+ const checkoutAttemptId = returnContext?.checkoutAttemptId;
16132
+ if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
16133
+ options.setRegistrationResult({ status: "confirmed" });
16134
+ options.setStep("success");
16135
+ }
16222
16136
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
16223
16137
  const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
16224
- const checkoutAttemptIdRef = useRef(null);
16138
+ const returnContextRef = useRef(null);
16225
16139
  const lookupInFlightRef = useRef(false);
16226
16140
  const [lookupState, setLookupState] = useState("settled");
16227
16141
  const resolveReturn = useCallback(async () => {
@@ -16229,7 +16143,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
16229
16143
  lookupInFlightRef.current = true;
16230
16144
  setLookupState("loading");
16231
16145
  try {
16232
- setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
16146
+ setLookupState(await handleCheckoutReturn(options, deps, returnContextRef));
16233
16147
  } finally {
16234
16148
  lookupInFlightRef.current = false;
16235
16149
  }
@@ -16252,13 +16166,8 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
16252
16166
  resolveReturn();
16253
16167
  }, []);
16254
16168
  useEffect(() => {
16255
- if (verification.status === "completed") {
16256
- const siteId = api.config.siteId;
16257
- const checkoutAttemptId = checkoutAttemptIdRef.current;
16258
- if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
16259
- setRegistrationResult({ status: "confirmed" });
16260
- setStep("success");
16261
- } else if (verification.status === "failed") {
16169
+ if (verification.status === "completed") completeVerifiedRegistration(options, deps, returnContextRef.current);
16170
+ else if (verification.status === "failed") {
16262
16171
  setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
16263
16172
  setStep("payment-failed");
16264
16173
  } else if (verification.status === "timeout") setStep("verifying-timeout");
@@ -16287,7 +16196,10 @@ function registrationErrorPresentation(error) {
16287
16196
  const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
16288
16197
  if (reason) return eventRegistrationErrors[reason];
16289
16198
  if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
16290
- return eventRegistrationErrors.paymentUncertain;
16199
+ return {
16200
+ message: "Something went wrong. Please try again.",
16201
+ action: "retry"
16202
+ };
16291
16203
  }
16292
16204
  //#endregion
16293
16205
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
@@ -16316,26 +16228,6 @@ function useEventRegistrationSubmit(options) {
16316
16228
  * and the balance left, which the block's copy cannot. Every other method keeps
16317
16229
  * whatever success message the site configured.
16318
16230
  */
16319
- function toRegistrationResult(result) {
16320
- if (result.alreadyRegistered) {
16321
- const waitlisted = result.status === "waitlist";
16322
- return {
16323
- status: result.status,
16324
- variant: "info",
16325
- title: waitlisted ? "Already waitlisted" : "Already registered",
16326
- message: result.message ?? (waitlisted ? "You are already on the waitlist for this event." : "You are already registered for this event.")
16327
- };
16328
- }
16329
- if (result.status === "waitlist") return {
16330
- status: "waitlist",
16331
- title: "Added to waitlist",
16332
- message: result.message ?? (typeof result.waitlistPosition === "number" ? `You've been added to the waitlist (position ${result.waitlistPosition}).` : "You've been added to the waitlist.")
16333
- };
16334
- return {
16335
- status: result.status,
16336
- message: result.paymentMethod === "pass" ? result.message : void 0
16337
- };
16338
- }
16339
16231
  function useEventRegistrationSubmitWithDeps(options, deps) {
16340
16232
  /**
16341
16233
  * The command identity for the submission currently in progress.
@@ -16349,7 +16241,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16349
16241
  * Identical retries retain the command after a lost response. Changed form
16350
16242
  * details get a new command so the server applies the new booking intent.
16351
16243
  */
16352
- const commandRef = useRef(null);
16244
+ const { commandRef } = options;
16353
16245
  const inFlightRef = useRef(null);
16354
16246
  const submit = async (method, passId, membershipId, selection, submitOptions) => {
16355
16247
  const target = options.registrationTarget;
@@ -16362,19 +16254,15 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16362
16254
  const registrationCommandId = commandRef.current?.id ?? deps.createClientCommandId();
16363
16255
  try {
16364
16256
  let prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
16365
- if (prepared.subject.kind === "wholeSeries" && method === "stripe") {
16366
- commandRef.current = null;
16367
- return await submitWholeSeriesCheckout(options, deps, {
16257
+ let request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
16258
+ const intentKey = registrationRequestIntentKey(request);
16259
+ if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) {
16260
+ prepared = {
16368
16261
  ...prepared,
16369
- subject: prepared.subject
16370
- }, submitOptions);
16262
+ registrationCommandId: deps.createClientCommandId()
16263
+ };
16264
+ request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
16371
16265
  }
16372
- const request = buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId);
16373
- const intentKey = registrationRequestIntentKey(request);
16374
- if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) prepared = {
16375
- ...prepared,
16376
- registrationCommandId: deps.createClientCommandId()
16377
- };
16378
16266
  commandRef.current = {
16379
16267
  id: prepared.registrationCommandId,
16380
16268
  intentKey
@@ -16383,8 +16271,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16383
16271
  ...request,
16384
16272
  registrationCommandId: prepared.registrationCommandId
16385
16273
  });
16386
- commandRef.current = null;
16387
- return await completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions);
16274
+ return await completeRegistrationSubmission(options, deps, prepared, result, submitOptions);
16388
16275
  } catch (error) {
16389
16276
  if (error instanceof BlockApiError && error.tag === "registrationPreparationChanged") commandRef.current = null;
16390
16277
  const { message, action } = registrationErrorPresentation(error);
@@ -16408,9 +16295,40 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16408
16295
  return pending;
16409
16296
  };
16410
16297
  }
16411
- async function completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions) {
16412
- if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
16413
- options.setRegistrationResult(toRegistrationResult(result));
16298
+ async function completeRegistrationSubmission(options, deps, prepared, result, submitOptions) {
16299
+ switch (result.kind) {
16300
+ case "paymentStatusRequired":
16301
+ options.setPaymentStatusRecovery({
16302
+ kind: "ready",
16303
+ subject: result.subject
16304
+ });
16305
+ options.setStep("payment-status");
16306
+ return {
16307
+ ok: true,
16308
+ outcome: { kind: "payment_status" }
16309
+ };
16310
+ case "checkoutRequired":
16311
+ options.setPendingRegistrationReference(result.reference);
16312
+ savePendingCheckout(options, deps, prepared, result.submissionId, result.reference);
16313
+ options.setStep("redirecting");
16314
+ return redirectToCheckout(result.checkoutUrl, submitOptions);
16315
+ case "waitlisted":
16316
+ options.setRegistrationResult({
16317
+ status: "waitlist",
16318
+ title: "Added to waitlist",
16319
+ message: result.waitlist.message
16320
+ });
16321
+ break;
16322
+ case "bookingComplete":
16323
+ case "paymentDeferred":
16324
+ options.setPendingRegistrationReference(result.reference);
16325
+ options.setRegistrationResult({
16326
+ status: "confirmed",
16327
+ message: result.summary.paymentMethod === "pass" ? result.summary.message : void 0
16328
+ });
16329
+ break;
16330
+ }
16331
+ options.commandRef.current = null;
16414
16332
  options.setStep("success");
16415
16333
  return {
16416
16334
  ok: true,
@@ -16441,15 +16359,7 @@ function prepareSubmitContext(options, registrationCommandId, target, method, pa
16441
16359
  email: guest.email?.trim().toLowerCase() || void 0,
16442
16360
  phone: guest.phone?.trim() || void 0
16443
16361
  })) : void 0,
16444
- subject: target.subject.kind === "occurrence" ? {
16445
- kind: "occurrence",
16446
- occurrenceId: target.subject.occurrenceId,
16447
- attendanceMode: options.selectedAttendanceMode ?? void 0
16448
- } : {
16449
- kind: "wholeSeries",
16450
- eventSeriesId: target.subject.eventSeriesId,
16451
- attendanceMode: options.selectedAttendanceMode ?? void 0
16452
- }
16362
+ subject: toRegistrationRequestSubject(target.subject, options.selectedAttendanceMode)
16453
16363
  };
16454
16364
  }
16455
16365
  function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId, selection) {
@@ -16473,85 +16383,6 @@ function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId,
16473
16383
  if (method === "free") return { method };
16474
16384
  throw new Error("Choose an available payment option.");
16475
16385
  }
16476
- async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions) {
16477
- options.setStep("redirecting");
16478
- if (prepared.selectionSnapshot.method !== "stripe") throw new Error("Choose an available payment option.");
16479
- const checkoutAttemptId = deps.createClientCommandId();
16480
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
16481
- const result = await options.api.createEventCheckout({
16482
- subject: prepared.subject,
16483
- ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
16484
- selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
16485
- email: options.formData.email.trim().toLowerCase(),
16486
- name: options.formData.name.trim(),
16487
- phone: options.formData.phone.trim() || void 0,
16488
- formData: options.formData,
16489
- consentAccepted: options.formData.consentAccepted,
16490
- altchaSolution: options.altchaSolution,
16491
- guests: prepared.guestsPayload,
16492
- claimToken: options.claimToken ?? void 0,
16493
- waitlistEntryId: options.waitlistEntryId ?? void 0,
16494
- discountCode: options.discountCode.trim() || void 0,
16495
- collectionOptionKind: prepared.selectionSnapshot.collectionOptionKind,
16496
- checkoutAttemptId,
16497
- ...urls
16498
- });
16499
- if (result.scope !== "wholeSeries") throw new Error("Unexpected registration scope for event checkout.");
16500
- if ("waitlistEntryId" in result) {
16501
- options.setRegistrationResult({
16502
- status: "waitlist",
16503
- variant: result.alreadyRegistered ? "info" : void 0,
16504
- title: result.alreadyRegistered ? "Already waitlisted" : "Added to waitlist",
16505
- message: result.alreadyRegistered ? "You are already on the waitlist for this event." : "You've been added to the waitlist."
16506
- });
16507
- options.setStep("success");
16508
- return {
16509
- ok: true,
16510
- outcome: { kind: "completed" }
16511
- };
16512
- }
16513
- if (result.bypassed) return completeBypassedCheckout(options);
16514
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
16515
- savePendingCheckout(options, deps, prepared, result.checkoutAttemptId, {
16516
- scope: "wholeSeries",
16517
- registrationId: result.registrationId
16518
- });
16519
- return redirectToCheckout(result.checkoutUrl, submitOptions);
16520
- }
16521
- function needsOccurrenceCheckout(result, method) {
16522
- return result.scope === "occurrence" && result.status === "confirmed" && result.pendingPayment && [
16523
- "stripe",
16524
- "pass_bundle",
16525
- "membership_bundle"
16526
- ].includes(method);
16527
- }
16528
- async function submitOccurrenceCheckout(options, deps, prepared, registration, method, submitOptions) {
16529
- const reference = {
16530
- scope: "occurrence",
16531
- attendeeId: registration.attendeeId
16532
- };
16533
- options.setPendingRegistrationReference(reference);
16534
- options.setStep("redirecting");
16535
- const checkoutAttemptId = deps.createClientCommandId();
16536
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
16537
- const result = await options.api.createEventCheckout({
16538
- subject: {
16539
- kind: "occurrence",
16540
- attendeeId: registration.attendeeId
16541
- },
16542
- registrationCommandId: prepared.registrationCommandId,
16543
- checkoutType: method === "pass_bundle" ? "pass_bundle" : method === "membership_bundle" ? "membership_bundle" : "event",
16544
- productId: prepared.bundleProductId,
16545
- discountCode: options.discountCode.trim() || void 0,
16546
- collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0,
16547
- ...urls
16548
- });
16549
- if (result.scope !== "occurrence") throw new Error("Unexpected registration scope for event checkout.");
16550
- if (result.bypassed) return completeBypassedCheckout(options);
16551
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
16552
- if (prepared.subject.kind === "occurrence") savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference);
16553
- return redirectToCheckout(result.checkoutUrl, submitOptions);
16554
- }
16555
16386
  function savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference) {
16556
16387
  const siteId = options.api.config.siteId;
16557
16388
  if (!siteId) return;
@@ -16564,14 +16395,6 @@ function savePendingCheckout(options, deps, prepared, checkoutAttemptId, referen
16564
16395
  paymentSelection: prepared.selectionSnapshot
16565
16396
  });
16566
16397
  }
16567
- function completeBypassedCheckout(options) {
16568
- options.setRegistrationResult({ status: "confirmed" });
16569
- options.setStep("success");
16570
- return {
16571
- ok: true,
16572
- outcome: { kind: "completed" }
16573
- };
16574
- }
16575
16398
  function redirectToCheckout(checkoutUrl, submitOptions) {
16576
16399
  if (submitOptions?.redirect !== "manual") window.location.href = checkoutUrl;
16577
16400
  return {
@@ -16583,12 +16406,23 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
16583
16406
  };
16584
16407
  }
16585
16408
  function registrationRequestIntentKey(request) {
16586
- const { registrationCommandId: _command, altchaSolution: _proof, ...intent } = request;
16587
- return JSON.stringify(intent);
16409
+ const { registrationCommandId: _command, altchaSolution: _proof, checkout, ...intent } = request;
16410
+ return JSON.stringify({
16411
+ ...intent,
16412
+ checkout: checkout ? {
16413
+ discountCode: checkout.discountCode,
16414
+ collectionOptionKind: checkout.collectionOptionKind
16415
+ } : void 0
16416
+ });
16588
16417
  }
16589
- function buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId) {
16418
+ function buildRegistrationRequest(options, prepared, method, passId, membershipId) {
16590
16419
  return {
16591
16420
  registrationCommandId: prepared.registrationCommandId,
16421
+ checkout: {
16422
+ ...buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: prepared.registrationCommandId }),
16423
+ discountCode: options.discountCode.trim() || void 0,
16424
+ collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0
16425
+ },
16592
16426
  subject: prepared.subject,
16593
16427
  ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
16594
16428
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
@@ -16608,64 +16442,6 @@ function buildOccurrenceRegistrationRequest(options, prepared, method, passId, m
16608
16442
  };
16609
16443
  }
16610
16444
  //#endregion
16611
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
16612
- function eventMatchesOccurrenceId(event, occurrenceId) {
16613
- return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
16614
- }
16615
- function findEventByOccurrenceId(events, occurrenceId) {
16616
- return events.find((event) => eventMatchesOccurrenceId(event, occurrenceId)) ?? null;
16617
- }
16618
- function findPreSelectedEvent(input) {
16619
- const occurrenceContext = input.occurrenceContext;
16620
- if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
16621
- const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
16622
- startsAt: occurrenceContext.startsAt,
16623
- timeZone: occurrenceContext.timeZone
16624
- }));
16625
- return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
16626
- startsAt: event.startsAt,
16627
- timeZone: event.timeZone
16628
- })) === contextDate) ?? null;
16629
- }
16630
- function resolveRegistrationTarget(input) {
16631
- const subject = input.occurrenceContext ? {
16632
- kind: "occurrence",
16633
- occurrenceId: input.occurrenceContext.id
16634
- } : input.selectedEvent?.registrationSubject ?? null;
16635
- if (!subject) return null;
16636
- return {
16637
- subject,
16638
- selectedEventId: input.selectedEvent?.selectionId ?? null
16639
- };
16640
- }
16641
- function resolveEventRequiresPayment(input) {
16642
- if (input.eligibility) return input.eligibility.requiresPayment;
16643
- if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
16644
- if (!input.event) return false;
16645
- return publicEventPricingType(input.event.pricing) === "paid";
16646
- }
16647
- function resolveDiscountContext(input) {
16648
- const selection = input.paymentSelection.method ? input.paymentSelection : null;
16649
- if (!input.event) return null;
16650
- if (selection?.method === "pass_bundle" && selection.bundlePassId) return {
16651
- type: "pass",
16652
- passId: selection.bundlePassId
16653
- };
16654
- if (selection?.method === "membership_bundle" && selection.bundleMembershipId) return {
16655
- type: "membership",
16656
- membershipId: selection.bundleMembershipId
16657
- };
16658
- if (!input.eventRequiresPayment) return null;
16659
- if (input.ticketsCount <= 0) return null;
16660
- if (selection?.method && selection.method !== "stripe") return null;
16661
- return {
16662
- type: "event",
16663
- eventSeriesId: input.event.seriesId,
16664
- eventCategoryId: input.event.eventCategory?.id ?? null,
16665
- ticketsCount: input.ticketsCount
16666
- };
16667
- }
16668
- //#endregion
16669
16445
  //#region ../blocks/src/system/runtime/nodes/event-registration/useSettledCommerceView.ts
16670
16446
  function buildCandidate(input) {
16671
16447
  if (!input.eligibilityIsSettled || !input.eligibility || !input.currentEvent) return null;
@@ -16886,7 +16662,7 @@ function computeAutoSelection(input) {
16886
16662
  return { method: null };
16887
16663
  }
16888
16664
  function computeDirectPaymentSelection(eligibility) {
16889
- if (!eligibility?.canPayDirectly) return { method: null };
16665
+ if (!eligibility?.requiresPayment) return { method: null };
16890
16666
  const approvedKinds = getApprovedDirectPaymentCollectionOptionKinds(eligibility.paymentOptions);
16891
16667
  const collectionOptionKind = approvedKinds.includes("upfront") ? "upfront" : approvedKinds[0];
16892
16668
  return collectionOptionKind ? {
@@ -16900,7 +16676,7 @@ function isSelectionValid(input) {
16900
16676
  if (!eligibility) return selection.method === "stripe" || selection.method === "pass_bundle" || selection.method === "membership_bundle";
16901
16677
  switch (selection.method) {
16902
16678
  case "free": return eligibility.isFree;
16903
- case "stripe": return Boolean(eligibility.canPayDirectly && eligibility.price);
16679
+ case "stripe": return Boolean(eligibility.requiresPayment && eligibility.price);
16904
16680
  case "pass": return isAuthenticated && ticketsCount === 1 && !!selection.passId && eligibility.eligiblePasses.some((pass) => pass.id === selection.passId);
16905
16681
  case "membership": return isAuthenticated && ticketsCount === 1 && !!selection.membershipId && eligibility.eligibleMemberships.some((membership) => membership.id === selection.membershipId);
16906
16682
  case "pass_bundle": return ticketsCount === 1 && !!selection.bundlePassId && eligibility.availablePasses.some((pass) => pass.id === selection.bundlePassId);
@@ -16919,7 +16695,7 @@ function normalizeRegistrationFlowState(state) {
16919
16695
  ticketsCount: state.ticketsCount,
16920
16696
  passOptOut: nextPassOptOut
16921
16697
  });
16922
- const canKeepManualSelection = state.step === "select-payment" && state.selectionSource === "manual" && isSelectionValid({
16698
+ const canKeepManualSelection = state.step !== "select-tickets" && state.selectionSource === "manual" && isSelectionValid({
16923
16699
  selection: state.paymentSelection,
16924
16700
  isAuthenticated: state.isAuthenticated,
16925
16701
  eligibility: state.eligibility,
@@ -16985,6 +16761,24 @@ function requiresPaymentStep(params) {
16985
16761
  if (!params.eventRequiresPayment) return false;
16986
16762
  return !isEntitlementSelection(params.paymentSelection);
16987
16763
  }
16764
+ //#endregion
16765
+ //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
16766
+ function isRecord$1(value) {
16767
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16768
+ }
16769
+ function isRuntimeEventDestination(value) {
16770
+ if (!isRecord$1(value) || typeof value.kind !== "string") return false;
16771
+ if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
16772
+ return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
16773
+ }
16774
+ function resolveRuntimeEventDestination(event) {
16775
+ if (isRuntimeEventDestination(event.destination)) return event.destination;
16776
+ return {
16777
+ kind: "native",
16778
+ mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
16779
+ path: typeof event.path === "string" ? event.path : ""
16780
+ };
16781
+ }
16988
16782
  const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
16989
16783
  kind: z.literal("unsupported"),
16990
16784
  reason: z.enum([
@@ -17227,7 +17021,7 @@ function resolveEventRegistrationSubmitPlan(input) {
17227
17021
  const selection = input.paymentSelection.method ? input.paymentSelection : void 0;
17228
17022
  if (input.canJoinWaitlist) return {
17229
17023
  ok: true,
17230
- args: withOptionalSelection({ method: "free" }, selection)
17024
+ args: { method: "free" }
17231
17025
  };
17232
17026
  switch (input.paymentSelection.method) {
17233
17027
  case "pass": return input.paymentSelection.passId ? {
@@ -17278,6 +17072,10 @@ function resolveHeadlessEventRegistrationStatus(wizard) {
17278
17072
  case "redirecting": return { tag: "redirecting" };
17279
17073
  case "verifying-payment": return { tag: "verifying-payment" };
17280
17074
  case "verifying-timeout": return { tag: "verifying-timeout" };
17075
+ case "payment-status": return wizard.paymentStatusRecovery ? {
17076
+ tag: "payment-status",
17077
+ state: wizard.paymentStatusRecovery
17078
+ } : { tag: "ready" };
17281
17079
  case "payment-failed": return {
17282
17080
  tag: "payment-failed",
17283
17081
  message: wizard.errorMessage
@@ -17324,6 +17122,10 @@ function resolveHeadlessEventRegistrationView(wizard) {
17324
17122
  kind: "verifying",
17325
17123
  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."
17326
17124
  };
17125
+ case "payment-status": return wizard.paymentStatusRecovery ? {
17126
+ kind: "payment-status",
17127
+ state: wizard.paymentStatusRecovery
17128
+ } : { kind: "form" };
17327
17129
  case "select-event":
17328
17130
  case "select-tickets":
17329
17131
  case "enter-details":
@@ -17590,20 +17392,124 @@ function useWaitlistClaimRestoration(input) {
17590
17392
  }
17591
17393
  //#endregion
17592
17394
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationCompletion.ts
17395
+ function isSelectionSoldOut(availability) {
17396
+ return availability?.kind === "unavailable" && availability.reason === "full";
17397
+ }
17593
17398
  function resolveRegistrationAvailabilityBlocker(input) {
17594
17399
  if (input.hasWaitlistClaim) return null;
17595
17400
  if (input.eligibilityError) return input.eligibilityError;
17596
17401
  if (!input.eligibility) return "Registration is not available for this event.";
17402
+ if (input.eligibility.registration.kind === "pending_payment") return null;
17403
+ if (isSelectionSoldOut(input.eligibility.availability)) return null;
17597
17404
  if (input.eligibility.availability.kind === "unavailable" && input.eligibility.availability.reason === "paymentUnavailable") return "Payment options are not available for this event.";
17598
- if (input.eligibility.availability.kind === "unavailable" || !input.eligibility.eligible) return "Registration is not available for this event.";
17405
+ if (input.eligibility.availability.kind === "unavailable" || input.eligibility.registration.kind === "waitlisted") return "Registration is not available for this event.";
17599
17406
  return null;
17600
17407
  }
17601
17408
  function resolvePricingSelectionBlocker(input) {
17602
17409
  if (input.selectionRequired && !input.hasValidSelection) return "Choose at least one available ticket to continue.";
17603
17410
  return input.slidingScaleSelectionMessage;
17604
17411
  }
17605
- function isSoldOutWithoutWaitlist(input) {
17606
- return Boolean(!input.hasWaitlistClaim && input.eligibility?.isFull && !input.eligibility.canJoinWaitlist);
17412
+ //#endregion
17413
+ //#region ../blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.ts
17414
+ function isCheckableRecovery(state) {
17415
+ return state.kind === "ready" || state.kind === "processing" || state.kind === "temporarilyUnavailable";
17416
+ }
17417
+ function assistanceRequired(recovery) {
17418
+ return {
17419
+ kind: "assistanceRequired",
17420
+ subject: recovery.subject
17421
+ };
17422
+ }
17423
+ function applyStatusOutcome(outcome, recovery, setters, setRecovery) {
17424
+ if (outcome.kind === "resolved") {
17425
+ setters.setPendingRegistrationReference(outcome.reference);
17426
+ setters.setRegistrationResult({ status: "confirmed" });
17427
+ setRecovery(null);
17428
+ setters.setStep("success");
17429
+ return;
17430
+ }
17431
+ switch (outcome.kind) {
17432
+ case "paymentProcessing":
17433
+ setRecovery({
17434
+ ...recovery,
17435
+ kind: "processing"
17436
+ });
17437
+ return;
17438
+ case "paymentAvailable":
17439
+ setRecovery({
17440
+ ...recovery,
17441
+ kind: "paymentAvailable",
17442
+ checkoutUrl: outcome.checkoutUrl
17443
+ });
17444
+ return;
17445
+ case "paymentCheckUnavailable":
17446
+ setRecovery({
17447
+ ...recovery,
17448
+ kind: "temporarilyUnavailable"
17449
+ });
17450
+ return;
17451
+ case "paymentRetryAvailable":
17452
+ setRecovery({
17453
+ ...recovery,
17454
+ kind: "retryAvailable"
17455
+ });
17456
+ return;
17457
+ case "abandoned":
17458
+ case "notFound":
17459
+ case "reconciliationRequired":
17460
+ case "paymentAssistanceRequired":
17461
+ setRecovery(assistanceRequired(recovery));
17462
+ return;
17463
+ }
17464
+ }
17465
+ function usePaymentStatusRecovery(options) {
17466
+ const [recovery, setRecovery] = useState(null);
17467
+ const checkRef = useRef(null);
17468
+ return {
17469
+ recovery,
17470
+ showRecovery: useCallback((next) => setRecovery(next), []),
17471
+ check: useCallback(() => {
17472
+ if (!recovery || !isCheckableRecovery(recovery) || checkRef.current) return;
17473
+ const pending = (async () => {
17474
+ setRecovery({
17475
+ ...recovery,
17476
+ kind: "checking"
17477
+ });
17478
+ try {
17479
+ applyStatusOutcome(await options.api.resolveEventCheckoutReturn({
17480
+ kind: "checkStatus",
17481
+ subject: recovery.subject
17482
+ }), recovery, options, setRecovery);
17483
+ } catch {
17484
+ setRecovery({
17485
+ ...recovery,
17486
+ kind: "temporarilyUnavailable"
17487
+ });
17488
+ }
17489
+ })();
17490
+ checkRef.current = pending;
17491
+ pending.finally(() => {
17492
+ if (checkRef.current === pending) checkRef.current = null;
17493
+ });
17494
+ }, [options, recovery]),
17495
+ continuePayment: useCallback(() => {
17496
+ if (recovery?.kind === "paymentAvailable") window.location.assign(recovery.checkoutUrl);
17497
+ }, [recovery]),
17498
+ returnToPayment: useCallback(() => {
17499
+ if (recovery?.kind !== "retryAvailable") return;
17500
+ options.beginNewSubmission();
17501
+ setRecovery(null);
17502
+ options.setOptionsRefreshKey((value) => value + 1);
17503
+ options.setStep("select-payment");
17504
+ }, [options, recovery]),
17505
+ returnToBooking: useCallback(() => {
17506
+ if (recovery?.kind !== "assistanceRequired") return;
17507
+ options.beginNewSubmission();
17508
+ setRecovery(null);
17509
+ options.setOptionsRefreshKey((value) => value + 1);
17510
+ options.setStep("select-tickets");
17511
+ }, [options, recovery])
17512
+ };
17607
17513
  }
17608
17514
  //#endregion
17609
17515
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
@@ -17661,6 +17567,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17661
17567
  const [errorAction, setErrorAction] = useState("retry");
17662
17568
  const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
17663
17569
  const [altchaSolution, setAltchaSolution] = useState(null);
17570
+ const [checkingNextStep, setCheckingNextStep] = useState(false);
17664
17571
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
17665
17572
  const waitlistClaim = deps.useWaitlistClaim();
17666
17573
  const [showMagicLinkModal, setShowMagicLinkModal] = useState(false);
@@ -17687,28 +17594,29 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17687
17594
  }), [options.occurrenceContext, selectedEvent]);
17688
17595
  const registrationOccurrenceId = getRegistrationOccurrenceId(registrationTarget);
17689
17596
  const currentEvent = selectedEvent ?? preSelectedEvent;
17597
+ const commandRef = useRef(null);
17690
17598
  const setStep = useCallback((nextStep) => {
17691
17599
  dispatchFlow({
17692
17600
  type: "set-step",
17693
17601
  step: nextStep
17694
17602
  });
17695
17603
  }, []);
17696
- const { checkoutRecovery, startCheckoutRecovery } = useCheckoutRecovery(currentEvent, setStep);
17697
- const currentEventUsesTicketTypes = usesTieredPricing(currentEvent);
17698
- const currentCanJoinWaitlist = canJoinWaitlistOutsideRecovery(currentEvent, selectedAttendanceMode, checkoutRecovery);
17699
- const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
17604
+ const { recovery: paymentStatusRecovery, showRecovery: setPaymentStatusRecovery, check: handleCheckPaymentStatus, continuePayment: handleContinueExistingPayment, returnToPayment: handleReturnToPayment, returnToBooking: handleReturnToBooking } = usePaymentStatusRecovery({
17605
+ api,
17606
+ beginNewSubmission: () => {
17607
+ commandRef.current = null;
17608
+ },
17609
+ setPendingRegistrationReference,
17610
+ setRegistrationResult,
17611
+ setOptionsRefreshKey,
17612
+ setStep
17613
+ });
17614
+ const usesTicketTypeSelections = usesTieredPricing(currentEvent);
17700
17615
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
17701
17616
  event: currentEvent,
17702
17617
  eligibility: null,
17703
17618
  selectedAttendanceMode
17704
- }).map((option) => checkoutRecovery ? {
17705
- ...option,
17706
- availability: checkoutRecoveryOptionAvailability(option.availability)
17707
- } : option), [
17708
- currentEvent,
17709
- selectedAttendanceMode,
17710
- checkoutRecovery
17711
- ]);
17619
+ }), [currentEvent, selectedAttendanceMode]);
17712
17620
  const normalizedSelectedTicketLines = useMemo(() => normalizeTicketSelectionLines(selectedTicketLines), [selectedTicketLines]);
17713
17621
  const ticketTypeSelectionTotal = useMemo(() => getTicketSelectionTotal(normalizedSelectedTicketLines), [normalizedSelectedTicketLines]);
17714
17622
  const ticketsCount = usesTicketTypeSelections ? ticketTypeSelectionTotal : flowState.ticketsCount;
@@ -17746,7 +17654,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17746
17654
  isAuthenticated: portalAuthState === "authenticated",
17747
17655
  occurrenceId: registrationOccurrenceId
17748
17656
  });
17749
- const eligibility = useCheckoutRecoveryEligibility(eligibilityResult.eligibility, step, checkoutRecovery, startCheckoutRecovery);
17657
+ const eligibility = eligibilityResult.eligibility;
17750
17658
  const eligibilityLoading = eligibilityResult.isLoading;
17751
17659
  const eligibilityError = eligibilityResult.error;
17752
17660
  const portalCreditState = eligibilityResult.creditState;
@@ -17858,14 +17766,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17858
17766
  event: currentEvent,
17859
17767
  eligibility,
17860
17768
  selectedAttendanceMode
17861
- }).map((option) => checkoutRecovery ? {
17862
- ...option,
17863
- availability: checkoutRecoveryOptionAvailability(option.availability)
17864
- } : option), [
17769
+ }), [
17865
17770
  currentEvent,
17866
17771
  selectedAttendanceMode,
17867
- eligibility,
17868
- checkoutRecovery
17772
+ eligibility
17869
17773
  ]);
17870
17774
  const singleSelectedPricingOptionId = getSingleSelectedPricingOptionId(normalizedSelectedTicketLines);
17871
17775
  const selectedTicketType = useMemo(() => findSelectedTicketType({
@@ -17889,12 +17793,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17889
17793
  const event = currentEvent;
17890
17794
  if (!usesTieredPricing(event)) return false;
17891
17795
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
17892
- return checkoutRecovery || !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
17893
- }, [
17894
- currentEvent,
17895
- selectedAttendanceMode,
17896
- checkoutRecovery
17897
- ]);
17796
+ return true;
17797
+ }, [currentEvent, selectedAttendanceMode]);
17898
17798
  const hasValidTicketSelection = hasValidPricingOptionSelection({
17899
17799
  selectionRequired: ticketTypeSelectionRequired,
17900
17800
  lines: normalizedSelectedTicketLines,
@@ -17905,8 +17805,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17905
17805
  if (selectedTicketLines.length > 0) setSelectedTicketLines([]);
17906
17806
  return;
17907
17807
  }
17908
- const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => ticketType.availability.kind === "available");
17909
- const implicitTicketType = getImplicitSingleAvailableTicketType(eventTicketTypesForSelection);
17808
+ const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => canRequestTicketOption(ticketType.availability));
17809
+ const implicitTicketType = getImplicitSingleTicketType(eventTicketTypesForSelection);
17910
17810
  if (implicitTicketType && normalizedSelectedTicketLines.length === 0) {
17911
17811
  setSelectedTicketLines([{
17912
17812
  pricingOptionId: implicitTicketType.pricingOptionId,
@@ -17973,6 +17873,26 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17973
17873
  selectedEventSlidingScale,
17974
17874
  selectedSlidingAmount
17975
17875
  ]);
17876
+ const checkDetailsNextStep = async () => {
17877
+ if (portalAuthState === "authenticated" || waitlistClaim.claim || eligibility?.availability.kind === "available" || !registrationTarget) return { kind: canRecoverToWaitlist(eligibility) ? "waitlist" : "continue" };
17878
+ setCheckingNextStep(true);
17879
+ try {
17880
+ return await api.checkRegistrationNextStep({
17881
+ subject: toRegistrationRequestSubject(registrationTarget.subject, selectedAttendanceMode),
17882
+ selections: ticketSelections,
17883
+ selectedSlidingAmount: selectedSlidingAmount ?? void 0,
17884
+ email: formData.email,
17885
+ altchaSolution
17886
+ });
17887
+ } catch {
17888
+ return {
17889
+ kind: "unavailable",
17890
+ message: "We could not check availability. Please try again."
17891
+ };
17892
+ } finally {
17893
+ setCheckingNextStep(false);
17894
+ }
17895
+ };
17976
17896
  const handleDetailsComplete = async () => {
17977
17897
  const blockDetailsCompletion = (message) => {
17978
17898
  setErrorAction("places");
@@ -18004,11 +17924,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18004
17924
  message: "Check the highlighted fields and try again."
18005
17925
  };
18006
17926
  }
18007
- if (isSoldOutWithoutWaitlist({
18008
- hasWaitlistClaim: waitlistClaim.claim !== null,
18009
- eligibility
18010
- })) return blockDetailsCompletion("Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.");
18011
- if (!eligibility?.canJoinWaitlist && requiresPaymentStep({
17927
+ const next = await checkDetailsNextStep();
17928
+ if (next.kind === "unavailable") return blockDetailsCompletion(next.message);
17929
+ const canJoinWaitlist = next.kind === "waitlist";
17930
+ if (!canJoinWaitlist && requiresPaymentStep({
18012
17931
  eventRequiresPayment,
18013
17932
  paymentSelection
18014
17933
  })) {
@@ -18021,7 +17940,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18021
17940
  const submitPlan = resolveEventRegistrationSubmitPlan({
18022
17941
  paymentSelection,
18023
17942
  eventRequiresPayment,
18024
- canJoinWaitlist: eligibility?.canJoinWaitlist
17943
+ canJoinWaitlist
18025
17944
  });
18026
17945
  if (!submitPlan.ok) return blockDetailsCompletion(submitPlan.message);
18027
17946
  const submitResult = await handleSubmit(submitPlan.args.method, submitPlan.args.passId, submitPlan.args.membershipId, submitPlan.args.selection);
@@ -18039,7 +17958,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18039
17958
  const submitPlan = resolveEventRegistrationSubmitPlan({
18040
17959
  paymentSelection,
18041
17960
  eventRequiresPayment,
18042
- canJoinWaitlist: eligibility?.canJoinWaitlist
17961
+ canJoinWaitlist: false
18043
17962
  });
18044
17963
  if (!submitPlan.ok) {
18045
17964
  setErrorMessage(submitPlan.message);
@@ -18071,6 +17990,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18071
17990
  const selectionMethod = paymentSelection.method;
18072
17991
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
18073
17992
  const handleSubmit = deps.useEventRegistrationSubmit({
17993
+ commandRef,
18074
17994
  setErrorAction,
18075
17995
  api,
18076
17996
  registrationTarget,
@@ -18087,7 +18007,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18087
18007
  setPendingRegistrationReference,
18088
18008
  setStep,
18089
18009
  setRegistrationResult: (result) => setRegistrationResult(result),
18090
- setErrorMessage
18010
+ setErrorMessage,
18011
+ setPaymentStatusRecovery
18091
18012
  });
18092
18013
  const handleEventSelect = (event) => {
18093
18014
  setSelectedEvent(event);
@@ -18146,8 +18067,6 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18146
18067
  scopedEvents,
18147
18068
  preSelectedEvent,
18148
18069
  hasPreSelectedContext,
18149
- checkoutRecovery,
18150
- startCheckoutRecovery,
18151
18070
  step,
18152
18071
  setStep,
18153
18072
  selectedEvent,
@@ -18201,7 +18120,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18201
18120
  waitlistClaim,
18202
18121
  eligibility,
18203
18122
  registrationOptionsState: eligibilityResult.state,
18204
- eligibilityLoading,
18123
+ eligibilityLoading: eligibilityLoading || checkingNextStep,
18205
18124
  eligibilityError,
18206
18125
  existingRegistration: existingRegistrationResult.registration,
18207
18126
  existingRegistrationLoading: existingRegistrationResult.isLoading || portalAuthState === "authenticated" && eligibilityLoading,
@@ -18221,6 +18140,11 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18221
18140
  discountContext,
18222
18141
  shouldShowDiscountToggle,
18223
18142
  verification,
18143
+ paymentStatusRecovery,
18144
+ handleCheckPaymentStatus,
18145
+ handleContinueExistingPayment,
18146
+ handleReturnToPayment,
18147
+ handleReturnToBooking,
18224
18148
  handleDetailsComplete,
18225
18149
  handlePaymentSelect,
18226
18150
  handleUseCardInsteadOfPass,
@@ -18273,7 +18197,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18273
18197
  selection: wizard.paymentSelection
18274
18198
  }),
18275
18199
  eventRequiresPayment: wizard.eventRequiresPayment,
18276
- canJoinWaitlist: wizard.eligibility?.canJoinWaitlist
18200
+ canJoinWaitlist: wizard.step !== "select-payment" && wizard.eligibility?.availability.kind === "waitlist" && wizard.eligibility.registration.kind !== "pending_payment"
18277
18201
  });
18278
18202
  if (!plan.ok) return {
18279
18203
  ok: false,
@@ -18318,6 +18242,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18318
18242
  case "redirecting":
18319
18243
  case "verifying-payment":
18320
18244
  case "verifying-timeout":
18245
+ case "payment-status":
18321
18246
  case "payment-failed": return {
18322
18247
  ok: false,
18323
18248
  reason: "blocked",
@@ -18325,27 +18250,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18325
18250
  };
18326
18251
  }
18327
18252
  }, [submit, wizard]);
18328
- const back = useCallback(() => {
18329
- switch (wizard.step) {
18330
- case "enter-details":
18331
- wizard.setStep("select-tickets");
18332
- return;
18333
- case "select-payment":
18334
- wizard.setStep("enter-details");
18335
- return;
18336
- case "select-tickets":
18337
- if (!wizard.hasPreSelectedContext) wizard.setStep("select-event");
18338
- return;
18339
- case "select-event":
18340
- case "error":
18341
- case "success":
18342
- case "submitting":
18343
- case "redirecting":
18344
- case "verifying-payment":
18345
- case "verifying-timeout":
18346
- case "payment-failed": return;
18347
- }
18348
- }, [wizard]);
18253
+ const back = useEventRegistrationBack(wizard);
18349
18254
  const updateAttendee = useCallback((values) => {
18350
18255
  wizard.setFormData(values);
18351
18256
  }, [wizard]);
@@ -18432,7 +18337,8 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18432
18337
  terminal: {
18433
18338
  result: wizard.registrationResult,
18434
18339
  errorMessage: wizard.errorMessage,
18435
- verification: wizard.verification
18340
+ verification: wizard.verification,
18341
+ paymentStatusRecovery: wizard.paymentStatusRecovery
18436
18342
  },
18437
18343
  actions: {
18438
18344
  selectEvent: wizard.handleEventSelect,
@@ -18456,12 +18362,40 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18456
18362
  next,
18457
18363
  back,
18458
18364
  submit,
18459
- retry: wizard.handleRetry
18365
+ retry: wizard.handleRetry,
18366
+ checkPaymentStatus: wizard.handleCheckPaymentStatus,
18367
+ continueExistingPayment: wizard.handleContinueExistingPayment,
18368
+ returnToPayment: wizard.handleReturnToPayment,
18369
+ returnToBooking: wizard.handleReturnToBooking
18460
18370
  }
18461
18371
  },
18462
18372
  wizard
18463
18373
  };
18464
18374
  }
18375
+ function useEventRegistrationBack(wizard) {
18376
+ return useCallback(() => {
18377
+ switch (wizard.step) {
18378
+ case "enter-details":
18379
+ wizard.setStep("select-tickets");
18380
+ return;
18381
+ case "select-payment":
18382
+ wizard.setStep("enter-details");
18383
+ return;
18384
+ case "select-tickets":
18385
+ if (!wizard.hasPreSelectedContext) wizard.setStep("select-event");
18386
+ return;
18387
+ case "select-event":
18388
+ case "error":
18389
+ case "success":
18390
+ case "submitting":
18391
+ case "redirecting":
18392
+ case "verifying-payment":
18393
+ case "verifying-timeout":
18394
+ case "payment-status":
18395
+ case "payment-failed": return;
18396
+ }
18397
+ }, [wizard]);
18398
+ }
18465
18399
  function useEventRegistrationWizardControllerWithDeps(options, deps) {
18466
18400
  const maxTicketsNum = normalizeEventRegistrationMaxTickets(options.maxTickets);
18467
18401
  return { wizard: deps.useEventRegistrationWizard({
@@ -18517,7 +18451,7 @@ function resolvePaymentModel(wizard, directPaymentOptions) {
18517
18451
  }
18518
18452
  function resolveHeadlessDirectPaymentOptions(wizard) {
18519
18453
  const eligibility = wizard.eligibility;
18520
- if (!eligibility?.eligible || !eligibility.canPayDirectly || !eligibility.price) return [];
18454
+ if (!eligibility?.requiresPayment || !eligibility.price) return [];
18521
18455
  const event = wizard.selectedEvent ?? wizard.preSelectedEvent;
18522
18456
  return buildEventRegistrationPaymentOptionsViewModel({
18523
18457
  eventTitle: event?.title ?? "Event",