@riverbankcms/sdk 0.91.0 → 0.92.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 (104) hide show
  1. package/dist/{PreviewEditorSidebar-3TT47WCF.mjs → PreviewEditorSidebar-3CI5MKV3.mjs} +2 -2
  2. package/dist/{PreviewEditorUI-ORCLLUNZ.mjs → PreviewEditorUI-DKEP636I.mjs} +2 -2
  3. package/dist/SdkPreviewModeRuntime-BRGRDQFH.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/customerMembershipOutcomes.d.ts +4 -4
  8. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +6 -0
  9. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +32 -0
  10. package/dist/_dts/blocks/src/system/blocks/courses/shared/pricing.d.ts +12 -1
  11. package/dist/_dts/blocks/src/system/blocks/courses/shared/schemas.d.ts +12 -0
  12. package/dist/_dts/blocks/src/system/blocks/courses/shared/types.d.ts +4 -1
  13. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +44 -0
  14. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +7 -0
  15. package/dist/_dts/blocks/src/system/runtime/api/types.d.ts +8 -1
  16. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.d.ts +8 -1
  17. package/dist/_dts/blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.d.ts +1 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.headless.state.d.ts +4 -0
  19. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.interactive.d.ts +40 -0
  20. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.reserved-fields.d.ts +1 -1
  21. package/dist/_dts/blocks/src/system/runtime/nodes/booking-form.state.d.ts +29 -0
  22. package/dist/_dts/blocks/src/system/runtime/nodes/booking-runtime-sources.d.ts +1 -0
  23. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/components.d.ts +6 -0
  24. package/dist/_dts/blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.d.ts +8 -0
  25. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/TicketSelectionStep.d.ts +8 -0
  26. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +1 -0
  27. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/types.d.ts +1 -1
  28. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +1 -0
  29. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +9 -0
  30. package/dist/_dts/content-model/src/entry-slots/types.d.ts +110 -2
  31. package/dist/_dts/core/src/branded.d.ts +3 -0
  32. package/dist/_dts/core/src/event-pricing.d.ts +18 -1
  33. package/dist/_dts/core/src/money-input.d.ts +34 -4
  34. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +70 -0
  35. package/dist/_dts/media-core/src/fileAssetCompatibility.d.ts +1 -1
  36. package/dist/_dts/media-core/src/filePersistence.d.ts +5 -1
  37. package/dist/_dts/media-core/src/fileStorage.d.ts +1 -0
  38. package/dist/_dts/media-core/src/videoPrep.d.ts +15 -6
  39. package/dist/_dts/media-core/src/videoSlotPolicy.d.ts +5 -5
  40. package/dist/_dts/sdk/src/cli/commands/push/execute/diff.d.ts +2 -0
  41. package/dist/_dts/sdk/src/cli/commands/push/execute/localMediaReporting.d.ts +23 -0
  42. package/dist/_dts/sdk/src/cli/commands/push/scopes/commandBackedDatedOfferingScope.d.ts +54 -0
  43. package/dist/_dts/sdk/src/cli/commands/push/scopes/mediaPrep.d.ts +7 -2
  44. package/dist/_dts/sdk/src/cli/commands/push/scopes/types.d.ts +6 -0
  45. package/dist/_dts/sdk/src/cli/media/local-sync.d.ts +35 -0
  46. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +23 -1
  47. package/dist/_dts/sdk/src/version.d.ts +1 -1
  48. package/dist/{chunk-AFPMCLS7.mjs → chunk-6D7PLF4M.mjs} +1 -1
  49. package/dist/{chunk-RQUZYSES.mjs → chunk-I6EOKV6B.mjs} +520 -40
  50. package/dist/{chunk-S3YHJWEU.mjs → chunk-LERKJM3E.mjs} +5 -5
  51. package/dist/{chunk-GDT2QGIL.mjs → chunk-UOMTCHZD.mjs} +1 -1
  52. package/dist/cli/index.mjs +552 -80
  53. package/dist/client/{accordion-AFX37652.mjs → accordion-BUDH2G3S.mjs} +2 -2
  54. package/dist/client/{bookingForm-32V5QCOW.mjs → bookingForm-NQMCELVB.mjs} +89 -14
  55. package/dist/client/bookings.mjs +243 -17
  56. package/dist/client/{carousel-TJGQJ7ER.mjs → carousel-ONCEW6IJ.mjs} +3 -3
  57. package/dist/client/{cart-JUU2JEW4.mjs → cart-V2TCK2GI.mjs} +7 -7
  58. package/dist/client/{checkout-RD6BTFX4.mjs → checkout-ZI7SWNCS.mjs} +7 -7
  59. package/dist/client/{chunk-FMZZHAIR.mjs → chunk-AUMRQ242.mjs} +52 -5
  60. package/dist/client/{chunk-PMNAV6LO.mjs → chunk-B5UC2D6X.mjs} +7 -6
  61. package/dist/client/{chunk-6RN62XBV.mjs → chunk-ERQDHUS2.mjs} +1 -1
  62. package/dist/client/{chunk-RWYFG625.mjs → chunk-GH6IIOOE.mjs} +30 -2
  63. package/dist/client/{chunk-C4LP6BPG.mjs → chunk-HQWIJLYN.mjs} +1 -1
  64. package/dist/client/{chunk-RMOFBBNA.mjs → chunk-JXY6NHG6.mjs} +6 -6
  65. package/dist/client/{chunk-6QZIBNGV.mjs → chunk-KXYEKEV6.mjs} +154 -16
  66. package/dist/client/{chunk-E6VIWZUG.mjs → chunk-N2WJPTF7.mjs} +3 -3
  67. package/dist/client/{chunk-ISAUXU5R.mjs → chunk-QQNVQ62W.mjs} +1 -1
  68. package/dist/client/{chunk-MLBNVN4H.mjs → chunk-R3CZQLXF.mjs} +67 -4
  69. package/dist/client/{chunk-WHZQDPBZ.mjs → chunk-TNGL2WJF.mjs} +1 -0
  70. package/dist/client/{chunk-CKXEUWWA.mjs → chunk-ZJDBOLX7.mjs} +3 -3
  71. package/dist/client/client.mjs +515 -38
  72. package/dist/client/{courseRegistration-X63QAIVV.mjs → courseRegistration-JPWAQ6KM.mjs} +111 -11
  73. package/dist/client/{eventCalendar-5T24GCZL.mjs → eventCalendar-QDOCHBBV.mjs} +9 -9
  74. package/dist/client/{eventCombined-AZVTF22X.mjs → eventCombined-ZS2CXSIE.mjs} +9 -9
  75. package/dist/client/{eventDetails-7GPFNLGF.mjs → eventDetails-5LOBMPLN.mjs} +9 -9
  76. package/dist/client/{eventListing-QEUMTMUM.mjs → eventListing-SBGY4I3L.mjs} +9 -9
  77. package/dist/client/{eventRegistration-P2EDKCQZ.mjs → eventRegistration-XGPIU3Z2.mjs} +9 -9
  78. package/dist/client/{form-NUJ7H4IN.mjs → form-R7D4LZWW.mjs} +3 -3
  79. package/dist/client/{headerSection-JB7LECPL.mjs → headerSection-RRLYUCGC.mjs} +3 -3
  80. package/dist/client/hooks.mjs +22 -2
  81. package/dist/client/{locationMap-UEYWUCSW.mjs → locationMap-35ZJMRBZ.mjs} +3 -3
  82. package/dist/client/{newsletterForm-S2YZQ2X7.mjs → newsletterForm-MJ2VCFUT.mjs} +3 -3
  83. package/dist/client/{productDetail-H5MADGG7.mjs → productDetail-2O7YQNBX.mjs} +7 -7
  84. package/dist/client/{productList-DK7YON7H.mjs → productList-FDP5BH7W.mjs} +7 -7
  85. package/dist/client/rendering/client.mjs +495 -36
  86. package/dist/client/rendering/islands.mjs +19 -19
  87. package/dist/client/rendering.mjs +514 -37
  88. package/dist/client/{shop-6PA7RDRJ.mjs → shop-BGYJ3LDT.mjs} +7 -7
  89. package/dist/preview-next/client/runtime.mjs +3 -3
  90. package/dist/{sdk-runtime-JP5347OB.mjs → sdk-runtime-B3TFXRXZ.mjs} +4 -4
  91. package/dist/server/components.mjs +166 -7
  92. package/dist/server/config-validation.mjs +23 -2
  93. package/dist/server/config.mjs +23 -2
  94. package/dist/server/data.mjs +22 -2
  95. package/dist/server/index.mjs +1 -1
  96. package/dist/server/next.mjs +167 -8
  97. package/dist/server/page-converter.mjs +19 -2
  98. package/dist/server/prebuild.mjs +1 -1
  99. package/dist/server/rendering/server.mjs +166 -7
  100. package/dist/server/rendering.mjs +166 -7
  101. package/dist/server/routing.mjs +29 -5
  102. package/dist/server/server.mjs +23 -3
  103. package/package.json +1 -1
  104. package/dist/SdkPreviewModeRuntime-GMCDQPZZ.mjs +0 -8
@@ -117,12 +117,13 @@ var init_result = __esm({
117
117
  });
118
118
 
119
119
  // ../core/src/branded.ts
120
- var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
120
+ var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsStoragePath, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
121
121
  var init_branded = __esm({
122
122
  "../core/src/branded.ts"() {
123
123
  "use strict";
124
124
  unsafeAsMediaAssetId = (id) => id;
125
125
  unsafeAsStorageBucketName = (value) => value;
126
+ unsafeAsStoragePath = (value) => value;
126
127
  unsafeAsPositiveByteCount = (value) => value;
127
128
  unsafeAsPositiveSeconds = (value) => value;
128
129
  unsafeAsFormId = (id) => id;
@@ -268,6 +269,7 @@ function publicEventPricingType(pricing) {
268
269
  return "free";
269
270
  case "single_paid":
270
271
  case "ticket_types":
272
+ case "sliding_scale":
271
273
  case "course":
272
274
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
273
275
  case "external":
@@ -280,6 +282,7 @@ function publicEventPricingCurrency(pricing) {
280
282
  case "free_registration":
281
283
  case "single_paid":
282
284
  case "ticket_types":
285
+ case "sliding_scale":
283
286
  return pricing.currency;
284
287
  case "course":
285
288
  return pricing.display.display.currency;
@@ -297,6 +300,8 @@ function publicEventPricingMode(pricing) {
297
300
  return "single";
298
301
  case "ticket_types":
299
302
  return "ticket_types";
303
+ case "sliding_scale":
304
+ return "sliding_scale";
300
305
  case "drop_in":
301
306
  case "course":
302
307
  case "external":
@@ -330,6 +335,8 @@ function formatCoursePriceDisplayLabel(display, options) {
330
335
  return `Course (${formatCurrencyAmount(display.amount, display.currency, options)})`;
331
336
  case "from":
332
337
  return `Course (From ${formatCurrencyAmount(display.amount, display.currency, options)})`;
338
+ case "range":
339
+ return `Course (${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)})`;
333
340
  default:
334
341
  return assertNever(display);
335
342
  }
@@ -1074,9 +1081,20 @@ var init_src = __esm({
1074
1081
  });
1075
1082
 
1076
1083
  // ../media-core/src/fileSafety.ts
1084
+ function containsUnsafePathSegmentCharacter(value) {
1085
+ return value.includes("/") || value.includes("\\") || CONTROL_CHARACTER_PATTERN.test(value);
1086
+ }
1087
+ function isReservedPathSegment(value) {
1088
+ return value === "." || value === "..";
1089
+ }
1090
+ function isUnsafePathSegment(segment) {
1091
+ return segment.trim().length === 0 || segment !== segment.trim() || isReservedPathSegment(segment) || containsUnsafePathSegmentCharacter(segment);
1092
+ }
1093
+ var CONTROL_CHARACTER_PATTERN;
1077
1094
  var init_fileSafety = __esm({
1078
1095
  "../media-core/src/fileSafety.ts"() {
1079
1096
  "use strict";
1097
+ CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
1080
1098
  }
1081
1099
  });
1082
1100
 
@@ -1116,11 +1134,21 @@ var init_fileDomain = __esm({
1116
1134
  });
1117
1135
 
1118
1136
  // ../media-core/src/fileStorage.ts
1137
+ function parseStoragePath(value) {
1138
+ return joinStoragePathSegments(value.split("/"));
1139
+ }
1140
+ function joinStoragePathSegments(segments) {
1141
+ if (segments.some(isUnsafePathSegment)) {
1142
+ return err("unsafe-path-segment");
1143
+ }
1144
+ return ok(unsafeAsStoragePath(segments.join("/")));
1145
+ }
1119
1146
  var FILE_STORAGE_BUCKETS, PUBLIC_SITE_FILE_STORAGE_BUCKETS, PRIVATE_SITE_FILE_BUCKET_MIME_TYPES, TEMP_UPLOAD_BUCKET_MIME_TYPES;
1120
1147
  var init_fileStorage = __esm({
1121
1148
  "../media-core/src/fileStorage.ts"() {
1122
1149
  "use strict";
1123
1150
  init_src();
1151
+ init_fileSafety();
1124
1152
  FILE_STORAGE_BUCKETS = {
1125
1153
  publicSite: unsafeAsStorageBucketName("media"),
1126
1154
  privateSite: unsafeAsStorageBucketName("site-files-private"),
@@ -1306,14 +1334,18 @@ var init_uploadVerificationContract = __esm({
1306
1334
  });
1307
1335
 
1308
1336
  // ../media-core/src/videoPrep.ts
1337
+ function unsafeAsPositiveVideoDurationSeconds(value) {
1338
+ return value;
1339
+ }
1309
1340
  var VIDEO_PREP_LIMITS;
1310
1341
  var init_videoPrep = __esm({
1311
1342
  "../media-core/src/videoPrep.ts"() {
1312
1343
  "use strict";
1344
+ init_src();
1313
1345
  VIDEO_PREP_LIMITS = {
1314
- maxRawByteCount: 500 * 1024 * 1024,
1315
- inlineContentMaxDurationSeconds: 60,
1316
- backgroundVideoMaxDurationSeconds: 30,
1346
+ maxRawByteCount: unsafeAsPositiveByteCount(500 * 1024 * 1024),
1347
+ inlineContentMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(60),
1348
+ backgroundVideoMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(30),
1317
1349
  targetMaxDimensionPixels: 1080
1318
1350
  };
1319
1351
  }
@@ -1384,10 +1416,10 @@ var init_identifiers = __esm({
1384
1416
 
1385
1417
  // ../media-core/src/url.ts
1386
1418
  function isRelativeDirectImageStoragePath(storagePath) {
1387
- return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix));
1419
+ return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix)) && parseStoragePath(storagePath).ok;
1388
1420
  }
1389
1421
  function isSiteScopedDirectImageStoragePath(storagePath) {
1390
- return storagePath.startsWith("sites/");
1422
+ return storagePath.startsWith("sites/") && parseStoragePath(storagePath).ok;
1391
1423
  }
1392
1424
  function encodeStoragePath(storagePath) {
1393
1425
  return storagePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
@@ -1468,6 +1500,7 @@ var DEFAULT_BASE_PATH, RELATIVE_STORAGE_PATH_PREFIXES, formatNumber, ORDER, getI
1468
1500
  var init_url = __esm({
1469
1501
  "../media-core/src/url.ts"() {
1470
1502
  "use strict";
1503
+ init_fileStorage();
1471
1504
  DEFAULT_BASE_PATH = "/api/img";
1472
1505
  RELATIVE_STORAGE_PATH_PREFIXES = [
1473
1506
  "originals/",
@@ -2051,9 +2084,12 @@ var init_schemas = __esm({
2051
2084
  });
2052
2085
  coursePricingSchema = z5.object({
2053
2086
  bookingRequirement: z5.enum(["none", "required"]),
2054
- pricingStrategy: z5.enum(["free", "single", "tiered"]),
2087
+ pricingStrategy: z5.enum(["free", "single", "tiered", "sliding_scale"]),
2055
2088
  currency: z5.string(),
2056
2089
  singlePriceAmount: z5.number().nullable(),
2090
+ slidingMinAmount: z5.number().nullable().optional(),
2091
+ slidingMaxAmount: z5.number().nullable().optional(),
2092
+ slidingSuggestedAmount: z5.number().nullable().optional(),
2057
2093
  paymentTerms: blockBookingPaymentTermsSchema,
2058
2094
  options: z5.array(coursePricingOptionSchema)
2059
2095
  });
@@ -2218,6 +2254,19 @@ var init_schemas2 = __esm({
2218
2254
  ticketTypes: z6.array(publicEventTicketTypeSchema),
2219
2255
  display: publicEventTicketTypePriceDisplaySchema
2220
2256
  }),
2257
+ z6.object({
2258
+ kind: z6.literal("sliding_scale"),
2259
+ minAmount: z6.number(),
2260
+ maxAmount: z6.number(),
2261
+ suggestedAmount: z6.number().nullable(),
2262
+ currency: z6.string(),
2263
+ display: z6.object({
2264
+ kind: z6.literal("range"),
2265
+ minAmount: z6.number(),
2266
+ maxAmount: z6.number(),
2267
+ currency: z6.string()
2268
+ })
2269
+ }),
2221
2270
  z6.object({
2222
2271
  kind: z6.literal("course"),
2223
2272
  display: z6.object({ kind: z6.literal("course"), display: publicCoursePriceDisplaySchema })
@@ -23338,6 +23387,7 @@ var init_booking_form_reserved_fields = __esm({
23338
23387
  "selectedDate",
23339
23388
  "selectedSlot",
23340
23389
  "discountCode",
23390
+ "selectedSlidingAmountInput",
23341
23391
  "selectedCollectionOptionKind",
23342
23392
  "selectedAppointmentPackageId"
23343
23393
  ];
@@ -23623,6 +23673,32 @@ function readOptionalString(value) {
23623
23673
  function readOptionalNumber(value) {
23624
23674
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
23625
23675
  }
23676
+ function getBookingServiceSlidingScale(service) {
23677
+ const pricing = service?.pricing;
23678
+ if (!pricing || pricing.pricingStrategy !== "sliding_scale" || pricing.slidingMinAmount === null || pricing.slidingMinAmount === void 0 || pricing.slidingMaxAmount === null || pricing.slidingMaxAmount === void 0) {
23679
+ return null;
23680
+ }
23681
+ return {
23682
+ minAmount: pricing.slidingMinAmount,
23683
+ maxAmount: pricing.slidingMaxAmount,
23684
+ suggestedAmount: pricing.slidingSuggestedAmount ?? null,
23685
+ currency: pricing.currency
23686
+ };
23687
+ }
23688
+ function parseBookingCurrencyInputToCents(value) {
23689
+ const trimmed = value?.trim() ?? "";
23690
+ if (!trimmed) return null;
23691
+ if (!/^(?:\d+(?:\.\d{0,2})?|\.\d{1,2})$/.test(trimmed)) return null;
23692
+ const parsed = Number(trimmed);
23693
+ if (!Number.isFinite(parsed) || parsed < 0) return null;
23694
+ return Math.round(parsed * 100);
23695
+ }
23696
+ function formatBookingCentsAsCurrencyInput(amount) {
23697
+ return amount % 100 === 0 ? String(amount / 100) : (amount / 100).toFixed(2);
23698
+ }
23699
+ function isBookingServiceSlidingAmountInRange(slidingScale, selectedAmount) {
23700
+ return selectedAmount !== null && selectedAmount >= slidingScale.minAmount && selectedAmount <= slidingScale.maxAmount;
23701
+ }
23626
23702
  function readStringArray(value) {
23627
23703
  if (!Array.isArray(value)) return void 0;
23628
23704
  const strings = value.filter((item) => typeof item === "string");
@@ -23811,6 +23887,20 @@ function prepareBookingSubmission(input) {
23811
23887
  };
23812
23888
  }
23813
23889
  const paymentSelectionMode = getBookingServicePaymentSelectionMode(selectedService);
23890
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
23891
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
23892
+ input.data.selectedSlidingAmountInput
23893
+ );
23894
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(slidingScale, selectedSlidingAmount)) {
23895
+ return {
23896
+ ok: false,
23897
+ error: {
23898
+ tag: selectedSlidingAmount === null ? "sliding-amount-required" : "sliding-amount-out-of-range",
23899
+ serviceId,
23900
+ serviceTitle: selectedService.title || serviceId
23901
+ }
23902
+ };
23903
+ }
23814
23904
  if (paymentSelectionMode === "choice_required" && !toSelectedCollectionOptionKind(input.data.selectedCollectionOptionKind)) {
23815
23905
  return {
23816
23906
  ok: false,
@@ -23838,6 +23928,7 @@ function prepareBookingSubmission(input) {
23838
23928
  ...common,
23839
23929
  kind: "standard",
23840
23930
  ...discountCode ? { discountCode } : {},
23931
+ ...slidingScale && selectedSlidingAmount !== null ? { selectedSlidingAmount } : {},
23841
23932
  ...selectedCollectionOptionKind ? { selectedCollectionOptionKind } : {},
23842
23933
  altchaSolution: input.altchaSolution ?? null
23843
23934
  }
@@ -23851,6 +23942,10 @@ function formatBookingSubmissionPreparationError(error) {
23851
23942
  return `Service not found: ${error.serviceId}`;
23852
23943
  case "payment-option-required":
23853
23944
  return `Please choose how you want to pay for "${error.serviceTitle}".`;
23945
+ case "sliding-amount-required":
23946
+ return `Please choose an amount for "${error.serviceTitle}".`;
23947
+ case "sliding-amount-out-of-range":
23948
+ return `Please choose an amount within the sliding scale for "${error.serviceTitle}".`;
23854
23949
  case "payment-option-unsupported":
23855
23950
  return `Service "${error.serviceTitle}" has a payment option that is not supported yet.`;
23856
23951
  case "service-missing-duration":
@@ -24001,6 +24096,7 @@ var init_booking_form_interactive = __esm({
24001
24096
  title: z36.string(),
24002
24097
  description: z36.string().nullable().optional().transform((value) => value ?? void 0),
24003
24098
  durationMinutes: z36.number().optional(),
24099
+ pricing: z36.custom().optional(),
24004
24100
  priceAmount: z36.number().nullable().optional(),
24005
24101
  priceCurrency: z36.string().nullable().optional(),
24006
24102
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
@@ -24989,7 +25085,7 @@ function isRecord6(value) {
24989
25085
  }
24990
25086
  function isPublicEventPricing(value) {
24991
25087
  if (!isRecord6(value) || typeof value.kind !== "string") return false;
24992
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
25088
+ 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";
24993
25089
  }
24994
25090
  function isPublicEventTicketType(value) {
24995
25091
  return isRecord6(value) && typeof value.id === "string";
@@ -33593,12 +33689,13 @@ var init_carousel_client = __esm({
33593
33689
  ]
33594
33690
  );
33595
33691
  const resolvedSlides = resolveSlidesToShowMobile(normalized.slidesToShow);
33692
+ const canAutoplay = normalized.autoplay && slides.length > 1;
33596
33693
  const cssVars = React5.useMemo(
33597
33694
  () => getCarouselSlidesToShowCssVars(normalized.slidesToShow),
33598
33695
  [normalized.slidesToShow]
33599
33696
  );
33600
33697
  const plugins = React5.useMemo(() => {
33601
- if (!normalized.autoplay) return void 0;
33698
+ if (!canAutoplay) return void 0;
33602
33699
  return [
33603
33700
  Autoplay({
33604
33701
  delay: normalized.autoplayDelay,
@@ -33608,7 +33705,7 @@ var init_carousel_client = __esm({
33608
33705
  // Continue after manual navigation
33609
33706
  })
33610
33707
  ];
33611
- }, [normalized.autoplay, normalized.autoplayDelay]);
33708
+ }, [canAutoplay, normalized.autoplayDelay]);
33612
33709
  const [viewportRef, api] = useEmblaCarousel(
33613
33710
  {
33614
33711
  align: "start",
@@ -33647,7 +33744,7 @@ var init_carousel_client = __esm({
33647
33744
  };
33648
33745
  }, [api]);
33649
33746
  React5.useEffect(() => {
33650
- if (!api || !normalized.autoplay) return;
33747
+ if (!api || !canAutoplay) return;
33651
33748
  const autoplayPlugin = api.plugins()?.autoplay;
33652
33749
  if (!autoplayPlugin) return;
33653
33750
  const handleVisibility = () => {
@@ -33656,7 +33753,7 @@ var init_carousel_client = __esm({
33656
33753
  };
33657
33754
  document.addEventListener("visibilitychange", handleVisibility);
33658
33755
  return () => document.removeEventListener("visibilitychange", handleVisibility);
33659
- }, [api, normalized.autoplay]);
33756
+ }, [api, canAutoplay]);
33660
33757
  const inner = /* @__PURE__ */ jsxs13(Fragment3, { children: [
33661
33758
  /* @__PURE__ */ jsx15("div", { className: "carousel-frame rb-overflow-hidden rb-w-full", children: /* @__PURE__ */ jsx15(
33662
33759
  "div",
@@ -33707,7 +33804,7 @@ var init_carousel_client = __esm({
33707
33804
  "data-show-controls": normalized.showControls ? "true" : "false",
33708
33805
  "data-show-dots": normalized.showDots ? "true" : "false",
33709
33806
  "data-arrow-visibility": normalized.arrowVisibility,
33710
- "data-autoplay": normalized.autoplay ? "true" : "false",
33807
+ "data-autoplay": canAutoplay ? "true" : "false",
33711
33808
  "data-loop": normalized.loop ? "true" : "false",
33712
33809
  children: inner
33713
33810
  }
@@ -34710,6 +34807,7 @@ function createBlockApi(config) {
34710
34807
  body: {
34711
34808
  courseId: data.courseId,
34712
34809
  ...data.pricingOptionId ? { pricingOptionId: data.pricingOptionId } : {},
34810
+ ...data.selectedSlidingAmount !== void 0 ? { selectedSlidingAmount: data.selectedSlidingAmount } : {},
34713
34811
  email: data.email,
34714
34812
  name: data.name,
34715
34813
  phone: data.phone,
@@ -38947,6 +39045,7 @@ function useBookingSubmission(_siteId) {
38947
39045
  customFields: data.customFields,
38948
39046
  timezone: data.timezone,
38949
39047
  discountCode: data.discountCode,
39048
+ selectedSlidingAmount: data.selectedSlidingAmount,
38950
39049
  selectedCollectionOptionKind: data.selectedCollectionOptionKind,
38951
39050
  altchaSolution: data.altchaSolution ?? null,
38952
39051
  successUrl: data.successUrl,
@@ -39475,6 +39574,9 @@ function createInitialBookingFlowReducerState(input) {
39475
39574
  fieldValues: collectInitialFieldValues(initialData),
39476
39575
  touchedFields: {},
39477
39576
  discountCode: toOptionalString(initialData.discountCode) ?? "",
39577
+ selectedSlidingAmountInput: toOptionalString(
39578
+ initialData.selectedSlidingAmountInput
39579
+ ) ?? "",
39478
39580
  selectedCollectionOptionKind: toSelectedCollectionOptionKind(
39479
39581
  initialData.selectedCollectionOptionKind
39480
39582
  ),
@@ -39527,6 +39629,7 @@ function bookingFlowReducer(state, event) {
39527
39629
  selectedDate: void 0,
39528
39630
  selectedSlot: null,
39529
39631
  selectedAppointmentPackageId: void 0,
39632
+ selectedSlidingAmountInput: "",
39530
39633
  selectedCollectionOptionKind: void 0,
39531
39634
  validationErrors: clearBookingFormError(state.validationErrors)
39532
39635
  };
@@ -39580,6 +39683,13 @@ function bookingFlowReducer(state, event) {
39580
39683
  selectedAppointmentPackageId: void 0,
39581
39684
  validationErrors: clearBookingFormError(state.validationErrors)
39582
39685
  };
39686
+ case "sliding-amount-changed":
39687
+ return {
39688
+ ...state,
39689
+ selectedSlidingAmountInput: event.value,
39690
+ selectedAppointmentPackageId: void 0,
39691
+ validationErrors: clearBookingFormError(state.validationErrors)
39692
+ };
39583
39693
  case "appointment-package-selected":
39584
39694
  return {
39585
39695
  ...state,
@@ -39682,6 +39792,7 @@ function toBookingFormData(state) {
39682
39792
  selectedDate: state.selectedDate,
39683
39793
  selectedSlot: state.selectedSlot?.startAt,
39684
39794
  discountCode: state.discountCode || void 0,
39795
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput || void 0,
39685
39796
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
39686
39797
  selectedAppointmentPackageId: state.selectedAppointmentPackageId
39687
39798
  };
@@ -39699,7 +39810,8 @@ function buildBookingStepResolution(input) {
39699
39810
  input.form.settings?.serviceIds && input.form.settings.serviceIds.length > 1
39700
39811
  );
39701
39812
  const selectedService = paymentResolution.service;
39702
- const needsPayment = paymentResolution.mode !== "hidden" || Boolean(
39813
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
39814
+ const needsPayment = Boolean(selectedServiceSlidingScale) || paymentResolution.mode !== "hidden" || Boolean(
39703
39815
  input.enableAppointmentPackages && selectedService && (selectedService.priceAmount ?? 0) > 0
39704
39816
  );
39705
39817
  const hasFields = Boolean(input.form.schema?.fields.length);
@@ -39743,6 +39855,18 @@ function validateBookingStep(input) {
39743
39855
  services: input.services,
39744
39856
  defaultServiceId: input.defaultServiceId
39745
39857
  });
39858
+ const slidingScale = getBookingServiceSlidingScale(resolution.service);
39859
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(
39860
+ slidingScale,
39861
+ parseBookingCurrencyInputToCents(input.data.selectedSlidingAmountInput)
39862
+ )) {
39863
+ return {
39864
+ ok: false,
39865
+ errors: createFormBookingValidationError(
39866
+ "Please choose an amount within the sliding scale"
39867
+ )
39868
+ };
39869
+ }
39746
39870
  if (resolution.mode === "hidden" || resolution.mode === "disclosure_only") {
39747
39871
  return { ok: true };
39748
39872
  }
@@ -40141,6 +40265,7 @@ var init_useHeadlessBookingFlow_state = __esm({
40141
40265
  "../blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.ts"() {
40142
40266
  "use strict";
40143
40267
  init_booking_form_headless_state();
40268
+ init_booking_form_state();
40144
40269
  }
40145
40270
  });
40146
40271
 
@@ -40180,11 +40305,27 @@ function useHeadlessBookingFlow(options) {
40180
40305
  const selectedServiceId = state.selectedServiceId ?? defaultServiceId;
40181
40306
  const selectedResourceId = state.selectedResourceId ?? defaultResourceId;
40182
40307
  const selectedService = options.services.find((service) => service.id === selectedServiceId) ?? null;
40308
+ const lastSlidingServiceIdRef = useRef9(null);
40309
+ useEffect8(() => {
40310
+ const selectedId = selectedService?.id ?? null;
40311
+ if (lastSlidingServiceIdRef.current === selectedId) return;
40312
+ lastSlidingServiceIdRef.current = selectedId;
40313
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
40314
+ dispatch({
40315
+ type: "sliding-amount-changed",
40316
+ value: slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatBookingCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
40317
+ });
40318
+ }, [selectedService]);
40183
40319
  const hasPortalBearerAuth = (api.config.authOverrides?.portal ?? api.config.auth).type === "bearer";
40320
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
40321
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
40322
+ state.selectedSlidingAmountInput
40323
+ );
40324
+ const selectedAppointmentPriceAmount = selectedServiceSlidingScale && selectedSlidingAmount !== null ? selectedSlidingAmount : selectedService?.priceAmount ?? 0;
40184
40325
  const canLoadAppointmentPackages = Boolean(
40185
- options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && (selectedService.priceAmount ?? 0) > 0
40326
+ options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && !selectedServiceSlidingScale && (selectedService.priceAmount ?? 0) > 0
40186
40327
  );
40187
- const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
40328
+ const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || selectedServiceSlidingScale || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
40188
40329
  const data = useMemo6(() => toBookingFormData(state), [state]);
40189
40330
  const availabilityDatesQuery = useMemo6(
40190
40331
  () => buildBookingAvailabilityQuery({
@@ -40414,13 +40555,13 @@ function useHeadlessBookingFlow(options) {
40414
40555
  state.selectedSlot
40415
40556
  ]);
40416
40557
  const appointmentDiscountContext = useMemo6(
40417
- () => selectedService && (selectedService.priceAmount ?? 0) > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
40418
- [selectedService?.id, selectedService?.priceAmount]
40558
+ () => selectedService && selectedAppointmentPriceAmount > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
40559
+ [selectedAppointmentPriceAmount, selectedService?.id]
40419
40560
  );
40420
40561
  const discountUi = useDiscountCodeUi({
40421
40562
  api,
40422
40563
  discountContext: appointmentDiscountContext,
40423
- subtotalCents: selectedService?.priceAmount ?? 0
40564
+ subtotalCents: selectedAppointmentPriceAmount
40424
40565
  });
40425
40566
  const { submit, clearFeedback } = useBookingSubmission(options.siteId);
40426
40567
  const submittingRef = useRef9(false);
@@ -40563,6 +40704,7 @@ function useHeadlessBookingFlow(options) {
40563
40704
  type: "appointment-package-selected",
40564
40705
  packageId: packageId ? unsafeAsCustomerPassId(packageId) : null
40565
40706
  }),
40707
+ setSlidingAmountInput: (value) => dispatch({ type: "sliding-amount-changed", value }),
40566
40708
  setDiscountCode: (code) => {
40567
40709
  dispatch({ type: "discount-code-changed", code });
40568
40710
  latestRef.current.setDiscountCode(code);
@@ -40633,11 +40775,11 @@ function useHeadlessBookingFlow(options) {
40633
40775
  () => selectedService && paymentResolution.mode !== "unsupported" ? buildBookingPaymentSelectionViewModel({
40634
40776
  serviceTitle: selectedService.title,
40635
40777
  paymentTerms: selectedService.paymentTerms,
40636
- priceAmount: selectedService.priceAmount,
40778
+ priceAmount: selectedAppointmentPriceAmount,
40637
40779
  priceCurrency: selectedService.priceCurrency,
40638
40780
  bookingStartAt: state.selectedSlot?.startAt
40639
40781
  }) : null,
40640
- [paymentResolution.mode, selectedService, state.selectedSlot?.startAt]
40782
+ [paymentResolution.mode, selectedAppointmentPriceAmount, selectedService, state.selectedSlot?.startAt]
40641
40783
  );
40642
40784
  const appointmentPackageItems = appointmentPackages.tag === "loaded" ? appointmentPackages.value : [];
40643
40785
  const selectedAppointmentPackage = appointmentPackageItems.find(
@@ -40666,6 +40808,9 @@ function useHeadlessBookingFlow(options) {
40666
40808
  selection: null,
40667
40809
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
40668
40810
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
40811
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
40812
+ selectedSlidingAmount,
40813
+ slidingScale: selectedServiceSlidingScale,
40669
40814
  appointmentPackages: appointmentPackagesModel,
40670
40815
  errorMessage: paymentResolution.errorMessage ?? "This service cannot be booked right now."
40671
40816
  } : {
@@ -40674,6 +40819,9 @@ function useHeadlessBookingFlow(options) {
40674
40819
  selection: paymentSelection,
40675
40820
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
40676
40821
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
40822
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
40823
+ selectedSlidingAmount,
40824
+ slidingScale: selectedServiceSlidingScale,
40677
40825
  appointmentPackages: appointmentPackagesModel
40678
40826
  };
40679
40827
  return {
@@ -41368,6 +41516,9 @@ var init_FieldValidationMessage = __esm({
41368
41516
 
41369
41517
  // ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
41370
41518
  import { Fragment as Fragment5, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
41519
+ function formatBookingAmount(amount, currency) {
41520
+ return formatCurrencyAmount(amount, currency, { decimals: "auto" });
41521
+ }
41371
41522
  function HeadlessBookingDefaultForm({
41372
41523
  booking,
41373
41524
  onSubmit
@@ -41644,6 +41795,7 @@ function HeadlessBookingPaymentSelectionStep({
41644
41795
  }
41645
41796
  const packageOptions = booking.payment.appointmentPackages.items;
41646
41797
  const hasPackageOptions = packageOptions.length > 0;
41798
+ const slidingScale = booking.payment.slidingScale;
41647
41799
  return /* @__PURE__ */ jsxs21("div", { className: "ab-stack", children: [
41648
41800
  /* @__PURE__ */ jsxs21("div", { className: "ab-info-box", children: [
41649
41801
  /* @__PURE__ */ jsx27("h3", { className: "ab-subtitle", children: viewModel.sectionTitle }),
@@ -41654,6 +41806,28 @@ function HeadlessBookingPaymentSelectionStep({
41654
41806
  booking.payment.appointmentPackages.state.message,
41655
41807
  " You can continue with the normal payment option."
41656
41808
  ] }) : null,
41809
+ slidingScale ? /* @__PURE__ */ jsxs21("div", { className: "rb-space-y-2", children: [
41810
+ /* @__PURE__ */ jsx27("label", { htmlFor: "appointment-sliding-amount", className: "form-label", children: "Choose your price" }),
41811
+ /* @__PURE__ */ jsxs21("p", { className: "rb-caption status-muted", children: [
41812
+ formatBookingAmount(slidingScale.minAmount, slidingScale.currency),
41813
+ " to ",
41814
+ formatBookingAmount(slidingScale.maxAmount, slidingScale.currency),
41815
+ slidingScale.suggestedAmount !== null ? `, suggested ${formatBookingAmount(slidingScale.suggestedAmount, slidingScale.currency)}` : ""
41816
+ ] }),
41817
+ /* @__PURE__ */ jsx27(
41818
+ "input",
41819
+ {
41820
+ id: "appointment-sliding-amount",
41821
+ type: "number",
41822
+ className: "form-input",
41823
+ min: slidingScale.minAmount / 100,
41824
+ max: slidingScale.maxAmount / 100,
41825
+ step: "0.01",
41826
+ value: booking.payment.selectedSlidingAmountInput,
41827
+ onChange: (event) => booking.actions.setSlidingAmountInput(event.target.value)
41828
+ }
41829
+ )
41830
+ ] }) : null,
41657
41831
  /* @__PURE__ */ jsxs21("div", { className: "ab-stack", role: "list", "aria-label": "Payment options", children: [
41658
41832
  packageOptions.map((appointmentPackage) => {
41659
41833
  const isSelected = booking.payment.selectedAppointmentPackageId === appointmentPackage.customerPassId;
@@ -43500,6 +43674,7 @@ function useEligibility(params) {
43500
43674
  occurrenceId: params.occurrenceId,
43501
43675
  attendanceMode: params.attendanceMode ?? void 0,
43502
43676
  ticketTypeId: params.ticketTypeId ?? void 0,
43677
+ selectedSlidingAmount: params.selectedSlidingAmount ?? void 0,
43503
43678
  email: params.email.trim().toLowerCase(),
43504
43679
  ticketsCount: params.ticketsCount
43505
43680
  });
@@ -43520,7 +43695,7 @@ function useEligibility(params) {
43520
43695
  return () => {
43521
43696
  cancelled = true;
43522
43697
  };
43523
- }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.email, params.ticketsCount]);
43698
+ }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.selectedSlidingAmount, params.email, params.ticketsCount]);
43524
43699
  return {
43525
43700
  state,
43526
43701
  eligibility: state.eligibility,
@@ -43871,6 +44046,41 @@ function TicketCounterLimit(props2) {
43871
44046
  ] }) : null
43872
44047
  ] });
43873
44048
  }
44049
+ function formatCurrencyInputAmount(amount) {
44050
+ const major = amount / 100;
44051
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
44052
+ }
44053
+ function SlidingScaleAmountField(props2) {
44054
+ const minLabel = formatPrice(props2.slidingScale.minAmount, props2.currency);
44055
+ const maxLabel = formatPrice(props2.slidingScale.maxAmount, props2.currency);
44056
+ const suggestedLabel = props2.slidingScale.suggestedAmount !== null ? formatPrice(props2.slidingScale.suggestedAmount, props2.currency) : null;
44057
+ return /* @__PURE__ */ jsxs28("div", { className: "er-field", children: [
44058
+ /* @__PURE__ */ jsx42("label", { htmlFor: "er-sliding-amount", className: "er-label", children: "Amount" }),
44059
+ /* @__PURE__ */ jsx42(
44060
+ "input",
44061
+ {
44062
+ id: "er-sliding-amount",
44063
+ type: "number",
44064
+ min: formatCurrencyInputAmount(props2.slidingScale.minAmount),
44065
+ max: formatCurrencyInputAmount(props2.slidingScale.maxAmount),
44066
+ step: "0.01",
44067
+ inputMode: "decimal",
44068
+ className: "er-input",
44069
+ value: props2.value,
44070
+ onChange: (event) => props2.onChange?.(event.target.value)
44071
+ }
44072
+ ),
44073
+ /* @__PURE__ */ jsxs28("p", { className: "er-text er-text--muted", children: [
44074
+ "Choose between ",
44075
+ minLabel,
44076
+ " and ",
44077
+ maxLabel,
44078
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
44079
+ "."
44080
+ ] }),
44081
+ props2.message ? /* @__PURE__ */ jsx42("p", { className: "er-text er-text--muted", children: props2.message }) : null
44082
+ ] });
44083
+ }
43874
44084
  function getTicketTypeCounterAvailabilityLabel(input) {
43875
44085
  if (input.selectedRemainingCapacity !== null) {
43876
44086
  return `${input.selectedRemainingCapacity} remaining for this ticket type`;
@@ -43942,6 +44152,10 @@ var init_TicketSelectionStep = __esm({
43942
44152
  maxTickets,
43943
44153
  selectedTicketTypeId = null,
43944
44154
  onTicketTypeChange,
44155
+ slidingScale = null,
44156
+ selectedSlidingAmountInput = "",
44157
+ slidingScaleSelectionMessage = null,
44158
+ onSlidingAmountChange,
43945
44159
  onTicketsChange
43946
44160
  }) => {
43947
44161
  const deliveryMode = event?.deliveryMode ?? "in_person";
@@ -44057,6 +44271,16 @@ var init_TicketSelectionStep = __esm({
44057
44271
  ] })
44058
44272
  ] })
44059
44273
  ] }) : /* @__PURE__ */ jsxs28(Fragment8, { children: [
44274
+ slidingScale ? /* @__PURE__ */ jsx42(
44275
+ SlidingScaleAmountField,
44276
+ {
44277
+ slidingScale,
44278
+ currency: eventPricingCurrency ?? DEFAULT_EVENT_CURRENCY2,
44279
+ value: selectedSlidingAmountInput,
44280
+ message: slidingScaleSelectionMessage,
44281
+ onChange: onSlidingAmountChange
44282
+ }
44283
+ ) : null,
44060
44284
  /* @__PURE__ */ jsx42("h3", { className: "er-title", children: canJoinWaitlist ? "How many waitlist spots?" : "How many tickets?" }),
44061
44285
  /* @__PURE__ */ jsxs28("div", { className: "er-ticket-counter-group", children: [
44062
44286
  /* @__PURE__ */ jsx42(
@@ -45071,6 +45295,10 @@ function EventRegistrationWizardContent(props2) {
45071
45295
  maxTickets: wizard.maxTicketsNum,
45072
45296
  selectedTicketTypeId: wizard.selectedTicketTypeId,
45073
45297
  onTicketTypeChange: wizard.setSelectedTicketTypeId,
45298
+ slidingScale: wizard.selectedEventSlidingScale,
45299
+ selectedSlidingAmountInput: wizard.selectedSlidingAmountInput,
45300
+ slidingScaleSelectionMessage: wizard.slidingScaleSelectionMessage,
45301
+ onSlidingAmountChange: wizard.setSelectedSlidingAmountInput,
45074
45302
  onTicketsChange: wizard.setTicketsCount
45075
45303
  }
45076
45304
  )
@@ -45287,7 +45515,7 @@ function EventRegistrationSummaryBar(props2) {
45287
45515
  const summaryTimeZone = wizard.occurrenceContext?.timeZone ?? summaryEvent?.timeZone ?? null;
45288
45516
  const occurrenceOverrideTitle = wizard.occurrenceContext?.overrides?.title;
45289
45517
  const summaryTitle = occurrenceOverrideTitle ?? summaryEvent?.title ?? "";
45290
- const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
45518
+ const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (wizard.selectedEventSlidingScale ? wizard.selectedSlidingAmount : null) ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
45291
45519
  const summaryCurrency = (summaryEvent ? publicEventPricingCurrency(summaryEvent.pricing) : null) ?? DEFAULT_EVENT_CURRENCY2;
45292
45520
  const teamMemberNames = (summaryEvent?.teamMembers ?? []).map((teamMember) => teamMember.name).filter(Boolean);
45293
45521
  const subtotalCents = Math.max(0, summaryPrice * wizard.ticketsCount);
@@ -45738,6 +45966,7 @@ function useEventRegistrationSubmit(options) {
45738
45966
  occurrenceId: submitOccurrenceId,
45739
45967
  attendanceMode: options.selectedAttendanceMode ?? void 0,
45740
45968
  ticketTypeId: options.selectedTicketTypeId ?? void 0,
45969
+ selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
45741
45970
  email: options.formData.email.trim().toLowerCase(),
45742
45971
  name: options.formData.name.trim(),
45743
45972
  ticketsCount: options.ticketsCount,
@@ -45847,6 +46076,35 @@ var init_useEventRegistrationSubmit = __esm({
45847
46076
 
45848
46077
  // ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
45849
46078
  import { useCallback as useCallback8, useEffect as useEffect15, useMemo as useMemo10, useReducer as useReducer10, useState as useState7 } from "react";
46079
+ function parseCurrencyInputToCents(value) {
46080
+ const trimmed = value.trim();
46081
+ if (!trimmed) return null;
46082
+ const amount = Number(trimmed);
46083
+ if (!Number.isFinite(amount) || amount < 0) return null;
46084
+ return Math.round(amount * 100);
46085
+ }
46086
+ function formatCentsAsCurrencyInput(amount) {
46087
+ const major = amount / 100;
46088
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
46089
+ }
46090
+ function getSlidingScaleForEvent(event) {
46091
+ const pricing = event ? resolveRuntimeEventPricing(event) : null;
46092
+ return pricing?.kind === "sliding_scale" ? {
46093
+ minAmount: pricing.minAmount,
46094
+ maxAmount: pricing.maxAmount,
46095
+ suggestedAmount: pricing.suggestedAmount
46096
+ } : null;
46097
+ }
46098
+ function getSlidingScaleSelectionMessage(input) {
46099
+ if (!input.slidingScale) return null;
46100
+ if (input.selectedAmount === null) {
46101
+ return "Choose an amount to continue.";
46102
+ }
46103
+ if (input.selectedAmount < input.slidingScale.minAmount || input.selectedAmount > input.slidingScale.maxAmount) {
46104
+ return "Choose an amount within the sliding scale.";
46105
+ }
46106
+ return null;
46107
+ }
45850
46108
  function useEventRegistrationWizard(options) {
45851
46109
  const api = useBlockApi();
45852
46110
  const registrationEvents = useMemo10(
@@ -45891,6 +46149,10 @@ function useEventRegistrationWizard(options) {
45891
46149
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
45892
46150
  );
45893
46151
  const [selectedTicketTypeId, setSelectedTicketTypeId] = useState7(null);
46152
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState7(() => {
46153
+ const slidingScale = getSlidingScaleForEvent(preSelectedEvent ?? null);
46154
+ return slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "";
46155
+ });
45894
46156
  const [registrationResult, setRegistrationResult] = useState7(null);
45895
46157
  const [errorMessage, setErrorMessage] = useState7("");
45896
46158
  const [altchaSolution, setAltchaSolution] = useState7(null);
@@ -45927,6 +46189,7 @@ function useEventRegistrationWizard(options) {
45927
46189
  occurrenceId,
45928
46190
  attendanceMode: selectedAttendanceMode,
45929
46191
  ticketTypeId: selectedTicketTypeId,
46192
+ selectedSlidingAmount: parseCurrencyInputToCents(selectedSlidingAmountInput),
45930
46193
  email: formData.email,
45931
46194
  ticketsCount
45932
46195
  });
@@ -46062,6 +46325,18 @@ function useEventRegistrationWizard(options) {
46062
46325
  }),
46063
46326
  [selectedAttendanceMode, selectedEvent, preSelectedEvent, eligibility, selectedTicketTypeId]
46064
46327
  );
46328
+ const selectedSlidingAmount = useMemo10(
46329
+ () => parseCurrencyInputToCents(selectedSlidingAmountInput),
46330
+ [selectedSlidingAmountInput]
46331
+ );
46332
+ const selectedEventSlidingScale = useMemo10(
46333
+ () => getSlidingScaleForEvent(selectedEvent ?? preSelectedEvent),
46334
+ [selectedEvent, preSelectedEvent]
46335
+ );
46336
+ const slidingScaleSelectionMessage = getSlidingScaleSelectionMessage({
46337
+ slidingScale: selectedEventSlidingScale,
46338
+ selectedAmount: selectedSlidingAmount
46339
+ });
46065
46340
  const ticketTypeSelectionRequired = useMemo10(() => {
46066
46341
  const event = selectedEvent ?? preSelectedEvent;
46067
46342
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") {
@@ -46130,12 +46405,24 @@ function useEventRegistrationWizard(options) {
46130
46405
  setGuestSectionShake(false);
46131
46406
  };
46132
46407
  const eventRequiresPayment = useMemo10(
46133
- () => resolveEventRequiresPayment({
46408
+ () => {
46409
+ if (selectedEventSlidingScale) {
46410
+ return (selectedSlidingAmount ?? 0) > 0;
46411
+ }
46412
+ return resolveEventRequiresPayment({
46413
+ eligibility,
46414
+ selectedTicketType,
46415
+ event: selectedEvent ?? preSelectedEvent
46416
+ });
46417
+ },
46418
+ [
46134
46419
  eligibility,
46135
46420
  selectedTicketType,
46136
- event: selectedEvent ?? preSelectedEvent
46137
- }),
46138
- [eligibility, selectedTicketType, selectedEvent, preSelectedEvent]
46421
+ selectedEvent,
46422
+ preSelectedEvent,
46423
+ selectedEventSlidingScale,
46424
+ selectedSlidingAmount
46425
+ ]
46139
46426
  );
46140
46427
  const handleDetailsComplete = async () => {
46141
46428
  if (ticketTypeSelectionRequired && !hasValidSelectedTicketType) {
@@ -46144,6 +46431,11 @@ function useEventRegistrationWizard(options) {
46144
46431
  setStep("error");
46145
46432
  return { ok: false, reason: "blocked", message };
46146
46433
  }
46434
+ if (slidingScaleSelectionMessage) {
46435
+ setErrorMessage(slidingScaleSelectionMessage);
46436
+ setStep("error");
46437
+ return { ok: false, reason: "blocked", message: slidingScaleSelectionMessage };
46438
+ }
46147
46439
  if (!validateForm()) {
46148
46440
  setGuestSectionShake(true);
46149
46441
  return {
@@ -46228,9 +46520,18 @@ function useEventRegistrationWizard(options) {
46228
46520
  const discountSubtotalCents = useMemo10(() => {
46229
46521
  if (discountContext?.type !== "event") return void 0;
46230
46522
  const event = selectedEvent ?? preSelectedEvent;
46231
- const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
46523
+ const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (selectedEventSlidingScale ? selectedSlidingAmount : null) ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
46232
46524
  return Math.max(0, eventPriceCents * ticketsCount);
46233
- }, [discountContext, selectedTicketType, eligibility, selectedEvent, preSelectedEvent, ticketsCount]);
46525
+ }, [
46526
+ discountContext,
46527
+ selectedTicketType,
46528
+ eligibility,
46529
+ selectedEvent,
46530
+ preSelectedEvent,
46531
+ selectedEventSlidingScale,
46532
+ selectedSlidingAmount,
46533
+ ticketsCount
46534
+ ]);
46234
46535
  const discountUi = useDiscountCodeUi({ api, discountContext, subtotalCents: discountSubtotalCents });
46235
46536
  const selectionMethod = paymentSelection.method;
46236
46537
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
@@ -46239,6 +46540,7 @@ function useEventRegistrationWizard(options) {
46239
46540
  registrationTarget,
46240
46541
  selectedAttendanceMode,
46241
46542
  selectedTicketTypeId,
46543
+ selectedSlidingAmount,
46242
46544
  ticketsCount,
46243
46545
  formData,
46244
46546
  altchaSolution,
@@ -46253,6 +46555,10 @@ function useEventRegistrationWizard(options) {
46253
46555
  });
46254
46556
  const handleEventSelect = (event) => {
46255
46557
  setSelectedEvent(event);
46558
+ const slidingScale = getSlidingScaleForEvent(event);
46559
+ setSelectedSlidingAmountInput(
46560
+ slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
46561
+ );
46256
46562
  setSelectedAttendanceMode(
46257
46563
  (event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person"
46258
46564
  );
@@ -46262,6 +46568,7 @@ function useEventRegistrationWizard(options) {
46262
46568
  const handleRetry = () => {
46263
46569
  dispatchFlow({ type: "reset", hasPreSelectedContext });
46264
46570
  setSelectedTicketTypeId(null);
46571
+ setSelectedSlidingAmountInput("");
46265
46572
  setSelectedAttendanceMode(
46266
46573
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
46267
46574
  );
@@ -46298,6 +46605,11 @@ function useEventRegistrationWizard(options) {
46298
46605
  setSelectedAttendanceMode,
46299
46606
  selectedTicketTypeId,
46300
46607
  setSelectedTicketTypeId,
46608
+ selectedSlidingAmountInput,
46609
+ setSelectedSlidingAmountInput,
46610
+ selectedSlidingAmount,
46611
+ selectedEventSlidingScale,
46612
+ slidingScaleSelectionMessage,
46301
46613
  selectedTicketType,
46302
46614
  visibleTicketTypes,
46303
46615
  ticketTypeSelectionRequired,
@@ -47255,7 +47567,9 @@ function getSelectableCoursePricingOptionsForNow(course, now) {
47255
47567
  return left.name.localeCompare(right.name);
47256
47568
  });
47257
47569
  }
47258
- function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {}) {
47570
+ function resolveCoursePricingState(course, selectedPricingOptionId, selectedSlidingAmountOrNowInput, nowInput = {}) {
47571
+ const selectedSlidingAmount = typeof selectedSlidingAmountOrNowInput === "number" || selectedSlidingAmountOrNowInput === null ? selectedSlidingAmountOrNowInput : null;
47572
+ const effectiveNowInput = typeof selectedSlidingAmountOrNowInput === "object" && selectedSlidingAmountOrNowInput !== null ? selectedSlidingAmountOrNowInput : nowInput;
47259
47573
  const currency = course.pricing?.currency ?? course.currency;
47260
47574
  if (!course.pricing) {
47261
47575
  const legacyAmount = course.priceCents ?? 0;
@@ -47265,6 +47579,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
47265
47579
  selectableOptions: [],
47266
47580
  requiresSelection: false,
47267
47581
  selectedOption: null,
47582
+ slidingScale: null,
47583
+ selectedSlidingAmount: null,
47268
47584
  selectedPriceAmount: legacyAmount,
47269
47585
  selectedRequiresPayment: legacyAmount > 0,
47270
47586
  hasPaidSelection: legacyAmount > 0,
@@ -47279,6 +47595,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
47279
47595
  selectableOptions: [],
47280
47596
  requiresSelection: false,
47281
47597
  selectedOption: null,
47598
+ slidingScale: null,
47599
+ selectedSlidingAmount: null,
47282
47600
  selectedPriceAmount: 0,
47283
47601
  selectedRequiresPayment: false,
47284
47602
  hasPaidSelection: false,
@@ -47292,6 +47610,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
47292
47610
  selectableOptions: [],
47293
47611
  requiresSelection: false,
47294
47612
  selectedOption: null,
47613
+ slidingScale: null,
47614
+ selectedSlidingAmount: null,
47295
47615
  selectedPriceAmount: amount,
47296
47616
  selectedRequiresPayment: amount > 0,
47297
47617
  hasPaidSelection: amount > 0,
@@ -47301,7 +47621,7 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
47301
47621
  case "tiered": {
47302
47622
  const selectableOptions = getSelectableCoursePricingOptionsForNow(
47303
47623
  course,
47304
- resolvePlanningDateInput(nowInput)
47624
+ resolvePlanningDateInput(effectiveNowInput)
47305
47625
  );
47306
47626
  const selectedOption = (selectedPricingOptionId ? selectableOptions.find((option) => option.id === selectedPricingOptionId) : null) ?? (selectableOptions.length === 1 ? selectableOptions[0] ?? null : null);
47307
47627
  const lowestAmount = selectableOptions.reduce((lowest, option) => {
@@ -47317,12 +47637,51 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
47317
47637
  selectableOptions,
47318
47638
  requiresSelection: selectableOptions.length > 1,
47319
47639
  selectedOption,
47640
+ slidingScale: null,
47641
+ selectedSlidingAmount: null,
47320
47642
  selectedPriceAmount: selectedOption?.priceAmount ?? null,
47321
47643
  selectedRequiresPayment: (selectedOption?.priceAmount ?? 0) > 0,
47322
47644
  hasPaidSelection: selectableOptions.some((option) => option.priceAmount > 0),
47323
47645
  hasFreeSelection: selectableOptions.some((option) => option.priceAmount === 0)
47324
47646
  };
47325
47647
  }
47648
+ case "sliding_scale": {
47649
+ const minAmount = course.pricing.slidingMinAmount ?? null;
47650
+ const maxAmount = course.pricing.slidingMaxAmount ?? null;
47651
+ if (minAmount === null || maxAmount === null || minAmount < 0 || maxAmount < minAmount) {
47652
+ return {
47653
+ currency,
47654
+ display: { kind: "unavailable", currency },
47655
+ selectableOptions: [],
47656
+ requiresSelection: true,
47657
+ selectedOption: null,
47658
+ slidingScale: null,
47659
+ selectedSlidingAmount: null,
47660
+ selectedPriceAmount: null,
47661
+ selectedRequiresPayment: false,
47662
+ hasPaidSelection: false,
47663
+ hasFreeSelection: false
47664
+ };
47665
+ }
47666
+ const normalizedSelectedAmount = typeof selectedSlidingAmount === "number" && Number.isFinite(selectedSlidingAmount) ? selectedSlidingAmount : null;
47667
+ return {
47668
+ currency,
47669
+ display: { kind: "range", minAmount, maxAmount, currency },
47670
+ selectableOptions: [],
47671
+ requiresSelection: normalizedSelectedAmount === null,
47672
+ selectedOption: null,
47673
+ slidingScale: {
47674
+ minAmount,
47675
+ maxAmount,
47676
+ suggestedAmount: course.pricing.slidingSuggestedAmount ?? null
47677
+ },
47678
+ selectedSlidingAmount: normalizedSelectedAmount,
47679
+ selectedPriceAmount: normalizedSelectedAmount,
47680
+ selectedRequiresPayment: (normalizedSelectedAmount ?? 0) > 0,
47681
+ hasPaidSelection: maxAmount > 0,
47682
+ hasFreeSelection: minAmount === 0
47683
+ };
47684
+ }
47326
47685
  }
47327
47686
  }
47328
47687
  function formatCoursePriceDisplay(display, locale) {
@@ -51514,6 +51873,44 @@ function CoursePricingSelector({
51514
51873
  )
51515
51874
  ] });
51516
51875
  }
51876
+ function CourseSlidingScaleInput({
51877
+ pricing,
51878
+ value,
51879
+ onChange
51880
+ }) {
51881
+ const slidingScale = pricing.slidingScale;
51882
+ if (!slidingScale) {
51883
+ return null;
51884
+ }
51885
+ const minLabel = formatCoursePriceAmount(slidingScale.minAmount, pricing.currency);
51886
+ const maxLabel = formatCoursePriceAmount(slidingScale.maxAmount, pricing.currency);
51887
+ const suggestedLabel = slidingScale.suggestedAmount !== null ? formatCoursePriceAmount(slidingScale.suggestedAmount, pricing.currency) : null;
51888
+ return /* @__PURE__ */ jsxs65("div", { className: "cr-form__field", children: [
51889
+ /* @__PURE__ */ jsx95("label", { htmlFor: "cr-sliding-amount", className: "cr-form__label", children: "Amount" }),
51890
+ /* @__PURE__ */ jsx95(
51891
+ "input",
51892
+ {
51893
+ id: "cr-sliding-amount",
51894
+ type: "number",
51895
+ min: slidingScale.minAmount / 100,
51896
+ max: slidingScale.maxAmount / 100,
51897
+ step: "0.01",
51898
+ inputMode: "decimal",
51899
+ className: "cr-form__input",
51900
+ value,
51901
+ onChange: (event) => onChange(event.target.value)
51902
+ }
51903
+ ),
51904
+ /* @__PURE__ */ jsxs65("p", { className: "cr-text cr-text--muted", children: [
51905
+ "Choose between ",
51906
+ minLabel,
51907
+ " and ",
51908
+ maxLabel,
51909
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
51910
+ "."
51911
+ ] })
51912
+ ] });
51913
+ }
51517
51914
  function EnrollmentForm({ formData, errors, onChange, isEmailLocked, isLoading, onLogout }) {
51518
51915
  const nameErrorId = "cr-name-error";
51519
51916
  const emailErrorId = "cr-email-error";
@@ -51609,6 +52006,17 @@ var init_components2 = __esm({
51609
52006
 
51610
52007
  // ../blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.ts
51611
52008
  import { useEffect as useEffect25, useMemo as useMemo19, useState as useState18 } from "react";
52009
+ function parseCurrencyInputToCents2(value) {
52010
+ const trimmed = value.trim();
52011
+ if (!trimmed) return null;
52012
+ const amount = Number(trimmed);
52013
+ if (!Number.isFinite(amount) || amount < 0) return null;
52014
+ return Math.round(amount * 100);
52015
+ }
52016
+ function formatCentsAsCurrencyInput2(amount) {
52017
+ const major = amount / 100;
52018
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
52019
+ }
51612
52020
  function shouldFetchCourseEligibility(course, hasPaidSelection, portalAuthState, hasWaitlistClaimToken) {
51613
52021
  if (!course || portalAuthState === "checking") return false;
51614
52022
  if (hasWaitlistClaimToken) return false;
@@ -51626,12 +52034,27 @@ function getSessionIdFromUrl() {
51626
52034
  }
51627
52035
  function getMissingCoursePricingSelectionMessage(pricing) {
51628
52036
  if (pricing?.requiresSelection && !pricing.selectedOption) {
52037
+ if (pricing.slidingScale) {
52038
+ return "Choose an amount to continue.";
52039
+ }
51629
52040
  return "Choose a pricing option to continue.";
51630
52041
  }
52042
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount < pricing.slidingScale.minAmount) {
52043
+ return "Choose an amount within the sliding scale.";
52044
+ }
52045
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount > pricing.slidingScale.maxAmount) {
52046
+ return "Choose an amount within the sliding scale.";
52047
+ }
51631
52048
  return null;
51632
52049
  }
51633
52050
  function buildCoursePricingOptionPayload(pricing) {
51634
- return pricing?.selectedOption ? { pricingOptionId: pricing.selectedOption.id } : {};
52051
+ if (pricing?.selectedOption) {
52052
+ return { pricingOptionId: pricing.selectedOption.id };
52053
+ }
52054
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null) {
52055
+ return { selectedSlidingAmount: pricing.selectedSlidingAmount };
52056
+ }
52057
+ return {};
51635
52058
  }
51636
52059
  function resolveHasCoursePayment(pricing) {
51637
52060
  if (!pricing) {
@@ -51672,6 +52095,7 @@ function useCourseRegistrationWizard(props2) {
51672
52095
  scopedCourses.length === 1 ? scopedCourses[0] ?? null : null
51673
52096
  );
51674
52097
  const [selectedPricingOptionId, setSelectedPricingOptionId] = useState18(null);
52098
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState18("");
51675
52099
  const [formData, setFormData] = useState18({
51676
52100
  name: "",
51677
52101
  email: "",
@@ -51693,8 +52117,12 @@ function useCourseRegistrationWizard(props2) {
51693
52117
  const [bookingMode, setBookingMode] = useState18(null);
51694
52118
  const waitlistClaim = useWaitlistClaim();
51695
52119
  const selectedCoursePricing = useMemo19(
51696
- () => selectedCourse ? resolveCoursePricingState(selectedCourse, selectedPricingOptionId) : null,
51697
- [selectedCourse, selectedPricingOptionId]
52120
+ () => selectedCourse ? resolveCoursePricingState(
52121
+ selectedCourse,
52122
+ selectedPricingOptionId,
52123
+ parseCurrencyInputToCents2(selectedSlidingAmountInput)
52124
+ ) : null,
52125
+ [selectedCourse, selectedPricingOptionId, selectedSlidingAmountInput]
51698
52126
  );
51699
52127
  const missingPricingSelectionMessage = getMissingCoursePricingSelectionMessage(
51700
52128
  selectedCoursePricing
@@ -51767,9 +52195,13 @@ function useCourseRegistrationWizard(props2) {
51767
52195
  useEffect25(() => {
51768
52196
  if (!selectedCourse) {
51769
52197
  setSelectedPricingOptionId(null);
52198
+ setSelectedSlidingAmountInput("");
51770
52199
  return;
51771
52200
  }
51772
52201
  const defaultPricing = resolveCoursePricingState(selectedCourse, null);
52202
+ setSelectedSlidingAmountInput(
52203
+ defaultPricing.slidingScale?.suggestedAmount !== null && defaultPricing.slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput2(defaultPricing.slidingScale.suggestedAmount) : ""
52204
+ );
51773
52205
  setSelectedPricingOptionId((previous) => {
51774
52206
  if (previous && defaultPricing.selectableOptions.some((option) => option.id === previous)) {
51775
52207
  return previous;
@@ -52144,6 +52576,8 @@ function useCourseRegistrationWizard(props2) {
52144
52576
  selectedCourse,
52145
52577
  selectedPricingOptionId,
52146
52578
  setSelectedPricingOptionId,
52579
+ selectedSlidingAmountInput,
52580
+ setSelectedSlidingAmountInput,
52147
52581
  selectedCoursePricing,
52148
52582
  setSelectedCourse,
52149
52583
  formData,
@@ -52209,6 +52643,19 @@ var init_useCourseRegistrationWizard = __esm({
52209
52643
 
52210
52644
  // ../blocks/src/system/runtime/nodes/course-registration/CourseRegistrationWizard.tsx
52211
52645
  import { Fragment as Fragment23, jsx as jsx96, jsxs as jsxs66 } from "react/jsx-runtime";
52646
+ function getCourseSlidingScaleMessage(pricing) {
52647
+ if (!pricing.slidingScale) {
52648
+ return null;
52649
+ }
52650
+ const selectedAmount = pricing.selectedSlidingAmount;
52651
+ if (selectedAmount === null) {
52652
+ return "Choose an amount to continue.";
52653
+ }
52654
+ if (selectedAmount < pricing.slidingScale.minAmount || selectedAmount > pricing.slidingScale.maxAmount) {
52655
+ return "Choose an amount within the sliding scale.";
52656
+ }
52657
+ return null;
52658
+ }
52212
52659
  function CourseRegistrationWizard(props2) {
52213
52660
  const {
52214
52661
  showSessions = true,
@@ -52230,6 +52677,8 @@ function CourseRegistrationWizard(props2) {
52230
52677
  selectedCourse,
52231
52678
  selectedPricingOptionId,
52232
52679
  setSelectedPricingOptionId,
52680
+ selectedSlidingAmountInput,
52681
+ setSelectedSlidingAmountInput,
52233
52682
  selectedCoursePricing,
52234
52683
  formData,
52235
52684
  setFormData,
@@ -52304,7 +52753,8 @@ function CourseRegistrationWizard(props2) {
52304
52753
  const canShowDiscountUi = !discountsAvailableLoading && paymentChoice === "checkout" && discountsAvailable && manualCodeEntryAllowed;
52305
52754
  const courseSoldOut = waitlistClaim.claim === null && courseEligibility?.isFull === true && courseEligibility.canJoinWaitlist !== true;
52306
52755
  const detailsSubmitLabel = courseEligibility?.canJoinWaitlist ? "Join waitlist" : buttonText;
52307
- const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption;
52756
+ const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption && !selectedCoursePricing.slidingScale;
52757
+ const slidingScaleMessage = selectedCoursePricing ? getCourseSlidingScaleMessage(selectedCoursePricing) : null;
52308
52758
  const pricingUnavailable = selectedCoursePricing?.display.kind === "unavailable";
52309
52759
  const selectedCourseWithPricing = selectedCourse && selectedCoursePricing ? {
52310
52760
  course: selectedCourse,
@@ -52397,6 +52847,14 @@ function CourseRegistrationWizard(props2) {
52397
52847
  onSelect: setSelectedPricingOptionId
52398
52848
  }
52399
52849
  ),
52850
+ /* @__PURE__ */ jsx96(
52851
+ CourseSlidingScaleInput,
52852
+ {
52853
+ pricing: selectedCourseWithPricing.pricing,
52854
+ value: selectedSlidingAmountInput,
52855
+ onChange: setSelectedSlidingAmountInput
52856
+ }
52857
+ ),
52400
52858
  showSpamProtection && /* @__PURE__ */ jsx96("div", { className: "cr-altcha", children: /* @__PURE__ */ jsx96(
52401
52859
  AltchaWidget,
52402
52860
  {
@@ -52434,7 +52892,7 @@ function CourseRegistrationWizard(props2) {
52434
52892
  {
52435
52893
  type: "submit",
52436
52894
  className: buttonClass,
52437
- disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || pricingUnavailable,
52895
+ disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || slidingScaleMessage !== null || pricingUnavailable,
52438
52896
  children: eligibilityLoading ? "Checking availability\u2026" : courseSoldOut ? "Sold out" : detailsSubmitLabel
52439
52897
  }
52440
52898
  )
@@ -52444,6 +52902,7 @@ function CourseRegistrationWizard(props2) {
52444
52902
  ),
52445
52903
  courseSoldOut && /* @__PURE__ */ jsx96("p", { className: "cr-text cr-text--muted", children: "This course is fully booked and there is no waitlist available right now." }),
52446
52904
  pricingSelectionMissing && /* @__PURE__ */ jsx96("p", { className: "cr-text cr-text--muted", children: "Choose a pricing option to continue." }),
52905
+ slidingScaleMessage && /* @__PURE__ */ jsx96("p", { className: "cr-text cr-text--muted", children: slidingScaleMessage }),
52447
52906
  pricingUnavailable && /* @__PURE__ */ jsx96("p", { className: "cr-text cr-text--muted", children: "Pricing is not available for this course right now." }),
52448
52907
  eligibilityError && /* @__PURE__ */ jsx96("p", { className: "cr-text cr-text--error", children: eligibilityError })
52449
52908
  ] })
@@ -56754,6 +57213,21 @@ function fieldPlanToZod(plan, options = {}) {
56754
57213
  }
56755
57214
  });
56756
57215
  }
57216
+ function fieldPresenceForPlan(plan) {
57217
+ return plan.required ? { kind: "required" } : { kind: "optional" };
57218
+ }
57219
+ function fieldPlanToObjectPropertyZod(plan, options = {}) {
57220
+ const schema = fieldPlanToZod(plan, options);
57221
+ const presence = fieldPresenceForPlan(plan);
57222
+ switch (presence.kind) {
57223
+ case "required":
57224
+ return schema;
57225
+ case "optional":
57226
+ return schema.optional();
57227
+ default:
57228
+ return assertNever7(presence);
57229
+ }
57230
+ }
56757
57231
  function addFieldIssueToZodContext(zodCtx, validationIssue, rootPath) {
56758
57232
  const message = fieldIssueToMessage(validationIssue);
56759
57233
  const path = relativeZodIssuePath(validationIssue.path, rootPath);
@@ -56966,7 +57440,7 @@ function createManifestValidator(manifestOrFields, options = {}) {
56966
57440
  function deriveContentZodFromPlans(plans, options) {
56967
57441
  const shape = {};
56968
57442
  for (const plan of plans) {
56969
- shape[plan.fieldId] = fieldPlanToZod(plan, options);
57443
+ shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
56970
57444
  }
56971
57445
  return z54.object(shape).catchall(z54.unknown()).passthrough();
56972
57446
  }
@@ -97877,6 +98351,7 @@ var CANONICAL_REFUND_CONFLICT_CODES = [
97877
98351
  ];
97878
98352
  var CANONICAL_REFUND_PLANNER_ERROR_CODES = [
97879
98353
  ...CANONICAL_REFUND_CONFLICT_CODES,
98354
+ "invalid_amount",
97880
98355
  "amount_exceeds_remaining",
97881
98356
  "pass_revoke_requires_full_refund",
97882
98357
  "refund_failed"
@@ -97903,6 +98378,7 @@ var CUSTOMER_MEMBERSHIP_PAYMENT_REFUND_FAILED_CODES = [
97903
98378
  // ../api/src/bookingRefundOutcomes.ts
97904
98379
  var STANDARD_LINE_ITEM_REFUND_INVALID_CODES = [
97905
98380
  "missing_amount",
98381
+ "invalid_amount",
97906
98382
  "amount_exceeds_remaining",
97907
98383
  "credit_refunds_disabled",
97908
98384
  "missing_credit_customer",
@@ -97917,6 +98393,7 @@ var EVENT_ATTENDEE_REFUND_INVALID_CODES = [
97917
98393
  var MANAGED_PRODUCT_REFUND_INVALID_CODES = [
97918
98394
  "credit_refunds_disabled",
97919
98395
  "not_refundable",
98396
+ "invalid_amount",
97920
98397
  "amount_exceeds_remaining"
97921
98398
  ];
97922
98399
  var CUSTOMER_PASS_REFUND_INVALID_CODES = [