@riverbankcms/sdk 0.91.0 → 0.93.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 (118) hide show
  1. package/dist/{PreviewEditorSidebar-3TT47WCF.mjs → PreviewEditorSidebar-D3PAF7NW.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-ORCLLUNZ.mjs → PreviewEditorUI-G5JVJEIJ.mjs} +3 -3
  3. package/dist/SdkPreviewModeRuntime-NHTLRNQD.mjs +8 -0
  4. package/dist/_dts/api/src/bookingConfig.d.ts +13 -1
  5. package/dist/_dts/api/src/bookingOperations.d.ts +1 -0
  6. package/dist/_dts/api/src/bookingRefundOutcomes.d.ts +6 -6
  7. package/dist/_dts/api/src/common/envelope.d.ts +10 -0
  8. package/dist/_dts/api/src/customerMembershipOutcomes.d.ts +4 -4
  9. package/dist/_dts/api/src/index.d.ts +2 -2
  10. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +6 -0
  11. package/dist/_dts/api/src/request.d.ts +1 -1
  12. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +32 -0
  13. package/dist/_dts/blocks/src/system/blocks/courses/shared/pricing.d.ts +12 -1
  14. package/dist/_dts/blocks/src/system/blocks/courses/shared/schemas.d.ts +12 -0
  15. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +4 -1
  16. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +44 -0
  17. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +8 -0
  18. package/dist/_dts/blocks/src/system/runtime/api/types.d.ts +8 -1
  19. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  20. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -0
  21. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +4 -0
  22. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +40 -0
  23. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.reserved-fields.d.ts +1 -1
  24. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +29 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +1 -0
  26. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/components.d.ts +6 -0
  27. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.d.ts +8 -0
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +8 -0
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +1 -0
  30. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +1 -1
  31. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +1 -0
  32. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +9 -0
  33. package/dist/_dts/content-editor/src/controller/EntryControllerProvider.d.ts +14 -14
  34. package/dist/_dts/content-editor/src/controller/useEntrySyncHelpers.d.ts +1 -1
  35. package/dist/_dts/content-editor/src/hooks/useEntryActions.d.ts +5 -15
  36. package/dist/_dts/content-editor/src/ui/editorSnapshot.d.ts +46 -0
  37. package/dist/_dts/content-editor/src/ui/editorUiStore.d.ts +43 -36
  38. package/dist/_dts/content-model/src/entry-slots/types.d.ts +110 -2
  39. package/dist/_dts/core/src/branded.d.ts +3 -0
  40. package/dist/_dts/core/src/event-pricing.d.ts +18 -1
  41. package/dist/_dts/core/src/money-input.d.ts +34 -4
  42. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +70 -0
  43. package/dist/_dts/media-core/src/fileAssetCompatibility.d.ts +1 -1
  44. package/dist/_dts/media-core/src/filePersistence.d.ts +5 -1
  45. package/dist/_dts/media-core/src/fileStorage.d.ts +1 -0
  46. package/dist/_dts/media-core/src/fileUploadFinalization.d.ts +29 -1
  47. package/dist/_dts/media-core/src/index.d.ts +1 -0
  48. package/dist/_dts/media-core/src/legacyMediaValue.d.ts +11 -0
  49. package/dist/_dts/media-core/src/videoPrep.d.ts +15 -6
  50. package/dist/_dts/media-core/src/videoSlotPolicy.d.ts +5 -5
  51. package/dist/_dts/media-react/src/hooks/useApiMediaUploader.d.ts +2 -1
  52. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +2 -0
  53. package/dist/_dts/sdk/src/cli/commands/push/execute/localMediaReporting.d.ts +23 -0
  54. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +54 -0
  55. package/dist/_dts/sdk/src/cli/commands/push/scopes/mediaPrep.d.ts +7 -2
  56. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +6 -0
  57. package/dist/_dts/sdk/src/cli/media/local-sync.d.ts +35 -0
  58. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +23 -1
  59. package/dist/_dts/sdk/src/version.d.ts +1 -1
  60. package/dist/{chunk-GDT2QGIL.mjs → chunk-A63UXN3P.mjs} +21 -19
  61. package/dist/{chunk-U5YM4AWE.mjs → chunk-AJZGT6YG.mjs} +1 -1
  62. package/dist/{chunk-AFPMCLS7.mjs → chunk-AQ6RT6LZ.mjs} +2 -2
  63. package/dist/{chunk-ZA2S7H34.mjs → chunk-NT76QGIJ.mjs} +98 -1
  64. package/dist/{chunk-RQUZYSES.mjs → chunk-PCNCETBI.mjs} +1196 -426
  65. package/dist/{chunk-S3YHJWEU.mjs → chunk-XIK2HYI3.mjs} +7 -7
  66. package/dist/cli/index.mjs +749 -166
  67. package/dist/client/{accordion-AFX37652.mjs → accordion-RNQIPG55.mjs} +2 -2
  68. package/dist/client/{bookingForm-32V5QCOW.mjs → bookingForm-HYEEVRLP.mjs} +89 -14
  69. package/dist/client/bookings.mjs +243 -17
  70. package/dist/client/{carousel-TJGQJ7ER.mjs → carousel-W7DQ3YPU.mjs} +3 -3
  71. package/dist/client/{cart-JUU2JEW4.mjs → cart-PXJ3SXXO.mjs} +7 -7
  72. package/dist/client/{checkout-RD6BTFX4.mjs → checkout-X3JYSOXI.mjs} +7 -7
  73. package/dist/client/{chunk-MLBNVN4H.mjs → chunk-6SYGVVWA.mjs} +67 -4
  74. package/dist/client/{chunk-E6VIWZUG.mjs → chunk-DUOHLWLF.mjs} +3 -3
  75. package/dist/client/{chunk-6QZIBNGV.mjs → chunk-IXDSXF2D.mjs} +154 -16
  76. package/dist/client/{chunk-CKXEUWWA.mjs → chunk-J6TMMMXW.mjs} +3 -3
  77. package/dist/client/{chunk-PMNAV6LO.mjs → chunk-JNE6UI6U.mjs} +7 -6
  78. package/dist/client/{chunk-ISAUXU5R.mjs → chunk-OLAOAZEO.mjs} +1 -1
  79. package/dist/client/{chunk-RWYFG625.mjs → chunk-PCHACQEB.mjs} +30 -2
  80. package/dist/client/{chunk-FMZZHAIR.mjs → chunk-PICR3NTQ.mjs} +52 -5
  81. package/dist/client/{chunk-6RN62XBV.mjs → chunk-Q47XGAGS.mjs} +1 -1
  82. package/dist/client/{chunk-WHZQDPBZ.mjs → chunk-TNGL2WJF.mjs} +1 -0
  83. package/dist/client/{chunk-C4LP6BPG.mjs → chunk-WERRJXNJ.mjs} +1 -1
  84. package/dist/client/{chunk-RMOFBBNA.mjs → chunk-YYVGSYBO.mjs} +6 -6
  85. package/dist/client/client.mjs +641 -54
  86. package/dist/client/{courseRegistration-X63QAIVV.mjs → courseRegistration-QZ67PVHO.mjs} +111 -11
  87. package/dist/client/{eventCalendar-5T24GCZL.mjs → eventCalendar-V3BDHQNT.mjs} +9 -9
  88. package/dist/client/{eventCombined-AZVTF22X.mjs → eventCombined-XYFRN2RG.mjs} +9 -9
  89. package/dist/client/{eventDetails-7GPFNLGF.mjs → eventDetails-TFE5TWKV.mjs} +9 -9
  90. package/dist/client/{eventListing-QEUMTMUM.mjs → eventListing-IQ6AGWZ6.mjs} +9 -9
  91. package/dist/client/{eventRegistration-P2EDKCQZ.mjs → eventRegistration-WGJVFKQJ.mjs} +9 -9
  92. package/dist/client/{form-NUJ7H4IN.mjs → form-6UZY6RUA.mjs} +3 -3
  93. package/dist/client/{headerSection-JB7LECPL.mjs → headerSection-RSFXY6V3.mjs} +3 -3
  94. package/dist/client/hooks.mjs +57 -5
  95. package/dist/client/{locationMap-UEYWUCSW.mjs → locationMap-24FH67I4.mjs} +3 -3
  96. package/dist/client/{newsletterForm-S2YZQ2X7.mjs → newsletterForm-QVL3MLTN.mjs} +3 -3
  97. package/dist/client/{productDetail-H5MADGG7.mjs → productDetail-ZDYN3G4T.mjs} +7 -7
  98. package/dist/client/{productList-DK7YON7H.mjs → productList-4BMJLQHZ.mjs} +7 -7
  99. package/dist/client/rendering/client.mjs +503 -36
  100. package/dist/client/rendering/islands.mjs +19 -19
  101. package/dist/client/rendering.mjs +583 -39
  102. package/dist/client/{shop-6PA7RDRJ.mjs → shop-U5C634FF.mjs} +7 -7
  103. package/dist/preview-next/client/runtime.mjs +5 -5
  104. package/dist/{sdk-runtime-JP5347OB.mjs → sdk-runtime-VGV46WVB.mjs} +6 -6
  105. package/dist/server/components.mjs +287 -68
  106. package/dist/server/config-validation.mjs +58 -5
  107. package/dist/server/config.mjs +58 -5
  108. package/dist/server/data.mjs +57 -5
  109. package/dist/server/index.mjs +104 -29
  110. package/dist/server/next.mjs +347 -85
  111. package/dist/server/page-converter.mjs +22 -5
  112. package/dist/server/prebuild.mjs +1 -1
  113. package/dist/server/rendering/server.mjs +287 -68
  114. package/dist/server/rendering.mjs +287 -68
  115. package/dist/server/routing.mjs +131 -67
  116. package/dist/server/server.mjs +189 -94
  117. package/package.json +1 -1
  118. package/dist/SdkPreviewModeRuntime-GMCDQPZZ.mjs +0 -8
@@ -826,6 +826,7 @@ function createBlockApi(config) {
826
826
  body: {
827
827
  courseId: data.courseId,
828
828
  ...data.pricingOptionId ? { pricingOptionId: data.pricingOptionId } : {},
829
+ ...data.selectedSlidingAmount !== void 0 ? { selectedSlidingAmount: data.selectedSlidingAmount } : {},
829
830
  email: data.email,
830
831
  name: data.name,
831
832
  phone: data.phone,
@@ -2986,12 +2987,13 @@ var init_result = __esm({
2986
2987
  });
2987
2988
 
2988
2989
  // ../core/src/branded.ts
2989
- var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
2990
+ var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsStoragePath, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
2990
2991
  var init_branded = __esm({
2991
2992
  "../core/src/branded.ts"() {
2992
2993
  "use strict";
2993
2994
  unsafeAsMediaAssetId = (id) => id;
2994
2995
  unsafeAsStorageBucketName = (value) => value;
2996
+ unsafeAsStoragePath = (value) => value;
2995
2997
  unsafeAsPositiveByteCount = (value) => value;
2996
2998
  unsafeAsPositiveSeconds = (value) => value;
2997
2999
  unsafeAsFormId = (id) => id;
@@ -3137,6 +3139,7 @@ function publicEventPricingType(pricing) {
3137
3139
  return "free";
3138
3140
  case "single_paid":
3139
3141
  case "ticket_types":
3142
+ case "sliding_scale":
3140
3143
  case "course":
3141
3144
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
3142
3145
  case "external":
@@ -3149,6 +3152,7 @@ function publicEventPricingCurrency(pricing) {
3149
3152
  case "free_registration":
3150
3153
  case "single_paid":
3151
3154
  case "ticket_types":
3155
+ case "sliding_scale":
3152
3156
  return pricing.currency;
3153
3157
  case "course":
3154
3158
  return pricing.display.display.currency;
@@ -3166,6 +3170,8 @@ function publicEventPricingMode(pricing) {
3166
3170
  return "single";
3167
3171
  case "ticket_types":
3168
3172
  return "ticket_types";
3173
+ case "sliding_scale":
3174
+ return "sliding_scale";
3169
3175
  case "drop_in":
3170
3176
  case "course":
3171
3177
  case "external":
@@ -3199,6 +3205,8 @@ function formatCoursePriceDisplayLabel(display, options) {
3199
3205
  return `Course (${formatCurrencyAmount(display.amount, display.currency, options)})`;
3200
3206
  case "from":
3201
3207
  return `Course (From ${formatCurrencyAmount(display.amount, display.currency, options)})`;
3208
+ case "range":
3209
+ return `Course (${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)})`;
3202
3210
  default:
3203
3211
  return assertNever2(display);
3204
3212
  }
@@ -3950,9 +3958,20 @@ var init_types5 = __esm({
3950
3958
  });
3951
3959
 
3952
3960
  // ../media-core/src/fileSafety.ts
3961
+ function containsUnsafePathSegmentCharacter(value) {
3962
+ return value.includes("/") || value.includes("\\") || CONTROL_CHARACTER_PATTERN.test(value);
3963
+ }
3964
+ function isReservedPathSegment(value) {
3965
+ return value === "." || value === "..";
3966
+ }
3967
+ function isUnsafePathSegment(segment) {
3968
+ return segment.trim().length === 0 || segment !== segment.trim() || isReservedPathSegment(segment) || containsUnsafePathSegmentCharacter(segment);
3969
+ }
3970
+ var CONTROL_CHARACTER_PATTERN;
3953
3971
  var init_fileSafety = __esm({
3954
3972
  "../media-core/src/fileSafety.ts"() {
3955
3973
  "use strict";
3974
+ CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
3956
3975
  }
3957
3976
  });
3958
3977
 
@@ -3992,11 +4011,21 @@ var init_fileDomain = __esm({
3992
4011
  });
3993
4012
 
3994
4013
  // ../media-core/src/fileStorage.ts
4014
+ function parseStoragePath(value) {
4015
+ return joinStoragePathSegments(value.split("/"));
4016
+ }
4017
+ function joinStoragePathSegments(segments) {
4018
+ if (segments.some(isUnsafePathSegment)) {
4019
+ return err("unsafe-path-segment");
4020
+ }
4021
+ return ok(unsafeAsStoragePath(segments.join("/")));
4022
+ }
3995
4023
  var FILE_STORAGE_BUCKETS, PUBLIC_SITE_FILE_STORAGE_BUCKETS, PRIVATE_SITE_FILE_BUCKET_MIME_TYPES, TEMP_UPLOAD_BUCKET_MIME_TYPES;
3996
4024
  var init_fileStorage = __esm({
3997
4025
  "../media-core/src/fileStorage.ts"() {
3998
4026
  "use strict";
3999
4027
  init_src();
4028
+ init_fileSafety();
4000
4029
  FILE_STORAGE_BUCKETS = {
4001
4030
  publicSite: unsafeAsStorageBucketName("media"),
4002
4031
  privateSite: unsafeAsStorageBucketName("site-files-private"),
@@ -4182,14 +4211,18 @@ var init_uploadVerificationContract = __esm({
4182
4211
  });
4183
4212
 
4184
4213
  // ../media-core/src/videoPrep.ts
4214
+ function unsafeAsPositiveVideoDurationSeconds(value) {
4215
+ return value;
4216
+ }
4185
4217
  var VIDEO_PREP_LIMITS;
4186
4218
  var init_videoPrep = __esm({
4187
4219
  "../media-core/src/videoPrep.ts"() {
4188
4220
  "use strict";
4221
+ init_src();
4189
4222
  VIDEO_PREP_LIMITS = {
4190
- maxRawByteCount: 500 * 1024 * 1024,
4191
- inlineContentMaxDurationSeconds: 60,
4192
- backgroundVideoMaxDurationSeconds: 30,
4223
+ maxRawByteCount: unsafeAsPositiveByteCount(500 * 1024 * 1024),
4224
+ inlineContentMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(60),
4225
+ backgroundVideoMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(30),
4193
4226
  targetMaxDimensionPixels: 1080
4194
4227
  };
4195
4228
  }
@@ -4260,10 +4293,10 @@ var init_identifiers = __esm({
4260
4293
 
4261
4294
  // ../media-core/src/url.ts
4262
4295
  function isRelativeDirectImageStoragePath(storagePath) {
4263
- return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix));
4296
+ return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix)) && parseStoragePath(storagePath).ok;
4264
4297
  }
4265
4298
  function isSiteScopedDirectImageStoragePath(storagePath) {
4266
- return storagePath.startsWith("sites/");
4299
+ return storagePath.startsWith("sites/") && parseStoragePath(storagePath).ok;
4267
4300
  }
4268
4301
  function encodeStoragePath(storagePath) {
4269
4302
  return storagePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
@@ -4344,6 +4377,7 @@ var DEFAULT_BASE_PATH, RELATIVE_STORAGE_PATH_PREFIXES, formatNumber, ORDER, getI
4344
4377
  var init_url = __esm({
4345
4378
  "../media-core/src/url.ts"() {
4346
4379
  "use strict";
4380
+ init_fileStorage();
4347
4381
  DEFAULT_BASE_PATH = "/api/img";
4348
4382
  RELATIVE_STORAGE_PATH_PREFIXES = [
4349
4383
  "originals/",
@@ -4587,6 +4621,13 @@ var init_renderableMediaAsset = __esm({
4587
4621
  }
4588
4622
  });
4589
4623
 
4624
+ // ../media-core/src/legacyMediaValue.ts
4625
+ var init_legacyMediaValue = __esm({
4626
+ "../media-core/src/legacyMediaValue.ts"() {
4627
+ "use strict";
4628
+ }
4629
+ });
4630
+
4590
4631
  // ../media-core/src/index.ts
4591
4632
  var init_src2 = __esm({
4592
4633
  "../media-core/src/index.ts"() {
@@ -4629,6 +4670,7 @@ var init_src2 = __esm({
4629
4670
  init_mediaUsageProjection();
4630
4671
  init_portableMediaReferences();
4631
4672
  init_renderableMediaAsset();
4673
+ init_legacyMediaValue();
4632
4674
  }
4633
4675
  });
4634
4676
 
@@ -7098,6 +7140,7 @@ var init_booking_form_reserved_fields = __esm({
7098
7140
  "selectedDate",
7099
7141
  "selectedSlot",
7100
7142
  "discountCode",
7143
+ "selectedSlidingAmountInput",
7101
7144
  "selectedCollectionOptionKind",
7102
7145
  "selectedAppointmentPackageId"
7103
7146
  ];
@@ -7383,6 +7426,32 @@ function readOptionalString(value) {
7383
7426
  function readOptionalNumber(value) {
7384
7427
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
7385
7428
  }
7429
+ function getBookingServiceSlidingScale(service) {
7430
+ const pricing = service?.pricing;
7431
+ if (!pricing || pricing.pricingStrategy !== "sliding_scale" || pricing.slidingMinAmount === null || pricing.slidingMinAmount === void 0 || pricing.slidingMaxAmount === null || pricing.slidingMaxAmount === void 0) {
7432
+ return null;
7433
+ }
7434
+ return {
7435
+ minAmount: pricing.slidingMinAmount,
7436
+ maxAmount: pricing.slidingMaxAmount,
7437
+ suggestedAmount: pricing.slidingSuggestedAmount ?? null,
7438
+ currency: pricing.currency
7439
+ };
7440
+ }
7441
+ function parseBookingCurrencyInputToCents(value) {
7442
+ const trimmed = value?.trim() ?? "";
7443
+ if (!trimmed) return null;
7444
+ if (!/^(?:\d+(?:\.\d{0,2})?|\.\d{1,2})$/.test(trimmed)) return null;
7445
+ const parsed = Number(trimmed);
7446
+ if (!Number.isFinite(parsed) || parsed < 0) return null;
7447
+ return Math.round(parsed * 100);
7448
+ }
7449
+ function formatBookingCentsAsCurrencyInput(amount) {
7450
+ return amount % 100 === 0 ? String(amount / 100) : (amount / 100).toFixed(2);
7451
+ }
7452
+ function isBookingServiceSlidingAmountInRange(slidingScale, selectedAmount) {
7453
+ return selectedAmount !== null && selectedAmount >= slidingScale.minAmount && selectedAmount <= slidingScale.maxAmount;
7454
+ }
7386
7455
  function readStringArray(value) {
7387
7456
  if (!Array.isArray(value)) return void 0;
7388
7457
  const strings = value.filter((item) => typeof item === "string");
@@ -7571,6 +7640,20 @@ function prepareBookingSubmission(input) {
7571
7640
  };
7572
7641
  }
7573
7642
  const paymentSelectionMode = getBookingServicePaymentSelectionMode(selectedService);
7643
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
7644
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
7645
+ input.data.selectedSlidingAmountInput
7646
+ );
7647
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(slidingScale, selectedSlidingAmount)) {
7648
+ return {
7649
+ ok: false,
7650
+ error: {
7651
+ tag: selectedSlidingAmount === null ? "sliding-amount-required" : "sliding-amount-out-of-range",
7652
+ serviceId,
7653
+ serviceTitle: selectedService.title || serviceId
7654
+ }
7655
+ };
7656
+ }
7574
7657
  if (paymentSelectionMode === "choice_required" && !toSelectedCollectionOptionKind(input.data.selectedCollectionOptionKind)) {
7575
7658
  return {
7576
7659
  ok: false,
@@ -7598,6 +7681,7 @@ function prepareBookingSubmission(input) {
7598
7681
  ...common,
7599
7682
  kind: "standard",
7600
7683
  ...discountCode ? { discountCode } : {},
7684
+ ...slidingScale && selectedSlidingAmount !== null ? { selectedSlidingAmount } : {},
7601
7685
  ...selectedCollectionOptionKind ? { selectedCollectionOptionKind } : {},
7602
7686
  altchaSolution: input.altchaSolution ?? null
7603
7687
  }
@@ -7611,6 +7695,10 @@ function formatBookingSubmissionPreparationError(error) {
7611
7695
  return `Service not found: ${error.serviceId}`;
7612
7696
  case "payment-option-required":
7613
7697
  return `Please choose how you want to pay for "${error.serviceTitle}".`;
7698
+ case "sliding-amount-required":
7699
+ return `Please choose an amount for "${error.serviceTitle}".`;
7700
+ case "sliding-amount-out-of-range":
7701
+ return `Please choose an amount within the sliding scale for "${error.serviceTitle}".`;
7614
7702
  case "payment-option-unsupported":
7615
7703
  return `Service "${error.serviceTitle}" has a payment option that is not supported yet.`;
7616
7704
  case "service-missing-duration":
@@ -7831,6 +7919,7 @@ var init_booking_form_interactive = __esm({
7831
7919
  title: z10.string(),
7832
7920
  description: z10.string().nullable().optional().transform((value) => value ?? void 0),
7833
7921
  durationMinutes: z10.number().optional(),
7922
+ pricing: z10.custom().optional(),
7834
7923
  priceAmount: z10.number().nullable().optional(),
7835
7924
  priceCurrency: z10.string().nullable().optional(),
7836
7925
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
@@ -8117,9 +8206,12 @@ var init_schemas = __esm({
8117
8206
  });
8118
8207
  coursePricingSchema = z11.object({
8119
8208
  bookingRequirement: z11.enum(["none", "required"]),
8120
- pricingStrategy: z11.enum(["free", "single", "tiered"]),
8209
+ pricingStrategy: z11.enum(["free", "single", "tiered", "sliding_scale"]),
8121
8210
  currency: z11.string(),
8122
8211
  singlePriceAmount: z11.number().nullable(),
8212
+ slidingMinAmount: z11.number().nullable().optional(),
8213
+ slidingMaxAmount: z11.number().nullable().optional(),
8214
+ slidingSuggestedAmount: z11.number().nullable().optional(),
8123
8215
  paymentTerms: blockBookingPaymentTermsSchema,
8124
8216
  options: z11.array(coursePricingOptionSchema)
8125
8217
  });
@@ -8181,7 +8273,9 @@ function getSelectableCoursePricingOptionsForNow(course, now) {
8181
8273
  return left.name.localeCompare(right.name);
8182
8274
  });
8183
8275
  }
8184
- function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {}) {
8276
+ function resolveCoursePricingState(course, selectedPricingOptionId, selectedSlidingAmountOrNowInput, nowInput = {}) {
8277
+ const selectedSlidingAmount = typeof selectedSlidingAmountOrNowInput === "number" || selectedSlidingAmountOrNowInput === null ? selectedSlidingAmountOrNowInput : null;
8278
+ const effectiveNowInput = typeof selectedSlidingAmountOrNowInput === "object" && selectedSlidingAmountOrNowInput !== null ? selectedSlidingAmountOrNowInput : nowInput;
8185
8279
  const currency = course.pricing?.currency ?? course.currency;
8186
8280
  if (!course.pricing) {
8187
8281
  const legacyAmount = course.priceCents ?? 0;
@@ -8191,6 +8285,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
8191
8285
  selectableOptions: [],
8192
8286
  requiresSelection: false,
8193
8287
  selectedOption: null,
8288
+ slidingScale: null,
8289
+ selectedSlidingAmount: null,
8194
8290
  selectedPriceAmount: legacyAmount,
8195
8291
  selectedRequiresPayment: legacyAmount > 0,
8196
8292
  hasPaidSelection: legacyAmount > 0,
@@ -8205,6 +8301,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
8205
8301
  selectableOptions: [],
8206
8302
  requiresSelection: false,
8207
8303
  selectedOption: null,
8304
+ slidingScale: null,
8305
+ selectedSlidingAmount: null,
8208
8306
  selectedPriceAmount: 0,
8209
8307
  selectedRequiresPayment: false,
8210
8308
  hasPaidSelection: false,
@@ -8218,6 +8316,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
8218
8316
  selectableOptions: [],
8219
8317
  requiresSelection: false,
8220
8318
  selectedOption: null,
8319
+ slidingScale: null,
8320
+ selectedSlidingAmount: null,
8221
8321
  selectedPriceAmount: amount,
8222
8322
  selectedRequiresPayment: amount > 0,
8223
8323
  hasPaidSelection: amount > 0,
@@ -8227,7 +8327,7 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
8227
8327
  case "tiered": {
8228
8328
  const selectableOptions = getSelectableCoursePricingOptionsForNow(
8229
8329
  course,
8230
- resolvePlanningDateInput(nowInput)
8330
+ resolvePlanningDateInput(effectiveNowInput)
8231
8331
  );
8232
8332
  const selectedOption = (selectedPricingOptionId ? selectableOptions.find((option) => option.id === selectedPricingOptionId) : null) ?? (selectableOptions.length === 1 ? selectableOptions[0] ?? null : null);
8233
8333
  const lowestAmount = selectableOptions.reduce((lowest, option) => {
@@ -8243,12 +8343,51 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
8243
8343
  selectableOptions,
8244
8344
  requiresSelection: selectableOptions.length > 1,
8245
8345
  selectedOption,
8346
+ slidingScale: null,
8347
+ selectedSlidingAmount: null,
8246
8348
  selectedPriceAmount: selectedOption?.priceAmount ?? null,
8247
8349
  selectedRequiresPayment: (selectedOption?.priceAmount ?? 0) > 0,
8248
8350
  hasPaidSelection: selectableOptions.some((option) => option.priceAmount > 0),
8249
8351
  hasFreeSelection: selectableOptions.some((option) => option.priceAmount === 0)
8250
8352
  };
8251
8353
  }
8354
+ case "sliding_scale": {
8355
+ const minAmount = course.pricing.slidingMinAmount ?? null;
8356
+ const maxAmount = course.pricing.slidingMaxAmount ?? null;
8357
+ if (minAmount === null || maxAmount === null || minAmount < 0 || maxAmount < minAmount) {
8358
+ return {
8359
+ currency,
8360
+ display: { kind: "unavailable", currency },
8361
+ selectableOptions: [],
8362
+ requiresSelection: true,
8363
+ selectedOption: null,
8364
+ slidingScale: null,
8365
+ selectedSlidingAmount: null,
8366
+ selectedPriceAmount: null,
8367
+ selectedRequiresPayment: false,
8368
+ hasPaidSelection: false,
8369
+ hasFreeSelection: false
8370
+ };
8371
+ }
8372
+ const normalizedSelectedAmount = typeof selectedSlidingAmount === "number" && Number.isFinite(selectedSlidingAmount) ? selectedSlidingAmount : null;
8373
+ return {
8374
+ currency,
8375
+ display: { kind: "range", minAmount, maxAmount, currency },
8376
+ selectableOptions: [],
8377
+ requiresSelection: normalizedSelectedAmount === null,
8378
+ selectedOption: null,
8379
+ slidingScale: {
8380
+ minAmount,
8381
+ maxAmount,
8382
+ suggestedAmount: course.pricing.slidingSuggestedAmount ?? null
8383
+ },
8384
+ selectedSlidingAmount: normalizedSelectedAmount,
8385
+ selectedPriceAmount: normalizedSelectedAmount,
8386
+ selectedRequiresPayment: (normalizedSelectedAmount ?? 0) > 0,
8387
+ hasPaidSelection: maxAmount > 0,
8388
+ hasFreeSelection: minAmount === 0
8389
+ };
8390
+ }
8252
8391
  }
8253
8392
  }
8254
8393
  function formatCoursePriceDisplay(display, locale) {
@@ -8357,7 +8496,7 @@ function isRecord7(value) {
8357
8496
  }
8358
8497
  function isPublicEventPricing(value) {
8359
8498
  if (!isRecord7(value) || typeof value.kind !== "string") return false;
8360
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
8499
+ return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "sliding_scale" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
8361
8500
  }
8362
8501
  function isPublicEventTicketType(value) {
8363
8502
  return isRecord7(value) && typeof value.id === "string";
@@ -10388,6 +10527,19 @@ var init_schemas2 = __esm({
10388
10527
  ticketTypes: z14.array(publicEventTicketTypeSchema),
10389
10528
  display: publicEventTicketTypePriceDisplaySchema
10390
10529
  }),
10530
+ z14.object({
10531
+ kind: z14.literal("sliding_scale"),
10532
+ minAmount: z14.number(),
10533
+ maxAmount: z14.number(),
10534
+ suggestedAmount: z14.number().nullable(),
10535
+ currency: z14.string(),
10536
+ display: z14.object({
10537
+ kind: z14.literal("range"),
10538
+ minAmount: z14.number(),
10539
+ maxAmount: z14.number(),
10540
+ currency: z14.string()
10541
+ })
10542
+ }),
10391
10543
  z14.object({
10392
10544
  kind: z14.literal("course"),
10393
10545
  display: z14.object({ kind: z14.literal("course"), display: publicCoursePriceDisplaySchema })
@@ -28331,6 +28483,7 @@ function useBookingSubmission(_siteId) {
28331
28483
  customFields: data.customFields,
28332
28484
  timezone: data.timezone,
28333
28485
  discountCode: data.discountCode,
28486
+ selectedSlidingAmount: data.selectedSlidingAmount,
28334
28487
  selectedCollectionOptionKind: data.selectedCollectionOptionKind,
28335
28488
  altchaSolution: data.altchaSolution ?? null,
28336
28489
  successUrl: data.successUrl,
@@ -28859,6 +29012,9 @@ function createInitialBookingFlowReducerState(input) {
28859
29012
  fieldValues: collectInitialFieldValues(initialData),
28860
29013
  touchedFields: {},
28861
29014
  discountCode: toOptionalString(initialData.discountCode) ?? "",
29015
+ selectedSlidingAmountInput: toOptionalString(
29016
+ initialData.selectedSlidingAmountInput
29017
+ ) ?? "",
28862
29018
  selectedCollectionOptionKind: toSelectedCollectionOptionKind(
28863
29019
  initialData.selectedCollectionOptionKind
28864
29020
  ),
@@ -28911,6 +29067,7 @@ function bookingFlowReducer(state, event) {
28911
29067
  selectedDate: void 0,
28912
29068
  selectedSlot: null,
28913
29069
  selectedAppointmentPackageId: void 0,
29070
+ selectedSlidingAmountInput: "",
28914
29071
  selectedCollectionOptionKind: void 0,
28915
29072
  validationErrors: clearBookingFormError(state.validationErrors)
28916
29073
  };
@@ -28964,6 +29121,13 @@ function bookingFlowReducer(state, event) {
28964
29121
  selectedAppointmentPackageId: void 0,
28965
29122
  validationErrors: clearBookingFormError(state.validationErrors)
28966
29123
  };
29124
+ case "sliding-amount-changed":
29125
+ return {
29126
+ ...state,
29127
+ selectedSlidingAmountInput: event.value,
29128
+ selectedAppointmentPackageId: void 0,
29129
+ validationErrors: clearBookingFormError(state.validationErrors)
29130
+ };
28967
29131
  case "appointment-package-selected":
28968
29132
  return {
28969
29133
  ...state,
@@ -29066,6 +29230,7 @@ function toBookingFormData(state) {
29066
29230
  selectedDate: state.selectedDate,
29067
29231
  selectedSlot: state.selectedSlot?.startAt,
29068
29232
  discountCode: state.discountCode || void 0,
29233
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput || void 0,
29069
29234
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
29070
29235
  selectedAppointmentPackageId: state.selectedAppointmentPackageId
29071
29236
  };
@@ -29083,7 +29248,8 @@ function buildBookingStepResolution(input) {
29083
29248
  input.form.settings?.serviceIds && input.form.settings.serviceIds.length > 1
29084
29249
  );
29085
29250
  const selectedService = paymentResolution.service;
29086
- const needsPayment = paymentResolution.mode !== "hidden" || Boolean(
29251
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
29252
+ const needsPayment = Boolean(selectedServiceSlidingScale) || paymentResolution.mode !== "hidden" || Boolean(
29087
29253
  input.enableAppointmentPackages && selectedService && (selectedService.priceAmount ?? 0) > 0
29088
29254
  );
29089
29255
  const hasFields = Boolean(input.form.schema?.fields.length);
@@ -29127,6 +29293,18 @@ function validateBookingStep(input) {
29127
29293
  services: input.services,
29128
29294
  defaultServiceId: input.defaultServiceId
29129
29295
  });
29296
+ const slidingScale = getBookingServiceSlidingScale(resolution.service);
29297
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(
29298
+ slidingScale,
29299
+ parseBookingCurrencyInputToCents(input.data.selectedSlidingAmountInput)
29300
+ )) {
29301
+ return {
29302
+ ok: false,
29303
+ errors: createFormBookingValidationError(
29304
+ "Please choose an amount within the sliding scale"
29305
+ )
29306
+ };
29307
+ }
29130
29308
  if (resolution.mode === "hidden" || resolution.mode === "disclosure_only") {
29131
29309
  return { ok: true };
29132
29310
  }
@@ -29525,6 +29703,7 @@ var init_useHeadlessBookingFlow_state = __esm({
29525
29703
  "../blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.ts"() {
29526
29704
  "use strict";
29527
29705
  init_booking_form_headless_state();
29706
+ init_booking_form_state();
29528
29707
  }
29529
29708
  });
29530
29709
 
@@ -29564,11 +29743,27 @@ function useHeadlessBookingFlow(options) {
29564
29743
  const selectedServiceId = state.selectedServiceId ?? defaultServiceId;
29565
29744
  const selectedResourceId = state.selectedResourceId ?? defaultResourceId;
29566
29745
  const selectedService = options.services.find((service) => service.id === selectedServiceId) ?? null;
29746
+ const lastSlidingServiceIdRef = useRef6(null);
29747
+ useEffect6(() => {
29748
+ const selectedId = selectedService?.id ?? null;
29749
+ if (lastSlidingServiceIdRef.current === selectedId) return;
29750
+ lastSlidingServiceIdRef.current = selectedId;
29751
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
29752
+ dispatch({
29753
+ type: "sliding-amount-changed",
29754
+ value: slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatBookingCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
29755
+ });
29756
+ }, [selectedService]);
29567
29757
  const hasPortalBearerAuth = (api.config.authOverrides?.portal ?? api.config.auth).type === "bearer";
29758
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
29759
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
29760
+ state.selectedSlidingAmountInput
29761
+ );
29762
+ const selectedAppointmentPriceAmount = selectedServiceSlidingScale && selectedSlidingAmount !== null ? selectedSlidingAmount : selectedService?.priceAmount ?? 0;
29568
29763
  const canLoadAppointmentPackages = Boolean(
29569
- options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && (selectedService.priceAmount ?? 0) > 0
29764
+ options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && !selectedServiceSlidingScale && (selectedService.priceAmount ?? 0) > 0
29570
29765
  );
29571
- const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
29766
+ const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || selectedServiceSlidingScale || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
29572
29767
  const data = useMemo2(() => toBookingFormData(state), [state]);
29573
29768
  const availabilityDatesQuery = useMemo2(
29574
29769
  () => buildBookingAvailabilityQuery({
@@ -29798,13 +29993,13 @@ function useHeadlessBookingFlow(options) {
29798
29993
  state.selectedSlot
29799
29994
  ]);
29800
29995
  const appointmentDiscountContext = useMemo2(
29801
- () => selectedService && (selectedService.priceAmount ?? 0) > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
29802
- [selectedService?.id, selectedService?.priceAmount]
29996
+ () => selectedService && selectedAppointmentPriceAmount > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
29997
+ [selectedAppointmentPriceAmount, selectedService?.id]
29803
29998
  );
29804
29999
  const discountUi = useDiscountCodeUi({
29805
30000
  api,
29806
30001
  discountContext: appointmentDiscountContext,
29807
- subtotalCents: selectedService?.priceAmount ?? 0
30002
+ subtotalCents: selectedAppointmentPriceAmount
29808
30003
  });
29809
30004
  const { submit, clearFeedback } = useBookingSubmission(options.siteId);
29810
30005
  const submittingRef = useRef6(false);
@@ -29947,6 +30142,7 @@ function useHeadlessBookingFlow(options) {
29947
30142
  type: "appointment-package-selected",
29948
30143
  packageId: packageId ? unsafeAsCustomerPassId(packageId) : null
29949
30144
  }),
30145
+ setSlidingAmountInput: (value) => dispatch({ type: "sliding-amount-changed", value }),
29950
30146
  setDiscountCode: (code) => {
29951
30147
  dispatch({ type: "discount-code-changed", code });
29952
30148
  latestRef.current.setDiscountCode(code);
@@ -30017,11 +30213,11 @@ function useHeadlessBookingFlow(options) {
30017
30213
  () => selectedService && paymentResolution.mode !== "unsupported" ? buildBookingPaymentSelectionViewModel({
30018
30214
  serviceTitle: selectedService.title,
30019
30215
  paymentTerms: selectedService.paymentTerms,
30020
- priceAmount: selectedService.priceAmount,
30216
+ priceAmount: selectedAppointmentPriceAmount,
30021
30217
  priceCurrency: selectedService.priceCurrency,
30022
30218
  bookingStartAt: state.selectedSlot?.startAt
30023
30219
  }) : null,
30024
- [paymentResolution.mode, selectedService, state.selectedSlot?.startAt]
30220
+ [paymentResolution.mode, selectedAppointmentPriceAmount, selectedService, state.selectedSlot?.startAt]
30025
30221
  );
30026
30222
  const appointmentPackageItems = appointmentPackages.tag === "loaded" ? appointmentPackages.value : [];
30027
30223
  const selectedAppointmentPackage = appointmentPackageItems.find(
@@ -30050,6 +30246,9 @@ function useHeadlessBookingFlow(options) {
30050
30246
  selection: null,
30051
30247
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
30052
30248
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
30249
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
30250
+ selectedSlidingAmount,
30251
+ slidingScale: selectedServiceSlidingScale,
30053
30252
  appointmentPackages: appointmentPackagesModel,
30054
30253
  errorMessage: paymentResolution.errorMessage ?? "This service cannot be booked right now."
30055
30254
  } : {
@@ -30058,6 +30257,9 @@ function useHeadlessBookingFlow(options) {
30058
30257
  selection: paymentSelection,
30059
30258
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
30060
30259
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
30260
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
30261
+ selectedSlidingAmount,
30262
+ slidingScale: selectedServiceSlidingScale,
30061
30263
  appointmentPackages: appointmentPackagesModel
30062
30264
  };
30063
30265
  return {
@@ -30672,6 +30874,9 @@ var init_FieldValidationMessage = __esm({
30672
30874
 
30673
30875
  // ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
30674
30876
  import { Fragment as Fragment9, jsx as jsx62, jsxs as jsxs46 } from "react/jsx-runtime";
30877
+ function formatBookingAmount(amount, currency) {
30878
+ return formatCurrencyAmount(amount, currency, { decimals: "auto" });
30879
+ }
30675
30880
  function HeadlessBookingDefaultForm({
30676
30881
  booking,
30677
30882
  onSubmit
@@ -30948,6 +31153,7 @@ function HeadlessBookingPaymentSelectionStep({
30948
31153
  }
30949
31154
  const packageOptions = booking.payment.appointmentPackages.items;
30950
31155
  const hasPackageOptions = packageOptions.length > 0;
31156
+ const slidingScale = booking.payment.slidingScale;
30951
31157
  return /* @__PURE__ */ jsxs46("div", { className: "ab-stack", children: [
30952
31158
  /* @__PURE__ */ jsxs46("div", { className: "ab-info-box", children: [
30953
31159
  /* @__PURE__ */ jsx62("h3", { className: "ab-subtitle", children: viewModel.sectionTitle }),
@@ -30958,6 +31164,28 @@ function HeadlessBookingPaymentSelectionStep({
30958
31164
  booking.payment.appointmentPackages.state.message,
30959
31165
  " You can continue with the normal payment option."
30960
31166
  ] }) : null,
31167
+ slidingScale ? /* @__PURE__ */ jsxs46("div", { className: "rb-space-y-2", children: [
31168
+ /* @__PURE__ */ jsx62("label", { htmlFor: "appointment-sliding-amount", className: "form-label", children: "Choose your price" }),
31169
+ /* @__PURE__ */ jsxs46("p", { className: "rb-caption status-muted", children: [
31170
+ formatBookingAmount(slidingScale.minAmount, slidingScale.currency),
31171
+ " to ",
31172
+ formatBookingAmount(slidingScale.maxAmount, slidingScale.currency),
31173
+ slidingScale.suggestedAmount !== null ? `, suggested ${formatBookingAmount(slidingScale.suggestedAmount, slidingScale.currency)}` : ""
31174
+ ] }),
31175
+ /* @__PURE__ */ jsx62(
31176
+ "input",
31177
+ {
31178
+ id: "appointment-sliding-amount",
31179
+ type: "number",
31180
+ className: "form-input",
31181
+ min: slidingScale.minAmount / 100,
31182
+ max: slidingScale.maxAmount / 100,
31183
+ step: "0.01",
31184
+ value: booking.payment.selectedSlidingAmountInput,
31185
+ onChange: (event) => booking.actions.setSlidingAmountInput(event.target.value)
31186
+ }
31187
+ )
31188
+ ] }) : null,
30961
31189
  /* @__PURE__ */ jsxs46("div", { className: "ab-stack", role: "list", "aria-label": "Payment options", children: [
30962
31190
  packageOptions.map((appointmentPackage) => {
30963
31191
  const isSelected = booking.payment.selectedAppointmentPackageId === appointmentPackage.customerPassId;
@@ -32893,6 +33121,7 @@ function useEligibility(params) {
32893
33121
  occurrenceId: params.occurrenceId,
32894
33122
  attendanceMode: params.attendanceMode ?? void 0,
32895
33123
  ticketTypeId: params.ticketTypeId ?? void 0,
33124
+ selectedSlidingAmount: params.selectedSlidingAmount ?? void 0,
32896
33125
  email: params.email.trim().toLowerCase(),
32897
33126
  ticketsCount: params.ticketsCount
32898
33127
  });
@@ -32913,7 +33142,7 @@ function useEligibility(params) {
32913
33142
  return () => {
32914
33143
  cancelled = true;
32915
33144
  };
32916
- }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.email, params.ticketsCount]);
33145
+ }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.selectedSlidingAmount, params.email, params.ticketsCount]);
32917
33146
  return {
32918
33147
  state,
32919
33148
  eligibility: state.eligibility,
@@ -33264,6 +33493,41 @@ function TicketCounterLimit(props2) {
33264
33493
  ] }) : null
33265
33494
  ] });
33266
33495
  }
33496
+ function formatCurrencyInputAmount(amount) {
33497
+ const major = amount / 100;
33498
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
33499
+ }
33500
+ function SlidingScaleAmountField(props2) {
33501
+ const minLabel = formatPrice(props2.slidingScale.minAmount, props2.currency);
33502
+ const maxLabel = formatPrice(props2.slidingScale.maxAmount, props2.currency);
33503
+ const suggestedLabel = props2.slidingScale.suggestedAmount !== null ? formatPrice(props2.slidingScale.suggestedAmount, props2.currency) : null;
33504
+ return /* @__PURE__ */ jsxs54("div", { className: "er-field", children: [
33505
+ /* @__PURE__ */ jsx77("label", { htmlFor: "er-sliding-amount", className: "er-label", children: "Amount" }),
33506
+ /* @__PURE__ */ jsx77(
33507
+ "input",
33508
+ {
33509
+ id: "er-sliding-amount",
33510
+ type: "number",
33511
+ min: formatCurrencyInputAmount(props2.slidingScale.minAmount),
33512
+ max: formatCurrencyInputAmount(props2.slidingScale.maxAmount),
33513
+ step: "0.01",
33514
+ inputMode: "decimal",
33515
+ className: "er-input",
33516
+ value: props2.value,
33517
+ onChange: (event) => props2.onChange?.(event.target.value)
33518
+ }
33519
+ ),
33520
+ /* @__PURE__ */ jsxs54("p", { className: "er-text er-text--muted", children: [
33521
+ "Choose between ",
33522
+ minLabel,
33523
+ " and ",
33524
+ maxLabel,
33525
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
33526
+ "."
33527
+ ] }),
33528
+ props2.message ? /* @__PURE__ */ jsx77("p", { className: "er-text er-text--muted", children: props2.message }) : null
33529
+ ] });
33530
+ }
33267
33531
  function getTicketTypeCounterAvailabilityLabel(input) {
33268
33532
  if (input.selectedRemainingCapacity !== null) {
33269
33533
  return `${input.selectedRemainingCapacity} remaining for this ticket type`;
@@ -33335,6 +33599,10 @@ var init_TicketSelectionStep = __esm({
33335
33599
  maxTickets,
33336
33600
  selectedTicketTypeId = null,
33337
33601
  onTicketTypeChange,
33602
+ slidingScale = null,
33603
+ selectedSlidingAmountInput = "",
33604
+ slidingScaleSelectionMessage = null,
33605
+ onSlidingAmountChange,
33338
33606
  onTicketsChange
33339
33607
  }) => {
33340
33608
  const deliveryMode = event?.deliveryMode ?? "in_person";
@@ -33450,6 +33718,16 @@ var init_TicketSelectionStep = __esm({
33450
33718
  ] })
33451
33719
  ] })
33452
33720
  ] }) : /* @__PURE__ */ jsxs54(Fragment12, { children: [
33721
+ slidingScale ? /* @__PURE__ */ jsx77(
33722
+ SlidingScaleAmountField,
33723
+ {
33724
+ slidingScale,
33725
+ currency: eventPricingCurrency ?? DEFAULT_EVENT_CURRENCY2,
33726
+ value: selectedSlidingAmountInput,
33727
+ message: slidingScaleSelectionMessage,
33728
+ onChange: onSlidingAmountChange
33729
+ }
33730
+ ) : null,
33453
33731
  /* @__PURE__ */ jsx77("h3", { className: "er-title", children: canJoinWaitlist ? "How many waitlist spots?" : "How many tickets?" }),
33454
33732
  /* @__PURE__ */ jsxs54("div", { className: "er-ticket-counter-group", children: [
33455
33733
  /* @__PURE__ */ jsx77(
@@ -34826,6 +35104,10 @@ function EventRegistrationWizardContent(props2) {
34826
35104
  maxTickets: wizard.maxTicketsNum,
34827
35105
  selectedTicketTypeId: wizard.selectedTicketTypeId,
34828
35106
  onTicketTypeChange: wizard.setSelectedTicketTypeId,
35107
+ slidingScale: wizard.selectedEventSlidingScale,
35108
+ selectedSlidingAmountInput: wizard.selectedSlidingAmountInput,
35109
+ slidingScaleSelectionMessage: wizard.slidingScaleSelectionMessage,
35110
+ onSlidingAmountChange: wizard.setSelectedSlidingAmountInput,
34829
35111
  onTicketsChange: wizard.setTicketsCount
34830
35112
  }
34831
35113
  )
@@ -35042,7 +35324,7 @@ function EventRegistrationSummaryBar(props2) {
35042
35324
  const summaryTimeZone = wizard.occurrenceContext?.timeZone ?? summaryEvent?.timeZone ?? null;
35043
35325
  const occurrenceOverrideTitle = wizard.occurrenceContext?.overrides?.title;
35044
35326
  const summaryTitle = occurrenceOverrideTitle ?? summaryEvent?.title ?? "";
35045
- const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
35327
+ const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (wizard.selectedEventSlidingScale ? wizard.selectedSlidingAmount : null) ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
35046
35328
  const summaryCurrency = (summaryEvent ? publicEventPricingCurrency(summaryEvent.pricing) : null) ?? DEFAULT_EVENT_CURRENCY2;
35047
35329
  const teamMemberNames = (summaryEvent?.teamMembers ?? []).map((teamMember) => teamMember.name).filter(Boolean);
35048
35330
  const subtotalCents = Math.max(0, summaryPrice * wizard.ticketsCount);
@@ -35493,6 +35775,7 @@ function useEventRegistrationSubmit(options) {
35493
35775
  occurrenceId: submitOccurrenceId,
35494
35776
  attendanceMode: options.selectedAttendanceMode ?? void 0,
35495
35777
  ticketTypeId: options.selectedTicketTypeId ?? void 0,
35778
+ selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
35496
35779
  email: options.formData.email.trim().toLowerCase(),
35497
35780
  name: options.formData.name.trim(),
35498
35781
  ticketsCount: options.ticketsCount,
@@ -35602,6 +35885,35 @@ var init_useEventRegistrationSubmit = __esm({
35602
35885
 
35603
35886
  // ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
35604
35887
  import { useCallback as useCallback7, useEffect as useEffect14, useMemo as useMemo7, useReducer as useReducer9, useState as useState4 } from "react";
35888
+ function parseCurrencyInputToCents(value) {
35889
+ const trimmed = value.trim();
35890
+ if (!trimmed) return null;
35891
+ const amount = Number(trimmed);
35892
+ if (!Number.isFinite(amount) || amount < 0) return null;
35893
+ return Math.round(amount * 100);
35894
+ }
35895
+ function formatCentsAsCurrencyInput(amount) {
35896
+ const major = amount / 100;
35897
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
35898
+ }
35899
+ function getSlidingScaleForEvent(event) {
35900
+ const pricing = event ? resolveRuntimeEventPricing(event) : null;
35901
+ return pricing?.kind === "sliding_scale" ? {
35902
+ minAmount: pricing.minAmount,
35903
+ maxAmount: pricing.maxAmount,
35904
+ suggestedAmount: pricing.suggestedAmount
35905
+ } : null;
35906
+ }
35907
+ function getSlidingScaleSelectionMessage(input) {
35908
+ if (!input.slidingScale) return null;
35909
+ if (input.selectedAmount === null) {
35910
+ return "Choose an amount to continue.";
35911
+ }
35912
+ if (input.selectedAmount < input.slidingScale.minAmount || input.selectedAmount > input.slidingScale.maxAmount) {
35913
+ return "Choose an amount within the sliding scale.";
35914
+ }
35915
+ return null;
35916
+ }
35605
35917
  function useEventRegistrationWizard(options) {
35606
35918
  const api = useBlockApi();
35607
35919
  const registrationEvents = useMemo7(
@@ -35646,6 +35958,10 @@ function useEventRegistrationWizard(options) {
35646
35958
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
35647
35959
  );
35648
35960
  const [selectedTicketTypeId, setSelectedTicketTypeId] = useState4(null);
35961
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState4(() => {
35962
+ const slidingScale = getSlidingScaleForEvent(preSelectedEvent ?? null);
35963
+ return slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "";
35964
+ });
35649
35965
  const [registrationResult, setRegistrationResult] = useState4(null);
35650
35966
  const [errorMessage, setErrorMessage] = useState4("");
35651
35967
  const [altchaSolution, setAltchaSolution] = useState4(null);
@@ -35682,6 +35998,7 @@ function useEventRegistrationWizard(options) {
35682
35998
  occurrenceId,
35683
35999
  attendanceMode: selectedAttendanceMode,
35684
36000
  ticketTypeId: selectedTicketTypeId,
36001
+ selectedSlidingAmount: parseCurrencyInputToCents(selectedSlidingAmountInput),
35685
36002
  email: formData.email,
35686
36003
  ticketsCount
35687
36004
  });
@@ -35817,6 +36134,18 @@ function useEventRegistrationWizard(options) {
35817
36134
  }),
35818
36135
  [selectedAttendanceMode, selectedEvent, preSelectedEvent, eligibility, selectedTicketTypeId]
35819
36136
  );
36137
+ const selectedSlidingAmount = useMemo7(
36138
+ () => parseCurrencyInputToCents(selectedSlidingAmountInput),
36139
+ [selectedSlidingAmountInput]
36140
+ );
36141
+ const selectedEventSlidingScale = useMemo7(
36142
+ () => getSlidingScaleForEvent(selectedEvent ?? preSelectedEvent),
36143
+ [selectedEvent, preSelectedEvent]
36144
+ );
36145
+ const slidingScaleSelectionMessage = getSlidingScaleSelectionMessage({
36146
+ slidingScale: selectedEventSlidingScale,
36147
+ selectedAmount: selectedSlidingAmount
36148
+ });
35820
36149
  const ticketTypeSelectionRequired = useMemo7(() => {
35821
36150
  const event = selectedEvent ?? preSelectedEvent;
35822
36151
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") {
@@ -35885,12 +36214,24 @@ function useEventRegistrationWizard(options) {
35885
36214
  setGuestSectionShake(false);
35886
36215
  };
35887
36216
  const eventRequiresPayment = useMemo7(
35888
- () => resolveEventRequiresPayment({
36217
+ () => {
36218
+ if (selectedEventSlidingScale) {
36219
+ return (selectedSlidingAmount ?? 0) > 0;
36220
+ }
36221
+ return resolveEventRequiresPayment({
36222
+ eligibility,
36223
+ selectedTicketType,
36224
+ event: selectedEvent ?? preSelectedEvent
36225
+ });
36226
+ },
36227
+ [
35889
36228
  eligibility,
35890
36229
  selectedTicketType,
35891
- event: selectedEvent ?? preSelectedEvent
35892
- }),
35893
- [eligibility, selectedTicketType, selectedEvent, preSelectedEvent]
36230
+ selectedEvent,
36231
+ preSelectedEvent,
36232
+ selectedEventSlidingScale,
36233
+ selectedSlidingAmount
36234
+ ]
35894
36235
  );
35895
36236
  const handleDetailsComplete = async () => {
35896
36237
  if (ticketTypeSelectionRequired && !hasValidSelectedTicketType) {
@@ -35899,6 +36240,11 @@ function useEventRegistrationWizard(options) {
35899
36240
  setStep("error");
35900
36241
  return { ok: false, reason: "blocked", message };
35901
36242
  }
36243
+ if (slidingScaleSelectionMessage) {
36244
+ setErrorMessage(slidingScaleSelectionMessage);
36245
+ setStep("error");
36246
+ return { ok: false, reason: "blocked", message: slidingScaleSelectionMessage };
36247
+ }
35902
36248
  if (!validateForm()) {
35903
36249
  setGuestSectionShake(true);
35904
36250
  return {
@@ -35983,9 +36329,18 @@ function useEventRegistrationWizard(options) {
35983
36329
  const discountSubtotalCents = useMemo7(() => {
35984
36330
  if (discountContext?.type !== "event") return void 0;
35985
36331
  const event = selectedEvent ?? preSelectedEvent;
35986
- const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
36332
+ const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (selectedEventSlidingScale ? selectedSlidingAmount : null) ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
35987
36333
  return Math.max(0, eventPriceCents * ticketsCount);
35988
- }, [discountContext, selectedTicketType, eligibility, selectedEvent, preSelectedEvent, ticketsCount]);
36334
+ }, [
36335
+ discountContext,
36336
+ selectedTicketType,
36337
+ eligibility,
36338
+ selectedEvent,
36339
+ preSelectedEvent,
36340
+ selectedEventSlidingScale,
36341
+ selectedSlidingAmount,
36342
+ ticketsCount
36343
+ ]);
35989
36344
  const discountUi = useDiscountCodeUi({ api, discountContext, subtotalCents: discountSubtotalCents });
35990
36345
  const selectionMethod = paymentSelection.method;
35991
36346
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
@@ -35994,6 +36349,7 @@ function useEventRegistrationWizard(options) {
35994
36349
  registrationTarget,
35995
36350
  selectedAttendanceMode,
35996
36351
  selectedTicketTypeId,
36352
+ selectedSlidingAmount,
35997
36353
  ticketsCount,
35998
36354
  formData,
35999
36355
  altchaSolution,
@@ -36008,6 +36364,10 @@ function useEventRegistrationWizard(options) {
36008
36364
  });
36009
36365
  const handleEventSelect = (event) => {
36010
36366
  setSelectedEvent(event);
36367
+ const slidingScale = getSlidingScaleForEvent(event);
36368
+ setSelectedSlidingAmountInput(
36369
+ slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
36370
+ );
36011
36371
  setSelectedAttendanceMode(
36012
36372
  (event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person"
36013
36373
  );
@@ -36017,6 +36377,7 @@ function useEventRegistrationWizard(options) {
36017
36377
  const handleRetry = () => {
36018
36378
  dispatchFlow({ type: "reset", hasPreSelectedContext });
36019
36379
  setSelectedTicketTypeId(null);
36380
+ setSelectedSlidingAmountInput("");
36020
36381
  setSelectedAttendanceMode(
36021
36382
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
36022
36383
  );
@@ -36053,6 +36414,11 @@ function useEventRegistrationWizard(options) {
36053
36414
  setSelectedAttendanceMode,
36054
36415
  selectedTicketTypeId,
36055
36416
  setSelectedTicketTypeId,
36417
+ selectedSlidingAmountInput,
36418
+ setSelectedSlidingAmountInput,
36419
+ selectedSlidingAmount,
36420
+ selectedEventSlidingScale,
36421
+ slidingScaleSelectionMessage,
36056
36422
  selectedTicketType,
36057
36423
  visibleTicketTypes,
36058
36424
  ticketTypeSelectionRequired,
@@ -37368,6 +37734,44 @@ function CoursePricingSelector({
37368
37734
  )
37369
37735
  ] });
37370
37736
  }
37737
+ function CourseSlidingScaleInput({
37738
+ pricing,
37739
+ value,
37740
+ onChange
37741
+ }) {
37742
+ const slidingScale = pricing.slidingScale;
37743
+ if (!slidingScale) {
37744
+ return null;
37745
+ }
37746
+ const minLabel = formatCoursePriceAmount(slidingScale.minAmount, pricing.currency);
37747
+ const maxLabel = formatCoursePriceAmount(slidingScale.maxAmount, pricing.currency);
37748
+ const suggestedLabel = slidingScale.suggestedAmount !== null ? formatCoursePriceAmount(slidingScale.suggestedAmount, pricing.currency) : null;
37749
+ return /* @__PURE__ */ jsxs75("div", { className: "cr-form__field", children: [
37750
+ /* @__PURE__ */ jsx105("label", { htmlFor: "cr-sliding-amount", className: "cr-form__label", children: "Amount" }),
37751
+ /* @__PURE__ */ jsx105(
37752
+ "input",
37753
+ {
37754
+ id: "cr-sliding-amount",
37755
+ type: "number",
37756
+ min: slidingScale.minAmount / 100,
37757
+ max: slidingScale.maxAmount / 100,
37758
+ step: "0.01",
37759
+ inputMode: "decimal",
37760
+ className: "cr-form__input",
37761
+ value,
37762
+ onChange: (event) => onChange(event.target.value)
37763
+ }
37764
+ ),
37765
+ /* @__PURE__ */ jsxs75("p", { className: "cr-text cr-text--muted", children: [
37766
+ "Choose between ",
37767
+ minLabel,
37768
+ " and ",
37769
+ maxLabel,
37770
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
37771
+ "."
37772
+ ] })
37773
+ ] });
37774
+ }
37371
37775
  function EnrollmentForm({ formData, errors, onChange, isEmailLocked, isLoading, onLogout }) {
37372
37776
  const nameErrorId = "cr-name-error";
37373
37777
  const emailErrorId = "cr-email-error";
@@ -37463,6 +37867,17 @@ var init_components2 = __esm({
37463
37867
 
37464
37868
  // ../blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.ts
37465
37869
  import { useEffect as useEffect18, useMemo as useMemo9, useState as useState9 } from "react";
37870
+ function parseCurrencyInputToCents2(value) {
37871
+ const trimmed = value.trim();
37872
+ if (!trimmed) return null;
37873
+ const amount = Number(trimmed);
37874
+ if (!Number.isFinite(amount) || amount < 0) return null;
37875
+ return Math.round(amount * 100);
37876
+ }
37877
+ function formatCentsAsCurrencyInput2(amount) {
37878
+ const major = amount / 100;
37879
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
37880
+ }
37466
37881
  function shouldFetchCourseEligibility(course, hasPaidSelection, portalAuthState, hasWaitlistClaimToken) {
37467
37882
  if (!course || portalAuthState === "checking") return false;
37468
37883
  if (hasWaitlistClaimToken) return false;
@@ -37480,12 +37895,27 @@ function getSessionIdFromUrl() {
37480
37895
  }
37481
37896
  function getMissingCoursePricingSelectionMessage(pricing) {
37482
37897
  if (pricing?.requiresSelection && !pricing.selectedOption) {
37898
+ if (pricing.slidingScale) {
37899
+ return "Choose an amount to continue.";
37900
+ }
37483
37901
  return "Choose a pricing option to continue.";
37484
37902
  }
37903
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount < pricing.slidingScale.minAmount) {
37904
+ return "Choose an amount within the sliding scale.";
37905
+ }
37906
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount > pricing.slidingScale.maxAmount) {
37907
+ return "Choose an amount within the sliding scale.";
37908
+ }
37485
37909
  return null;
37486
37910
  }
37487
37911
  function buildCoursePricingOptionPayload(pricing) {
37488
- return pricing?.selectedOption ? { pricingOptionId: pricing.selectedOption.id } : {};
37912
+ if (pricing?.selectedOption) {
37913
+ return { pricingOptionId: pricing.selectedOption.id };
37914
+ }
37915
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null) {
37916
+ return { selectedSlidingAmount: pricing.selectedSlidingAmount };
37917
+ }
37918
+ return {};
37489
37919
  }
37490
37920
  function resolveHasCoursePayment(pricing) {
37491
37921
  if (!pricing) {
@@ -37526,6 +37956,7 @@ function useCourseRegistrationWizard(props2) {
37526
37956
  scopedCourses.length === 1 ? scopedCourses[0] ?? null : null
37527
37957
  );
37528
37958
  const [selectedPricingOptionId, setSelectedPricingOptionId] = useState9(null);
37959
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState9("");
37529
37960
  const [formData, setFormData] = useState9({
37530
37961
  name: "",
37531
37962
  email: "",
@@ -37547,8 +37978,12 @@ function useCourseRegistrationWizard(props2) {
37547
37978
  const [bookingMode, setBookingMode] = useState9(null);
37548
37979
  const waitlistClaim = useWaitlistClaim();
37549
37980
  const selectedCoursePricing = useMemo9(
37550
- () => selectedCourse ? resolveCoursePricingState(selectedCourse, selectedPricingOptionId) : null,
37551
- [selectedCourse, selectedPricingOptionId]
37981
+ () => selectedCourse ? resolveCoursePricingState(
37982
+ selectedCourse,
37983
+ selectedPricingOptionId,
37984
+ parseCurrencyInputToCents2(selectedSlidingAmountInput)
37985
+ ) : null,
37986
+ [selectedCourse, selectedPricingOptionId, selectedSlidingAmountInput]
37552
37987
  );
37553
37988
  const missingPricingSelectionMessage = getMissingCoursePricingSelectionMessage(
37554
37989
  selectedCoursePricing
@@ -37621,9 +38056,13 @@ function useCourseRegistrationWizard(props2) {
37621
38056
  useEffect18(() => {
37622
38057
  if (!selectedCourse) {
37623
38058
  setSelectedPricingOptionId(null);
38059
+ setSelectedSlidingAmountInput("");
37624
38060
  return;
37625
38061
  }
37626
38062
  const defaultPricing = resolveCoursePricingState(selectedCourse, null);
38063
+ setSelectedSlidingAmountInput(
38064
+ defaultPricing.slidingScale?.suggestedAmount !== null && defaultPricing.slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput2(defaultPricing.slidingScale.suggestedAmount) : ""
38065
+ );
37627
38066
  setSelectedPricingOptionId((previous) => {
37628
38067
  if (previous && defaultPricing.selectableOptions.some((option) => option.id === previous)) {
37629
38068
  return previous;
@@ -37998,6 +38437,8 @@ function useCourseRegistrationWizard(props2) {
37998
38437
  selectedCourse,
37999
38438
  selectedPricingOptionId,
38000
38439
  setSelectedPricingOptionId,
38440
+ selectedSlidingAmountInput,
38441
+ setSelectedSlidingAmountInput,
38001
38442
  selectedCoursePricing,
38002
38443
  setSelectedCourse,
38003
38444
  formData,
@@ -38063,6 +38504,19 @@ var init_useCourseRegistrationWizard = __esm({
38063
38504
 
38064
38505
  // ../blocks/src/system/runtime/nodes/course-registration/CourseRegistrationWizard.tsx
38065
38506
  import { Fragment as Fragment23, jsx as jsx106, jsxs as jsxs76 } from "react/jsx-runtime";
38507
+ function getCourseSlidingScaleMessage(pricing) {
38508
+ if (!pricing.slidingScale) {
38509
+ return null;
38510
+ }
38511
+ const selectedAmount = pricing.selectedSlidingAmount;
38512
+ if (selectedAmount === null) {
38513
+ return "Choose an amount to continue.";
38514
+ }
38515
+ if (selectedAmount < pricing.slidingScale.minAmount || selectedAmount > pricing.slidingScale.maxAmount) {
38516
+ return "Choose an amount within the sliding scale.";
38517
+ }
38518
+ return null;
38519
+ }
38066
38520
  function CourseRegistrationWizard(props2) {
38067
38521
  const {
38068
38522
  showSessions = true,
@@ -38084,6 +38538,8 @@ function CourseRegistrationWizard(props2) {
38084
38538
  selectedCourse,
38085
38539
  selectedPricingOptionId,
38086
38540
  setSelectedPricingOptionId,
38541
+ selectedSlidingAmountInput,
38542
+ setSelectedSlidingAmountInput,
38087
38543
  selectedCoursePricing,
38088
38544
  formData,
38089
38545
  setFormData,
@@ -38158,7 +38614,8 @@ function CourseRegistrationWizard(props2) {
38158
38614
  const canShowDiscountUi = !discountsAvailableLoading && paymentChoice === "checkout" && discountsAvailable && manualCodeEntryAllowed;
38159
38615
  const courseSoldOut = waitlistClaim.claim === null && courseEligibility?.isFull === true && courseEligibility.canJoinWaitlist !== true;
38160
38616
  const detailsSubmitLabel = courseEligibility?.canJoinWaitlist ? "Join waitlist" : buttonText;
38161
- const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption;
38617
+ const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption && !selectedCoursePricing.slidingScale;
38618
+ const slidingScaleMessage = selectedCoursePricing ? getCourseSlidingScaleMessage(selectedCoursePricing) : null;
38162
38619
  const pricingUnavailable = selectedCoursePricing?.display.kind === "unavailable";
38163
38620
  const selectedCourseWithPricing = selectedCourse && selectedCoursePricing ? {
38164
38621
  course: selectedCourse,
@@ -38251,6 +38708,14 @@ function CourseRegistrationWizard(props2) {
38251
38708
  onSelect: setSelectedPricingOptionId
38252
38709
  }
38253
38710
  ),
38711
+ /* @__PURE__ */ jsx106(
38712
+ CourseSlidingScaleInput,
38713
+ {
38714
+ pricing: selectedCourseWithPricing.pricing,
38715
+ value: selectedSlidingAmountInput,
38716
+ onChange: setSelectedSlidingAmountInput
38717
+ }
38718
+ ),
38254
38719
  showSpamProtection && /* @__PURE__ */ jsx106("div", { className: "cr-altcha", children: /* @__PURE__ */ jsx106(
38255
38720
  AltchaWidget,
38256
38721
  {
@@ -38288,7 +38753,7 @@ function CourseRegistrationWizard(props2) {
38288
38753
  {
38289
38754
  type: "submit",
38290
38755
  className: buttonClass,
38291
- disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || pricingUnavailable,
38756
+ disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || slidingScaleMessage !== null || pricingUnavailable,
38292
38757
  children: eligibilityLoading ? "Checking availability\u2026" : courseSoldOut ? "Sold out" : detailsSubmitLabel
38293
38758
  }
38294
38759
  )
@@ -38298,6 +38763,7 @@ function CourseRegistrationWizard(props2) {
38298
38763
  ),
38299
38764
  courseSoldOut && /* @__PURE__ */ jsx106("p", { className: "cr-text cr-text--muted", children: "This course is fully booked and there is no waitlist available right now." }),
38300
38765
  pricingSelectionMissing && /* @__PURE__ */ jsx106("p", { className: "cr-text cr-text--muted", children: "Choose a pricing option to continue." }),
38766
+ slidingScaleMessage && /* @__PURE__ */ jsx106("p", { className: "cr-text cr-text--muted", children: slidingScaleMessage }),
38301
38767
  pricingUnavailable && /* @__PURE__ */ jsx106("p", { className: "cr-text cr-text--muted", children: "Pricing is not available for this course right now." }),
38302
38768
  eligibilityError && /* @__PURE__ */ jsx106("p", { className: "cr-text cr-text--error", children: eligibilityError })
38303
38769
  ] })
@@ -41897,12 +42363,13 @@ var init_carousel_client = __esm({
41897
42363
  ]
41898
42364
  );
41899
42365
  const resolvedSlides = resolveSlidesToShowMobile(normalized.slidesToShow);
42366
+ const canAutoplay = normalized.autoplay && slides.length > 1;
41900
42367
  const cssVars = React20.useMemo(
41901
42368
  () => getCarouselSlidesToShowCssVars(normalized.slidesToShow),
41902
42369
  [normalized.slidesToShow]
41903
42370
  );
41904
42371
  const plugins = React20.useMemo(() => {
41905
- if (!normalized.autoplay) return void 0;
42372
+ if (!canAutoplay) return void 0;
41906
42373
  return [
41907
42374
  Autoplay({
41908
42375
  delay: normalized.autoplayDelay,
@@ -41912,7 +42379,7 @@ var init_carousel_client = __esm({
41912
42379
  // Continue after manual navigation
41913
42380
  })
41914
42381
  ];
41915
- }, [normalized.autoplay, normalized.autoplayDelay]);
42382
+ }, [canAutoplay, normalized.autoplayDelay]);
41916
42383
  const [viewportRef, api] = useEmblaCarousel(
41917
42384
  {
41918
42385
  align: "start",
@@ -41951,7 +42418,7 @@ var init_carousel_client = __esm({
41951
42418
  };
41952
42419
  }, [api]);
41953
42420
  React20.useEffect(() => {
41954
- if (!api || !normalized.autoplay) return;
42421
+ if (!api || !canAutoplay) return;
41955
42422
  const autoplayPlugin = api.plugins()?.autoplay;
41956
42423
  if (!autoplayPlugin) return;
41957
42424
  const handleVisibility = () => {
@@ -41960,7 +42427,7 @@ var init_carousel_client = __esm({
41960
42427
  };
41961
42428
  document.addEventListener("visibilitychange", handleVisibility);
41962
42429
  return () => document.removeEventListener("visibilitychange", handleVisibility);
41963
- }, [api, normalized.autoplay]);
42430
+ }, [api, canAutoplay]);
41964
42431
  const inner = /* @__PURE__ */ jsxs81(Fragment25, { children: [
41965
42432
  /* @__PURE__ */ jsx113("div", { className: "carousel-frame rb-overflow-hidden rb-w-full", children: /* @__PURE__ */ jsx113(
41966
42433
  "div",
@@ -42011,7 +42478,7 @@ var init_carousel_client = __esm({
42011
42478
  "data-show-controls": normalized.showControls ? "true" : "false",
42012
42479
  "data-show-dots": normalized.showDots ? "true" : "false",
42013
42480
  "data-arrow-visibility": normalized.arrowVisibility,
42014
- "data-autoplay": normalized.autoplay ? "true" : "false",
42481
+ "data-autoplay": canAutoplay ? "true" : "false",
42015
42482
  "data-loop": normalized.loop ? "true" : "false",
42016
42483
  children: inner
42017
42484
  }