@riverbankcms/sdk 0.142.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 (90) hide show
  1. package/dist/{PreviewEditorSidebar-Dk6Dt11f.mjs → PreviewEditorSidebar-DROGfMKi.mjs} +4 -4
  2. package/dist/{PreviewEditorUI-D6EQTsCA.mjs → PreviewEditorUI-BaRjhFu-.mjs} +3 -3
  3. package/dist/{PreviewQueryProvider-CC-EPkyQ.mjs → PreviewQueryProvider-BCYICImX.mjs} +1 -1
  4. package/dist/{SdkPreviewModeRuntime-DH1n4ytY.mjs → SdkPreviewModeRuntime-OIABYLES.mjs} +5 -5
  5. package/dist/{SiteChromeCustomizeContext-DunIzY0K.mjs → SiteChromeCustomizeContext-ohjTvZbc.mjs} +491 -525
  6. package/dist/_dts/api/src/endpoints.d.ts +7 -0
  7. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  8. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +48 -41
  9. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +2 -1
  10. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  11. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +95 -35
  12. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/PaymentStatusStep.d.ts +10 -0
  13. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +0 -2
  14. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +12 -1
  15. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +2 -2
  16. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationCompletion.d.ts +1 -4
  17. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationDerivations.d.ts +12 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +1 -1
  19. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/sessionStorage.d.ts +1 -0
  20. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.d.ts +4 -5
  21. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +9 -11
  22. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +56 -2
  23. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +10 -4
  24. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.d.ts +23 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/utils.d.ts +1 -1
  26. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +4 -4
  27. package/dist/_dts/commerce/src/purchase/eventCheckoutRecoveryWindow.d.ts +8 -0
  28. package/dist/_dts/commerce/src/purchase/index.d.ts +2 -1
  29. package/dist/_dts/commerce/src/purchase/stateTransitions.d.ts +1 -13
  30. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +3113 -3077
  31. package/dist/_dts/sdk/src/version.d.ts +1 -1
  32. package/dist/client/{BookingConsentAgreement-CnJeq3_r.mjs → BookingConsentAgreement-BoCtvzzu.mjs} +2 -2
  33. package/dist/client/{DiscountCodeDisclosure-BINEO6J9.mjs → DiscountCodeDisclosure-B5S30nZk.mjs} +1 -1
  34. package/dist/client/{EventCombined.client-srxNQ6sZ.mjs → EventCombined.client-CeZ2-zSt.mjs} +5 -5
  35. package/dist/client/{EventListing.client-DbyVyGkX.mjs → EventListing.client-DJN3j8B3.mjs} +2 -2
  36. package/dist/client/{LeafletStaticMap-B-wpijzL.mjs → LeafletStaticMap-RjtGzDQT.mjs} +1 -1
  37. package/dist/client/{accordion-B-iUXU7n.mjs → accordion-DOmnDoyg.mjs} +1 -1
  38. package/dist/client/{bookingForm-BXYPke5q.mjs → bookingForm-C0vk7pgS.mjs} +5 -5
  39. package/dist/client/bookings.mjs +449 -525
  40. package/dist/client/{carousel-Cr4aj63H.mjs → carousel-BZJ7pno3.mjs} +1 -1
  41. package/dist/client/{cart-DibP9WWa.mjs → cart-CaeVAHJl.mjs} +3 -3
  42. package/dist/client/{checkout-CrSBVnL8.mjs → checkout-7wmoNDtG.mjs} +3 -3
  43. package/dist/client/client.mjs +518 -540
  44. package/dist/client/{context-Ces-AgdR.mjs → context-BOimxE3U.mjs} +12 -1
  45. package/dist/client/{dates-BXyNeNhB.mjs → dates-BnzVksi4.mjs} +2 -2
  46. package/dist/client/{eventCalendar-HwpPeqrH.mjs → eventCalendar-l-fpaslC.mjs} +7 -7
  47. package/dist/client/{eventCombined-BpFUkuEI.mjs → eventCombined-DGGvssK6.mjs} +3 -3
  48. package/dist/client/{eventDetails-DK_agiWy.mjs → eventDetails-DRiCm_hL.mjs} +4 -4
  49. package/dist/client/{eventListing-DhzHqP1c.mjs → eventListing-dph8loYX.mjs} +3 -3
  50. package/dist/client/{eventRegistration-BdQ_Dn2d.mjs → eventRegistration-ClBohmbY.mjs} +472 -530
  51. package/dist/client/{form-B1frcuzy.mjs → form-BLVVAnd3.mjs} +2 -2
  52. package/dist/client/{headerSection-CT5z460s.mjs → headerSection-DHy4C8Ix.mjs} +2 -2
  53. package/dist/client/hooks.mjs +12 -0
  54. package/dist/client/{locationMap-CIQXB9i2.mjs → locationMap-6HuS28ck.mjs} +2 -2
  55. package/dist/client/{newsletterForm-BYNYZSes.mjs → newsletterForm-xOdcOhZP.mjs} +2 -2
  56. package/dist/client/{pricing-CbTlAsmt.mjs → pricing-ChinRYFt.mjs} +1 -1
  57. package/dist/client/{productDetail-CR7Z3yjS.mjs → productDetail-0chA5gbK.mjs} +3 -3
  58. package/dist/client/{productList-DXj00vzR.mjs → productList-OoINA0h1.mjs} +3 -3
  59. package/dist/client/{propsDecoder-DiLiBdek.mjs → propsDecoder-CIZ-hbIG.mjs} +14 -1
  60. package/dist/client/rendering/client.mjs +506 -539
  61. package/dist/client/rendering/islands.mjs +17 -17
  62. package/dist/client/rendering.mjs +517 -539
  63. package/dist/client/{scrollToBlock-CrcbBf0h.mjs → scrollToBlock-BPZl0gPZ.mjs} +1 -1
  64. package/dist/client/{shop-DgqiB_1b.mjs → shop-CLJFqNIq.mjs} +6 -6
  65. package/dist/client/{shop-commerce-D_J781in.mjs → shop-commerce-PpANg5wB.mjs} +5 -5
  66. package/dist/client/{shop-commerce-store-PGUbDwqe.mjs → shop-commerce-store-TA_Kz2ku.mjs} +1 -1
  67. package/dist/client/{shopShared-C_Bb2zwG.mjs → shopShared-DzcFcA5g.mjs} +3 -3
  68. package/dist/client/{useEventFilters-CM17Xpr1.mjs → useEventFilters-CFU21QZo.mjs} +5 -5
  69. package/dist/preview-next/before-render.mjs +11 -0
  70. package/dist/preview-next/client/runtime.mjs +4 -4
  71. package/dist/preview-next/keepAlive.mjs +11 -0
  72. package/dist/preview-next/middleware.mjs +11 -0
  73. package/dist/{previewAuthStore-oVttoRDO.mjs → previewAuthStore-JA6-I_sL.mjs} +11 -0
  74. package/dist/{sdk-runtime-DhmuRhIX.mjs → sdk-runtime-CDKl0Fr7.mjs} +2 -2
  75. package/dist/{separator-C3hlg9LH.mjs → separator-BTp96X32.mjs} +1 -1
  76. package/dist/server/components.mjs +12 -2
  77. package/dist/server/config-validation.mjs +12 -0
  78. package/dist/server/config.mjs +12 -0
  79. package/dist/server/data.mjs +12 -0
  80. package/dist/server/index.mjs +12 -1
  81. package/dist/server/next.mjs +13 -3
  82. package/dist/server/page-converter.mjs +1 -0
  83. package/dist/server/prebuild.mjs +1 -1
  84. package/dist/server/rendering/server.mjs +12 -0
  85. package/dist/server/rendering.mjs +12 -2
  86. package/dist/server/routing.mjs +12 -0
  87. package/dist/server/server.mjs +13 -1
  88. package/package.json +1 -1
  89. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.d.ts +0 -14
  90. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.d.ts +0 -8
@@ -8360,6 +8360,10 @@ const EVENTS = {
8360
8360
  method: "POST",
8361
8361
  authScope: "portal"
8362
8362
  },
8363
+ registrationNextStep: {
8364
+ path: "/public/sites/{siteId}/events/registration-next-step",
8365
+ method: "POST"
8366
+ },
8363
8367
  registrationStatus: {
8364
8368
  path: "/public/sites/{siteId}/events/registration-status",
8365
8369
  method: "GET"
@@ -8964,6 +8968,13 @@ function createBlockApi(config) {
8964
8968
  body: data
8965
8969
  });
8966
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
+ },
8967
8978
  async getEventRegistrationStatus(params) {
8968
8979
  const sid = requireSiteId();
8969
8980
  const path = buildPath(EVENTS.registrationStatus.path, { siteId: sid });
@@ -14911,188 +14922,15 @@ const BookingFormClient$1 = ({ siteId: siteIdProp, render }) => {
14911
14922
  });
14912
14923
  };
14913
14924
  //#endregion
14914
- //#region ../blocks/src/system/runtime/nodes/events/domain/capacity.ts
14915
- /**
14916
- * Compute remaining spots for an event from `availableSpots` with a fallback
14917
- * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
14918
- * for uncapped events so call sites can render no capacity indicator at all.
14919
- *
14920
- * The single source of truth for capacity math across event cards, agenda
14921
- * rows, and timetable cells — keep wording variations in the call sites.
14922
- */
14923
- function getEventAvailability(event) {
14924
- if (event.capacity == null) return {
14925
- available: null,
14926
- isFull: false
14927
- };
14928
- const available = event.availableSpots ?? event.capacity - event.registeredCount;
14929
- if (available <= 0) return {
14930
- available: 0,
14931
- isFull: true
14932
- };
14933
- return {
14934
- available,
14935
- isFull: false
14936
- };
14937
- }
14938
- /**
14939
- * Pick the capacity row a booking decision should read: the selected
14940
- * attendance mode's availability when the visitor has chosen one and the
14941
- * event publishes it, otherwise the event-level aggregate.
14942
- *
14943
- * The mode row is used wholesale. A mode with `capacity: null` on a capped
14944
- * event is uncapped *by design*: the registration write path applies the
14945
- * same rule via `getEffectiveOccurrenceCapacityForAttendanceMode`, so falling
14946
- * back to the event's `availableSpots` here would block bookings the server
14947
- * accepts. (Waitlist claims are still occurrence-scoped; see issue 1377.)
14948
- */
14949
- function selectEventCapacitySource(event, attendanceMode) {
14950
- if (!attendanceMode) return event;
14951
- return (event.attendanceModes ?? []).find((mode) => mode.mode === attendanceMode) ?? event;
14952
- }
14953
- /**
14954
- * Whether the visitor's next step is the waitlist: the source is full *and*
14955
- * a waitlist is open. The one place this is decided — cards, rows, the
14956
- * wizard's steps and its action bar all route through here so they agree,
14957
- * including for producers that omit `availableSpots` and rely on the
14958
- * `capacity - registeredCount` fallback.
14959
- */
14960
- function canJoinEventWaitlist(event, options) {
14961
- const source = selectEventCapacitySource(event, options?.attendanceMode);
14962
- return getEventAvailability(source).isFull && source.waitlistEnabled === true;
14963
- }
14964
- //#endregion
14965
- //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
14966
- function isRecord$1(value) {
14967
- return typeof value === "object" && value !== null && !Array.isArray(value);
14968
- }
14969
- function isRuntimeEventDestination(value) {
14970
- if (!isRecord$1(value) || typeof value.kind !== "string") return false;
14971
- if (value.kind === "native") return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
14972
- return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
14973
- }
14974
- function resolveRuntimeEventDestination(event) {
14975
- if (isRuntimeEventDestination(event.destination)) return event.destination;
14976
- return {
14977
- kind: "native",
14978
- mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
14979
- path: typeof event.path === "string" ? event.path : ""
14980
- };
14981
- }
14982
- //#endregion
14983
- //#region ../blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.ts
14984
- /** Recovery may select held places; checkout still owns admission and payment validation. */
14985
- function checkoutRecoveryOptionAvailability(availability) {
14986
- if (availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable") return {
14987
- kind: "available",
14988
- remainingPlaces: null
14989
- };
14990
- return availability;
14991
- }
14992
- /**
14993
- * A form-only projection for the explicit existing-checkout path. It permits
14994
- * collecting the original selection without claiming a new place is available.
14995
- * The public checkout service identifies the customer and remains the admission
14996
- * authority; no reservation/customer data is exposed through this projection.
14997
- */
14998
- function getCheckoutRecoveryFormEligibility(eligibility) {
14999
- 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;
15000
- const amountCents = eligibility.price ?? 0;
15001
- if (amountCents <= 0 || !eligibility.currency || eligibility.availability.kind === "unavailable" && eligibility.availability.reason === "selectionRequired") return eligibility.registration.kind === "pending_payment" ? {
15002
- ...eligibility,
15003
- registration: { kind: "none" },
15004
- ticketTypes: eligibility.ticketTypes.map((option) => ({
15005
- ...option,
15006
- availability: checkoutRecoveryOptionAvailability(option.availability)
15007
- }))
15008
- } : eligibility;
15009
- const currency = eligibility.currency;
15010
- const paymentOptions = eligibility.paymentTerms.allowedOptions.map((option) => {
15011
- switch (option.kind) {
15012
- case "upfront": return {
15013
- kind: "payInFull",
15014
- amountCents,
15015
- currency
15016
- };
15017
- case "deferred_manual": return {
15018
- kind: "deferredManual",
15019
- amountCents,
15020
- currency
15021
- };
15022
- case "instalment_plan": return {
15023
- kind: "instalmentPlan",
15024
- amountCents,
15025
- currency
15026
- };
15027
- case "flexible_balance": return {
15028
- kind: "flexibleBalance",
15029
- amountCents,
15030
- currency
15031
- };
15032
- }
15033
- });
15034
- return {
15035
- ...eligibility,
15036
- registration: { kind: "none" },
15037
- availability: {
15038
- kind: "available",
15039
- remainingPlaces: null,
15040
- waitlistEnabled: eligibility.waitlistEnabled
15041
- },
15042
- eligible: paymentOptions.length > 0,
15043
- isFull: false,
15044
- canJoinWaitlist: false,
15045
- isFree: false,
15046
- requiresPayment: true,
15047
- canPayDirectly: paymentOptions.length > 0,
15048
- paymentOptions,
15049
- eligiblePasses: [],
15050
- eligibleMemberships: [],
15051
- ticketTypes: eligibility.ticketTypes.map((option) => ({
15052
- ...option,
15053
- availability: checkoutRecoveryOptionAvailability(option.availability)
15054
- }))
15055
- };
15056
- }
15057
- function canJoinWaitlistOutsideRecovery(event, attendanceMode, checkoutRecovery) {
15058
- return !checkoutRecovery && event !== null && canJoinEventWaitlist(event, { attendanceMode });
15059
- }
15060
- //#endregion
15061
- //#region ../blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.ts
15062
- /** Recovery belongs to the selected booking subject; changing events resets its scope. */
15063
- function useCheckoutRecovery(event, setStep) {
15064
- const [recoveryEventId, setRecoveryEventId] = useState(null);
15065
- return {
15066
- checkoutRecovery: event !== null && recoveryEventId === event.selectionId,
15067
- startCheckoutRecovery: useCallback(() => {
15068
- if (!event) return;
15069
- setRecoveryEventId(event.selectionId);
15070
- setStep("select-tickets");
15071
- }, [event, setStep])
15072
- };
15073
- }
15074
- /** Authenticated pending bookings enter the same form-only recovery path as guests. */
15075
- function useCheckoutRecoveryEligibility(eligibility, step, checkoutRecovery, startCheckoutRecovery) {
15076
- const registrationKind = eligibility?.registration.kind;
15077
- useEffect(() => {
15078
- if ((step === "select-event" || step === "select-tickets" || step === "enter-details" || step === "select-payment") && registrationKind === "pending_payment" && !checkoutRecovery) startCheckoutRecovery();
15079
- }, [
15080
- registrationKind,
15081
- step,
15082
- checkoutRecovery,
15083
- startCheckoutRecovery
15084
- ]);
15085
- return useMemo(() => checkoutRecovery ? getCheckoutRecoveryFormEligibility(eligibility) : eligibility, [checkoutRecovery, eligibility]);
15086
- }
15087
- //#endregion
15088
14925
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
15089
14926
  function resolveRegistrationRecoveryAction(step, action) {
15090
14927
  if (step === "payment-failed") return "payment-options";
15091
- 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";
15092
14930
  return action;
15093
14931
  }
15094
14932
  function canRecoverToWaitlist(eligibility) {
15095
- return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
14933
+ return eligibility?.availability.kind === "waitlist" && eligibility.registration.kind !== "pending_payment";
15096
14934
  }
15097
14935
  function shouldRefreshAfterRegistrationError(step, action, enabled) {
15098
14936
  return enabled || step === "error" && action === "places";
@@ -15109,19 +14947,19 @@ function resetRegistrationAfterExpiry(input) {
15109
14947
  input.setErrorMessage("");
15110
14948
  input.setPendingRegistrationReference(null);
15111
14949
  }
15112
- /** 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. */
15113
14951
  function recoverRegistrationError(action, handlers) {
15114
14952
  switch (action) {
15115
14953
  case "none": return;
15116
14954
  case "portal":
15117
- case "payment-status":
15118
14955
  window.location.assign("/portal");
15119
14956
  return;
15120
14957
  case "event":
15121
14958
  window.location.assign(window.location.pathname);
15122
14959
  return;
15123
14960
  case "retry":
15124
- window.location.reload();
14961
+ handlers.clearError();
14962
+ handlers.setStep("select-payment");
15125
14963
  return;
15126
14964
  case "details":
15127
14965
  handlers.clearError();
@@ -15159,13 +14997,43 @@ function validateFormData(formData, requiresConsent = false) {
15159
14997
  return errors;
15160
14998
  }
15161
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
15162
15032
  //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
15163
15033
  function getPortalSubjectEmail(profile) {
15164
15034
  return profile?.email ?? null;
15165
15035
  }
15166
15036
  function transformRegistrationOptionsResponse(response) {
15167
- const canJoinWaitlist = response.availability.kind === "waitlist";
15168
- const isFull = response.availability.remainingPlaces === 0 || response.availability.kind === "unavailable" && response.availability.reason === "full";
15169
15037
  const eligiblePasses = response.entitlements.passes.map((pass) => ({
15170
15038
  id: pass.customerPassId,
15171
15039
  passName: pass.passName,
@@ -15178,7 +15046,6 @@ function transformRegistrationOptionsResponse(response) {
15178
15046
  const eventAcceptsPasses = response.entitlements.requiresLoginForPasses || eligiblePasses.length > 0 || response.entitlements.availableForPurchase.passes.length > 0;
15179
15047
  const eventAcceptsMemberships = response.entitlements.requiresLoginForMemberships || eligibleMemberships.length > 0 || response.entitlements.availableForPurchase.memberships.length > 0;
15180
15048
  const monetaryPaymentAvailable = hasMonetaryPaymentOption(response);
15181
- const registrationAvailable = response.availability.kind === "available" || response.availability.kind === "waitlist";
15182
15049
  return {
15183
15050
  availability: response.availability,
15184
15051
  registration: response.registration,
@@ -15193,7 +15060,6 @@ function transformRegistrationOptionsResponse(response) {
15193
15060
  },
15194
15061
  transactions: []
15195
15062
  } : null,
15196
- eligible: response.registration.kind === "none" && registrationAvailable,
15197
15063
  requiresPayment: monetaryPaymentAvailable,
15198
15064
  pricingStrategy: response.pricing.strategy,
15199
15065
  paymentTerms: response.pricing.paymentTerms,
@@ -15208,16 +15074,12 @@ function transformRegistrationOptionsResponse(response) {
15208
15074
  currency: response.pricing.currency
15209
15075
  })),
15210
15076
  ticketTypes: response.pricing.options,
15211
- isFull,
15212
- waitlistEnabled: response.availability.waitlistEnabled,
15213
- canJoinWaitlist,
15214
15077
  price: response.pricing.subtotalCents,
15215
15078
  currency: response.pricing.currency,
15216
15079
  requiresAuthForPasses: response.entitlements.requiresLoginForPasses,
15217
15080
  requiresAuthForMemberships: response.entitlements.requiresLoginForMemberships,
15218
15081
  eligiblePasses,
15219
15082
  eligibleMemberships,
15220
- canPayDirectly: monetaryPaymentAvailable,
15221
15083
  eventAcceptsPasses,
15222
15084
  eventAcceptsMemberships,
15223
15085
  availablePasses: [...response.entitlements.availableForPurchase.passes],
@@ -15234,8 +15096,8 @@ function getVisibleTicketTypes(input) {
15234
15096
  })) : [];
15235
15097
  return (input.eligibility?.ticketTypes ?? eventTicketTypes).filter((ticketType) => ticketType.isActive && (!input.selectedAttendanceMode || (ticketType.attendanceScope ?? "both") === "both" || (ticketType.attendanceScope ?? "both") === input.selectedAttendanceMode));
15236
15098
  }
15237
- function getImplicitSingleAvailableTicketType(ticketTypes) {
15238
- 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;
15239
15101
  }
15240
15102
  function findSelectedTicketType(input) {
15241
15103
  if (!input.selectedPricingOptionId) return null;
@@ -15246,32 +15108,74 @@ function findSelectedTicketType(input) {
15246
15108
  }).find((ticketType) => ticketType.pricingOptionId === input.selectedPricingOptionId) ?? null;
15247
15109
  }
15248
15110
  //#endregion
15249
- //#region ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
15250
- function normalizeTicketSelectionLines(lines) {
15251
- const quantitiesByTicketType = /* @__PURE__ */ new Map();
15252
- for (const line of lines) {
15253
- const quantity = Math.floor(line.quantity);
15254
- if (quantity <= 0) continue;
15255
- const key = line.pricingOptionId ?? null;
15256
- quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
15257
- }
15258
- return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
15259
- pricingOptionId,
15260
- 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
15261
15136
  }));
15137
+ return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
15138
+ startsAt: event.startsAt,
15139
+ timeZone: event.timeZone
15140
+ })) === contextDate) ?? null;
15262
15141
  }
15263
- function getTicketSelectionTotal(lines) {
15264
- 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
+ };
15265
15152
  }
15266
- function getSingleSelectedPricingOptionId(lines) {
15267
- const normalized = normalizeTicketSelectionLines(lines);
15268
- 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";
15269
15158
  }
15270
- function hasValidPricingOptionSelection(input) {
15271
- if (!input.selectionRequired) return true;
15272
- if (input.lines.length === 0) return false;
15273
- const availableOptionIds = new Set(input.visibleOptions.filter((option) => option.availability.kind === "available").map((option) => option.pricingOptionId));
15274
- 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
+ };
15275
15179
  }
15276
15180
  //#endregion
15277
15181
  //#region ../blocks/src/system/runtime/nodes/event-registration/hooks.state.ts
@@ -15391,17 +15295,6 @@ function usePortalAuthWithDeps(deps) {
15391
15295
  logout
15392
15296
  };
15393
15297
  }
15394
- function toRegistrationOptionsSubject(subject, attendanceMode) {
15395
- return subject.kind === "occurrence" ? {
15396
- kind: "occurrence",
15397
- occurrenceId: subject.occurrenceId,
15398
- attendanceMode: attendanceMode ?? void 0
15399
- } : {
15400
- kind: "wholeSeries",
15401
- eventSeriesId: subject.eventSeriesId,
15402
- attendanceMode: attendanceMode ?? void 0
15403
- };
15404
- }
15405
15298
  function useEligibility(params) {
15406
15299
  return useEligibilityWithDeps(params, eventRegistrationHooksDeps);
15407
15300
  }
@@ -15442,7 +15335,7 @@ function useEligibilityWithDeps(params, deps) {
15442
15335
  requestIdRef.current = requestId;
15443
15336
  dispatch({ type: "loading-started" });
15444
15337
  try {
15445
- const subject = toRegistrationOptionsSubject(params.subject, params.attendanceMode);
15338
+ const subject = toRegistrationRequestSubject(params.subject, params.attendanceMode);
15446
15339
  const result = await api.getRegistrationOptions({
15447
15340
  subject,
15448
15341
  selections: ticketSelections.map((selection) => ({
@@ -15679,9 +15572,9 @@ const eventRegistrationErrors = {
15679
15572
  message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
15680
15573
  action: "none"
15681
15574
  },
15682
- paymentUncertain: {
15683
- 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.",
15684
- 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"
15685
15578
  },
15686
15579
  paymentProviderUnavailable: {
15687
15580
  message: "We couldn’t reach the payment provider. Please try again in a minute.",
@@ -16179,7 +16072,30 @@ function checkoutReturnCommand(params) {
16179
16072
  };
16180
16073
  return null;
16181
16074
  }
16182
- 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) {
16183
16099
  deps.cleanupStaleRegistrations();
16184
16100
  const params = deps.readReturnParams();
16185
16101
  if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
@@ -16193,34 +16109,15 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
16193
16109
  }
16194
16110
  const { checkoutAttemptId } = command;
16195
16111
  const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
16196
- checkoutAttemptIdRef.current = checkoutAttemptId;
16112
+ returnContextRef.current = { checkoutAttemptId };
16197
16113
  if (pending) restorePendingPresentation(options, pending);
16198
16114
  options.setStep("verifying-payment");
16199
16115
  try {
16200
16116
  const outcome = await options.api.resolveEventCheckoutReturn(command);
16201
- switch (outcome.kind) {
16202
- case "resolved":
16203
- deps.clearReturnParams();
16204
- options.setPendingRegistrationReference(outcome.reference);
16205
- options.setStep("verifying-payment");
16206
- return "settled";
16207
- case "abandoned":
16208
- deps.clearReturnParams();
16209
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16210
- options.setStep("select-payment");
16211
- return "settled";
16212
- case "notFound":
16213
- if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
16214
- deps.clearReturnParams();
16215
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
16216
- options.setStep("select-payment");
16217
- return "settled";
16218
- }
16219
- 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.");
16220
- return "settled";
16221
- case "reconciliationRequired": break;
16222
- }
16223
- 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);
16224
16121
  } catch {
16225
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.");
16226
16123
  }
@@ -16229,9 +16126,16 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
16229
16126
  function useEventRegistrationStripeReturn(options) {
16230
16127
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
16231
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
+ }
16232
16136
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
16233
16137
  const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
16234
- const checkoutAttemptIdRef = useRef(null);
16138
+ const returnContextRef = useRef(null);
16235
16139
  const lookupInFlightRef = useRef(false);
16236
16140
  const [lookupState, setLookupState] = useState("settled");
16237
16141
  const resolveReturn = useCallback(async () => {
@@ -16239,7 +16143,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
16239
16143
  lookupInFlightRef.current = true;
16240
16144
  setLookupState("loading");
16241
16145
  try {
16242
- setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
16146
+ setLookupState(await handleCheckoutReturn(options, deps, returnContextRef));
16243
16147
  } finally {
16244
16148
  lookupInFlightRef.current = false;
16245
16149
  }
@@ -16262,13 +16166,8 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
16262
16166
  resolveReturn();
16263
16167
  }, []);
16264
16168
  useEffect(() => {
16265
- if (verification.status === "completed") {
16266
- const siteId = api.config.siteId;
16267
- const checkoutAttemptId = checkoutAttemptIdRef.current;
16268
- if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
16269
- setRegistrationResult({ status: "confirmed" });
16270
- setStep("success");
16271
- } else if (verification.status === "failed") {
16169
+ if (verification.status === "completed") completeVerifiedRegistration(options, deps, returnContextRef.current);
16170
+ else if (verification.status === "failed") {
16272
16171
  setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
16273
16172
  setStep("payment-failed");
16274
16173
  } else if (verification.status === "timeout") setStep("verifying-timeout");
@@ -16297,7 +16196,10 @@ function registrationErrorPresentation(error) {
16297
16196
  const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
16298
16197
  if (reason) return eventRegistrationErrors[reason];
16299
16198
  if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
16300
- return eventRegistrationErrors.paymentUncertain;
16199
+ return {
16200
+ message: "Something went wrong. Please try again.",
16201
+ action: "retry"
16202
+ };
16301
16203
  }
16302
16204
  //#endregion
16303
16205
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
@@ -16326,26 +16228,6 @@ function useEventRegistrationSubmit(options) {
16326
16228
  * and the balance left, which the block's copy cannot. Every other method keeps
16327
16229
  * whatever success message the site configured.
16328
16230
  */
16329
- function toRegistrationResult(result) {
16330
- if (result.alreadyRegistered) {
16331
- const waitlisted = result.status === "waitlist";
16332
- return {
16333
- status: result.status,
16334
- variant: "info",
16335
- title: waitlisted ? "Already waitlisted" : "Already registered",
16336
- message: result.message ?? (waitlisted ? "You are already on the waitlist for this event." : "You are already registered for this event.")
16337
- };
16338
- }
16339
- if (result.status === "waitlist") return {
16340
- status: "waitlist",
16341
- title: "Added to waitlist",
16342
- message: result.message ?? (typeof result.waitlistPosition === "number" ? `You've been added to the waitlist (position ${result.waitlistPosition}).` : "You've been added to the waitlist.")
16343
- };
16344
- return {
16345
- status: result.status,
16346
- message: result.paymentMethod === "pass" ? result.message : void 0
16347
- };
16348
- }
16349
16231
  function useEventRegistrationSubmitWithDeps(options, deps) {
16350
16232
  /**
16351
16233
  * The command identity for the submission currently in progress.
@@ -16359,7 +16241,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16359
16241
  * Identical retries retain the command after a lost response. Changed form
16360
16242
  * details get a new command so the server applies the new booking intent.
16361
16243
  */
16362
- const commandRef = useRef(null);
16244
+ const { commandRef } = options;
16363
16245
  const inFlightRef = useRef(null);
16364
16246
  const submit = async (method, passId, membershipId, selection, submitOptions) => {
16365
16247
  const target = options.registrationTarget;
@@ -16372,19 +16254,15 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16372
16254
  const registrationCommandId = commandRef.current?.id ?? deps.createClientCommandId();
16373
16255
  try {
16374
16256
  let prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
16375
- if (prepared.subject.kind === "wholeSeries" && method === "stripe") {
16376
- commandRef.current = null;
16377
- 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 = {
16378
16261
  ...prepared,
16379
- subject: prepared.subject
16380
- }, submitOptions);
16262
+ registrationCommandId: deps.createClientCommandId()
16263
+ };
16264
+ request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
16381
16265
  }
16382
- const request = buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId);
16383
- const intentKey = registrationRequestIntentKey(request);
16384
- if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) prepared = {
16385
- ...prepared,
16386
- registrationCommandId: deps.createClientCommandId()
16387
- };
16388
16266
  commandRef.current = {
16389
16267
  id: prepared.registrationCommandId,
16390
16268
  intentKey
@@ -16393,8 +16271,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16393
16271
  ...request,
16394
16272
  registrationCommandId: prepared.registrationCommandId
16395
16273
  });
16396
- commandRef.current = null;
16397
- return await completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions);
16274
+ return await completeRegistrationSubmission(options, deps, prepared, result, submitOptions);
16398
16275
  } catch (error) {
16399
16276
  if (error instanceof BlockApiError && error.tag === "registrationPreparationChanged") commandRef.current = null;
16400
16277
  const { message, action } = registrationErrorPresentation(error);
@@ -16418,9 +16295,40 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
16418
16295
  return pending;
16419
16296
  };
16420
16297
  }
16421
- async function completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions) {
16422
- if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
16423
- 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;
16424
16332
  options.setStep("success");
16425
16333
  return {
16426
16334
  ok: true,
@@ -16451,15 +16359,7 @@ function prepareSubmitContext(options, registrationCommandId, target, method, pa
16451
16359
  email: guest.email?.trim().toLowerCase() || void 0,
16452
16360
  phone: guest.phone?.trim() || void 0
16453
16361
  })) : void 0,
16454
- subject: target.subject.kind === "occurrence" ? {
16455
- kind: "occurrence",
16456
- occurrenceId: target.subject.occurrenceId,
16457
- attendanceMode: options.selectedAttendanceMode ?? void 0
16458
- } : {
16459
- kind: "wholeSeries",
16460
- eventSeriesId: target.subject.eventSeriesId,
16461
- attendanceMode: options.selectedAttendanceMode ?? void 0
16462
- }
16362
+ subject: toRegistrationRequestSubject(target.subject, options.selectedAttendanceMode)
16463
16363
  };
16464
16364
  }
16465
16365
  function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId, selection) {
@@ -16483,85 +16383,6 @@ function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId,
16483
16383
  if (method === "free") return { method };
16484
16384
  throw new Error("Choose an available payment option.");
16485
16385
  }
16486
- async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions) {
16487
- options.setStep("redirecting");
16488
- if (prepared.selectionSnapshot.method !== "stripe") throw new Error("Choose an available payment option.");
16489
- const checkoutAttemptId = deps.createClientCommandId();
16490
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
16491
- const result = await options.api.createEventCheckout({
16492
- subject: prepared.subject,
16493
- ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
16494
- selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
16495
- email: options.formData.email.trim().toLowerCase(),
16496
- name: options.formData.name.trim(),
16497
- phone: options.formData.phone.trim() || void 0,
16498
- formData: options.formData,
16499
- consentAccepted: options.formData.consentAccepted,
16500
- altchaSolution: options.altchaSolution,
16501
- guests: prepared.guestsPayload,
16502
- claimToken: options.claimToken ?? void 0,
16503
- waitlistEntryId: options.waitlistEntryId ?? void 0,
16504
- discountCode: options.discountCode.trim() || void 0,
16505
- collectionOptionKind: prepared.selectionSnapshot.collectionOptionKind,
16506
- checkoutAttemptId,
16507
- ...urls
16508
- });
16509
- if (result.scope !== "wholeSeries") throw new Error("Unexpected registration scope for event checkout.");
16510
- if ("waitlistEntryId" in result) {
16511
- options.setRegistrationResult({
16512
- status: "waitlist",
16513
- variant: result.alreadyRegistered ? "info" : void 0,
16514
- title: result.alreadyRegistered ? "Already waitlisted" : "Added to waitlist",
16515
- message: result.alreadyRegistered ? "You are already on the waitlist for this event." : "You've been added to the waitlist."
16516
- });
16517
- options.setStep("success");
16518
- return {
16519
- ok: true,
16520
- outcome: { kind: "completed" }
16521
- };
16522
- }
16523
- if (result.bypassed) return completeBypassedCheckout(options);
16524
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
16525
- savePendingCheckout(options, deps, prepared, result.checkoutAttemptId, {
16526
- scope: "wholeSeries",
16527
- registrationId: result.registrationId
16528
- });
16529
- return redirectToCheckout(result.checkoutUrl, submitOptions);
16530
- }
16531
- function needsOccurrenceCheckout(result, method) {
16532
- return result.scope === "occurrence" && result.status === "confirmed" && result.pendingPayment && [
16533
- "stripe",
16534
- "pass_bundle",
16535
- "membership_bundle"
16536
- ].includes(method);
16537
- }
16538
- async function submitOccurrenceCheckout(options, deps, prepared, registration, method, submitOptions) {
16539
- const reference = {
16540
- scope: "occurrence",
16541
- attendeeId: registration.attendeeId
16542
- };
16543
- options.setPendingRegistrationReference(reference);
16544
- options.setStep("redirecting");
16545
- const checkoutAttemptId = deps.createClientCommandId();
16546
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
16547
- const result = await options.api.createEventCheckout({
16548
- subject: {
16549
- kind: "occurrence",
16550
- attendeeId: registration.attendeeId
16551
- },
16552
- registrationCommandId: prepared.registrationCommandId,
16553
- checkoutType: method === "pass_bundle" ? "pass_bundle" : method === "membership_bundle" ? "membership_bundle" : "event",
16554
- productId: prepared.bundleProductId,
16555
- discountCode: options.discountCode.trim() || void 0,
16556
- collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0,
16557
- ...urls
16558
- });
16559
- if (result.scope !== "occurrence") throw new Error("Unexpected registration scope for event checkout.");
16560
- if (result.bypassed) return completeBypassedCheckout(options);
16561
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
16562
- if (prepared.subject.kind === "occurrence") savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference);
16563
- return redirectToCheckout(result.checkoutUrl, submitOptions);
16564
- }
16565
16386
  function savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference) {
16566
16387
  const siteId = options.api.config.siteId;
16567
16388
  if (!siteId) return;
@@ -16574,14 +16395,6 @@ function savePendingCheckout(options, deps, prepared, checkoutAttemptId, referen
16574
16395
  paymentSelection: prepared.selectionSnapshot
16575
16396
  });
16576
16397
  }
16577
- function completeBypassedCheckout(options) {
16578
- options.setRegistrationResult({ status: "confirmed" });
16579
- options.setStep("success");
16580
- return {
16581
- ok: true,
16582
- outcome: { kind: "completed" }
16583
- };
16584
- }
16585
16398
  function redirectToCheckout(checkoutUrl, submitOptions) {
16586
16399
  if (submitOptions?.redirect !== "manual") window.location.href = checkoutUrl;
16587
16400
  return {
@@ -16593,12 +16406,23 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
16593
16406
  };
16594
16407
  }
16595
16408
  function registrationRequestIntentKey(request) {
16596
- const { registrationCommandId: _command, altchaSolution: _proof, ...intent } = request;
16597
- 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
+ });
16598
16417
  }
16599
- function buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId) {
16418
+ function buildRegistrationRequest(options, prepared, method, passId, membershipId) {
16600
16419
  return {
16601
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
+ },
16602
16426
  subject: prepared.subject,
16603
16427
  ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
16604
16428
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
@@ -16618,64 +16442,6 @@ function buildOccurrenceRegistrationRequest(options, prepared, method, passId, m
16618
16442
  };
16619
16443
  }
16620
16444
  //#endregion
16621
- //#region ../blocks/src/system/runtime/nodes/event-registration/registrationDerivations.ts
16622
- function eventMatchesOccurrenceId(event, occurrenceId) {
16623
- return event.registrationSubject.kind === "occurrence" && (event.registrationSubject.occurrenceId === occurrenceId || event.selectionId === occurrenceId);
16624
- }
16625
- function findEventByOccurrenceId(events, occurrenceId) {
16626
- return events.find((event) => eventMatchesOccurrenceId(event, occurrenceId)) ?? null;
16627
- }
16628
- function findPreSelectedEvent(input) {
16629
- const occurrenceContext = input.occurrenceContext;
16630
- if (!occurrenceContext) return input.scopedEvents.length === 1 ? input.scopedEvents[0] ?? null : null;
16631
- const contextDate = getBookingLocalDateKey(toScheduledBookingDateTime({
16632
- startsAt: occurrenceContext.startsAt,
16633
- timeZone: occurrenceContext.timeZone
16634
- }));
16635
- return input.scopedEvents.find((event) => eventMatchesOccurrenceId(event, occurrenceContext.id) || getBookingLocalDateKey(toScheduledBookingDateTime({
16636
- startsAt: event.startsAt,
16637
- timeZone: event.timeZone
16638
- })) === contextDate) ?? null;
16639
- }
16640
- function resolveRegistrationTarget(input) {
16641
- const subject = input.occurrenceContext ? {
16642
- kind: "occurrence",
16643
- occurrenceId: input.occurrenceContext.id
16644
- } : input.selectedEvent?.registrationSubject ?? null;
16645
- if (!subject) return null;
16646
- return {
16647
- subject,
16648
- selectedEventId: input.selectedEvent?.selectionId ?? null
16649
- };
16650
- }
16651
- function resolveEventRequiresPayment(input) {
16652
- if (input.eligibility) return input.eligibility.requiresPayment;
16653
- if (input.selectedTicketType) return input.selectedTicketType.priceAmount > 0;
16654
- if (!input.event) return false;
16655
- return publicEventPricingType(input.event.pricing) === "paid";
16656
- }
16657
- function resolveDiscountContext(input) {
16658
- const selection = input.paymentSelection.method ? input.paymentSelection : null;
16659
- if (!input.event) return null;
16660
- if (selection?.method === "pass_bundle" && selection.bundlePassId) return {
16661
- type: "pass",
16662
- passId: selection.bundlePassId
16663
- };
16664
- if (selection?.method === "membership_bundle" && selection.bundleMembershipId) return {
16665
- type: "membership",
16666
- membershipId: selection.bundleMembershipId
16667
- };
16668
- if (!input.eventRequiresPayment) return null;
16669
- if (input.ticketsCount <= 0) return null;
16670
- if (selection?.method && selection.method !== "stripe") return null;
16671
- return {
16672
- type: "event",
16673
- eventSeriesId: input.event.seriesId,
16674
- eventCategoryId: input.event.eventCategory?.id ?? null,
16675
- ticketsCount: input.ticketsCount
16676
- };
16677
- }
16678
- //#endregion
16679
16445
  //#region ../blocks/src/system/runtime/nodes/event-registration/useSettledCommerceView.ts
16680
16446
  function buildCandidate(input) {
16681
16447
  if (!input.eligibilityIsSettled || !input.eligibility || !input.currentEvent) return null;
@@ -16896,7 +16662,7 @@ function computeAutoSelection(input) {
16896
16662
  return { method: null };
16897
16663
  }
16898
16664
  function computeDirectPaymentSelection(eligibility) {
16899
- if (!eligibility?.canPayDirectly) return { method: null };
16665
+ if (!eligibility?.requiresPayment) return { method: null };
16900
16666
  const approvedKinds = getApprovedDirectPaymentCollectionOptionKinds(eligibility.paymentOptions);
16901
16667
  const collectionOptionKind = approvedKinds.includes("upfront") ? "upfront" : approvedKinds[0];
16902
16668
  return collectionOptionKind ? {
@@ -16910,7 +16676,7 @@ function isSelectionValid(input) {
16910
16676
  if (!eligibility) return selection.method === "stripe" || selection.method === "pass_bundle" || selection.method === "membership_bundle";
16911
16677
  switch (selection.method) {
16912
16678
  case "free": return eligibility.isFree;
16913
- case "stripe": return Boolean(eligibility.canPayDirectly && eligibility.price);
16679
+ case "stripe": return Boolean(eligibility.requiresPayment && eligibility.price);
16914
16680
  case "pass": return isAuthenticated && ticketsCount === 1 && !!selection.passId && eligibility.eligiblePasses.some((pass) => pass.id === selection.passId);
16915
16681
  case "membership": return isAuthenticated && ticketsCount === 1 && !!selection.membershipId && eligibility.eligibleMemberships.some((membership) => membership.id === selection.membershipId);
16916
16682
  case "pass_bundle": return ticketsCount === 1 && !!selection.bundlePassId && eligibility.availablePasses.some((pass) => pass.id === selection.bundlePassId);
@@ -16929,7 +16695,7 @@ function normalizeRegistrationFlowState(state) {
16929
16695
  ticketsCount: state.ticketsCount,
16930
16696
  passOptOut: nextPassOptOut
16931
16697
  });
16932
- const canKeepManualSelection = state.step === "select-payment" && state.selectionSource === "manual" && isSelectionValid({
16698
+ const canKeepManualSelection = state.step !== "select-tickets" && state.selectionSource === "manual" && isSelectionValid({
16933
16699
  selection: state.paymentSelection,
16934
16700
  isAuthenticated: state.isAuthenticated,
16935
16701
  eligibility: state.eligibility,
@@ -16995,6 +16761,24 @@ function requiresPaymentStep(params) {
16995
16761
  if (!params.eventRequiresPayment) return false;
16996
16762
  return !isEntitlementSelection(params.paymentSelection);
16997
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
+ }
16998
16782
  const unsupportedEventRegistrationPageSurfaceSubjectSchema = z.object({
16999
16783
  kind: z.literal("unsupported"),
17000
16784
  reason: z.enum([
@@ -17237,7 +17021,7 @@ function resolveEventRegistrationSubmitPlan(input) {
17237
17021
  const selection = input.paymentSelection.method ? input.paymentSelection : void 0;
17238
17022
  if (input.canJoinWaitlist) return {
17239
17023
  ok: true,
17240
- args: withOptionalSelection({ method: "free" }, selection)
17024
+ args: { method: "free" }
17241
17025
  };
17242
17026
  switch (input.paymentSelection.method) {
17243
17027
  case "pass": return input.paymentSelection.passId ? {
@@ -17288,6 +17072,10 @@ function resolveHeadlessEventRegistrationStatus(wizard) {
17288
17072
  case "redirecting": return { tag: "redirecting" };
17289
17073
  case "verifying-payment": return { tag: "verifying-payment" };
17290
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" };
17291
17079
  case "payment-failed": return {
17292
17080
  tag: "payment-failed",
17293
17081
  message: wizard.errorMessage
@@ -17334,6 +17122,10 @@ function resolveHeadlessEventRegistrationView(wizard) {
17334
17122
  kind: "verifying",
17335
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."
17336
17124
  };
17125
+ case "payment-status": return wizard.paymentStatusRecovery ? {
17126
+ kind: "payment-status",
17127
+ state: wizard.paymentStatusRecovery
17128
+ } : { kind: "form" };
17337
17129
  case "select-event":
17338
17130
  case "select-tickets":
17339
17131
  case "enter-details":
@@ -17600,20 +17392,124 @@ function useWaitlistClaimRestoration(input) {
17600
17392
  }
17601
17393
  //#endregion
17602
17394
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationCompletion.ts
17395
+ function isSelectionSoldOut(availability) {
17396
+ return availability?.kind === "unavailable" && availability.reason === "full";
17397
+ }
17603
17398
  function resolveRegistrationAvailabilityBlocker(input) {
17604
17399
  if (input.hasWaitlistClaim) return null;
17605
17400
  if (input.eligibilityError) return input.eligibilityError;
17606
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;
17607
17404
  if (input.eligibility.availability.kind === "unavailable" && input.eligibility.availability.reason === "paymentUnavailable") return "Payment options are not available for this event.";
17608
- 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.";
17609
17406
  return null;
17610
17407
  }
17611
17408
  function resolvePricingSelectionBlocker(input) {
17612
17409
  if (input.selectionRequired && !input.hasValidSelection) return "Choose at least one available ticket to continue.";
17613
17410
  return input.slidingScaleSelectionMessage;
17614
17411
  }
17615
- function isSoldOutWithoutWaitlist(input) {
17616
- 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
+ };
17617
17513
  }
17618
17514
  //#endregion
17619
17515
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
@@ -17671,6 +17567,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17671
17567
  const [errorAction, setErrorAction] = useState("retry");
17672
17568
  const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
17673
17569
  const [altchaSolution, setAltchaSolution] = useState(null);
17570
+ const [checkingNextStep, setCheckingNextStep] = useState(false);
17674
17571
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
17675
17572
  const waitlistClaim = deps.useWaitlistClaim();
17676
17573
  const [showMagicLinkModal, setShowMagicLinkModal] = useState(false);
@@ -17697,28 +17594,29 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17697
17594
  }), [options.occurrenceContext, selectedEvent]);
17698
17595
  const registrationOccurrenceId = getRegistrationOccurrenceId(registrationTarget);
17699
17596
  const currentEvent = selectedEvent ?? preSelectedEvent;
17597
+ const commandRef = useRef(null);
17700
17598
  const setStep = useCallback((nextStep) => {
17701
17599
  dispatchFlow({
17702
17600
  type: "set-step",
17703
17601
  step: nextStep
17704
17602
  });
17705
17603
  }, []);
17706
- const { checkoutRecovery, startCheckoutRecovery } = useCheckoutRecovery(currentEvent, setStep);
17707
- const currentEventUsesTicketTypes = usesTieredPricing(currentEvent);
17708
- const currentCanJoinWaitlist = canJoinWaitlistOutsideRecovery(currentEvent, selectedAttendanceMode, checkoutRecovery);
17709
- 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);
17710
17615
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
17711
17616
  event: currentEvent,
17712
17617
  eligibility: null,
17713
17618
  selectedAttendanceMode
17714
- }).map((option) => checkoutRecovery ? {
17715
- ...option,
17716
- availability: checkoutRecoveryOptionAvailability(option.availability)
17717
- } : option), [
17718
- currentEvent,
17719
- selectedAttendanceMode,
17720
- checkoutRecovery
17721
- ]);
17619
+ }), [currentEvent, selectedAttendanceMode]);
17722
17620
  const normalizedSelectedTicketLines = useMemo(() => normalizeTicketSelectionLines(selectedTicketLines), [selectedTicketLines]);
17723
17621
  const ticketTypeSelectionTotal = useMemo(() => getTicketSelectionTotal(normalizedSelectedTicketLines), [normalizedSelectedTicketLines]);
17724
17622
  const ticketsCount = usesTicketTypeSelections ? ticketTypeSelectionTotal : flowState.ticketsCount;
@@ -17756,7 +17654,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17756
17654
  isAuthenticated: portalAuthState === "authenticated",
17757
17655
  occurrenceId: registrationOccurrenceId
17758
17656
  });
17759
- const eligibility = useCheckoutRecoveryEligibility(eligibilityResult.eligibility, step, checkoutRecovery, startCheckoutRecovery);
17657
+ const eligibility = eligibilityResult.eligibility;
17760
17658
  const eligibilityLoading = eligibilityResult.isLoading;
17761
17659
  const eligibilityError = eligibilityResult.error;
17762
17660
  const portalCreditState = eligibilityResult.creditState;
@@ -17868,14 +17766,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17868
17766
  event: currentEvent,
17869
17767
  eligibility,
17870
17768
  selectedAttendanceMode
17871
- }).map((option) => checkoutRecovery ? {
17872
- ...option,
17873
- availability: checkoutRecoveryOptionAvailability(option.availability)
17874
- } : option), [
17769
+ }), [
17875
17770
  currentEvent,
17876
17771
  selectedAttendanceMode,
17877
- eligibility,
17878
- checkoutRecovery
17772
+ eligibility
17879
17773
  ]);
17880
17774
  const singleSelectedPricingOptionId = getSingleSelectedPricingOptionId(normalizedSelectedTicketLines);
17881
17775
  const selectedTicketType = useMemo(() => findSelectedTicketType({
@@ -17899,12 +17793,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17899
17793
  const event = currentEvent;
17900
17794
  if (!usesTieredPricing(event)) return false;
17901
17795
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
17902
- return checkoutRecovery || !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
17903
- }, [
17904
- currentEvent,
17905
- selectedAttendanceMode,
17906
- checkoutRecovery
17907
- ]);
17796
+ return true;
17797
+ }, [currentEvent, selectedAttendanceMode]);
17908
17798
  const hasValidTicketSelection = hasValidPricingOptionSelection({
17909
17799
  selectionRequired: ticketTypeSelectionRequired,
17910
17800
  lines: normalizedSelectedTicketLines,
@@ -17915,8 +17805,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17915
17805
  if (selectedTicketLines.length > 0) setSelectedTicketLines([]);
17916
17806
  return;
17917
17807
  }
17918
- const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => ticketType.availability.kind === "available");
17919
- const implicitTicketType = getImplicitSingleAvailableTicketType(eventTicketTypesForSelection);
17808
+ const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => canRequestTicketOption(ticketType.availability));
17809
+ const implicitTicketType = getImplicitSingleTicketType(eventTicketTypesForSelection);
17920
17810
  if (implicitTicketType && normalizedSelectedTicketLines.length === 0) {
17921
17811
  setSelectedTicketLines([{
17922
17812
  pricingOptionId: implicitTicketType.pricingOptionId,
@@ -17983,6 +17873,26 @@ function useEventRegistrationWizardWithDeps(options, deps) {
17983
17873
  selectedEventSlidingScale,
17984
17874
  selectedSlidingAmount
17985
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
+ };
17986
17896
  const handleDetailsComplete = async () => {
17987
17897
  const blockDetailsCompletion = (message) => {
17988
17898
  setErrorAction("places");
@@ -18014,11 +17924,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18014
17924
  message: "Check the highlighted fields and try again."
18015
17925
  };
18016
17926
  }
18017
- if (isSoldOutWithoutWaitlist({
18018
- hasWaitlistClaim: waitlistClaim.claim !== null,
18019
- eligibility
18020
- })) return blockDetailsCompletion("Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.");
18021
- 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({
18022
17931
  eventRequiresPayment,
18023
17932
  paymentSelection
18024
17933
  })) {
@@ -18031,7 +17940,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18031
17940
  const submitPlan = resolveEventRegistrationSubmitPlan({
18032
17941
  paymentSelection,
18033
17942
  eventRequiresPayment,
18034
- canJoinWaitlist: eligibility?.canJoinWaitlist
17943
+ canJoinWaitlist
18035
17944
  });
18036
17945
  if (!submitPlan.ok) return blockDetailsCompletion(submitPlan.message);
18037
17946
  const submitResult = await handleSubmit(submitPlan.args.method, submitPlan.args.passId, submitPlan.args.membershipId, submitPlan.args.selection);
@@ -18049,7 +17958,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18049
17958
  const submitPlan = resolveEventRegistrationSubmitPlan({
18050
17959
  paymentSelection,
18051
17960
  eventRequiresPayment,
18052
- canJoinWaitlist: eligibility?.canJoinWaitlist
17961
+ canJoinWaitlist: false
18053
17962
  });
18054
17963
  if (!submitPlan.ok) {
18055
17964
  setErrorMessage(submitPlan.message);
@@ -18081,6 +17990,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18081
17990
  const selectionMethod = paymentSelection.method;
18082
17991
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
18083
17992
  const handleSubmit = deps.useEventRegistrationSubmit({
17993
+ commandRef,
18084
17994
  setErrorAction,
18085
17995
  api,
18086
17996
  registrationTarget,
@@ -18097,7 +18007,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18097
18007
  setPendingRegistrationReference,
18098
18008
  setStep,
18099
18009
  setRegistrationResult: (result) => setRegistrationResult(result),
18100
- setErrorMessage
18010
+ setErrorMessage,
18011
+ setPaymentStatusRecovery
18101
18012
  });
18102
18013
  const handleEventSelect = (event) => {
18103
18014
  setSelectedEvent(event);
@@ -18156,8 +18067,6 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18156
18067
  scopedEvents,
18157
18068
  preSelectedEvent,
18158
18069
  hasPreSelectedContext,
18159
- checkoutRecovery,
18160
- startCheckoutRecovery,
18161
18070
  step,
18162
18071
  setStep,
18163
18072
  selectedEvent,
@@ -18211,7 +18120,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18211
18120
  waitlistClaim,
18212
18121
  eligibility,
18213
18122
  registrationOptionsState: eligibilityResult.state,
18214
- eligibilityLoading,
18123
+ eligibilityLoading: eligibilityLoading || checkingNextStep,
18215
18124
  eligibilityError,
18216
18125
  existingRegistration: existingRegistrationResult.registration,
18217
18126
  existingRegistrationLoading: existingRegistrationResult.isLoading || portalAuthState === "authenticated" && eligibilityLoading,
@@ -18231,6 +18140,11 @@ function useEventRegistrationWizardWithDeps(options, deps) {
18231
18140
  discountContext,
18232
18141
  shouldShowDiscountToggle,
18233
18142
  verification,
18143
+ paymentStatusRecovery,
18144
+ handleCheckPaymentStatus,
18145
+ handleContinueExistingPayment,
18146
+ handleReturnToPayment,
18147
+ handleReturnToBooking,
18234
18148
  handleDetailsComplete,
18235
18149
  handlePaymentSelect,
18236
18150
  handleUseCardInsteadOfPass,
@@ -18283,7 +18197,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18283
18197
  selection: wizard.paymentSelection
18284
18198
  }),
18285
18199
  eventRequiresPayment: wizard.eventRequiresPayment,
18286
- canJoinWaitlist: wizard.eligibility?.canJoinWaitlist
18200
+ canJoinWaitlist: wizard.step !== "select-payment" && wizard.eligibility?.availability.kind === "waitlist" && wizard.eligibility.registration.kind !== "pending_payment"
18287
18201
  });
18288
18202
  if (!plan.ok) return {
18289
18203
  ok: false,
@@ -18328,6 +18242,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18328
18242
  case "redirecting":
18329
18243
  case "verifying-payment":
18330
18244
  case "verifying-timeout":
18245
+ case "payment-status":
18331
18246
  case "payment-failed": return {
18332
18247
  ok: false,
18333
18248
  reason: "blocked",
@@ -18335,27 +18250,7 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18335
18250
  };
18336
18251
  }
18337
18252
  }, [submit, wizard]);
18338
- const back = useCallback(() => {
18339
- switch (wizard.step) {
18340
- case "enter-details":
18341
- wizard.setStep("select-tickets");
18342
- return;
18343
- case "select-payment":
18344
- wizard.setStep("enter-details");
18345
- return;
18346
- case "select-tickets":
18347
- if (!wizard.hasPreSelectedContext) wizard.setStep("select-event");
18348
- return;
18349
- case "select-event":
18350
- case "error":
18351
- case "success":
18352
- case "submitting":
18353
- case "redirecting":
18354
- case "verifying-payment":
18355
- case "verifying-timeout":
18356
- case "payment-failed": return;
18357
- }
18358
- }, [wizard]);
18253
+ const back = useEventRegistrationBack(wizard);
18359
18254
  const updateAttendee = useCallback((values) => {
18360
18255
  wizard.setFormData(values);
18361
18256
  }, [wizard]);
@@ -18442,7 +18337,8 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18442
18337
  terminal: {
18443
18338
  result: wizard.registrationResult,
18444
18339
  errorMessage: wizard.errorMessage,
18445
- verification: wizard.verification
18340
+ verification: wizard.verification,
18341
+ paymentStatusRecovery: wizard.paymentStatusRecovery
18446
18342
  },
18447
18343
  actions: {
18448
18344
  selectEvent: wizard.handleEventSelect,
@@ -18466,12 +18362,40 @@ function useEventRegistrationControllerWithDeps(options, deps) {
18466
18362
  next,
18467
18363
  back,
18468
18364
  submit,
18469
- retry: wizard.handleRetry
18365
+ retry: wizard.handleRetry,
18366
+ checkPaymentStatus: wizard.handleCheckPaymentStatus,
18367
+ continueExistingPayment: wizard.handleContinueExistingPayment,
18368
+ returnToPayment: wizard.handleReturnToPayment,
18369
+ returnToBooking: wizard.handleReturnToBooking
18470
18370
  }
18471
18371
  },
18472
18372
  wizard
18473
18373
  };
18474
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
+ }
18475
18399
  function useEventRegistrationWizardControllerWithDeps(options, deps) {
18476
18400
  const maxTicketsNum = normalizeEventRegistrationMaxTickets(options.maxTickets);
18477
18401
  return { wizard: deps.useEventRegistrationWizard({
@@ -18527,7 +18451,7 @@ function resolvePaymentModel(wizard, directPaymentOptions) {
18527
18451
  }
18528
18452
  function resolveHeadlessDirectPaymentOptions(wizard) {
18529
18453
  const eligibility = wizard.eligibility;
18530
- if (!eligibility?.eligible || !eligibility.canPayDirectly || !eligibility.price) return [];
18454
+ if (!eligibility?.requiresPayment || !eligibility.price) return [];
18531
18455
  const event = wizard.selectedEvent ?? wizard.preSelectedEvent;
18532
18456
  return buildEventRegistrationPaymentOptionsViewModel({
18533
18457
  eventTitle: event?.title ?? "Event",