@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
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement-CnJeq3_r.mjs";
3
- import { C as resolveRegistrationTarget, E as getWizardStepsForContext, Gt as formatBookingDate, Pt as assertNever, S as resolveEventRequiresPayment, T as getStepNumber, Yt as formatBookingTime, _ as toRegistrationEvents, b as findPreSelectedEvent, dn as publicEventPricingType, dt as themeButtonClassName, fn as formatCurrencyAmount, g as getRegistrationStartAt, h as buildEventRegistrationDisplayFromScopedEvents, k as resolveEventRegistrationScopedEvents, ln as publicEventPricingOptions, ot as groupPublicEventsByRun, pn as parseCurrencyCode, qt as formatBookingDateTime, rn as toScheduledBookingDateTime, sn as publicEventPricingCurrency, t as defineIslandPropsDecoder, tn as toBookingTimeZone, un as publicEventPricingStrategy, v as toWholeSeriesRegistrationEvents, w as WIZARD_STEPS, x as resolveDiscountContext, y as findEventByOccurrenceId } from "./propsDecoder-DiLiBdek.mjs";
4
- import { a as canJoinEventWaitlistOnAnyMode, c as formatTicketTypeRemaining, f as getRemainingPlaces, h as selectMostOpenCapacitySource, i as canJoinEventWaitlist, m as selectEventCapacitySource, o as formatEventRemainingPlaces, p as isEventFull, r as formatEventPriceDisplay, s as formatRemainingPlaces, u as getEventAvailability } from "./pricing-CbTlAsmt.mjs";
5
- import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-Ces-AgdR.mjs";
2
+ import { a as buildBookingPaymentSelectionViewModel, i as usePolling, n as StepIndicator$1, o as resolveBookingPaymentDueNowCents, r as SpinnerNode, s as formatPrice, t as BookingConsentAgreement } from "./BookingConsentAgreement-BoCtvzzu.mjs";
3
+ import { A as resolveEventRegistrationScopedEvents, C as resolveRegistrationTarget, D as getWizardStepsForContext, E as getStepNumber, Ft as assertNever, Jt as formatBookingDateTime, Kt as formatBookingDate, S as resolveEventRequiresPayment, T as WIZARD_STEPS, Xt as formatBookingTime, _ as toRegistrationEvents, b as findPreSelectedEvent, cn as publicEventPricingCurrency, dn as publicEventPricingStrategy, fn as publicEventPricingType, ft as themeButtonClassName, g as getRegistrationStartAt, h as buildEventRegistrationDisplayFromScopedEvents, in as toScheduledBookingDateTime, mn as parseCurrencyCode, nn as toBookingTimeZone, pn as formatCurrencyAmount, st as groupPublicEventsByRun, t as defineIslandPropsDecoder, un as publicEventPricingOptions, v as toWholeSeriesRegistrationEvents, w as toRegistrationRequestSubject, x as resolveDiscountContext, y as findEventByOccurrenceId } from "./propsDecoder-CIZ-hbIG.mjs";
4
+ import { a as canJoinEventWaitlistOnAnyMode, c as formatTicketTypeRemaining, f as getRemainingPlaces, h as selectMostOpenCapacitySource, i as canJoinEventWaitlist, m as selectEventCapacitySource, o as formatEventRemainingPlaces, p as isEventFull, r as formatEventPriceDisplay, s as formatRemainingPlaces, u as getEventAvailability } from "./pricing-ChinRYFt.mjs";
5
+ import { n as useBlockApi, r as BlockApiError, t as BlockApiProvider } from "./context-BOimxE3U.mjs";
6
6
  import { n as isSpamProtectionEnabled, r as AltchaWidget, t as getChallengeUrl } from "./src-B833rYRr.mjs";
7
- import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-CrcbBf0h.mjs";
8
- import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-BINEO6J9.mjs";
7
+ import { a as CheckIcon, i as StateIcon, n as useDiscountCodeUi, r as TerminalPanel, t as scrollToBlock } from "./scrollToBlock-BPZl0gPZ.mjs";
8
+ import { t as DiscountCodeDisclosure } from "./DiscountCodeDisclosure-B5S30nZk.mjs";
9
9
  import { t as FeedbackMessage } from "./FeedbackMessage-CtLvvS_d.mjs";
10
10
  import { n as getFieldValidationControlProps, t as FieldValidationMessage } from "./FieldValidationMessage-AYanWFb-.mjs";
11
11
  import { t as buildCheckoutReturnUrls } from "./checkoutUrls-FL6lRkrt.mjs";
@@ -276,7 +276,7 @@ function resolveEventRegistrationDirectPaymentSelection(input) {
276
276
  //#endregion
277
277
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationStateMachine.ts
278
278
  function hasBlockingExistingRegistration(input) {
279
- return !input.hasWaitlistClaim && input.isAuthenticated && input.registration.kind !== "none";
279
+ return !input.hasWaitlistClaim && input.isAuthenticated && input.registration.kind === "waitlisted";
280
280
  }
281
281
  function createInitialRegistrationFlowState(hasPreSelectedContext) {
282
282
  return {
@@ -328,7 +328,7 @@ function computeAutoSelection(input) {
328
328
  return { method: null };
329
329
  }
330
330
  function computeDirectPaymentSelection(eligibility) {
331
- if (!eligibility?.canPayDirectly) return { method: null };
331
+ if (!eligibility?.requiresPayment) return { method: null };
332
332
  const approvedKinds = getApprovedDirectPaymentCollectionOptionKinds(eligibility.paymentOptions);
333
333
  const collectionOptionKind = approvedKinds.includes("upfront") ? "upfront" : approvedKinds[0];
334
334
  return collectionOptionKind ? {
@@ -342,7 +342,7 @@ function isSelectionValid(input) {
342
342
  if (!eligibility) return selection.method === "stripe" || selection.method === "pass_bundle" || selection.method === "membership_bundle";
343
343
  switch (selection.method) {
344
344
  case "free": return eligibility.isFree;
345
- case "stripe": return Boolean(eligibility.canPayDirectly && eligibility.price);
345
+ case "stripe": return Boolean(eligibility.requiresPayment && eligibility.price);
346
346
  case "pass": return isAuthenticated && ticketsCount === 1 && !!selection.passId && eligibility.eligiblePasses.some((pass) => pass.id === selection.passId);
347
347
  case "membership": return isAuthenticated && ticketsCount === 1 && !!selection.membershipId && eligibility.eligibleMemberships.some((membership) => membership.id === selection.membershipId);
348
348
  case "pass_bundle": return ticketsCount === 1 && !!selection.bundlePassId && eligibility.availablePasses.some((pass) => pass.id === selection.bundlePassId);
@@ -361,7 +361,7 @@ function normalizeRegistrationFlowState(state) {
361
361
  ticketsCount: state.ticketsCount,
362
362
  passOptOut: nextPassOptOut
363
363
  });
364
- const canKeepManualSelection = state.step === "select-payment" && state.selectionSource === "manual" && isSelectionValid({
364
+ const canKeepManualSelection = state.step !== "select-tickets" && state.selectionSource === "manual" && isSelectionValid({
365
365
  selection: state.paymentSelection,
366
366
  isAuthenticated: state.isAuthenticated,
367
367
  eligibility: state.eligibility,
@@ -444,7 +444,7 @@ function resolveEventRegistrationSubmitPlan(input) {
444
444
  const selection = input.paymentSelection.method ? input.paymentSelection : void 0;
445
445
  if (input.canJoinWaitlist) return {
446
446
  ok: true,
447
- args: withOptionalSelection({ method: "free" }, selection)
447
+ args: { method: "free" }
448
448
  };
449
449
  switch (input.paymentSelection.method) {
450
450
  case "pass": return input.paymentSelection.passId ? {
@@ -503,88 +503,53 @@ function missingPaymentSelection() {
503
503
  };
504
504
  }
505
505
  //#endregion
506
- //#region ../blocks/src/system/runtime/nodes/event-registration/checkoutRecovery.ts
507
- /** Recovery may select held places; checkout still owns admission and payment validation. */
508
- function checkoutRecoveryOptionAvailability(availability) {
509
- if (availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable") return {
510
- kind: "available",
511
- remainingPlaces: null
512
- };
513
- return availability;
506
+ //#region ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
507
+ /** Ticket selection requests a quote; only the server admits a booking. */
508
+ function canRequestTicketOption(availability) {
509
+ return availability.kind === "available" || availability.reason === "sold_out" || availability.reason === "overallCapacityUnavailable";
514
510
  }
515
- /**
516
- * A form-only projection for the explicit existing-checkout path. It permits
517
- * collecting the original selection without claiming a new place is available.
518
- * The public checkout service identifies the customer and remains the admission
519
- * authority; no reservation/customer data is exposed through this projection.
520
- */
521
- function getCheckoutRecoveryFormEligibility(eligibility) {
522
- 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;
523
- const amountCents = eligibility.price ?? 0;
524
- if (amountCents <= 0 || !eligibility.currency || eligibility.availability.kind === "unavailable" && eligibility.availability.reason === "selectionRequired") return eligibility.registration.kind === "pending_payment" ? {
525
- ...eligibility,
526
- registration: { kind: "none" },
527
- ticketTypes: eligibility.ticketTypes.map((option) => ({
528
- ...option,
529
- availability: checkoutRecoveryOptionAvailability(option.availability)
530
- }))
531
- } : eligibility;
532
- const currency = eligibility.currency;
533
- const paymentOptions = eligibility.paymentTerms.allowedOptions.map((option) => {
534
- switch (option.kind) {
535
- case "upfront": return {
536
- kind: "payInFull",
537
- amountCents,
538
- currency
539
- };
540
- case "deferred_manual": return {
541
- kind: "deferredManual",
542
- amountCents,
543
- currency
544
- };
545
- case "instalment_plan": return {
546
- kind: "instalmentPlan",
547
- amountCents,
548
- currency
549
- };
550
- case "flexible_balance": return {
551
- kind: "flexibleBalance",
552
- amountCents,
553
- currency
554
- };
555
- }
556
- });
557
- return {
558
- ...eligibility,
559
- registration: { kind: "none" },
560
- availability: {
561
- kind: "available",
562
- remainingPlaces: null,
563
- waitlistEnabled: eligibility.waitlistEnabled
564
- },
565
- eligible: paymentOptions.length > 0,
566
- isFull: false,
567
- canJoinWaitlist: false,
568
- isFree: false,
569
- requiresPayment: true,
570
- canPayDirectly: paymentOptions.length > 0,
571
- paymentOptions,
572
- eligiblePasses: [],
573
- eligibleMemberships: [],
574
- ticketTypes: eligibility.ticketTypes.map((option) => ({
575
- ...option,
576
- availability: checkoutRecoveryOptionAvailability(option.availability)
577
- }))
578
- };
511
+ function normalizeTicketSelectionLines(lines) {
512
+ const quantitiesByTicketType = /* @__PURE__ */ new Map();
513
+ for (const line of lines) {
514
+ const quantity = Math.floor(line.quantity);
515
+ if (quantity <= 0) continue;
516
+ const key = line.pricingOptionId ?? null;
517
+ quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
518
+ }
519
+ return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
520
+ pricingOptionId,
521
+ quantity
522
+ }));
579
523
  }
580
- function isSoldOutEventSelectable(event, waitlistAvailable) {
581
- return waitlistAvailable || canRecoverEventCheckout(event);
524
+ function getTicketSelectionTotal(lines) {
525
+ return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
582
526
  }
583
- function canJoinWaitlistOutsideRecovery(event, attendanceMode, checkoutRecovery) {
584
- return !checkoutRecovery && event !== null && canJoinEventWaitlist(event, { attendanceMode });
527
+ function getTicketLineQuantity(lines, pricingOptionId) {
528
+ return normalizeTicketSelectionLines(lines).find((line) => line.pricingOptionId === pricingOptionId)?.quantity ?? 0;
529
+ }
530
+ function getSingleSelectedPricingOptionId(lines) {
531
+ const normalized = normalizeTicketSelectionLines(lines);
532
+ return normalized.length === 1 ? normalized[0]?.pricingOptionId ?? null : null;
585
533
  }
586
- function canRecoverEventCheckout(event) {
587
- return event !== null && (event.registrationSubject.kind === "wholeSeries" || event.pricing.kind !== "free" && event.pricing.kind !== "external");
534
+ function hasValidPricingOptionSelection(input) {
535
+ if (!input.selectionRequired) return true;
536
+ if (input.lines.length === 0) return false;
537
+ const availableOptionIds = new Set(input.visibleOptions.filter((option) => canRequestTicketOption(option.availability)).map((option) => option.pricingOptionId));
538
+ return input.lines.every((line) => line.pricingOptionId !== null && availableOptionIds.has(line.pricingOptionId));
539
+ }
540
+ function setTicketLineQuantity(input) {
541
+ const nextQuantity = Math.max(0, Math.floor(input.quantity));
542
+ const normalized = normalizeTicketSelectionLines(input.lines).filter((line) => line.pricingOptionId !== input.pricingOptionId);
543
+ if (nextQuantity <= 0) return normalized;
544
+ return [...normalized, {
545
+ pricingOptionId: input.pricingOptionId,
546
+ quantity: nextQuantity
547
+ }];
548
+ }
549
+ function getTicketTypeLineMax(input) {
550
+ const otherSelectedQuantity = input.totalSelectedQuantity - input.currentQuantity;
551
+ const maxByBlock = input.maxTickets - otherSelectedQuantity;
552
+ return Math.max(0, maxByBlock);
588
553
  }
589
554
  //#endregion
590
555
  //#region ../blocks/src/system/runtime/nodes/event-registration/utils.ts
@@ -651,8 +616,6 @@ function formatPassCreditUsageLabel(creditsRemaining) {
651
616
  return `Uses 1 of ${creditsRemaining} credits`;
652
617
  }
653
618
  function transformRegistrationOptionsResponse(response) {
654
- const canJoinWaitlist = response.availability.kind === "waitlist";
655
- const isFull = response.availability.remainingPlaces === 0 || response.availability.kind === "unavailable" && response.availability.reason === "full";
656
619
  const eligiblePasses = response.entitlements.passes.map((pass) => ({
657
620
  id: pass.customerPassId,
658
621
  passName: pass.passName,
@@ -665,7 +628,6 @@ function transformRegistrationOptionsResponse(response) {
665
628
  const eventAcceptsPasses = response.entitlements.requiresLoginForPasses || eligiblePasses.length > 0 || response.entitlements.availableForPurchase.passes.length > 0;
666
629
  const eventAcceptsMemberships = response.entitlements.requiresLoginForMemberships || eligibleMemberships.length > 0 || response.entitlements.availableForPurchase.memberships.length > 0;
667
630
  const monetaryPaymentAvailable = hasMonetaryPaymentOption(response);
668
- const registrationAvailable = response.availability.kind === "available" || response.availability.kind === "waitlist";
669
631
  return {
670
632
  availability: response.availability,
671
633
  registration: response.registration,
@@ -680,7 +642,6 @@ function transformRegistrationOptionsResponse(response) {
680
642
  },
681
643
  transactions: []
682
644
  } : null,
683
- eligible: response.registration.kind === "none" && registrationAvailable,
684
645
  requiresPayment: monetaryPaymentAvailable,
685
646
  pricingStrategy: response.pricing.strategy,
686
647
  paymentTerms: response.pricing.paymentTerms,
@@ -695,16 +656,12 @@ function transformRegistrationOptionsResponse(response) {
695
656
  currency: response.pricing.currency
696
657
  })),
697
658
  ticketTypes: response.pricing.options,
698
- isFull,
699
- waitlistEnabled: response.availability.waitlistEnabled,
700
- canJoinWaitlist,
701
659
  price: response.pricing.subtotalCents,
702
660
  currency: response.pricing.currency,
703
661
  requiresAuthForPasses: response.entitlements.requiresLoginForPasses,
704
662
  requiresAuthForMemberships: response.entitlements.requiresLoginForMemberships,
705
663
  eligiblePasses,
706
664
  eligibleMemberships,
707
- canPayDirectly: monetaryPaymentAvailable,
708
665
  eventAcceptsPasses,
709
666
  eventAcceptsMemberships,
710
667
  availablePasses: [...response.entitlements.availableForPurchase.passes],
@@ -734,8 +691,8 @@ function getVisibleTicketTypes(input) {
734
691
  })) : [];
735
692
  return (input.eligibility?.ticketTypes ?? eventTicketTypes).filter((ticketType) => ticketType.isActive && (!input.selectedAttendanceMode || (ticketType.attendanceScope ?? "both") === "both" || (ticketType.attendanceScope ?? "both") === input.selectedAttendanceMode));
736
693
  }
737
- function getImplicitSingleAvailableTicketType(ticketTypes) {
738
- return ticketTypes.length === 1 && ticketTypes[0]?.availability.kind === "available" ? ticketTypes[0] : null;
694
+ function getImplicitSingleTicketType(ticketTypes) {
695
+ return ticketTypes.length === 1 && ticketTypes[0] && canRequestTicketOption(ticketTypes[0].availability) ? ticketTypes[0] : null;
739
696
  }
740
697
  function findSelectedTicketType(input) {
741
698
  if (!input.selectedPricingOptionId) return null;
@@ -772,25 +729,19 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
772
729
  const remaining = getRemainingPlaces(selectMostOpenCapacitySource(event));
773
730
  const isSoldOut = isEventFull(event);
774
731
  const canJoinWaitlist = canJoinEventWaitlistOnAnyMode(event);
775
- const isUnavailable = isSoldOut && !isSoldOutEventSelectable(event, canJoinWaitlist);
776
732
  const isLowCapacity = remaining.kind === "low";
777
733
  const capacityLabel = formatEventRemainingPlaces(event, { full: "Sold out" });
778
734
  const timeZone = event.timeZone ?? null;
779
- const cardClasses = [
780
- "er-event-card",
781
- isSelected && "er-event-card--selected",
782
- isUnavailable && "er-event-card--disabled"
783
- ].filter(Boolean).join(" ");
735
+ const cardClasses = ["er-event-card", isSelected && "er-event-card--selected"].filter(Boolean).join(" ");
784
736
  const badgeClasses = [
785
737
  "er-capacity-badge",
786
- isUnavailable && "er-capacity-badge--soldout",
738
+ isSoldOut && "er-capacity-badge--soldout",
787
739
  isLowCapacity && "er-capacity-badge--low",
788
- (canJoinWaitlist || !isUnavailable && !isLowCapacity) && "er-capacity-badge--available"
740
+ (canJoinWaitlist || !isSoldOut && !isLowCapacity) && "er-capacity-badge--available"
789
741
  ].filter(Boolean).join(" ");
790
742
  return /* @__PURE__ */ jsx("button", {
791
743
  type: "button",
792
- onClick: () => !isUnavailable && onSelect(event),
793
- disabled: isUnavailable,
744
+ onClick: () => onSelect(event),
794
745
  className: cardClasses,
795
746
  children: /* @__PURE__ */ jsxs("div", {
796
747
  className: "er-event-option",
@@ -817,41 +768,15 @@ const EventSelectionStep = ({ events, selectedEvent, onSelect, showVenue, showCa
817
768
  });
818
769
  };
819
770
  //#endregion
820
- //#region ../blocks/src/system/runtime/nodes/event-registration/useCheckoutRecovery.ts
821
- /** Recovery belongs to the selected booking subject; changing events resets its scope. */
822
- function useCheckoutRecovery(event, setStep) {
823
- const [recoveryEventId, setRecoveryEventId] = useState(null);
824
- return {
825
- checkoutRecovery: event !== null && recoveryEventId === event.selectionId,
826
- startCheckoutRecovery: useCallback(() => {
827
- if (!event) return;
828
- setRecoveryEventId(event.selectionId);
829
- setStep("select-tickets");
830
- }, [event, setStep])
831
- };
832
- }
833
- /** Authenticated pending bookings enter the same form-only recovery path as guests. */
834
- function useCheckoutRecoveryEligibility(eligibility, step, checkoutRecovery, startCheckoutRecovery) {
835
- const registrationKind = eligibility?.registration.kind;
836
- useEffect(() => {
837
- if ((step === "select-event" || step === "select-tickets" || step === "enter-details" || step === "select-payment") && registrationKind === "pending_payment" && !checkoutRecovery) startCheckoutRecovery();
838
- }, [
839
- registrationKind,
840
- step,
841
- checkoutRecovery,
842
- startCheckoutRecovery
843
- ]);
844
- return useMemo(() => checkoutRecovery ? getCheckoutRecoveryFormEligibility(eligibility) : eligibility, [checkoutRecovery, eligibility]);
845
- }
846
- //#endregion
847
771
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.ts
848
772
  function resolveRegistrationRecoveryAction(step, action) {
849
773
  if (step === "payment-failed") return "payment-options";
850
- if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "payment-status";
774
+ if (step === "payment-status") return "none";
775
+ if (step === "verifying-payment" || step === "verifying-timeout" || step === "redirecting") return "none";
851
776
  return action;
852
777
  }
853
778
  function canRecoverToWaitlist(eligibility) {
854
- return Boolean(eligibility?.isFull && eligibility.canJoinWaitlist);
779
+ return eligibility?.availability.kind === "waitlist" && eligibility.registration.kind !== "pending_payment";
855
780
  }
856
781
  function shouldRefreshAfterRegistrationError(step, action, enabled) {
857
782
  return enabled || step === "error" && action === "places";
@@ -868,19 +793,19 @@ function resetRegistrationAfterExpiry(input) {
868
793
  input.setErrorMessage("");
869
794
  input.setPendingRegistrationReference(null);
870
795
  }
871
- /** Recovery never starts another payment. Unknown payment outcomes go to the portal. */
796
+ /** Retry the retained submission in place; explicit payment uncertainty stays status-only. */
872
797
  function recoverRegistrationError(action, handlers) {
873
798
  switch (action) {
874
799
  case "none": return;
875
800
  case "portal":
876
- case "payment-status":
877
801
  window.location.assign("/portal");
878
802
  return;
879
803
  case "event":
880
804
  window.location.assign(window.location.pathname);
881
805
  return;
882
806
  case "retry":
883
- window.location.reload();
807
+ handlers.clearError();
808
+ handlers.setStep("select-payment");
884
809
  return;
885
810
  case "details":
886
811
  handlers.clearError();
@@ -918,53 +843,6 @@ function validateFormData(formData, requiresConsent = false) {
918
843
  return errors;
919
844
  }
920
845
  //#endregion
921
- //#region ../blocks/src/system/runtime/nodes/event-registration/ticketSelectionLines.ts
922
- function normalizeTicketSelectionLines(lines) {
923
- const quantitiesByTicketType = /* @__PURE__ */ new Map();
924
- for (const line of lines) {
925
- const quantity = Math.floor(line.quantity);
926
- if (quantity <= 0) continue;
927
- const key = line.pricingOptionId ?? null;
928
- quantitiesByTicketType.set(key, (quantitiesByTicketType.get(key) ?? 0) + quantity);
929
- }
930
- return [...quantitiesByTicketType.entries()].map(([pricingOptionId, quantity]) => ({
931
- pricingOptionId,
932
- quantity
933
- }));
934
- }
935
- function getTicketSelectionTotal(lines) {
936
- return normalizeTicketSelectionLines(lines).reduce((total, line) => total + line.quantity, 0);
937
- }
938
- function getTicketLineQuantity(lines, pricingOptionId) {
939
- return normalizeTicketSelectionLines(lines).find((line) => line.pricingOptionId === pricingOptionId)?.quantity ?? 0;
940
- }
941
- function getSingleSelectedPricingOptionId(lines) {
942
- const normalized = normalizeTicketSelectionLines(lines);
943
- return normalized.length === 1 ? normalized[0]?.pricingOptionId ?? null : null;
944
- }
945
- function hasValidPricingOptionSelection(input) {
946
- if (!input.selectionRequired) return true;
947
- if (input.lines.length === 0) return false;
948
- const availableOptionIds = new Set(input.visibleOptions.filter((option) => option.availability.kind === "available").map((option) => option.pricingOptionId));
949
- return input.lines.every((line) => line.pricingOptionId !== null && availableOptionIds.has(line.pricingOptionId));
950
- }
951
- function setTicketLineQuantity(input) {
952
- const nextQuantity = Math.max(0, Math.floor(input.quantity));
953
- const normalized = normalizeTicketSelectionLines(input.lines).filter((line) => line.pricingOptionId !== input.pricingOptionId);
954
- if (nextQuantity <= 0) return normalized;
955
- return [...normalized, {
956
- pricingOptionId: input.pricingOptionId,
957
- quantity: nextQuantity
958
- }];
959
- }
960
- function getTicketTypeLineMax(input) {
961
- const otherSelectedQuantity = input.totalSelectedQuantity - input.currentQuantity;
962
- const maxByBlock = input.maxTickets - otherSelectedQuantity;
963
- const maxByEvent = input.availableSpots == null ? input.maxTickets : input.availableSpots - otherSelectedQuantity;
964
- const maxByTicketType = input.ticketTypeRemainingCapacity ?? input.maxTickets;
965
- return Math.max(0, Math.min(maxByBlock, maxByEvent, maxByTicketType));
966
- }
967
- //#endregion
968
846
  //#region ../blocks/src/system/runtime/nodes/event-registration/hooks.state.ts
969
847
  const initialEligibilityLookupState = {
970
848
  tag: "idle",
@@ -1082,17 +960,6 @@ function usePortalAuthWithDeps(deps) {
1082
960
  logout
1083
961
  };
1084
962
  }
1085
- function toRegistrationOptionsSubject(subject, attendanceMode) {
1086
- return subject.kind === "occurrence" ? {
1087
- kind: "occurrence",
1088
- occurrenceId: subject.occurrenceId,
1089
- attendanceMode: attendanceMode ?? void 0
1090
- } : {
1091
- kind: "wholeSeries",
1092
- eventSeriesId: subject.eventSeriesId,
1093
- attendanceMode: attendanceMode ?? void 0
1094
- };
1095
- }
1096
963
  function useEligibility(params) {
1097
964
  return useEligibilityWithDeps(params, eventRegistrationHooksDeps);
1098
965
  }
@@ -1133,7 +1000,7 @@ function useEligibilityWithDeps(params, deps) {
1133
1000
  requestIdRef.current = requestId;
1134
1001
  dispatch({ type: "loading-started" });
1135
1002
  try {
1136
- const subject = toRegistrationOptionsSubject(params.subject, params.attendanceMode);
1003
+ const subject = toRegistrationRequestSubject(params.subject, params.attendanceMode);
1137
1004
  const result = await api.getRegistrationOptions({
1138
1005
  subject,
1139
1006
  selections: ticketSelections.map((selection) => ({
@@ -1500,9 +1367,9 @@ const eventRegistrationErrors = {
1500
1367
  message: "Booking is currently closed for this event. Please contact the organiser if you’d like to find out more.",
1501
1368
  action: "none"
1502
1369
  },
1503
- paymentUncertain: {
1504
- 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.",
1505
- action: "payment-status"
1370
+ paymentInvestigationRequired: {
1371
+ message: "We still can’t confirm your payment. Please don’t make another payment. Contact the organiser so they can check your booking.",
1372
+ action: "none"
1506
1373
  },
1507
1374
  paymentProviderUnavailable: {
1508
1375
  message: "We couldn’t reach the payment provider. Please try again in a minute.",
@@ -2000,7 +1867,30 @@ function checkoutReturnCommand(params) {
2000
1867
  };
2001
1868
  return null;
2002
1869
  }
2003
- async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
1870
+ function handleCheckoutReturnOutcome(options, deps, context, outcome) {
1871
+ const { pending, params } = context;
1872
+ if (outcome.kind === "resolved") {
1873
+ deps.clearReturnParams();
1874
+ options.setPendingRegistrationReference(outcome.reference);
1875
+ options.setStep("verifying-payment");
1876
+ return "settled";
1877
+ }
1878
+ if (outcome.kind === "abandoned") {
1879
+ deps.clearReturnParams();
1880
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1881
+ options.setStep("select-payment");
1882
+ return "settled";
1883
+ }
1884
+ if (outcome.kind === "notFound" && params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
1885
+ deps.clearReturnParams();
1886
+ options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
1887
+ options.setStep("select-payment");
1888
+ return "settled";
1889
+ }
1890
+ 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.");
1891
+ return outcome.kind === "notFound" ? "settled" : "retryable";
1892
+ }
1893
+ async function handleCheckoutReturn(options, deps, returnContextRef) {
2004
1894
  deps.cleanupStaleRegistrations();
2005
1895
  const params = deps.readReturnParams();
2006
1896
  if (params.registration !== "cancelled" && params.registration !== "success") return "settled";
@@ -2014,34 +1904,15 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
2014
1904
  }
2015
1905
  const { checkoutAttemptId } = command;
2016
1906
  const pending = deps.getPendingRegistration(siteId, checkoutAttemptId);
2017
- checkoutAttemptIdRef.current = checkoutAttemptId;
1907
+ returnContextRef.current = { checkoutAttemptId };
2018
1908
  if (pending) restorePendingPresentation(options, pending);
2019
1909
  options.setStep("verifying-payment");
2020
1910
  try {
2021
1911
  const outcome = await options.api.resolveEventCheckoutReturn(command);
2022
- switch (outcome.kind) {
2023
- case "resolved":
2024
- deps.clearReturnParams();
2025
- options.setPendingRegistrationReference(outcome.reference);
2026
- options.setStep("verifying-payment");
2027
- return "settled";
2028
- case "abandoned":
2029
- deps.clearReturnParams();
2030
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
2031
- options.setStep("select-payment");
2032
- return "settled";
2033
- case "notFound":
2034
- if (params.registration === "cancelled" && (options.registrationSubject?.kind === "occurrence" || pending?.reference.scope === "occurrence")) {
2035
- deps.clearReturnParams();
2036
- options.setErrorMessage("Your payment was cancelled. You can choose a payment option and try again.");
2037
- options.setStep("select-payment");
2038
- return "settled";
2039
- }
2040
- 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.");
2041
- return "settled";
2042
- case "reconciliationRequired": break;
2043
- }
2044
- 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.");
1912
+ return handleCheckoutReturnOutcome(options, deps, {
1913
+ pending,
1914
+ params
1915
+ }, outcome);
2045
1916
  } catch {
2046
1917
  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.");
2047
1918
  }
@@ -2050,9 +1921,16 @@ async function handleCheckoutReturn(options, deps, checkoutAttemptIdRef) {
2050
1921
  function useEventRegistrationStripeReturn(options) {
2051
1922
  return useEventRegistrationStripeReturnWithDeps(options, eventRegistrationStripeReturnDeps);
2052
1923
  }
1924
+ function completeVerifiedRegistration(options, deps, returnContext) {
1925
+ const siteId = options.api.config.siteId;
1926
+ const checkoutAttemptId = returnContext?.checkoutAttemptId;
1927
+ if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
1928
+ options.setRegistrationResult({ status: "confirmed" });
1929
+ options.setStep("success");
1930
+ }
2053
1931
  function useEventRegistrationStripeReturnWithDeps(options, deps) {
2054
1932
  const { api, pendingRegistrationReference, setErrorMessage, setRegistrationResult, setStep, step } = options;
2055
- const checkoutAttemptIdRef = useRef(null);
1933
+ const returnContextRef = useRef(null);
2056
1934
  const lookupInFlightRef = useRef(false);
2057
1935
  const [lookupState, setLookupState] = useState("settled");
2058
1936
  const resolveReturn = useCallback(async () => {
@@ -2060,7 +1938,7 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
2060
1938
  lookupInFlightRef.current = true;
2061
1939
  setLookupState("loading");
2062
1940
  try {
2063
- setLookupState(await handleCheckoutReturn(options, deps, checkoutAttemptIdRef));
1941
+ setLookupState(await handleCheckoutReturn(options, deps, returnContextRef));
2064
1942
  } finally {
2065
1943
  lookupInFlightRef.current = false;
2066
1944
  }
@@ -2083,13 +1961,8 @@ function useEventRegistrationStripeReturnWithDeps(options, deps) {
2083
1961
  resolveReturn();
2084
1962
  }, []);
2085
1963
  useEffect(() => {
2086
- if (verification.status === "completed") {
2087
- const siteId = api.config.siteId;
2088
- const checkoutAttemptId = checkoutAttemptIdRef.current;
2089
- if (siteId && checkoutAttemptId) deps.clearPendingRegistration(siteId, checkoutAttemptId);
2090
- setRegistrationResult({ status: "confirmed" });
2091
- setStep("success");
2092
- } else if (verification.status === "failed") {
1964
+ if (verification.status === "completed") completeVerifiedRegistration(options, deps, returnContextRef.current);
1965
+ else if (verification.status === "failed") {
2093
1966
  setErrorMessage("Please try again or choose another payment option. If this keeps happening, contact the organiser for help.");
2094
1967
  setStep("payment-failed");
2095
1968
  } else if (verification.status === "timeout") setStep("verifying-timeout");
@@ -2118,7 +1991,10 @@ function registrationErrorPresentation(error) {
2118
1991
  const reason = error instanceof BlockApiError ? parseEventRegistrationErrorReason(error.tag) : null;
2119
1992
  if (reason) return eventRegistrationErrors[reason];
2120
1993
  if (error instanceof BlockApiError && error.status < 500) return eventRegistrationErrors.invalidCommitInput;
2121
- return eventRegistrationErrors.paymentUncertain;
1994
+ return {
1995
+ message: "Something went wrong. Please try again.",
1996
+ action: "retry"
1997
+ };
2122
1998
  }
2123
1999
  //#endregion
2124
2000
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.ts
@@ -2147,26 +2023,6 @@ function useEventRegistrationSubmit(options) {
2147
2023
  * and the balance left, which the block's copy cannot. Every other method keeps
2148
2024
  * whatever success message the site configured.
2149
2025
  */
2150
- function toRegistrationResult(result) {
2151
- if (result.alreadyRegistered) {
2152
- const waitlisted = result.status === "waitlist";
2153
- return {
2154
- status: result.status,
2155
- variant: "info",
2156
- title: waitlisted ? "Already waitlisted" : "Already registered",
2157
- message: result.message ?? (waitlisted ? "You are already on the waitlist for this event." : "You are already registered for this event.")
2158
- };
2159
- }
2160
- if (result.status === "waitlist") return {
2161
- status: "waitlist",
2162
- title: "Added to waitlist",
2163
- message: result.message ?? (typeof result.waitlistPosition === "number" ? `You've been added to the waitlist (position ${result.waitlistPosition}).` : "You've been added to the waitlist.")
2164
- };
2165
- return {
2166
- status: result.status,
2167
- message: result.paymentMethod === "pass" ? result.message : void 0
2168
- };
2169
- }
2170
2026
  function useEventRegistrationSubmitWithDeps(options, deps) {
2171
2027
  /**
2172
2028
  * The command identity for the submission currently in progress.
@@ -2180,7 +2036,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
2180
2036
  * Identical retries retain the command after a lost response. Changed form
2181
2037
  * details get a new command so the server applies the new booking intent.
2182
2038
  */
2183
- const commandRef = useRef(null);
2039
+ const { commandRef } = options;
2184
2040
  const inFlightRef = useRef(null);
2185
2041
  const submit = async (method, passId, membershipId, selection, submitOptions) => {
2186
2042
  const target = options.registrationTarget;
@@ -2193,19 +2049,15 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
2193
2049
  const registrationCommandId = commandRef.current?.id ?? deps.createClientCommandId();
2194
2050
  try {
2195
2051
  let prepared = prepareSubmitContext(options, registrationCommandId, target, method, passId, membershipId, selection);
2196
- if (prepared.subject.kind === "wholeSeries" && method === "stripe") {
2197
- commandRef.current = null;
2198
- return await submitWholeSeriesCheckout(options, deps, {
2052
+ let request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
2053
+ const intentKey = registrationRequestIntentKey(request);
2054
+ if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) {
2055
+ prepared = {
2199
2056
  ...prepared,
2200
- subject: prepared.subject
2201
- }, submitOptions);
2057
+ registrationCommandId: deps.createClientCommandId()
2058
+ };
2059
+ request = buildRegistrationRequest(options, prepared, method, passId, membershipId);
2202
2060
  }
2203
- const request = buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId);
2204
- const intentKey = registrationRequestIntentKey(request);
2205
- if (commandRef.current !== null && commandRef.current.intentKey !== intentKey) prepared = {
2206
- ...prepared,
2207
- registrationCommandId: deps.createClientCommandId()
2208
- };
2209
2061
  commandRef.current = {
2210
2062
  id: prepared.registrationCommandId,
2211
2063
  intentKey
@@ -2214,8 +2066,7 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
2214
2066
  ...request,
2215
2067
  registrationCommandId: prepared.registrationCommandId
2216
2068
  });
2217
- commandRef.current = null;
2218
- return await completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions);
2069
+ return await completeRegistrationSubmission(options, deps, prepared, result, submitOptions);
2219
2070
  } catch (error) {
2220
2071
  if (error instanceof BlockApiError && error.tag === "registrationPreparationChanged") commandRef.current = null;
2221
2072
  const { message, action } = registrationErrorPresentation(error);
@@ -2239,9 +2090,40 @@ function useEventRegistrationSubmitWithDeps(options, deps) {
2239
2090
  return pending;
2240
2091
  };
2241
2092
  }
2242
- async function completeRegistrationSubmission(options, deps, prepared, result, method, submitOptions) {
2243
- if (needsOccurrenceCheckout(result, method)) return await submitOccurrenceCheckout(options, deps, prepared, result, method, submitOptions);
2244
- options.setRegistrationResult(toRegistrationResult(result));
2093
+ async function completeRegistrationSubmission(options, deps, prepared, result, submitOptions) {
2094
+ switch (result.kind) {
2095
+ case "paymentStatusRequired":
2096
+ options.setPaymentStatusRecovery({
2097
+ kind: "ready",
2098
+ subject: result.subject
2099
+ });
2100
+ options.setStep("payment-status");
2101
+ return {
2102
+ ok: true,
2103
+ outcome: { kind: "payment_status" }
2104
+ };
2105
+ case "checkoutRequired":
2106
+ options.setPendingRegistrationReference(result.reference);
2107
+ savePendingCheckout(options, deps, prepared, result.submissionId, result.reference);
2108
+ options.setStep("redirecting");
2109
+ return redirectToCheckout(result.checkoutUrl, submitOptions);
2110
+ case "waitlisted":
2111
+ options.setRegistrationResult({
2112
+ status: "waitlist",
2113
+ title: "Added to waitlist",
2114
+ message: result.waitlist.message
2115
+ });
2116
+ break;
2117
+ case "bookingComplete":
2118
+ case "paymentDeferred":
2119
+ options.setPendingRegistrationReference(result.reference);
2120
+ options.setRegistrationResult({
2121
+ status: "confirmed",
2122
+ message: result.summary.paymentMethod === "pass" ? result.summary.message : void 0
2123
+ });
2124
+ break;
2125
+ }
2126
+ options.commandRef.current = null;
2245
2127
  options.setStep("success");
2246
2128
  return {
2247
2129
  ok: true,
@@ -2272,15 +2154,7 @@ function prepareSubmitContext(options, registrationCommandId, target, method, pa
2272
2154
  email: guest.email?.trim().toLowerCase() || void 0,
2273
2155
  phone: guest.phone?.trim() || void 0
2274
2156
  })) : void 0,
2275
- subject: target.subject.kind === "occurrence" ? {
2276
- kind: "occurrence",
2277
- occurrenceId: target.subject.occurrenceId,
2278
- attendanceMode: options.selectedAttendanceMode ?? void 0
2279
- } : {
2280
- kind: "wholeSeries",
2281
- eventSeriesId: target.subject.eventSeriesId,
2282
- attendanceMode: options.selectedAttendanceMode ?? void 0
2283
- }
2157
+ subject: toRegistrationRequestSubject(target.subject, options.selectedAttendanceMode)
2284
2158
  };
2285
2159
  }
2286
2160
  function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId, selection) {
@@ -2304,85 +2178,6 @@ function resolveSelectionSnapshot(method, passId, membershipId, bundleProductId,
2304
2178
  if (method === "free") return { method };
2305
2179
  throw new Error("Choose an available payment option.");
2306
2180
  }
2307
- async function submitWholeSeriesCheckout(options, deps, prepared, submitOptions) {
2308
- options.setStep("redirecting");
2309
- if (prepared.selectionSnapshot.method !== "stripe") throw new Error("Choose an available payment option.");
2310
- const checkoutAttemptId = deps.createClientCommandId();
2311
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
2312
- const result = await options.api.createEventCheckout({
2313
- subject: prepared.subject,
2314
- ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
2315
- selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
2316
- email: options.formData.email.trim().toLowerCase(),
2317
- name: options.formData.name.trim(),
2318
- phone: options.formData.phone.trim() || void 0,
2319
- formData: options.formData,
2320
- consentAccepted: options.formData.consentAccepted,
2321
- altchaSolution: options.altchaSolution,
2322
- guests: prepared.guestsPayload,
2323
- claimToken: options.claimToken ?? void 0,
2324
- waitlistEntryId: options.waitlistEntryId ?? void 0,
2325
- discountCode: options.discountCode.trim() || void 0,
2326
- collectionOptionKind: prepared.selectionSnapshot.collectionOptionKind,
2327
- checkoutAttemptId,
2328
- ...urls
2329
- });
2330
- if (result.scope !== "wholeSeries") throw new Error("Unexpected registration scope for event checkout.");
2331
- if ("waitlistEntryId" in result) {
2332
- options.setRegistrationResult({
2333
- status: "waitlist",
2334
- variant: result.alreadyRegistered ? "info" : void 0,
2335
- title: result.alreadyRegistered ? "Already waitlisted" : "Added to waitlist",
2336
- message: result.alreadyRegistered ? "You are already on the waitlist for this event." : "You've been added to the waitlist."
2337
- });
2338
- options.setStep("success");
2339
- return {
2340
- ok: true,
2341
- outcome: { kind: "completed" }
2342
- };
2343
- }
2344
- if (result.bypassed) return completeBypassedCheckout(options);
2345
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
2346
- savePendingCheckout(options, deps, prepared, result.checkoutAttemptId, {
2347
- scope: "wholeSeries",
2348
- registrationId: result.registrationId
2349
- });
2350
- return redirectToCheckout(result.checkoutUrl, submitOptions);
2351
- }
2352
- function needsOccurrenceCheckout(result, method) {
2353
- return result.scope === "occurrence" && result.status === "confirmed" && result.pendingPayment && [
2354
- "stripe",
2355
- "pass_bundle",
2356
- "membership_bundle"
2357
- ].includes(method);
2358
- }
2359
- async function submitOccurrenceCheckout(options, deps, prepared, registration, method, submitOptions) {
2360
- const reference = {
2361
- scope: "occurrence",
2362
- attendeeId: registration.attendeeId
2363
- };
2364
- options.setPendingRegistrationReference(reference);
2365
- options.setStep("redirecting");
2366
- const checkoutAttemptId = deps.createClientCommandId();
2367
- const urls = buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: checkoutAttemptId });
2368
- const result = await options.api.createEventCheckout({
2369
- subject: {
2370
- kind: "occurrence",
2371
- attendeeId: registration.attendeeId
2372
- },
2373
- registrationCommandId: prepared.registrationCommandId,
2374
- checkoutType: method === "pass_bundle" ? "pass_bundle" : method === "membership_bundle" ? "membership_bundle" : "event",
2375
- productId: prepared.bundleProductId,
2376
- discountCode: options.discountCode.trim() || void 0,
2377
- collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0,
2378
- ...urls
2379
- });
2380
- if (result.scope !== "occurrence") throw new Error("Unexpected registration scope for event checkout.");
2381
- if (result.bypassed) return completeBypassedCheckout(options);
2382
- if (!result.checkoutUrl) throw new Error("Checkout URL missing");
2383
- if (prepared.subject.kind === "occurrence") savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference);
2384
- return redirectToCheckout(result.checkoutUrl, submitOptions);
2385
- }
2386
2181
  function savePendingCheckout(options, deps, prepared, checkoutAttemptId, reference) {
2387
2182
  const siteId = options.api.config.siteId;
2388
2183
  if (!siteId) return;
@@ -2395,14 +2190,6 @@ function savePendingCheckout(options, deps, prepared, checkoutAttemptId, referen
2395
2190
  paymentSelection: prepared.selectionSnapshot
2396
2191
  });
2397
2192
  }
2398
- function completeBypassedCheckout(options) {
2399
- options.setRegistrationResult({ status: "confirmed" });
2400
- options.setStep("success");
2401
- return {
2402
- ok: true,
2403
- outcome: { kind: "completed" }
2404
- };
2405
- }
2406
2193
  function redirectToCheckout(checkoutUrl, submitOptions) {
2407
2194
  if (submitOptions?.redirect !== "manual") window.location.href = checkoutUrl;
2408
2195
  return {
@@ -2414,12 +2201,23 @@ function redirectToCheckout(checkoutUrl, submitOptions) {
2414
2201
  };
2415
2202
  }
2416
2203
  function registrationRequestIntentKey(request) {
2417
- const { registrationCommandId: _command, altchaSolution: _proof, ...intent } = request;
2418
- return JSON.stringify(intent);
2204
+ const { registrationCommandId: _command, altchaSolution: _proof, checkout, ...intent } = request;
2205
+ return JSON.stringify({
2206
+ ...intent,
2207
+ checkout: checkout ? {
2208
+ discountCode: checkout.discountCode,
2209
+ collectionOptionKind: checkout.collectionOptionKind
2210
+ } : void 0
2211
+ });
2419
2212
  }
2420
- function buildOccurrenceRegistrationRequest(options, prepared, method, passId, membershipId) {
2213
+ function buildRegistrationRequest(options, prepared, method, passId, membershipId) {
2421
2214
  return {
2422
2215
  registrationCommandId: prepared.registrationCommandId,
2216
+ checkout: {
2217
+ ...buildCheckoutReturnUrls("registration", EVENT_REGISTRATION_ANCHOR_ID, { [EVENT_REGISTRATION_CHECKOUT_ATTEMPT_PARAM]: prepared.registrationCommandId }),
2218
+ discountCode: options.discountCode.trim() || void 0,
2219
+ collectionOptionKind: prepared.selectionSnapshot.method === "stripe" ? prepared.selectionSnapshot.collectionOptionKind : void 0
2220
+ },
2423
2221
  subject: prepared.subject,
2424
2222
  ticketSelections: toTicketSelectionRequests(prepared.ticketSelections),
2425
2223
  selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
@@ -2637,20 +2435,124 @@ function useWaitlistClaimRestoration(input) {
2637
2435
  }
2638
2436
  //#endregion
2639
2437
  //#region ../blocks/src/system/runtime/nodes/event-registration/registrationCompletion.ts
2438
+ function isSelectionSoldOut(availability) {
2439
+ return availability?.kind === "unavailable" && availability.reason === "full";
2440
+ }
2640
2441
  function resolveRegistrationAvailabilityBlocker(input) {
2641
2442
  if (input.hasWaitlistClaim) return null;
2642
2443
  if (input.eligibilityError) return input.eligibilityError;
2643
2444
  if (!input.eligibility) return "Registration is not available for this event.";
2445
+ if (input.eligibility.registration.kind === "pending_payment") return null;
2446
+ if (isSelectionSoldOut(input.eligibility.availability)) return null;
2644
2447
  if (input.eligibility.availability.kind === "unavailable" && input.eligibility.availability.reason === "paymentUnavailable") return "Payment options are not available for this event.";
2645
- if (input.eligibility.availability.kind === "unavailable" || !input.eligibility.eligible) return "Registration is not available for this event.";
2448
+ if (input.eligibility.availability.kind === "unavailable" || input.eligibility.registration.kind === "waitlisted") return "Registration is not available for this event.";
2646
2449
  return null;
2647
2450
  }
2648
2451
  function resolvePricingSelectionBlocker(input) {
2649
2452
  if (input.selectionRequired && !input.hasValidSelection) return "Choose at least one available ticket to continue.";
2650
2453
  return input.slidingScaleSelectionMessage;
2651
2454
  }
2652
- function isSoldOutWithoutWaitlist(input) {
2653
- return Boolean(!input.hasWaitlistClaim && input.eligibility?.isFull && !input.eligibility.canJoinWaitlist);
2455
+ //#endregion
2456
+ //#region ../blocks/src/system/runtime/nodes/event-registration/usePaymentStatusRecovery.ts
2457
+ function isCheckableRecovery(state) {
2458
+ return state.kind === "ready" || state.kind === "processing" || state.kind === "temporarilyUnavailable";
2459
+ }
2460
+ function assistanceRequired(recovery) {
2461
+ return {
2462
+ kind: "assistanceRequired",
2463
+ subject: recovery.subject
2464
+ };
2465
+ }
2466
+ function applyStatusOutcome(outcome, recovery, setters, setRecovery) {
2467
+ if (outcome.kind === "resolved") {
2468
+ setters.setPendingRegistrationReference(outcome.reference);
2469
+ setters.setRegistrationResult({ status: "confirmed" });
2470
+ setRecovery(null);
2471
+ setters.setStep("success");
2472
+ return;
2473
+ }
2474
+ switch (outcome.kind) {
2475
+ case "paymentProcessing":
2476
+ setRecovery({
2477
+ ...recovery,
2478
+ kind: "processing"
2479
+ });
2480
+ return;
2481
+ case "paymentAvailable":
2482
+ setRecovery({
2483
+ ...recovery,
2484
+ kind: "paymentAvailable",
2485
+ checkoutUrl: outcome.checkoutUrl
2486
+ });
2487
+ return;
2488
+ case "paymentCheckUnavailable":
2489
+ setRecovery({
2490
+ ...recovery,
2491
+ kind: "temporarilyUnavailable"
2492
+ });
2493
+ return;
2494
+ case "paymentRetryAvailable":
2495
+ setRecovery({
2496
+ ...recovery,
2497
+ kind: "retryAvailable"
2498
+ });
2499
+ return;
2500
+ case "abandoned":
2501
+ case "notFound":
2502
+ case "reconciliationRequired":
2503
+ case "paymentAssistanceRequired":
2504
+ setRecovery(assistanceRequired(recovery));
2505
+ return;
2506
+ }
2507
+ }
2508
+ function usePaymentStatusRecovery(options) {
2509
+ const [recovery, setRecovery] = useState(null);
2510
+ const checkRef = useRef(null);
2511
+ return {
2512
+ recovery,
2513
+ showRecovery: useCallback((next) => setRecovery(next), []),
2514
+ check: useCallback(() => {
2515
+ if (!recovery || !isCheckableRecovery(recovery) || checkRef.current) return;
2516
+ const pending = (async () => {
2517
+ setRecovery({
2518
+ ...recovery,
2519
+ kind: "checking"
2520
+ });
2521
+ try {
2522
+ applyStatusOutcome(await options.api.resolveEventCheckoutReturn({
2523
+ kind: "checkStatus",
2524
+ subject: recovery.subject
2525
+ }), recovery, options, setRecovery);
2526
+ } catch {
2527
+ setRecovery({
2528
+ ...recovery,
2529
+ kind: "temporarilyUnavailable"
2530
+ });
2531
+ }
2532
+ })();
2533
+ checkRef.current = pending;
2534
+ pending.finally(() => {
2535
+ if (checkRef.current === pending) checkRef.current = null;
2536
+ });
2537
+ }, [options, recovery]),
2538
+ continuePayment: useCallback(() => {
2539
+ if (recovery?.kind === "paymentAvailable") window.location.assign(recovery.checkoutUrl);
2540
+ }, [recovery]),
2541
+ returnToPayment: useCallback(() => {
2542
+ if (recovery?.kind !== "retryAvailable") return;
2543
+ options.beginNewSubmission();
2544
+ setRecovery(null);
2545
+ options.setOptionsRefreshKey((value) => value + 1);
2546
+ options.setStep("select-payment");
2547
+ }, [options, recovery]),
2548
+ returnToBooking: useCallback(() => {
2549
+ if (recovery?.kind !== "assistanceRequired") return;
2550
+ options.beginNewSubmission();
2551
+ setRecovery(null);
2552
+ options.setOptionsRefreshKey((value) => value + 1);
2553
+ options.setStep("select-tickets");
2554
+ }, [options, recovery])
2555
+ };
2654
2556
  }
2655
2557
  //#endregion
2656
2558
  //#region ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
@@ -2708,6 +2610,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2708
2610
  const [errorAction, setErrorAction] = useState("retry");
2709
2611
  const [optionsRefreshKey, setOptionsRefreshKey] = useState(0);
2710
2612
  const [altchaSolution, setAltchaSolution] = useState(null);
2613
+ const [checkingNextStep, setCheckingNextStep] = useState(false);
2711
2614
  const { authState: portalAuthState, profile: portalProfile } = deps.usePortalAuth();
2712
2615
  const waitlistClaim = deps.useWaitlistClaim();
2713
2616
  const [showMagicLinkModal, setShowMagicLinkModal] = useState(false);
@@ -2734,28 +2637,29 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2734
2637
  }), [options.occurrenceContext, selectedEvent]);
2735
2638
  const registrationOccurrenceId = getRegistrationOccurrenceId(registrationTarget);
2736
2639
  const currentEvent = selectedEvent ?? preSelectedEvent;
2640
+ const commandRef = useRef(null);
2737
2641
  const setStep = useCallback((nextStep) => {
2738
2642
  dispatchFlow({
2739
2643
  type: "set-step",
2740
2644
  step: nextStep
2741
2645
  });
2742
2646
  }, []);
2743
- const { checkoutRecovery, startCheckoutRecovery } = useCheckoutRecovery(currentEvent, setStep);
2744
- const currentEventUsesTicketTypes = usesTieredPricing(currentEvent);
2745
- const currentCanJoinWaitlist = canJoinWaitlistOutsideRecovery(currentEvent, selectedAttendanceMode, checkoutRecovery);
2746
- const usesTicketTypeSelections = currentEventUsesTicketTypes && !currentCanJoinWaitlist;
2647
+ const { recovery: paymentStatusRecovery, showRecovery: setPaymentStatusRecovery, check: handleCheckPaymentStatus, continuePayment: handleContinueExistingPayment, returnToPayment: handleReturnToPayment, returnToBooking: handleReturnToBooking } = usePaymentStatusRecovery({
2648
+ api,
2649
+ beginNewSubmission: () => {
2650
+ commandRef.current = null;
2651
+ },
2652
+ setPendingRegistrationReference,
2653
+ setRegistrationResult,
2654
+ setOptionsRefreshKey,
2655
+ setStep
2656
+ });
2657
+ const usesTicketTypeSelections = usesTieredPricing(currentEvent);
2747
2658
  const eventTicketTypesForSelection = useMemo(() => getVisibleTicketTypes({
2748
2659
  event: currentEvent,
2749
2660
  eligibility: null,
2750
2661
  selectedAttendanceMode
2751
- }).map((option) => checkoutRecovery ? {
2752
- ...option,
2753
- availability: checkoutRecoveryOptionAvailability(option.availability)
2754
- } : option), [
2755
- currentEvent,
2756
- selectedAttendanceMode,
2757
- checkoutRecovery
2758
- ]);
2662
+ }), [currentEvent, selectedAttendanceMode]);
2759
2663
  const normalizedSelectedTicketLines = useMemo(() => normalizeTicketSelectionLines(selectedTicketLines), [selectedTicketLines]);
2760
2664
  const ticketTypeSelectionTotal = useMemo(() => getTicketSelectionTotal(normalizedSelectedTicketLines), [normalizedSelectedTicketLines]);
2761
2665
  const ticketsCount = usesTicketTypeSelections ? ticketTypeSelectionTotal : flowState.ticketsCount;
@@ -2793,7 +2697,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2793
2697
  isAuthenticated: portalAuthState === "authenticated",
2794
2698
  occurrenceId: registrationOccurrenceId
2795
2699
  });
2796
- const eligibility = useCheckoutRecoveryEligibility(eligibilityResult.eligibility, step, checkoutRecovery, startCheckoutRecovery);
2700
+ const eligibility = eligibilityResult.eligibility;
2797
2701
  const eligibilityLoading = eligibilityResult.isLoading;
2798
2702
  const eligibilityError = eligibilityResult.error;
2799
2703
  const portalCreditState = eligibilityResult.creditState;
@@ -2905,14 +2809,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2905
2809
  event: currentEvent,
2906
2810
  eligibility,
2907
2811
  selectedAttendanceMode
2908
- }).map((option) => checkoutRecovery ? {
2909
- ...option,
2910
- availability: checkoutRecoveryOptionAvailability(option.availability)
2911
- } : option), [
2812
+ }), [
2912
2813
  currentEvent,
2913
2814
  selectedAttendanceMode,
2914
- eligibility,
2915
- checkoutRecovery
2815
+ eligibility
2916
2816
  ]);
2917
2817
  const singleSelectedPricingOptionId = getSingleSelectedPricingOptionId(normalizedSelectedTicketLines);
2918
2818
  const selectedTicketType = useMemo(() => findSelectedTicketType({
@@ -2936,12 +2836,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2936
2836
  const event = currentEvent;
2937
2837
  if (!usesTieredPricing(event)) return false;
2938
2838
  if ((event.deliveryMode ?? "in_person") === "hybrid" && !selectedAttendanceMode) return false;
2939
- return checkoutRecovery || !canJoinEventWaitlist(event, { attendanceMode: selectedAttendanceMode });
2940
- }, [
2941
- currentEvent,
2942
- selectedAttendanceMode,
2943
- checkoutRecovery
2944
- ]);
2839
+ return true;
2840
+ }, [currentEvent, selectedAttendanceMode]);
2945
2841
  const hasValidTicketSelection = hasValidPricingOptionSelection({
2946
2842
  selectionRequired: ticketTypeSelectionRequired,
2947
2843
  lines: normalizedSelectedTicketLines,
@@ -2952,8 +2848,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
2952
2848
  if (selectedTicketLines.length > 0) setSelectedTicketLines([]);
2953
2849
  return;
2954
2850
  }
2955
- const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => ticketType.availability.kind === "available");
2956
- const implicitTicketType = getImplicitSingleAvailableTicketType(eventTicketTypesForSelection);
2851
+ const availableTicketTypes = eventTicketTypesForSelection.filter((ticketType) => canRequestTicketOption(ticketType.availability));
2852
+ const implicitTicketType = getImplicitSingleTicketType(eventTicketTypesForSelection);
2957
2853
  if (implicitTicketType && normalizedSelectedTicketLines.length === 0) {
2958
2854
  setSelectedTicketLines([{
2959
2855
  pricingOptionId: implicitTicketType.pricingOptionId,
@@ -3020,6 +2916,26 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3020
2916
  selectedEventSlidingScale,
3021
2917
  selectedSlidingAmount
3022
2918
  ]);
2919
+ const checkDetailsNextStep = async () => {
2920
+ if (portalAuthState === "authenticated" || waitlistClaim.claim || eligibility?.availability.kind === "available" || !registrationTarget) return { kind: canRecoverToWaitlist(eligibility) ? "waitlist" : "continue" };
2921
+ setCheckingNextStep(true);
2922
+ try {
2923
+ return await api.checkRegistrationNextStep({
2924
+ subject: toRegistrationRequestSubject(registrationTarget.subject, selectedAttendanceMode),
2925
+ selections: ticketSelections,
2926
+ selectedSlidingAmount: selectedSlidingAmount ?? void 0,
2927
+ email: formData.email,
2928
+ altchaSolution
2929
+ });
2930
+ } catch {
2931
+ return {
2932
+ kind: "unavailable",
2933
+ message: "We could not check availability. Please try again."
2934
+ };
2935
+ } finally {
2936
+ setCheckingNextStep(false);
2937
+ }
2938
+ };
3023
2939
  const handleDetailsComplete = async () => {
3024
2940
  const blockDetailsCompletion = (message) => {
3025
2941
  setErrorAction("places");
@@ -3051,11 +2967,10 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3051
2967
  message: "Check the highlighted fields and try again."
3052
2968
  };
3053
2969
  }
3054
- if (isSoldOutWithoutWaitlist({
3055
- hasWaitlistClaim: waitlistClaim.claim !== null,
3056
- eligibility
3057
- })) return blockDetailsCompletion("Sorry, there aren’t enough places left for your selection. Please check the available places and choose again.");
3058
- if (!eligibility?.canJoinWaitlist && requiresPaymentStep({
2970
+ const next = await checkDetailsNextStep();
2971
+ if (next.kind === "unavailable") return blockDetailsCompletion(next.message);
2972
+ const canJoinWaitlist = next.kind === "waitlist";
2973
+ if (!canJoinWaitlist && requiresPaymentStep({
3059
2974
  eventRequiresPayment,
3060
2975
  paymentSelection
3061
2976
  })) {
@@ -3068,7 +2983,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3068
2983
  const submitPlan = resolveEventRegistrationSubmitPlan({
3069
2984
  paymentSelection,
3070
2985
  eventRequiresPayment,
3071
- canJoinWaitlist: eligibility?.canJoinWaitlist
2986
+ canJoinWaitlist
3072
2987
  });
3073
2988
  if (!submitPlan.ok) return blockDetailsCompletion(submitPlan.message);
3074
2989
  const submitResult = await handleSubmit(submitPlan.args.method, submitPlan.args.passId, submitPlan.args.membershipId, submitPlan.args.selection);
@@ -3086,7 +3001,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3086
3001
  const submitPlan = resolveEventRegistrationSubmitPlan({
3087
3002
  paymentSelection,
3088
3003
  eventRequiresPayment,
3089
- canJoinWaitlist: eligibility?.canJoinWaitlist
3004
+ canJoinWaitlist: false
3090
3005
  });
3091
3006
  if (!submitPlan.ok) {
3092
3007
  setErrorMessage(submitPlan.message);
@@ -3118,6 +3033,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3118
3033
  const selectionMethod = paymentSelection.method;
3119
3034
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
3120
3035
  const handleSubmit = deps.useEventRegistrationSubmit({
3036
+ commandRef,
3121
3037
  setErrorAction,
3122
3038
  api,
3123
3039
  registrationTarget,
@@ -3134,7 +3050,8 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3134
3050
  setPendingRegistrationReference,
3135
3051
  setStep,
3136
3052
  setRegistrationResult: (result) => setRegistrationResult(result),
3137
- setErrorMessage
3053
+ setErrorMessage,
3054
+ setPaymentStatusRecovery
3138
3055
  });
3139
3056
  const handleEventSelect = (event) => {
3140
3057
  setSelectedEvent(event);
@@ -3193,8 +3110,6 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3193
3110
  scopedEvents,
3194
3111
  preSelectedEvent,
3195
3112
  hasPreSelectedContext,
3196
- checkoutRecovery,
3197
- startCheckoutRecovery,
3198
3113
  step,
3199
3114
  setStep,
3200
3115
  selectedEvent,
@@ -3248,7 +3163,7 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3248
3163
  waitlistClaim,
3249
3164
  eligibility,
3250
3165
  registrationOptionsState: eligibilityResult.state,
3251
- eligibilityLoading,
3166
+ eligibilityLoading: eligibilityLoading || checkingNextStep,
3252
3167
  eligibilityError,
3253
3168
  existingRegistration: existingRegistrationResult.registration,
3254
3169
  existingRegistrationLoading: existingRegistrationResult.isLoading || portalAuthState === "authenticated" && eligibilityLoading,
@@ -3268,6 +3183,11 @@ function useEventRegistrationWizardWithDeps(options, deps) {
3268
3183
  discountContext,
3269
3184
  shouldShowDiscountToggle,
3270
3185
  verification,
3186
+ paymentStatusRecovery,
3187
+ handleCheckPaymentStatus,
3188
+ handleContinueExistingPayment,
3189
+ handleReturnToPayment,
3190
+ handleReturnToBooking,
3271
3191
  handleDetailsComplete,
3272
3192
  handlePaymentSelect,
3273
3193
  handleUseCardInsteadOfPass,
@@ -3439,7 +3359,6 @@ const labels = {
3439
3359
  start: "Start again",
3440
3360
  details: "Check my details",
3441
3361
  "payment-options": "View payment options",
3442
- "payment-status": "Check payment status",
3443
3362
  portal: "View my bookings",
3444
3363
  event: "View event"
3445
3364
  };
@@ -3458,7 +3377,7 @@ const ErrorStep = ({ message, onRetry, action = "retry", canJoinWaitlist = false
3458
3377
  className: "er-text--muted er-message",
3459
3378
  children: offerWaitlist ? "Sorry, this event is now full. You can join the waiting list, and we’ll email you if a place becomes available." : message
3460
3379
  }),
3461
- actions: action === "none" ? void 0 : action === "portal" || action === "payment-status" ? /* @__PURE__ */ jsx("a", {
3380
+ actions: action === "none" ? void 0 : action === "portal" ? /* @__PURE__ */ jsx("a", {
3462
3381
  href: "/portal",
3463
3382
  className: "er-retry-btn",
3464
3383
  children: labels[action]
@@ -3712,8 +3631,11 @@ function EventRegistrationSummaryBar(props) {
3712
3631
  */
3713
3632
  function resolveDetailsButtonText(input) {
3714
3633
  const { wizard } = input;
3715
- if (wizard.eligibility?.canJoinWaitlist) return "Join waitlist";
3716
- if (!input.hasWaitlistClaim && wizard.eligibility?.isFull) return "Sold out";
3634
+ const availability = wizard.eligibility?.availability;
3635
+ const full = availability?.kind === "waitlist" || isSelectionSoldOut(availability);
3636
+ if (wizard.portalAuthState !== "authenticated" && full) return "Continue";
3637
+ if (canRecoverToWaitlist(wizard.eligibility)) return "Join waitlist";
3638
+ if (!input.hasWaitlistClaim && full && wizard.eligibility?.registration.kind !== "pending_payment") return "Sold out";
3717
3639
  if (wizard.paymentSelection.method === "pass") return "Book using class pass";
3718
3640
  if (wizard.paymentSelection.method === "membership") return input.fallbackText;
3719
3641
  return wizard.eventRequiresPayment ? "Continue to payment" : input.fallbackText;
@@ -3728,44 +3650,28 @@ function isExistingRegistrationBlocking$1(wizard) {
3728
3650
  hasWaitlistClaim: wizard.waitlistClaim.claim !== null
3729
3651
  });
3730
3652
  }
3731
- /**
3732
- * Full for the visitor's selected attendance mode (or the whole event when no
3733
- * mode is chosen) with no waitlist to fall back on. Reads the same capacity
3734
- * source as the steps above the action bar so the button never disagrees
3735
- * with them on a hybrid event.
3736
- */
3737
- function isFullWithNoWaitlist(event, attendanceMode) {
3738
- if (!event) return false;
3739
- const source = selectEventCapacitySource(event, attendanceMode);
3740
- return getEventAvailability(source).isFull && !source.waitlistEnabled;
3741
- }
3742
3653
  function resolveTicketSelectionContinueLabel(input) {
3743
3654
  if (input.isCheckingExistingRegistration) return "Checking registration...";
3744
3655
  if (input.registrationOptionsPending) return "Checking availability...";
3745
3656
  if (input.registrationOptionsFailed) return "Availability unavailable";
3746
3657
  if (input.hasExistingRegistration) return resolveExistingRegistrationLabel(input.wizard);
3747
- if (input.isSoldOutWithoutWaitlist) return "Sold out";
3748
3658
  if (input.ticketTypeSelectionBlocked) return "Choose a ticket";
3749
3659
  return "Continue";
3750
3660
  }
3751
3661
  function SelectTicketsActions(props) {
3752
3662
  const { wizard } = props;
3753
- const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
3754
- const currentEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
3755
3663
  const registrationOptionsPending = wizard.registrationOptionsState.tag === "idle" || wizard.registrationOptionsState.tag === "loading";
3756
3664
  const registrationOptionsFailed = wizard.registrationOptionsState.tag === "failed";
3757
- const isSoldOutWithoutWaitlist = !wizard.checkoutRecovery && !hasWaitlistClaim && isFullWithNoWaitlist(currentEvent, wizard.selectedAttendanceMode);
3758
3665
  const isCheckingExistingRegistration = wizard.portalAuthState === "authenticated" && wizard.existingRegistrationLoading;
3759
3666
  const hasExistingRegistration = isExistingRegistrationBlocking$1(wizard);
3760
3667
  const ticketTypeSelectionBlocked = wizard.ticketTypeSelectionRequired && !wizard.hasValidTicketSelection;
3761
- const isContinueDisabled = registrationOptionsPending || registrationOptionsFailed || isCheckingExistingRegistration || hasExistingRegistration || isSoldOutWithoutWaitlist || ticketTypeSelectionBlocked;
3668
+ const isContinueDisabled = registrationOptionsPending || registrationOptionsFailed || isCheckingExistingRegistration || hasExistingRegistration || ticketTypeSelectionBlocked;
3762
3669
  const continueLabel = resolveTicketSelectionContinueLabel({
3763
3670
  wizard,
3764
3671
  registrationOptionsPending,
3765
3672
  registrationOptionsFailed,
3766
3673
  isCheckingExistingRegistration,
3767
3674
  hasExistingRegistration,
3768
- isSoldOutWithoutWaitlist,
3769
3675
  ticketTypeSelectionBlocked
3770
3676
  });
3771
3677
  return /* @__PURE__ */ jsxs("div", {
@@ -3796,8 +3702,12 @@ function EnterDetailsActions(props) {
3796
3702
  const { wizard } = props;
3797
3703
  const hasWaitlistClaim = wizard.waitlistClaim.claim !== null;
3798
3704
  const isCheckingEligibility = wizard.eligibilityLoading;
3799
- const eligibilityUnavailable = !hasWaitlistClaim && (Boolean(wizard.eligibilityError) || !wizard.eligibility || wizard.eligibility.availability.kind === "unavailable" || !wizard.eligibility.eligible);
3800
- const isSoldOut = !hasWaitlistClaim && wizard.eligibility?.isFull === true && wizard.eligibility.canJoinWaitlist !== true;
3705
+ const eligibilityUnavailable = Boolean(resolveRegistrationAvailabilityBlocker({
3706
+ hasWaitlistClaim,
3707
+ eligibility: wizard.eligibility,
3708
+ eligibilityError: wizard.eligibilityError
3709
+ }));
3710
+ const isSoldOut = !hasWaitlistClaim && wizard.portalAuthState === "authenticated" && wizard.eligibility?.registration.kind !== "pending_payment" && isSelectionSoldOut(wizard.eligibility?.availability);
3801
3711
  const isContinueDisabled = isCheckingEligibility || eligibilityUnavailable || isSoldOut;
3802
3712
  const continueLabel = isCheckingEligibility ? "Checking availability..." : wizard.eligibilityError ? "Availability unavailable" : eligibilityUnavailable ? "Registration unavailable" : resolveDetailsButtonText({
3803
3713
  wizard,
@@ -3848,6 +3758,7 @@ function EventRegistrationWizardActions(props) {
3848
3758
  case "redirecting":
3849
3759
  case "verifying-payment":
3850
3760
  case "verifying-timeout":
3761
+ case "payment-status":
3851
3762
  case "payment-failed": return null;
3852
3763
  case "select-event": return null;
3853
3764
  case "select-tickets": return /* @__PURE__ */ jsx(SelectTicketsActions, { ...props });
@@ -3932,7 +3843,7 @@ function buildSelectedSiteCreditPreview(input) {
3932
3843
  function resolvePaymentStepStatus(input) {
3933
3844
  if (input.loading) return "loading";
3934
3845
  if (input.error || !input.eligibility) return "error";
3935
- if (input.eligibility.registration.kind !== "none" || !input.eligibility.eligible && !input.eligibility.isFree) return "not-eligible";
3846
+ if (input.eligibility.registration.kind === "waitlisted" || input.eligibility.paymentOptions.length === 0) return "not-eligible";
3936
3847
  return "ready";
3937
3848
  }
3938
3849
  function shouldPromptForLogin(status, isAuthenticated, eligibility) {
@@ -3967,7 +3878,7 @@ function UnavailableEntitlementMessage(props) {
3967
3878
  ] });
3968
3879
  }
3969
3880
  function IneligiblePaymentPanel(props) {
3970
- if (props.eligibility.isFull && !props.eligibility.canJoinWaitlist) return /* @__PURE__ */ jsxs("div", {
3881
+ if (isSelectionSoldOut(props.eligibility.availability)) return /* @__PURE__ */ jsxs("div", {
3971
3882
  className: "rb-reg-stack er-stack",
3972
3883
  children: [/* @__PURE__ */ jsx("h3", {
3973
3884
  className: "er-title",
@@ -4036,26 +3947,23 @@ function PaymentBlockingPanel(props) {
4036
3947
  })
4037
3948
  });
4038
3949
  if (!props.eligibility) return /* @__PURE__ */ jsx(MissingEligibilityPanel, {});
4039
- if (props.eligibility.registration.kind !== "none") {
4040
- const isBooked = props.eligibility.registration.kind === "booked";
4041
- return /* @__PURE__ */ jsxs("div", {
4042
- className: "rb-reg-stack er-stack",
4043
- children: [/* @__PURE__ */ jsx("h3", {
4044
- className: "er-title",
4045
- children: isBooked ? "Already booked" : "Already on the waitlist"
4046
- }), /* @__PURE__ */ jsxs("div", {
4047
- className: "er-center",
4048
- children: [/* @__PURE__ */ jsx("p", {
4049
- className: "er-text er-text--muted",
4050
- children: isBooked ? "You already have a booking for this event. Please check your bookings to see its status. If you need help, contact the organiser." : "You're already on the waitlist for this event."
4051
- }), /* @__PURE__ */ jsx("a", {
4052
- href: "/portal",
4053
- className: "er-retry-btn",
4054
- children: "View my bookings"
4055
- })]
3950
+ if (props.eligibility.registration.kind === "waitlisted") return /* @__PURE__ */ jsxs("div", {
3951
+ className: "rb-reg-stack er-stack",
3952
+ children: [/* @__PURE__ */ jsx("h3", {
3953
+ className: "er-title",
3954
+ children: "Already on the waitlist"
3955
+ }), /* @__PURE__ */ jsxs("div", {
3956
+ className: "er-center",
3957
+ children: [/* @__PURE__ */ jsx("p", {
3958
+ className: "er-text er-text--muted",
3959
+ children: "You're already on the waitlist for this event."
3960
+ }), /* @__PURE__ */ jsx("a", {
3961
+ href: "/portal",
3962
+ className: "er-retry-btn",
3963
+ children: "View my bookings"
4056
3964
  })]
4057
- });
4058
- }
3965
+ })]
3966
+ });
4059
3967
  if (props.eligibility.isFree) return /* @__PURE__ */ jsxs("div", {
4060
3968
  className: "rb-reg-stack er-stack",
4061
3969
  children: [/* @__PURE__ */ jsx("h3", {
@@ -4076,7 +3984,7 @@ function PaymentBlockingPanel(props) {
4076
3984
  })]
4077
3985
  })]
4078
3986
  });
4079
- return props.eligibility.eligible ? null : /* @__PURE__ */ jsx(IneligiblePaymentPanel, {
3987
+ return props.eligibility.paymentOptions.length > 0 ? null : /* @__PURE__ */ jsx(IneligiblePaymentPanel, {
4080
3988
  eligibility: props.eligibility,
4081
3989
  email: props.email,
4082
3990
  isAuthenticated: props.isAuthenticated,
@@ -4208,7 +4116,7 @@ function SlidingScaleAmountField(props) {
4208
4116
  }
4209
4117
  function TicketTypeQuantityRows(props) {
4210
4118
  const totalSelectedQuantity = getTicketSelectionTotal(props.ticketSelections);
4211
- const effectiveMaxTickets = props.availableSpots == null ? props.maxTickets : Math.min(props.maxTickets, props.availableSpots);
4119
+ const effectiveMaxTickets = props.maxTickets;
4212
4120
  return /* @__PURE__ */ jsxs("div", {
4213
4121
  className: "er-ticket-lines",
4214
4122
  children: [
@@ -4218,16 +4126,14 @@ function TicketTypeQuantityRows(props) {
4218
4126
  "aria-label": props.bookingUnit === "place" ? "Places" : "Tickets",
4219
4127
  children: props.ticketTypes.map((ticketType) => {
4220
4128
  const quantity = getTicketLineQuantity(props.ticketSelections, ticketType.pricingOptionId);
4221
- const disabled = ticketType.availability.kind === "unavailable";
4129
+ const disabled = !canRequestTicketOption(ticketType.availability);
4222
4130
  const availabilityLabel = getTicketTypeAvailabilityLabel(ticketType);
4223
4131
  const ticketTypeRemainingLabel = formatTicketTypeRemaining(ticketType.availability.remainingPlaces);
4224
4132
  const priceLabel = ticketType.priceAmount > 0 ? formatPrice(ticketType.priceAmount, props.currency) : "Free";
4225
4133
  const maxAllowed = disabled ? 0 : getTicketTypeLineMax({
4226
4134
  currentQuantity: quantity,
4227
4135
  totalSelectedQuantity,
4228
- maxTickets: props.maxTickets,
4229
- availableSpots: props.availableSpots,
4230
- ticketTypeRemainingCapacity: ticketType.availability.remainingPlaces
4136
+ maxTickets: props.maxTickets
4231
4137
  });
4232
4138
  return /* @__PURE__ */ jsxs("div", {
4233
4139
  className: `er-ticket-line${disabled ? " er-ticket-line--disabled" : ""}`,
@@ -4332,33 +4238,13 @@ function AttendanceModeTiles(props) {
4332
4238
  })
4333
4239
  });
4334
4240
  }
4335
- function getMaxAllowedTicketCount(input) {
4336
- if (input.canJoinWaitlist) return input.maxTickets;
4337
- if (!input.usesTicketTypes) return Math.min(input.maxTickets, input.availableSpots ?? input.maxTickets);
4338
- if (!input.hasSelectedTicketType) return 1;
4339
- return Math.max(1, Math.min(input.maxTickets, input.selectedTicketTypeRemainingCapacity ?? input.maxTickets, input.availableSpots ?? input.maxTickets));
4340
- }
4341
4241
  function resolveTicketSelectionPricing(event) {
4342
4242
  return {
4343
4243
  mode: event ? publicEventPricingStrategy(event.pricing) : void 0,
4344
4244
  currency: event ? publicEventPricingCurrency(event.pricing) : null
4345
4245
  };
4346
4246
  }
4347
- function CheckoutRecoveryEntry(props) {
4348
- return props.onContinue ? /* @__PURE__ */ jsx("button", {
4349
- type: "button",
4350
- className: "er-back-btn",
4351
- onClick: props.onContinue,
4352
- children: "Continue an existing booking"
4353
- }) : null;
4354
- }
4355
- function getTicketSelectionCapacity(event, attendanceMode, checkoutRecovery) {
4356
- if (checkoutRecovery) return {
4357
- availableSpots: null,
4358
- isSoldOut: false,
4359
- canJoinWaitlist: false,
4360
- eventAvailabilityLabel: null
4361
- };
4247
+ function getTicketSelectionCapacity(event, attendanceMode) {
4362
4248
  const source = event ? selectEventCapacitySource(event, attendanceMode) : null;
4363
4249
  const availability = source ? getEventAvailability(source) : null;
4364
4250
  const canJoinWaitlist = source ? canJoinEventWaitlist(source) : false;
@@ -4377,29 +4263,21 @@ function getTicketSelectionCapacity(event, attendanceMode, checkoutRecovery) {
4377
4263
  *
4378
4264
  * Can use either a full PublicEvent or just occurrence context (when on event page).
4379
4265
  */
4380
- const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, selectedAttendanceMode = null, onAttendanceModeChange, ticketsCount, maxTickets, visibleTicketTypes: visibleTicketTypesOverride, ticketSelections = [], onTicketSelectionsChange, slidingScale = null, selectedSlidingAmountInput = "", slidingScaleSelectionMessage = null, onSlidingAmountChange, onTicketsChange, bookingUnit = { kind: "ticket" }, checkoutRecovery = false, onCheckoutRecovery }) => {
4266
+ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, selectedAttendanceMode = null, onAttendanceModeChange, ticketsCount, maxTickets, visibleTicketTypes: visibleTicketTypesOverride, ticketSelections = [], onTicketSelectionsChange, slidingScale = null, selectedSlidingAmountInput = "", slidingScaleSelectionMessage = null, onSlidingAmountChange, onTicketsChange, bookingUnit = { kind: "ticket" } }) => {
4381
4267
  const unit = bookingUnit.kind;
4382
4268
  const deliveryMode = event?.deliveryMode ?? "in_person";
4383
4269
  const attendanceModes = event?.attendanceModes ?? [];
4384
- const { availableSpots, isSoldOut, canJoinWaitlist, eventAvailabilityLabel } = getTicketSelectionCapacity(event, selectedAttendanceMode, checkoutRecovery);
4270
+ const { availableSpots, isSoldOut, canJoinWaitlist, eventAvailabilityLabel } = getTicketSelectionCapacity(event, selectedAttendanceMode);
4385
4271
  const { mode: eventPricingMode, currency: eventPricingCurrency } = resolveTicketSelectionPricing(event);
4386
- const usesTicketTypes = eventPricingMode === "tiered" && !canJoinWaitlist;
4272
+ const usesTicketTypes = eventPricingMode === "tiered";
4387
4273
  const visibleTicketTypes = usesTicketTypes ? visibleTicketTypesOverride ?? getVisibleTicketTypes({
4388
4274
  event,
4389
4275
  eligibility: null,
4390
4276
  selectedAttendanceMode
4391
4277
  }) : [];
4392
- const implicitTicketType = usesTicketTypes ? getImplicitSingleAvailableTicketType(visibleTicketTypes) : null;
4393
4278
  const noVisibleTicketTypes = usesTicketTypes && visibleTicketTypes.length === 0;
4394
- const hasAvailableTicketTypes = usesTicketTypes && visibleTicketTypes.some((ticketType) => ticketType.availability.kind === "available");
4395
- const maxAllowed = getMaxAllowedTicketCount({
4396
- canJoinWaitlist,
4397
- usesTicketTypes,
4398
- maxTickets,
4399
- availableSpots,
4400
- selectedTicketTypeRemainingCapacity: implicitTicketType?.availability.remainingPlaces ?? null,
4401
- hasSelectedTicketType: implicitTicketType !== null
4402
- });
4279
+ const hasAvailableTicketTypes = usesTicketTypes && visibleTicketTypes.some((ticketType) => canRequestTicketOption(ticketType.availability));
4280
+ const maxAllowed = maxTickets;
4403
4281
  if (deliveryMode === "hybrid" && !selectedAttendanceMode) return /* @__PURE__ */ jsxs("div", {
4404
4282
  className: "rb-reg-stack er-stack",
4405
4283
  children: [
@@ -4418,20 +4296,6 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
4418
4296
  })
4419
4297
  ]
4420
4298
  });
4421
- if (isSoldOut && !canJoinWaitlist) return /* @__PURE__ */ jsxs("div", {
4422
- className: "rb-reg-stack er-stack",
4423
- children: [
4424
- /* @__PURE__ */ jsx("h3", {
4425
- className: "er-title",
4426
- children: "Sold out"
4427
- }),
4428
- /* @__PURE__ */ jsx("p", {
4429
- className: "er-text er-text--muted",
4430
- children: "Sorry you missed out! This event is fully booked."
4431
- }),
4432
- /* @__PURE__ */ jsx(CheckoutRecoveryEntry, { onContinue: onCheckoutRecovery })
4433
- ]
4434
- });
4435
4299
  if (!eventPricingCurrency) return /* @__PURE__ */ jsx("div", {
4436
4300
  className: "rb-reg-stack er-stack",
4437
4301
  children: /* @__PURE__ */ jsx("p", {
@@ -4498,14 +4362,14 @@ const TicketSelectionStep = ({ event, occurrenceContext: _occurrenceContext, sel
4498
4362
  })]
4499
4363
  })
4500
4364
  ] }),
4501
- canJoinWaitlist && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
4365
+ isSoldOut && !canJoinWaitlist && /* @__PURE__ */ jsx("p", {
4502
4366
  className: "er-text er-text--muted",
4503
- children: "This event is currently full. Continue to join the waitlist if a place opens up."
4504
- }), /* @__PURE__ */ jsx(CheckoutRecoveryEntry, { onContinue: onCheckoutRecovery })] }),
4505
- checkoutRecovery && /* @__PURE__ */ jsx("p", {
4367
+ children: "This event is fully booked."
4368
+ }),
4369
+ canJoinWaitlist && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("p", {
4506
4370
  className: "er-text er-text--muted",
4507
- children: "Enter the details from your earlier booking. We’ll check your checkout before continuing to payment."
4508
- })
4371
+ children: "This event is currently full. Continue to join the waitlist if a place opens up."
4372
+ }) })
4509
4373
  ]
4510
4374
  });
4511
4375
  };
@@ -5100,7 +4964,7 @@ const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, i
5100
4964
  const occurrenceId = occurrenceContext?.id ?? (event?.registrationSubject.kind === "occurrence" ? event.registrationSubject.occurrenceId : "");
5101
4965
  const selectedMethod = selectedSelection.method;
5102
4966
  const totalPrice = directPaymentTotalCents;
5103
- const canShowDirectPaymentOptions = Boolean(eligibility?.eligible && eligibility.canPayDirectly && eligibility.price);
4967
+ const canShowDirectPaymentOptions = Boolean(eligibility?.requiresPayment && eligibility.price);
5104
4968
  const directPaymentOptions = eligibility && canShowDirectPaymentOptions ? buildEventRegistrationPaymentOptionsViewModel({
5105
4969
  eventTitle: event?.title ?? "Event",
5106
4970
  approvedPaymentOptions: eligibility.paymentOptions,
@@ -5163,7 +5027,7 @@ const PaymentSelectionStep = ({ event, occurrenceContext, ticketsCount, email, i
5163
5027
  onRequestLogin
5164
5028
  });
5165
5029
  if (eligibilityLoading || eligibilityError || !eligibility) return blockingPanel;
5166
- if (eligibility.isFree || !eligibility.eligible) return blockingPanel;
5030
+ if (eligibility.isFree || paymentStepStatus !== "ready") return blockingPanel;
5167
5031
  const isMultiTicket = areEntitlementsDisabledForTicketCount(ticketsCount);
5168
5032
  return /* @__PURE__ */ jsxs("div", {
5169
5033
  className: "rb-reg-stack er-stack",
@@ -5397,6 +5261,7 @@ function EventRegistrationWizardContent(props) {
5397
5261
  case "redirecting":
5398
5262
  case "verifying-payment":
5399
5263
  case "verifying-timeout":
5264
+ case "payment-status":
5400
5265
  case "payment-failed": return null;
5401
5266
  case "select-event": return /* @__PURE__ */ jsx(EventSelectionStep, {
5402
5267
  events: wizard.scopedEvents,
@@ -5412,8 +5277,6 @@ function EventRegistrationWizardContent(props) {
5412
5277
  onBookForSomeoneElse: wizard.handleBookForSomeoneElse
5413
5278
  }), /* @__PURE__ */ jsx(TicketSelectionStep, {
5414
5279
  event: wizard.selectedEvent,
5415
- checkoutRecovery: wizard.checkoutRecovery,
5416
- onCheckoutRecovery: canRecoverEventCheckout(wizard.selectedEvent) ? wizard.startCheckoutRecovery : void 0,
5417
5280
  occurrenceContext: wizard.occurrenceContext,
5418
5281
  selectedAttendanceMode: wizard.selectedAttendanceMode,
5419
5282
  onAttendanceModeChange: wizard.setSelectedAttendanceMode,
@@ -5468,6 +5331,81 @@ const PaymentFailedStep = ({ onRetry }) => {
5468
5331
  });
5469
5332
  };
5470
5333
  //#endregion
5334
+ //#region ../blocks/src/system/runtime/nodes/event-registration/PaymentStatusStep.tsx
5335
+ function presentation(state) {
5336
+ switch (state.kind) {
5337
+ case "ready": return {
5338
+ title: "We haven’t confirmed your payment yet",
5339
+ message: "We’ve saved your booking details, but we haven’t been able to confirm that your payment was successful. Please don’t make another payment. Select Check payment status to get the latest update.",
5340
+ action: { label: "Check payment status" }
5341
+ };
5342
+ case "checking": return {
5343
+ title: "We haven’t confirmed your payment yet",
5344
+ message: "We’ve saved your booking details, but we haven’t been able to confirm that your payment was successful. Please don’t make another payment.",
5345
+ action: {
5346
+ label: "Checking payment…",
5347
+ disabled: true
5348
+ }
5349
+ };
5350
+ case "processing": return {
5351
+ title: "Your payment is still being processed",
5352
+ message: "Please don’t make another payment. You can check its status again in a moment.",
5353
+ action: { label: "Check payment status" }
5354
+ };
5355
+ case "paymentAvailable": return {
5356
+ title: "Your payment hasn’t been completed",
5357
+ message: "Your booking details are still saved. Continue to payment to finish your booking.",
5358
+ action: { label: "Continue to payment" }
5359
+ };
5360
+ case "retryAvailable": return {
5361
+ title: "Continue your booking",
5362
+ message: "You can return to payment. We’ll check your booking, any existing payment and availability before starting another checkout.",
5363
+ action: { label: "Return to payment" }
5364
+ };
5365
+ case "temporarilyUnavailable": return {
5366
+ title: "We couldn’t check your payment",
5367
+ message: "Please don’t make another payment yet. Try checking again in a moment.",
5368
+ action: { label: "Check payment status" }
5369
+ };
5370
+ case "assistanceRequired": return {
5371
+ title: "We still can’t confirm your payment",
5372
+ message: "Please don’t make another payment. Contact the organiser so they can check your booking.",
5373
+ action: { label: "Return to booking" }
5374
+ };
5375
+ }
5376
+ }
5377
+ function PaymentStatusStep(props) {
5378
+ const view = presentation(props.state);
5379
+ const onClick = props.state.kind === "paymentAvailable" ? props.onContinuePayment : props.state.kind === "retryAvailable" ? props.onReturnToPayment : props.state.kind === "assistanceRequired" ? props.onReturnToBooking : props.onCheck;
5380
+ return /* @__PURE__ */ jsx(TerminalPanel, {
5381
+ tone: "warning",
5382
+ className: "er-center",
5383
+ titleClassName: "er-title--lg er-message",
5384
+ icon: /* @__PURE__ */ jsx(StateIcon, {
5385
+ variant: "warning",
5386
+ children: /* @__PURE__ */ jsx(ClockIcon, {})
5387
+ }),
5388
+ title: view.title,
5389
+ body: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
5390
+ className: "er-text--muted er-message",
5391
+ children: view.message
5392
+ }), /* @__PURE__ */ jsx("span", {
5393
+ className: "rb-sr-only",
5394
+ "aria-live": "polite",
5395
+ "aria-atomic": "true",
5396
+ children: props.state.kind === "checking" ? "Checking payment…" : view.title
5397
+ })] }),
5398
+ actionsClassName: "er-button-group",
5399
+ actions: view.action ? /* @__PURE__ */ jsx("button", {
5400
+ type: "button",
5401
+ onClick,
5402
+ disabled: view.action.disabled,
5403
+ className: "er-btn er-btn--primary",
5404
+ children: /* @__PURE__ */ jsx("span", { children: view.action.label })
5405
+ }) : void 0
5406
+ });
5407
+ }
5408
+ //#endregion
5471
5409
  //#region ../blocks/src/system/runtime/nodes/event-registration/StepIndicator.tsx
5472
5410
  /**
5473
5411
  * Step indicator component for the event registration wizard.
@@ -5585,7 +5523,7 @@ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "We’re che
5585
5523
  title,
5586
5524
  body: /* @__PURE__ */ jsx("p", {
5587
5525
  className: "er-text--muted er-message",
5588
- children: message
5526
+ children: onCheckAgain ? message : "We still can’t confirm your payment. Please don’t make another payment. Contact the organiser so they can check your booking."
5589
5527
  }),
5590
5528
  actionsClassName: "er-button-group",
5591
5529
  actions: onCheckAgain ? /* @__PURE__ */ jsx("button", {
@@ -5593,11 +5531,7 @@ const VerifyingTimeoutStep = ({ onCheckAgain, supportEmail, title = "We’re che
5593
5531
  onClick: onCheckAgain,
5594
5532
  className: "er-btn er-btn--primary",
5595
5533
  children: "Check payment status"
5596
- }) : /* @__PURE__ */ jsx("a", {
5597
- href: "/portal",
5598
- className: "er-btn er-btn--primary",
5599
- children: "Check payment status"
5600
- }),
5534
+ }) : void 0,
5601
5535
  secondaryBody: supportEmail ? /* @__PURE__ */ jsxs("p", {
5602
5536
  className: "er-text--sm er-text--muted er-message",
5603
5537
  children: [
@@ -5698,6 +5632,13 @@ function EventRegistrationTerminalState(props) {
5698
5632
  message: wizard.verification.status === "timeout" ? void 0 : wizard.errorMessage,
5699
5633
  supportEmail: props.supportEmail
5700
5634
  });
5635
+ case "payment-status": return wizard.paymentStatusRecovery ? /* @__PURE__ */ jsx(PaymentStatusStep, {
5636
+ state: wizard.paymentStatusRecovery,
5637
+ onCheck: wizard.handleCheckPaymentStatus,
5638
+ onContinuePayment: wizard.handleContinueExistingPayment,
5639
+ onReturnToPayment: wizard.handleReturnToPayment,
5640
+ onReturnToBooking: wizard.handleReturnToBooking
5641
+ }) : null;
5701
5642
  case "success": return wizard.registrationResult ? /* @__PURE__ */ jsx(SuccessStep, {
5702
5643
  title: wizard.registrationResult.title,
5703
5644
  variant: wizard.registrationResult.variant,
@@ -5706,7 +5647,7 @@ function EventRegistrationTerminalState(props) {
5706
5647
  case "error": return /* @__PURE__ */ jsx(ErrorStep, {
5707
5648
  message: wizard.errorMessage,
5708
5649
  action: wizard.errorAction,
5709
- canJoinWaitlist: wizard.eligibility?.isFull && wizard.eligibility.canJoinWaitlist,
5650
+ canJoinWaitlist: wizard.eligibility?.availability.kind === "waitlist" && wizard.eligibility.registration.kind !== "pending_payment",
5710
5651
  onRetry: wizard.handleRetry
5711
5652
  });
5712
5653
  case "select-event":
@@ -5900,6 +5841,7 @@ function getWizardStepLabel(input) {
5900
5841
  case "redirecting":
5901
5842
  case "verifying-payment":
5902
5843
  case "verifying-timeout":
5844
+ case "payment-status":
5903
5845
  case "payment-failed": return "";
5904
5846
  }
5905
5847
  }