@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
@@ -23,12 +23,13 @@ var init_result = __esm({
23
23
  });
24
24
 
25
25
  // ../core/src/branded.ts
26
- var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
26
+ var unsafeAsMediaAssetId, unsafeAsStorageBucketName, unsafeAsStoragePath, unsafeAsPositiveByteCount, unsafeAsPositiveSeconds, unsafeAsFormId, unsafeAsPassId, unsafeAsCustomerPassId, unsafeAsAppointmentServiceId, unsafeAsAppointmentResourceId;
27
27
  var init_branded = __esm({
28
28
  "../core/src/branded.ts"() {
29
29
  "use strict";
30
30
  unsafeAsMediaAssetId = (id) => id;
31
31
  unsafeAsStorageBucketName = (value) => value;
32
+ unsafeAsStoragePath = (value) => value;
32
33
  unsafeAsPositiveByteCount = (value) => value;
33
34
  unsafeAsPositiveSeconds = (value) => value;
34
35
  unsafeAsFormId = (id) => id;
@@ -174,6 +175,7 @@ function publicEventPricingType(pricing) {
174
175
  return "free";
175
176
  case "single_paid":
176
177
  case "ticket_types":
178
+ case "sliding_scale":
177
179
  case "course":
178
180
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
179
181
  case "external":
@@ -186,6 +188,7 @@ function publicEventPricingCurrency(pricing) {
186
188
  case "free_registration":
187
189
  case "single_paid":
188
190
  case "ticket_types":
191
+ case "sliding_scale":
189
192
  return pricing.currency;
190
193
  case "course":
191
194
  return pricing.display.display.currency;
@@ -203,6 +206,8 @@ function publicEventPricingMode(pricing) {
203
206
  return "single";
204
207
  case "ticket_types":
205
208
  return "ticket_types";
209
+ case "sliding_scale":
210
+ return "sliding_scale";
206
211
  case "drop_in":
207
212
  case "course":
208
213
  case "external":
@@ -236,6 +241,8 @@ function formatCoursePriceDisplayLabel(display, options) {
236
241
  return `Course (${formatCurrencyAmount(display.amount, display.currency, options)})`;
237
242
  case "from":
238
243
  return `Course (From ${formatCurrencyAmount(display.amount, display.currency, options)})`;
244
+ case "range":
245
+ return `Course (${formatCurrencyAmount(display.minAmount, display.currency, options)} - ${formatCurrencyAmount(display.maxAmount, display.currency, options)})`;
239
246
  default:
240
247
  return assertNever(display);
241
248
  }
@@ -1039,9 +1046,20 @@ var init_types = __esm({
1039
1046
  });
1040
1047
 
1041
1048
  // ../media-core/src/fileSafety.ts
1049
+ function containsUnsafePathSegmentCharacter(value) {
1050
+ return value.includes("/") || value.includes("\\") || CONTROL_CHARACTER_PATTERN.test(value);
1051
+ }
1052
+ function isReservedPathSegment(value) {
1053
+ return value === "." || value === "..";
1054
+ }
1055
+ function isUnsafePathSegment(segment) {
1056
+ return segment.trim().length === 0 || segment !== segment.trim() || isReservedPathSegment(segment) || containsUnsafePathSegmentCharacter(segment);
1057
+ }
1058
+ var CONTROL_CHARACTER_PATTERN;
1042
1059
  var init_fileSafety = __esm({
1043
1060
  "../media-core/src/fileSafety.ts"() {
1044
1061
  "use strict";
1062
+ CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/u;
1045
1063
  }
1046
1064
  });
1047
1065
 
@@ -1081,11 +1099,21 @@ var init_fileDomain = __esm({
1081
1099
  });
1082
1100
 
1083
1101
  // ../media-core/src/fileStorage.ts
1102
+ function parseStoragePath(value) {
1103
+ return joinStoragePathSegments(value.split("/"));
1104
+ }
1105
+ function joinStoragePathSegments(segments) {
1106
+ if (segments.some(isUnsafePathSegment)) {
1107
+ return err("unsafe-path-segment");
1108
+ }
1109
+ return ok(unsafeAsStoragePath(segments.join("/")));
1110
+ }
1084
1111
  var FILE_STORAGE_BUCKETS, PUBLIC_SITE_FILE_STORAGE_BUCKETS, PRIVATE_SITE_FILE_BUCKET_MIME_TYPES, TEMP_UPLOAD_BUCKET_MIME_TYPES;
1085
1112
  var init_fileStorage = __esm({
1086
1113
  "../media-core/src/fileStorage.ts"() {
1087
1114
  "use strict";
1088
1115
  init_src();
1116
+ init_fileSafety();
1089
1117
  FILE_STORAGE_BUCKETS = {
1090
1118
  publicSite: unsafeAsStorageBucketName("media"),
1091
1119
  privateSite: unsafeAsStorageBucketName("site-files-private"),
@@ -1271,14 +1299,18 @@ var init_uploadVerificationContract = __esm({
1271
1299
  });
1272
1300
 
1273
1301
  // ../media-core/src/videoPrep.ts
1302
+ function unsafeAsPositiveVideoDurationSeconds(value) {
1303
+ return value;
1304
+ }
1274
1305
  var VIDEO_PREP_LIMITS;
1275
1306
  var init_videoPrep = __esm({
1276
1307
  "../media-core/src/videoPrep.ts"() {
1277
1308
  "use strict";
1309
+ init_src();
1278
1310
  VIDEO_PREP_LIMITS = {
1279
- maxRawByteCount: 500 * 1024 * 1024,
1280
- inlineContentMaxDurationSeconds: 60,
1281
- backgroundVideoMaxDurationSeconds: 30,
1311
+ maxRawByteCount: unsafeAsPositiveByteCount(500 * 1024 * 1024),
1312
+ inlineContentMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(60),
1313
+ backgroundVideoMaxDurationSeconds: unsafeAsPositiveVideoDurationSeconds(30),
1282
1314
  targetMaxDimensionPixels: 1080
1283
1315
  };
1284
1316
  }
@@ -1349,10 +1381,10 @@ var init_identifiers = __esm({
1349
1381
 
1350
1382
  // ../media-core/src/url.ts
1351
1383
  function isRelativeDirectImageStoragePath(storagePath) {
1352
- return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix));
1384
+ return RELATIVE_STORAGE_PATH_PREFIXES.some((prefix) => storagePath.startsWith(prefix)) && parseStoragePath(storagePath).ok;
1353
1385
  }
1354
1386
  function isSiteScopedDirectImageStoragePath(storagePath) {
1355
- return storagePath.startsWith("sites/");
1387
+ return storagePath.startsWith("sites/") && parseStoragePath(storagePath).ok;
1356
1388
  }
1357
1389
  function encodeStoragePath(storagePath) {
1358
1390
  return storagePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
@@ -1433,6 +1465,7 @@ var DEFAULT_BASE_PATH, RELATIVE_STORAGE_PATH_PREFIXES, formatNumber, ORDER, getI
1433
1465
  var init_url = __esm({
1434
1466
  "../media-core/src/url.ts"() {
1435
1467
  "use strict";
1468
+ init_fileStorage();
1436
1469
  DEFAULT_BASE_PATH = "/api/img";
1437
1470
  RELATIVE_STORAGE_PATH_PREFIXES = [
1438
1471
  "originals/",
@@ -22919,9 +22952,12 @@ var init_schemas = __esm({
22919
22952
  });
22920
22953
  coursePricingSchema = z45.object({
22921
22954
  bookingRequirement: z45.enum(["none", "required"]),
22922
- pricingStrategy: z45.enum(["free", "single", "tiered"]),
22955
+ pricingStrategy: z45.enum(["free", "single", "tiered", "sliding_scale"]),
22923
22956
  currency: z45.string(),
22924
22957
  singlePriceAmount: z45.number().nullable(),
22958
+ slidingMinAmount: z45.number().nullable().optional(),
22959
+ slidingMaxAmount: z45.number().nullable().optional(),
22960
+ slidingSuggestedAmount: z45.number().nullable().optional(),
22925
22961
  paymentTerms: blockBookingPaymentTermsSchema,
22926
22962
  options: z45.array(coursePricingOptionSchema)
22927
22963
  });
@@ -23086,6 +23122,19 @@ var init_schemas2 = __esm({
23086
23122
  ticketTypes: z46.array(publicEventTicketTypeSchema),
23087
23123
  display: publicEventTicketTypePriceDisplaySchema
23088
23124
  }),
23125
+ z46.object({
23126
+ kind: z46.literal("sliding_scale"),
23127
+ minAmount: z46.number(),
23128
+ maxAmount: z46.number(),
23129
+ suggestedAmount: z46.number().nullable(),
23130
+ currency: z46.string(),
23131
+ display: z46.object({
23132
+ kind: z46.literal("range"),
23133
+ minAmount: z46.number(),
23134
+ maxAmount: z46.number(),
23135
+ currency: z46.string()
23136
+ })
23137
+ }),
23089
23138
  z46.object({
23090
23139
  kind: z46.literal("course"),
23091
23140
  display: z46.object({ kind: z46.literal("course"), display: publicCoursePriceDisplaySchema })
@@ -23554,7 +23603,7 @@ function isRecord6(value) {
23554
23603
  }
23555
23604
  function isPublicEventPricing(value) {
23556
23605
  if (!isRecord6(value) || typeof value.kind !== "string") return false;
23557
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
23606
+ 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";
23558
23607
  }
23559
23608
  function isPublicEventTicketType(value) {
23560
23609
  return isRecord6(value) && typeof value.id === "string";
@@ -24067,7 +24116,9 @@ function getSelectableCoursePricingOptionsForNow(course, now) {
24067
24116
  return left.name.localeCompare(right.name);
24068
24117
  });
24069
24118
  }
24070
- function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {}) {
24119
+ function resolveCoursePricingState(course, selectedPricingOptionId, selectedSlidingAmountOrNowInput, nowInput = {}) {
24120
+ const selectedSlidingAmount = typeof selectedSlidingAmountOrNowInput === "number" || selectedSlidingAmountOrNowInput === null ? selectedSlidingAmountOrNowInput : null;
24121
+ const effectiveNowInput = typeof selectedSlidingAmountOrNowInput === "object" && selectedSlidingAmountOrNowInput !== null ? selectedSlidingAmountOrNowInput : nowInput;
24071
24122
  const currency = course.pricing?.currency ?? course.currency;
24072
24123
  if (!course.pricing) {
24073
24124
  const legacyAmount = course.priceCents ?? 0;
@@ -24077,6 +24128,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
24077
24128
  selectableOptions: [],
24078
24129
  requiresSelection: false,
24079
24130
  selectedOption: null,
24131
+ slidingScale: null,
24132
+ selectedSlidingAmount: null,
24080
24133
  selectedPriceAmount: legacyAmount,
24081
24134
  selectedRequiresPayment: legacyAmount > 0,
24082
24135
  hasPaidSelection: legacyAmount > 0,
@@ -24091,6 +24144,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
24091
24144
  selectableOptions: [],
24092
24145
  requiresSelection: false,
24093
24146
  selectedOption: null,
24147
+ slidingScale: null,
24148
+ selectedSlidingAmount: null,
24094
24149
  selectedPriceAmount: 0,
24095
24150
  selectedRequiresPayment: false,
24096
24151
  hasPaidSelection: false,
@@ -24104,6 +24159,8 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
24104
24159
  selectableOptions: [],
24105
24160
  requiresSelection: false,
24106
24161
  selectedOption: null,
24162
+ slidingScale: null,
24163
+ selectedSlidingAmount: null,
24107
24164
  selectedPriceAmount: amount,
24108
24165
  selectedRequiresPayment: amount > 0,
24109
24166
  hasPaidSelection: amount > 0,
@@ -24113,7 +24170,7 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
24113
24170
  case "tiered": {
24114
24171
  const selectableOptions = getSelectableCoursePricingOptionsForNow(
24115
24172
  course,
24116
- resolvePlanningDateInput(nowInput)
24173
+ resolvePlanningDateInput(effectiveNowInput)
24117
24174
  );
24118
24175
  const selectedOption = (selectedPricingOptionId ? selectableOptions.find((option) => option.id === selectedPricingOptionId) : null) ?? (selectableOptions.length === 1 ? selectableOptions[0] ?? null : null);
24119
24176
  const lowestAmount = selectableOptions.reduce((lowest, option) => {
@@ -24129,12 +24186,51 @@ function resolveCoursePricingState(course, selectedPricingOptionId, nowInput = {
24129
24186
  selectableOptions,
24130
24187
  requiresSelection: selectableOptions.length > 1,
24131
24188
  selectedOption,
24189
+ slidingScale: null,
24190
+ selectedSlidingAmount: null,
24132
24191
  selectedPriceAmount: selectedOption?.priceAmount ?? null,
24133
24192
  selectedRequiresPayment: (selectedOption?.priceAmount ?? 0) > 0,
24134
24193
  hasPaidSelection: selectableOptions.some((option) => option.priceAmount > 0),
24135
24194
  hasFreeSelection: selectableOptions.some((option) => option.priceAmount === 0)
24136
24195
  };
24137
24196
  }
24197
+ case "sliding_scale": {
24198
+ const minAmount = course.pricing.slidingMinAmount ?? null;
24199
+ const maxAmount = course.pricing.slidingMaxAmount ?? null;
24200
+ if (minAmount === null || maxAmount === null || minAmount < 0 || maxAmount < minAmount) {
24201
+ return {
24202
+ currency,
24203
+ display: { kind: "unavailable", currency },
24204
+ selectableOptions: [],
24205
+ requiresSelection: true,
24206
+ selectedOption: null,
24207
+ slidingScale: null,
24208
+ selectedSlidingAmount: null,
24209
+ selectedPriceAmount: null,
24210
+ selectedRequiresPayment: false,
24211
+ hasPaidSelection: false,
24212
+ hasFreeSelection: false
24213
+ };
24214
+ }
24215
+ const normalizedSelectedAmount = typeof selectedSlidingAmount === "number" && Number.isFinite(selectedSlidingAmount) ? selectedSlidingAmount : null;
24216
+ return {
24217
+ currency,
24218
+ display: { kind: "range", minAmount, maxAmount, currency },
24219
+ selectableOptions: [],
24220
+ requiresSelection: normalizedSelectedAmount === null,
24221
+ selectedOption: null,
24222
+ slidingScale: {
24223
+ minAmount,
24224
+ maxAmount,
24225
+ suggestedAmount: course.pricing.slidingSuggestedAmount ?? null
24226
+ },
24227
+ selectedSlidingAmount: normalizedSelectedAmount,
24228
+ selectedPriceAmount: normalizedSelectedAmount,
24229
+ selectedRequiresPayment: (normalizedSelectedAmount ?? 0) > 0,
24230
+ hasPaidSelection: maxAmount > 0,
24231
+ hasFreeSelection: minAmount === 0
24232
+ };
24233
+ }
24138
24234
  }
24139
24235
  }
24140
24236
  function formatCoursePriceDisplay(display, locale) {
@@ -29382,6 +29478,7 @@ var init_booking_form_reserved_fields = __esm({
29382
29478
  "selectedDate",
29383
29479
  "selectedSlot",
29384
29480
  "discountCode",
29481
+ "selectedSlidingAmountInput",
29385
29482
  "selectedCollectionOptionKind",
29386
29483
  "selectedAppointmentPackageId"
29387
29484
  ];
@@ -29667,6 +29764,32 @@ function readOptionalString(value) {
29667
29764
  function readOptionalNumber(value) {
29668
29765
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
29669
29766
  }
29767
+ function getBookingServiceSlidingScale(service) {
29768
+ const pricing = service?.pricing;
29769
+ if (!pricing || pricing.pricingStrategy !== "sliding_scale" || pricing.slidingMinAmount === null || pricing.slidingMinAmount === void 0 || pricing.slidingMaxAmount === null || pricing.slidingMaxAmount === void 0) {
29770
+ return null;
29771
+ }
29772
+ return {
29773
+ minAmount: pricing.slidingMinAmount,
29774
+ maxAmount: pricing.slidingMaxAmount,
29775
+ suggestedAmount: pricing.slidingSuggestedAmount ?? null,
29776
+ currency: pricing.currency
29777
+ };
29778
+ }
29779
+ function parseBookingCurrencyInputToCents(value) {
29780
+ const trimmed = value?.trim() ?? "";
29781
+ if (!trimmed) return null;
29782
+ if (!/^(?:\d+(?:\.\d{0,2})?|\.\d{1,2})$/.test(trimmed)) return null;
29783
+ const parsed = Number(trimmed);
29784
+ if (!Number.isFinite(parsed) || parsed < 0) return null;
29785
+ return Math.round(parsed * 100);
29786
+ }
29787
+ function formatBookingCentsAsCurrencyInput(amount) {
29788
+ return amount % 100 === 0 ? String(amount / 100) : (amount / 100).toFixed(2);
29789
+ }
29790
+ function isBookingServiceSlidingAmountInRange(slidingScale, selectedAmount) {
29791
+ return selectedAmount !== null && selectedAmount >= slidingScale.minAmount && selectedAmount <= slidingScale.maxAmount;
29792
+ }
29670
29793
  function readStringArray(value) {
29671
29794
  if (!Array.isArray(value)) return void 0;
29672
29795
  const strings = value.filter((item) => typeof item === "string");
@@ -29855,6 +29978,20 @@ function prepareBookingSubmission(input) {
29855
29978
  };
29856
29979
  }
29857
29980
  const paymentSelectionMode = getBookingServicePaymentSelectionMode(selectedService);
29981
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
29982
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
29983
+ input.data.selectedSlidingAmountInput
29984
+ );
29985
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(slidingScale, selectedSlidingAmount)) {
29986
+ return {
29987
+ ok: false,
29988
+ error: {
29989
+ tag: selectedSlidingAmount === null ? "sliding-amount-required" : "sliding-amount-out-of-range",
29990
+ serviceId,
29991
+ serviceTitle: selectedService.title || serviceId
29992
+ }
29993
+ };
29994
+ }
29858
29995
  if (paymentSelectionMode === "choice_required" && !toSelectedCollectionOptionKind(input.data.selectedCollectionOptionKind)) {
29859
29996
  return {
29860
29997
  ok: false,
@@ -29882,6 +30019,7 @@ function prepareBookingSubmission(input) {
29882
30019
  ...common,
29883
30020
  kind: "standard",
29884
30021
  ...discountCode ? { discountCode } : {},
30022
+ ...slidingScale && selectedSlidingAmount !== null ? { selectedSlidingAmount } : {},
29885
30023
  ...selectedCollectionOptionKind ? { selectedCollectionOptionKind } : {},
29886
30024
  altchaSolution: input.altchaSolution ?? null
29887
30025
  }
@@ -29895,6 +30033,10 @@ function formatBookingSubmissionPreparationError(error) {
29895
30033
  return `Service not found: ${error.serviceId}`;
29896
30034
  case "payment-option-required":
29897
30035
  return `Please choose how you want to pay for "${error.serviceTitle}".`;
30036
+ case "sliding-amount-required":
30037
+ return `Please choose an amount for "${error.serviceTitle}".`;
30038
+ case "sliding-amount-out-of-range":
30039
+ return `Please choose an amount within the sliding scale for "${error.serviceTitle}".`;
29898
30040
  case "payment-option-unsupported":
29899
30041
  return `Service "${error.serviceTitle}" has a payment option that is not supported yet.`;
29900
30042
  case "service-missing-duration":
@@ -30045,6 +30187,7 @@ var init_booking_form_interactive = __esm({
30045
30187
  title: z66.string(),
30046
30188
  description: z66.string().nullable().optional().transform((value) => value ?? void 0),
30047
30189
  durationMinutes: z66.number().optional(),
30190
+ pricing: z66.custom().optional(),
30048
30191
  priceAmount: z66.number().nullable().optional(),
30049
30192
  priceCurrency: z66.string().nullable().optional(),
30050
30193
  paymentTerms: blockBookingPaymentTermsSchema.optional(),
@@ -34963,12 +35106,13 @@ var init_carousel_client = __esm({
34963
35106
  ]
34964
35107
  );
34965
35108
  const resolvedSlides = resolveSlidesToShowMobile(normalized.slidesToShow);
35109
+ const canAutoplay = normalized.autoplay && slides.length > 1;
34966
35110
  const cssVars = React12.useMemo(
34967
35111
  () => getCarouselSlidesToShowCssVars(normalized.slidesToShow),
34968
35112
  [normalized.slidesToShow]
34969
35113
  );
34970
35114
  const plugins = React12.useMemo(() => {
34971
- if (!normalized.autoplay) return void 0;
35115
+ if (!canAutoplay) return void 0;
34972
35116
  return [
34973
35117
  Autoplay({
34974
35118
  delay: normalized.autoplayDelay,
@@ -34978,7 +35122,7 @@ var init_carousel_client = __esm({
34978
35122
  // Continue after manual navigation
34979
35123
  })
34980
35124
  ];
34981
- }, [normalized.autoplay, normalized.autoplayDelay]);
35125
+ }, [canAutoplay, normalized.autoplayDelay]);
34982
35126
  const [viewportRef, api] = useEmblaCarousel(
34983
35127
  {
34984
35128
  align: "start",
@@ -35017,7 +35161,7 @@ var init_carousel_client = __esm({
35017
35161
  };
35018
35162
  }, [api]);
35019
35163
  React12.useEffect(() => {
35020
- if (!api || !normalized.autoplay) return;
35164
+ if (!api || !canAutoplay) return;
35021
35165
  const autoplayPlugin = api.plugins()?.autoplay;
35022
35166
  if (!autoplayPlugin) return;
35023
35167
  const handleVisibility = () => {
@@ -35026,7 +35170,7 @@ var init_carousel_client = __esm({
35026
35170
  };
35027
35171
  document.addEventListener("visibilitychange", handleVisibility);
35028
35172
  return () => document.removeEventListener("visibilitychange", handleVisibility);
35029
- }, [api, normalized.autoplay]);
35173
+ }, [api, canAutoplay]);
35030
35174
  const inner = /* @__PURE__ */ jsxs53(Fragment13, { children: [
35031
35175
  /* @__PURE__ */ jsx68("div", { className: "carousel-frame rb-overflow-hidden rb-w-full", children: /* @__PURE__ */ jsx68(
35032
35176
  "div",
@@ -35077,7 +35221,7 @@ var init_carousel_client = __esm({
35077
35221
  "data-show-controls": normalized.showControls ? "true" : "false",
35078
35222
  "data-show-dots": normalized.showDots ? "true" : "false",
35079
35223
  "data-arrow-visibility": normalized.arrowVisibility,
35080
- "data-autoplay": normalized.autoplay ? "true" : "false",
35224
+ "data-autoplay": canAutoplay ? "true" : "false",
35081
35225
  "data-loop": normalized.loop ? "true" : "false",
35082
35226
  children: inner
35083
35227
  }
@@ -36080,6 +36224,7 @@ function createBlockApi(config) {
36080
36224
  body: {
36081
36225
  courseId: data.courseId,
36082
36226
  ...data.pricingOptionId ? { pricingOptionId: data.pricingOptionId } : {},
36227
+ ...data.selectedSlidingAmount !== void 0 ? { selectedSlidingAmount: data.selectedSlidingAmount } : {},
36083
36228
  email: data.email,
36084
36229
  name: data.name,
36085
36230
  phone: data.phone,
@@ -40280,6 +40425,7 @@ function useBookingSubmission(_siteId) {
40280
40425
  customFields: data.customFields,
40281
40426
  timezone: data.timezone,
40282
40427
  discountCode: data.discountCode,
40428
+ selectedSlidingAmount: data.selectedSlidingAmount,
40283
40429
  selectedCollectionOptionKind: data.selectedCollectionOptionKind,
40284
40430
  altchaSolution: data.altchaSolution ?? null,
40285
40431
  successUrl: data.successUrl,
@@ -40808,6 +40954,9 @@ function createInitialBookingFlowReducerState(input) {
40808
40954
  fieldValues: collectInitialFieldValues(initialData),
40809
40955
  touchedFields: {},
40810
40956
  discountCode: toOptionalString(initialData.discountCode) ?? "",
40957
+ selectedSlidingAmountInput: toOptionalString(
40958
+ initialData.selectedSlidingAmountInput
40959
+ ) ?? "",
40811
40960
  selectedCollectionOptionKind: toSelectedCollectionOptionKind(
40812
40961
  initialData.selectedCollectionOptionKind
40813
40962
  ),
@@ -40860,6 +41009,7 @@ function bookingFlowReducer(state, event) {
40860
41009
  selectedDate: void 0,
40861
41010
  selectedSlot: null,
40862
41011
  selectedAppointmentPackageId: void 0,
41012
+ selectedSlidingAmountInput: "",
40863
41013
  selectedCollectionOptionKind: void 0,
40864
41014
  validationErrors: clearBookingFormError(state.validationErrors)
40865
41015
  };
@@ -40913,6 +41063,13 @@ function bookingFlowReducer(state, event) {
40913
41063
  selectedAppointmentPackageId: void 0,
40914
41064
  validationErrors: clearBookingFormError(state.validationErrors)
40915
41065
  };
41066
+ case "sliding-amount-changed":
41067
+ return {
41068
+ ...state,
41069
+ selectedSlidingAmountInput: event.value,
41070
+ selectedAppointmentPackageId: void 0,
41071
+ validationErrors: clearBookingFormError(state.validationErrors)
41072
+ };
40916
41073
  case "appointment-package-selected":
40917
41074
  return {
40918
41075
  ...state,
@@ -41015,6 +41172,7 @@ function toBookingFormData(state) {
41015
41172
  selectedDate: state.selectedDate,
41016
41173
  selectedSlot: state.selectedSlot?.startAt,
41017
41174
  discountCode: state.discountCode || void 0,
41175
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput || void 0,
41018
41176
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
41019
41177
  selectedAppointmentPackageId: state.selectedAppointmentPackageId
41020
41178
  };
@@ -41032,7 +41190,8 @@ function buildBookingStepResolution(input) {
41032
41190
  input.form.settings?.serviceIds && input.form.settings.serviceIds.length > 1
41033
41191
  );
41034
41192
  const selectedService = paymentResolution.service;
41035
- const needsPayment = paymentResolution.mode !== "hidden" || Boolean(
41193
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
41194
+ const needsPayment = Boolean(selectedServiceSlidingScale) || paymentResolution.mode !== "hidden" || Boolean(
41036
41195
  input.enableAppointmentPackages && selectedService && (selectedService.priceAmount ?? 0) > 0
41037
41196
  );
41038
41197
  const hasFields = Boolean(input.form.schema?.fields.length);
@@ -41076,6 +41235,18 @@ function validateBookingStep(input) {
41076
41235
  services: input.services,
41077
41236
  defaultServiceId: input.defaultServiceId
41078
41237
  });
41238
+ const slidingScale = getBookingServiceSlidingScale(resolution.service);
41239
+ if (slidingScale && !isBookingServiceSlidingAmountInRange(
41240
+ slidingScale,
41241
+ parseBookingCurrencyInputToCents(input.data.selectedSlidingAmountInput)
41242
+ )) {
41243
+ return {
41244
+ ok: false,
41245
+ errors: createFormBookingValidationError(
41246
+ "Please choose an amount within the sliding scale"
41247
+ )
41248
+ };
41249
+ }
41079
41250
  if (resolution.mode === "hidden" || resolution.mode === "disclosure_only") {
41080
41251
  return { ok: true };
41081
41252
  }
@@ -41474,6 +41645,7 @@ var init_useHeadlessBookingFlow_state = __esm({
41474
41645
  "../blocks/src/system/runtime/hooks/useHeadlessBookingFlow.state.ts"() {
41475
41646
  "use strict";
41476
41647
  init_booking_form_headless_state();
41648
+ init_booking_form_state();
41477
41649
  }
41478
41650
  });
41479
41651
 
@@ -41513,11 +41685,27 @@ function useHeadlessBookingFlow(options) {
41513
41685
  const selectedServiceId = state.selectedServiceId ?? defaultServiceId;
41514
41686
  const selectedResourceId = state.selectedResourceId ?? defaultResourceId;
41515
41687
  const selectedService = options.services.find((service) => service.id === selectedServiceId) ?? null;
41688
+ const lastSlidingServiceIdRef = useRef9(null);
41689
+ useEffect10(() => {
41690
+ const selectedId = selectedService?.id ?? null;
41691
+ if (lastSlidingServiceIdRef.current === selectedId) return;
41692
+ lastSlidingServiceIdRef.current = selectedId;
41693
+ const slidingScale = getBookingServiceSlidingScale(selectedService);
41694
+ dispatch({
41695
+ type: "sliding-amount-changed",
41696
+ value: slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatBookingCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
41697
+ });
41698
+ }, [selectedService]);
41516
41699
  const hasPortalBearerAuth = (api.config.authOverrides?.portal ?? api.config.auth).type === "bearer";
41700
+ const selectedServiceSlidingScale = getBookingServiceSlidingScale(selectedService);
41701
+ const selectedSlidingAmount = parseBookingCurrencyInputToCents(
41702
+ state.selectedSlidingAmountInput
41703
+ );
41704
+ const selectedAppointmentPriceAmount = selectedServiceSlidingScale && selectedSlidingAmount !== null ? selectedSlidingAmount : selectedService?.priceAmount ?? 0;
41517
41705
  const canLoadAppointmentPackages = Boolean(
41518
- options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && (selectedService.priceAmount ?? 0) > 0
41706
+ options.enableAppointmentPackages && hasPortalBearerAuth && selectedService && !selectedServiceSlidingScale && (selectedService.priceAmount ?? 0) > 0
41519
41707
  );
41520
- const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
41708
+ const appointmentPackagesUnavailableReason = !options.enableAppointmentPackages ? "disabled" : !selectedService || selectedServiceSlidingScale || (selectedService.priceAmount ?? 0) <= 0 ? "missing-priced-service" : !hasPortalBearerAuth ? "missing-portal-auth" : null;
41521
41709
  const data = useMemo8(() => toBookingFormData(state), [state]);
41522
41710
  const availabilityDatesQuery = useMemo8(
41523
41711
  () => buildBookingAvailabilityQuery({
@@ -41747,13 +41935,13 @@ function useHeadlessBookingFlow(options) {
41747
41935
  state.selectedSlot
41748
41936
  ]);
41749
41937
  const appointmentDiscountContext = useMemo8(
41750
- () => selectedService && (selectedService.priceAmount ?? 0) > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
41751
- [selectedService?.id, selectedService?.priceAmount]
41938
+ () => selectedService && selectedAppointmentPriceAmount > 0 ? { type: "appointment", serviceId: selectedService.id } : null,
41939
+ [selectedAppointmentPriceAmount, selectedService?.id]
41752
41940
  );
41753
41941
  const discountUi = useDiscountCodeUi({
41754
41942
  api,
41755
41943
  discountContext: appointmentDiscountContext,
41756
- subtotalCents: selectedService?.priceAmount ?? 0
41944
+ subtotalCents: selectedAppointmentPriceAmount
41757
41945
  });
41758
41946
  const { submit, clearFeedback } = useBookingSubmission(options.siteId);
41759
41947
  const submittingRef = useRef9(false);
@@ -41896,6 +42084,7 @@ function useHeadlessBookingFlow(options) {
41896
42084
  type: "appointment-package-selected",
41897
42085
  packageId: packageId ? unsafeAsCustomerPassId(packageId) : null
41898
42086
  }),
42087
+ setSlidingAmountInput: (value) => dispatch({ type: "sliding-amount-changed", value }),
41899
42088
  setDiscountCode: (code) => {
41900
42089
  dispatch({ type: "discount-code-changed", code });
41901
42090
  latestRef.current.setDiscountCode(code);
@@ -41966,11 +42155,11 @@ function useHeadlessBookingFlow(options) {
41966
42155
  () => selectedService && paymentResolution.mode !== "unsupported" ? buildBookingPaymentSelectionViewModel({
41967
42156
  serviceTitle: selectedService.title,
41968
42157
  paymentTerms: selectedService.paymentTerms,
41969
- priceAmount: selectedService.priceAmount,
42158
+ priceAmount: selectedAppointmentPriceAmount,
41970
42159
  priceCurrency: selectedService.priceCurrency,
41971
42160
  bookingStartAt: state.selectedSlot?.startAt
41972
42161
  }) : null,
41973
- [paymentResolution.mode, selectedService, state.selectedSlot?.startAt]
42162
+ [paymentResolution.mode, selectedAppointmentPriceAmount, selectedService, state.selectedSlot?.startAt]
41974
42163
  );
41975
42164
  const appointmentPackageItems = appointmentPackages.tag === "loaded" ? appointmentPackages.value : [];
41976
42165
  const selectedAppointmentPackage = appointmentPackageItems.find(
@@ -41999,6 +42188,9 @@ function useHeadlessBookingFlow(options) {
41999
42188
  selection: null,
42000
42189
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
42001
42190
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
42191
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
42192
+ selectedSlidingAmount,
42193
+ slidingScale: selectedServiceSlidingScale,
42002
42194
  appointmentPackages: appointmentPackagesModel,
42003
42195
  errorMessage: paymentResolution.errorMessage ?? "This service cannot be booked right now."
42004
42196
  } : {
@@ -42007,6 +42199,9 @@ function useHeadlessBookingFlow(options) {
42007
42199
  selection: paymentSelection,
42008
42200
  selectedCollectionOptionKind: state.selectedCollectionOptionKind,
42009
42201
  selectedAppointmentPackageId: state.selectedAppointmentPackageId,
42202
+ selectedSlidingAmountInput: state.selectedSlidingAmountInput,
42203
+ selectedSlidingAmount,
42204
+ slidingScale: selectedServiceSlidingScale,
42010
42205
  appointmentPackages: appointmentPackagesModel
42011
42206
  };
42012
42207
  return {
@@ -42621,6 +42816,9 @@ var init_FieldValidationMessage = __esm({
42621
42816
 
42622
42817
  // ../blocks/src/system/runtime/nodes/booking-form.default-ui.tsx
42623
42818
  import { Fragment as Fragment15, jsx as jsx78, jsxs as jsxs59 } from "react/jsx-runtime";
42819
+ function formatBookingAmount(amount, currency) {
42820
+ return formatCurrencyAmount(amount, currency, { decimals: "auto" });
42821
+ }
42624
42822
  function HeadlessBookingDefaultForm({
42625
42823
  booking,
42626
42824
  onSubmit
@@ -42897,6 +43095,7 @@ function HeadlessBookingPaymentSelectionStep({
42897
43095
  }
42898
43096
  const packageOptions = booking.payment.appointmentPackages.items;
42899
43097
  const hasPackageOptions = packageOptions.length > 0;
43098
+ const slidingScale = booking.payment.slidingScale;
42900
43099
  return /* @__PURE__ */ jsxs59("div", { className: "ab-stack", children: [
42901
43100
  /* @__PURE__ */ jsxs59("div", { className: "ab-info-box", children: [
42902
43101
  /* @__PURE__ */ jsx78("h3", { className: "ab-subtitle", children: viewModel.sectionTitle }),
@@ -42907,6 +43106,28 @@ function HeadlessBookingPaymentSelectionStep({
42907
43106
  booking.payment.appointmentPackages.state.message,
42908
43107
  " You can continue with the normal payment option."
42909
43108
  ] }) : null,
43109
+ slidingScale ? /* @__PURE__ */ jsxs59("div", { className: "rb-space-y-2", children: [
43110
+ /* @__PURE__ */ jsx78("label", { htmlFor: "appointment-sliding-amount", className: "form-label", children: "Choose your price" }),
43111
+ /* @__PURE__ */ jsxs59("p", { className: "rb-caption status-muted", children: [
43112
+ formatBookingAmount(slidingScale.minAmount, slidingScale.currency),
43113
+ " to ",
43114
+ formatBookingAmount(slidingScale.maxAmount, slidingScale.currency),
43115
+ slidingScale.suggestedAmount !== null ? `, suggested ${formatBookingAmount(slidingScale.suggestedAmount, slidingScale.currency)}` : ""
43116
+ ] }),
43117
+ /* @__PURE__ */ jsx78(
43118
+ "input",
43119
+ {
43120
+ id: "appointment-sliding-amount",
43121
+ type: "number",
43122
+ className: "form-input",
43123
+ min: slidingScale.minAmount / 100,
43124
+ max: slidingScale.maxAmount / 100,
43125
+ step: "0.01",
43126
+ value: booking.payment.selectedSlidingAmountInput,
43127
+ onChange: (event) => booking.actions.setSlidingAmountInput(event.target.value)
43128
+ }
43129
+ )
43130
+ ] }) : null,
42910
43131
  /* @__PURE__ */ jsxs59("div", { className: "ab-stack", role: "list", "aria-label": "Payment options", children: [
42911
43132
  packageOptions.map((appointmentPackage) => {
42912
43133
  const isSelected = booking.payment.selectedAppointmentPackageId === appointmentPackage.customerPassId;
@@ -44753,6 +44974,7 @@ function useEligibility(params) {
44753
44974
  occurrenceId: params.occurrenceId,
44754
44975
  attendanceMode: params.attendanceMode ?? void 0,
44755
44976
  ticketTypeId: params.ticketTypeId ?? void 0,
44977
+ selectedSlidingAmount: params.selectedSlidingAmount ?? void 0,
44756
44978
  email: params.email.trim().toLowerCase(),
44757
44979
  ticketsCount: params.ticketsCount
44758
44980
  });
@@ -44773,7 +44995,7 @@ function useEligibility(params) {
44773
44995
  return () => {
44774
44996
  cancelled = true;
44775
44997
  };
44776
- }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.email, params.ticketsCount]);
44998
+ }, [api, params.enabled, params.occurrenceId, params.attendanceMode, params.ticketTypeId, params.selectedSlidingAmount, params.email, params.ticketsCount]);
44777
44999
  return {
44778
45000
  state,
44779
45001
  eligibility: state.eligibility,
@@ -45124,6 +45346,41 @@ function TicketCounterLimit(props2) {
45124
45346
  ] }) : null
45125
45347
  ] });
45126
45348
  }
45349
+ function formatCurrencyInputAmount(amount) {
45350
+ const major = amount / 100;
45351
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
45352
+ }
45353
+ function SlidingScaleAmountField(props2) {
45354
+ const minLabel = formatPrice(props2.slidingScale.minAmount, props2.currency);
45355
+ const maxLabel = formatPrice(props2.slidingScale.maxAmount, props2.currency);
45356
+ const suggestedLabel = props2.slidingScale.suggestedAmount !== null ? formatPrice(props2.slidingScale.suggestedAmount, props2.currency) : null;
45357
+ return /* @__PURE__ */ jsxs66("div", { className: "er-field", children: [
45358
+ /* @__PURE__ */ jsx93("label", { htmlFor: "er-sliding-amount", className: "er-label", children: "Amount" }),
45359
+ /* @__PURE__ */ jsx93(
45360
+ "input",
45361
+ {
45362
+ id: "er-sliding-amount",
45363
+ type: "number",
45364
+ min: formatCurrencyInputAmount(props2.slidingScale.minAmount),
45365
+ max: formatCurrencyInputAmount(props2.slidingScale.maxAmount),
45366
+ step: "0.01",
45367
+ inputMode: "decimal",
45368
+ className: "er-input",
45369
+ value: props2.value,
45370
+ onChange: (event) => props2.onChange?.(event.target.value)
45371
+ }
45372
+ ),
45373
+ /* @__PURE__ */ jsxs66("p", { className: "er-text er-text--muted", children: [
45374
+ "Choose between ",
45375
+ minLabel,
45376
+ " and ",
45377
+ maxLabel,
45378
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
45379
+ "."
45380
+ ] }),
45381
+ props2.message ? /* @__PURE__ */ jsx93("p", { className: "er-text er-text--muted", children: props2.message }) : null
45382
+ ] });
45383
+ }
45127
45384
  function getTicketTypeCounterAvailabilityLabel(input) {
45128
45385
  if (input.selectedRemainingCapacity !== null) {
45129
45386
  return `${input.selectedRemainingCapacity} remaining for this ticket type`;
@@ -45195,6 +45452,10 @@ var init_TicketSelectionStep = __esm({
45195
45452
  maxTickets,
45196
45453
  selectedTicketTypeId = null,
45197
45454
  onTicketTypeChange,
45455
+ slidingScale = null,
45456
+ selectedSlidingAmountInput = "",
45457
+ slidingScaleSelectionMessage = null,
45458
+ onSlidingAmountChange,
45198
45459
  onTicketsChange
45199
45460
  }) => {
45200
45461
  const deliveryMode = event?.deliveryMode ?? "in_person";
@@ -45310,6 +45571,16 @@ var init_TicketSelectionStep = __esm({
45310
45571
  ] })
45311
45572
  ] })
45312
45573
  ] }) : /* @__PURE__ */ jsxs66(Fragment18, { children: [
45574
+ slidingScale ? /* @__PURE__ */ jsx93(
45575
+ SlidingScaleAmountField,
45576
+ {
45577
+ slidingScale,
45578
+ currency: eventPricingCurrency ?? DEFAULT_EVENT_CURRENCY2,
45579
+ value: selectedSlidingAmountInput,
45580
+ message: slidingScaleSelectionMessage,
45581
+ onChange: onSlidingAmountChange
45582
+ }
45583
+ ) : null,
45313
45584
  /* @__PURE__ */ jsx93("h3", { className: "er-title", children: canJoinWaitlist ? "How many waitlist spots?" : "How many tickets?" }),
45314
45585
  /* @__PURE__ */ jsxs66("div", { className: "er-ticket-counter-group", children: [
45315
45586
  /* @__PURE__ */ jsx93(
@@ -46324,6 +46595,10 @@ function EventRegistrationWizardContent(props2) {
46324
46595
  maxTickets: wizard.maxTicketsNum,
46325
46596
  selectedTicketTypeId: wizard.selectedTicketTypeId,
46326
46597
  onTicketTypeChange: wizard.setSelectedTicketTypeId,
46598
+ slidingScale: wizard.selectedEventSlidingScale,
46599
+ selectedSlidingAmountInput: wizard.selectedSlidingAmountInput,
46600
+ slidingScaleSelectionMessage: wizard.slidingScaleSelectionMessage,
46601
+ onSlidingAmountChange: wizard.setSelectedSlidingAmountInput,
46327
46602
  onTicketsChange: wizard.setTicketsCount
46328
46603
  }
46329
46604
  )
@@ -46540,7 +46815,7 @@ function EventRegistrationSummaryBar(props2) {
46540
46815
  const summaryTimeZone = wizard.occurrenceContext?.timeZone ?? summaryEvent?.timeZone ?? null;
46541
46816
  const occurrenceOverrideTitle = wizard.occurrenceContext?.overrides?.title;
46542
46817
  const summaryTitle = occurrenceOverrideTitle ?? summaryEvent?.title ?? "";
46543
- const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
46818
+ const summaryPrice = wizard.selectedTicketType?.priceAmount ?? wizard.eligibility?.price ?? (wizard.selectedEventSlidingScale ? wizard.selectedSlidingAmount : null) ?? (summaryEvent ? publicEventScalarPrice(summaryEvent.pricing) : null) ?? 0;
46544
46819
  const summaryCurrency = (summaryEvent ? publicEventPricingCurrency(summaryEvent.pricing) : null) ?? DEFAULT_EVENT_CURRENCY2;
46545
46820
  const teamMemberNames = (summaryEvent?.teamMembers ?? []).map((teamMember) => teamMember.name).filter(Boolean);
46546
46821
  const subtotalCents = Math.max(0, summaryPrice * wizard.ticketsCount);
@@ -46991,6 +47266,7 @@ function useEventRegistrationSubmit(options) {
46991
47266
  occurrenceId: submitOccurrenceId,
46992
47267
  attendanceMode: options.selectedAttendanceMode ?? void 0,
46993
47268
  ticketTypeId: options.selectedTicketTypeId ?? void 0,
47269
+ selectedSlidingAmount: options.selectedSlidingAmount ?? void 0,
46994
47270
  email: options.formData.email.trim().toLowerCase(),
46995
47271
  name: options.formData.name.trim(),
46996
47272
  ticketsCount: options.ticketsCount,
@@ -47100,6 +47376,35 @@ var init_useEventRegistrationSubmit = __esm({
47100
47376
 
47101
47377
  // ../blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.ts
47102
47378
  import { useCallback as useCallback8, useEffect as useEffect17, useMemo as useMemo12, useReducer as useReducer10, useState as useState9 } from "react";
47379
+ function parseCurrencyInputToCents(value) {
47380
+ const trimmed = value.trim();
47381
+ if (!trimmed) return null;
47382
+ const amount = Number(trimmed);
47383
+ if (!Number.isFinite(amount) || amount < 0) return null;
47384
+ return Math.round(amount * 100);
47385
+ }
47386
+ function formatCentsAsCurrencyInput(amount) {
47387
+ const major = amount / 100;
47388
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
47389
+ }
47390
+ function getSlidingScaleForEvent(event) {
47391
+ const pricing = event ? resolveRuntimeEventPricing(event) : null;
47392
+ return pricing?.kind === "sliding_scale" ? {
47393
+ minAmount: pricing.minAmount,
47394
+ maxAmount: pricing.maxAmount,
47395
+ suggestedAmount: pricing.suggestedAmount
47396
+ } : null;
47397
+ }
47398
+ function getSlidingScaleSelectionMessage(input) {
47399
+ if (!input.slidingScale) return null;
47400
+ if (input.selectedAmount === null) {
47401
+ return "Choose an amount to continue.";
47402
+ }
47403
+ if (input.selectedAmount < input.slidingScale.minAmount || input.selectedAmount > input.slidingScale.maxAmount) {
47404
+ return "Choose an amount within the sliding scale.";
47405
+ }
47406
+ return null;
47407
+ }
47103
47408
  function useEventRegistrationWizard(options) {
47104
47409
  const api = useBlockApi();
47105
47410
  const registrationEvents = useMemo12(
@@ -47144,6 +47449,10 @@ function useEventRegistrationWizard(options) {
47144
47449
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
47145
47450
  );
47146
47451
  const [selectedTicketTypeId, setSelectedTicketTypeId] = useState9(null);
47452
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState9(() => {
47453
+ const slidingScale = getSlidingScaleForEvent(preSelectedEvent ?? null);
47454
+ return slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : "";
47455
+ });
47147
47456
  const [registrationResult, setRegistrationResult] = useState9(null);
47148
47457
  const [errorMessage, setErrorMessage] = useState9("");
47149
47458
  const [altchaSolution, setAltchaSolution] = useState9(null);
@@ -47180,6 +47489,7 @@ function useEventRegistrationWizard(options) {
47180
47489
  occurrenceId,
47181
47490
  attendanceMode: selectedAttendanceMode,
47182
47491
  ticketTypeId: selectedTicketTypeId,
47492
+ selectedSlidingAmount: parseCurrencyInputToCents(selectedSlidingAmountInput),
47183
47493
  email: formData.email,
47184
47494
  ticketsCount
47185
47495
  });
@@ -47315,6 +47625,18 @@ function useEventRegistrationWizard(options) {
47315
47625
  }),
47316
47626
  [selectedAttendanceMode, selectedEvent, preSelectedEvent, eligibility, selectedTicketTypeId]
47317
47627
  );
47628
+ const selectedSlidingAmount = useMemo12(
47629
+ () => parseCurrencyInputToCents(selectedSlidingAmountInput),
47630
+ [selectedSlidingAmountInput]
47631
+ );
47632
+ const selectedEventSlidingScale = useMemo12(
47633
+ () => getSlidingScaleForEvent(selectedEvent ?? preSelectedEvent),
47634
+ [selectedEvent, preSelectedEvent]
47635
+ );
47636
+ const slidingScaleSelectionMessage = getSlidingScaleSelectionMessage({
47637
+ slidingScale: selectedEventSlidingScale,
47638
+ selectedAmount: selectedSlidingAmount
47639
+ });
47318
47640
  const ticketTypeSelectionRequired = useMemo12(() => {
47319
47641
  const event = selectedEvent ?? preSelectedEvent;
47320
47642
  if (!event || publicEventPricingMode(resolveRuntimeEventPricing(event)) !== "ticket_types") {
@@ -47383,12 +47705,24 @@ function useEventRegistrationWizard(options) {
47383
47705
  setGuestSectionShake(false);
47384
47706
  };
47385
47707
  const eventRequiresPayment = useMemo12(
47386
- () => resolveEventRequiresPayment({
47708
+ () => {
47709
+ if (selectedEventSlidingScale) {
47710
+ return (selectedSlidingAmount ?? 0) > 0;
47711
+ }
47712
+ return resolveEventRequiresPayment({
47713
+ eligibility,
47714
+ selectedTicketType,
47715
+ event: selectedEvent ?? preSelectedEvent
47716
+ });
47717
+ },
47718
+ [
47387
47719
  eligibility,
47388
47720
  selectedTicketType,
47389
- event: selectedEvent ?? preSelectedEvent
47390
- }),
47391
- [eligibility, selectedTicketType, selectedEvent, preSelectedEvent]
47721
+ selectedEvent,
47722
+ preSelectedEvent,
47723
+ selectedEventSlidingScale,
47724
+ selectedSlidingAmount
47725
+ ]
47392
47726
  );
47393
47727
  const handleDetailsComplete = async () => {
47394
47728
  if (ticketTypeSelectionRequired && !hasValidSelectedTicketType) {
@@ -47397,6 +47731,11 @@ function useEventRegistrationWizard(options) {
47397
47731
  setStep("error");
47398
47732
  return { ok: false, reason: "blocked", message };
47399
47733
  }
47734
+ if (slidingScaleSelectionMessage) {
47735
+ setErrorMessage(slidingScaleSelectionMessage);
47736
+ setStep("error");
47737
+ return { ok: false, reason: "blocked", message: slidingScaleSelectionMessage };
47738
+ }
47400
47739
  if (!validateForm()) {
47401
47740
  setGuestSectionShake(true);
47402
47741
  return {
@@ -47481,9 +47820,18 @@ function useEventRegistrationWizard(options) {
47481
47820
  const discountSubtotalCents = useMemo12(() => {
47482
47821
  if (discountContext?.type !== "event") return void 0;
47483
47822
  const event = selectedEvent ?? preSelectedEvent;
47484
- const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
47823
+ const eventPriceCents = selectedTicketType?.priceAmount ?? eligibility?.price ?? (selectedEventSlidingScale ? selectedSlidingAmount : null) ?? (event ? publicEventScalarPrice(resolveRuntimeEventPricing(event)) : null) ?? 0;
47485
47824
  return Math.max(0, eventPriceCents * ticketsCount);
47486
- }, [discountContext, selectedTicketType, eligibility, selectedEvent, preSelectedEvent, ticketsCount]);
47825
+ }, [
47826
+ discountContext,
47827
+ selectedTicketType,
47828
+ eligibility,
47829
+ selectedEvent,
47830
+ preSelectedEvent,
47831
+ selectedEventSlidingScale,
47832
+ selectedSlidingAmount,
47833
+ ticketsCount
47834
+ ]);
47487
47835
  const discountUi = useDiscountCodeUi({ api, discountContext, subtotalCents: discountSubtotalCents });
47488
47836
  const selectionMethod = paymentSelection.method;
47489
47837
  const shouldShowDiscountToggle = discountContext !== null && discountUi.manualCodeEntryAllowed && (selectionMethod === "stripe" || selectionMethod === "pass_bundle" || selectionMethod === "membership_bundle");
@@ -47492,6 +47840,7 @@ function useEventRegistrationWizard(options) {
47492
47840
  registrationTarget,
47493
47841
  selectedAttendanceMode,
47494
47842
  selectedTicketTypeId,
47843
+ selectedSlidingAmount,
47495
47844
  ticketsCount,
47496
47845
  formData,
47497
47846
  altchaSolution,
@@ -47506,6 +47855,10 @@ function useEventRegistrationWizard(options) {
47506
47855
  });
47507
47856
  const handleEventSelect = (event) => {
47508
47857
  setSelectedEvent(event);
47858
+ const slidingScale = getSlidingScaleForEvent(event);
47859
+ setSelectedSlidingAmountInput(
47860
+ slidingScale?.suggestedAmount !== null && slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput(slidingScale.suggestedAmount) : ""
47861
+ );
47509
47862
  setSelectedAttendanceMode(
47510
47863
  (event.deliveryMode ?? "in_person") === "hybrid" ? null : (event.deliveryMode ?? "in_person") === "online" ? "online" : "in_person"
47511
47864
  );
@@ -47515,6 +47868,7 @@ function useEventRegistrationWizard(options) {
47515
47868
  const handleRetry = () => {
47516
47869
  dispatchFlow({ type: "reset", hasPreSelectedContext });
47517
47870
  setSelectedTicketTypeId(null);
47871
+ setSelectedSlidingAmountInput("");
47518
47872
  setSelectedAttendanceMode(
47519
47873
  preSelectedEvent?.deliveryMode === "online" ? "online" : preSelectedEvent?.deliveryMode === "in_person" ? "in_person" : null
47520
47874
  );
@@ -47551,6 +47905,11 @@ function useEventRegistrationWizard(options) {
47551
47905
  setSelectedAttendanceMode,
47552
47906
  selectedTicketTypeId,
47553
47907
  setSelectedTicketTypeId,
47908
+ selectedSlidingAmountInput,
47909
+ setSelectedSlidingAmountInput,
47910
+ selectedSlidingAmount,
47911
+ selectedEventSlidingScale,
47912
+ slidingScaleSelectionMessage,
47554
47913
  selectedTicketType,
47555
47914
  visibleTicketTypes,
47556
47915
  ticketTypeSelectionRequired,
@@ -51869,6 +52228,44 @@ function CoursePricingSelector({
51869
52228
  )
51870
52229
  ] });
51871
52230
  }
52231
+ function CourseSlidingScaleInput({
52232
+ pricing,
52233
+ value,
52234
+ onChange
52235
+ }) {
52236
+ const slidingScale = pricing.slidingScale;
52237
+ if (!slidingScale) {
52238
+ return null;
52239
+ }
52240
+ const minLabel = formatCoursePriceAmount(slidingScale.minAmount, pricing.currency);
52241
+ const maxLabel = formatCoursePriceAmount(slidingScale.maxAmount, pricing.currency);
52242
+ const suggestedLabel = slidingScale.suggestedAmount !== null ? formatCoursePriceAmount(slidingScale.suggestedAmount, pricing.currency) : null;
52243
+ return /* @__PURE__ */ jsxs97("div", { className: "cr-form__field", children: [
52244
+ /* @__PURE__ */ jsx140("label", { htmlFor: "cr-sliding-amount", className: "cr-form__label", children: "Amount" }),
52245
+ /* @__PURE__ */ jsx140(
52246
+ "input",
52247
+ {
52248
+ id: "cr-sliding-amount",
52249
+ type: "number",
52250
+ min: slidingScale.minAmount / 100,
52251
+ max: slidingScale.maxAmount / 100,
52252
+ step: "0.01",
52253
+ inputMode: "decimal",
52254
+ className: "cr-form__input",
52255
+ value,
52256
+ onChange: (event) => onChange(event.target.value)
52257
+ }
52258
+ ),
52259
+ /* @__PURE__ */ jsxs97("p", { className: "cr-text cr-text--muted", children: [
52260
+ "Choose between ",
52261
+ minLabel,
52262
+ " and ",
52263
+ maxLabel,
52264
+ suggestedLabel ? `; suggested ${suggestedLabel}` : "",
52265
+ "."
52266
+ ] })
52267
+ ] });
52268
+ }
51872
52269
  function EnrollmentForm({ formData, errors, onChange, isEmailLocked, isLoading, onLogout }) {
51873
52270
  const nameErrorId = "cr-name-error";
51874
52271
  const emailErrorId = "cr-email-error";
@@ -51964,6 +52361,17 @@ var init_components2 = __esm({
51964
52361
 
51965
52362
  // ../blocks/src/system/runtime/nodes/course-registration/useCourseRegistrationWizard.ts
51966
52363
  import { useEffect as useEffect27, useMemo as useMemo21, useState as useState20 } from "react";
52364
+ function parseCurrencyInputToCents2(value) {
52365
+ const trimmed = value.trim();
52366
+ if (!trimmed) return null;
52367
+ const amount = Number(trimmed);
52368
+ if (!Number.isFinite(amount) || amount < 0) return null;
52369
+ return Math.round(amount * 100);
52370
+ }
52371
+ function formatCentsAsCurrencyInput2(amount) {
52372
+ const major = amount / 100;
52373
+ return Number.isInteger(major) ? String(major) : major.toFixed(2);
52374
+ }
51967
52375
  function shouldFetchCourseEligibility(course, hasPaidSelection, portalAuthState, hasWaitlistClaimToken) {
51968
52376
  if (!course || portalAuthState === "checking") return false;
51969
52377
  if (hasWaitlistClaimToken) return false;
@@ -51981,12 +52389,27 @@ function getSessionIdFromUrl() {
51981
52389
  }
51982
52390
  function getMissingCoursePricingSelectionMessage(pricing) {
51983
52391
  if (pricing?.requiresSelection && !pricing.selectedOption) {
52392
+ if (pricing.slidingScale) {
52393
+ return "Choose an amount to continue.";
52394
+ }
51984
52395
  return "Choose a pricing option to continue.";
51985
52396
  }
52397
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount < pricing.slidingScale.minAmount) {
52398
+ return "Choose an amount within the sliding scale.";
52399
+ }
52400
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null && pricing.selectedSlidingAmount > pricing.slidingScale.maxAmount) {
52401
+ return "Choose an amount within the sliding scale.";
52402
+ }
51986
52403
  return null;
51987
52404
  }
51988
52405
  function buildCoursePricingOptionPayload(pricing) {
51989
- return pricing?.selectedOption ? { pricingOptionId: pricing.selectedOption.id } : {};
52406
+ if (pricing?.selectedOption) {
52407
+ return { pricingOptionId: pricing.selectedOption.id };
52408
+ }
52409
+ if (pricing?.slidingScale && pricing.selectedSlidingAmount !== null) {
52410
+ return { selectedSlidingAmount: pricing.selectedSlidingAmount };
52411
+ }
52412
+ return {};
51990
52413
  }
51991
52414
  function resolveHasCoursePayment(pricing) {
51992
52415
  if (!pricing) {
@@ -52027,6 +52450,7 @@ function useCourseRegistrationWizard(props2) {
52027
52450
  scopedCourses.length === 1 ? scopedCourses[0] ?? null : null
52028
52451
  );
52029
52452
  const [selectedPricingOptionId, setSelectedPricingOptionId] = useState20(null);
52453
+ const [selectedSlidingAmountInput, setSelectedSlidingAmountInput] = useState20("");
52030
52454
  const [formData, setFormData] = useState20({
52031
52455
  name: "",
52032
52456
  email: "",
@@ -52048,8 +52472,12 @@ function useCourseRegistrationWizard(props2) {
52048
52472
  const [bookingMode, setBookingMode] = useState20(null);
52049
52473
  const waitlistClaim = useWaitlistClaim();
52050
52474
  const selectedCoursePricing = useMemo21(
52051
- () => selectedCourse ? resolveCoursePricingState(selectedCourse, selectedPricingOptionId) : null,
52052
- [selectedCourse, selectedPricingOptionId]
52475
+ () => selectedCourse ? resolveCoursePricingState(
52476
+ selectedCourse,
52477
+ selectedPricingOptionId,
52478
+ parseCurrencyInputToCents2(selectedSlidingAmountInput)
52479
+ ) : null,
52480
+ [selectedCourse, selectedPricingOptionId, selectedSlidingAmountInput]
52053
52481
  );
52054
52482
  const missingPricingSelectionMessage = getMissingCoursePricingSelectionMessage(
52055
52483
  selectedCoursePricing
@@ -52122,9 +52550,13 @@ function useCourseRegistrationWizard(props2) {
52122
52550
  useEffect27(() => {
52123
52551
  if (!selectedCourse) {
52124
52552
  setSelectedPricingOptionId(null);
52553
+ setSelectedSlidingAmountInput("");
52125
52554
  return;
52126
52555
  }
52127
52556
  const defaultPricing = resolveCoursePricingState(selectedCourse, null);
52557
+ setSelectedSlidingAmountInput(
52558
+ defaultPricing.slidingScale?.suggestedAmount !== null && defaultPricing.slidingScale?.suggestedAmount !== void 0 ? formatCentsAsCurrencyInput2(defaultPricing.slidingScale.suggestedAmount) : ""
52559
+ );
52128
52560
  setSelectedPricingOptionId((previous) => {
52129
52561
  if (previous && defaultPricing.selectableOptions.some((option) => option.id === previous)) {
52130
52562
  return previous;
@@ -52499,6 +52931,8 @@ function useCourseRegistrationWizard(props2) {
52499
52931
  selectedCourse,
52500
52932
  selectedPricingOptionId,
52501
52933
  setSelectedPricingOptionId,
52934
+ selectedSlidingAmountInput,
52935
+ setSelectedSlidingAmountInput,
52502
52936
  selectedCoursePricing,
52503
52937
  setSelectedCourse,
52504
52938
  formData,
@@ -52564,6 +52998,19 @@ var init_useCourseRegistrationWizard = __esm({
52564
52998
 
52565
52999
  // ../blocks/src/system/runtime/nodes/course-registration/CourseRegistrationWizard.tsx
52566
53000
  import { Fragment as Fragment31, jsx as jsx141, jsxs as jsxs98 } from "react/jsx-runtime";
53001
+ function getCourseSlidingScaleMessage(pricing) {
53002
+ if (!pricing.slidingScale) {
53003
+ return null;
53004
+ }
53005
+ const selectedAmount = pricing.selectedSlidingAmount;
53006
+ if (selectedAmount === null) {
53007
+ return "Choose an amount to continue.";
53008
+ }
53009
+ if (selectedAmount < pricing.slidingScale.minAmount || selectedAmount > pricing.slidingScale.maxAmount) {
53010
+ return "Choose an amount within the sliding scale.";
53011
+ }
53012
+ return null;
53013
+ }
52567
53014
  function CourseRegistrationWizard(props2) {
52568
53015
  const {
52569
53016
  showSessions = true,
@@ -52585,6 +53032,8 @@ function CourseRegistrationWizard(props2) {
52585
53032
  selectedCourse,
52586
53033
  selectedPricingOptionId,
52587
53034
  setSelectedPricingOptionId,
53035
+ selectedSlidingAmountInput,
53036
+ setSelectedSlidingAmountInput,
52588
53037
  selectedCoursePricing,
52589
53038
  formData,
52590
53039
  setFormData,
@@ -52659,7 +53108,8 @@ function CourseRegistrationWizard(props2) {
52659
53108
  const canShowDiscountUi = !discountsAvailableLoading && paymentChoice === "checkout" && discountsAvailable && manualCodeEntryAllowed;
52660
53109
  const courseSoldOut = waitlistClaim.claim === null && courseEligibility?.isFull === true && courseEligibility.canJoinWaitlist !== true;
52661
53110
  const detailsSubmitLabel = courseEligibility?.canJoinWaitlist ? "Join waitlist" : buttonText;
52662
- const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption;
53111
+ const pricingSelectionMissing = selectedCoursePricing?.requiresSelection === true && !selectedCoursePricing.selectedOption && !selectedCoursePricing.slidingScale;
53112
+ const slidingScaleMessage = selectedCoursePricing ? getCourseSlidingScaleMessage(selectedCoursePricing) : null;
52663
53113
  const pricingUnavailable = selectedCoursePricing?.display.kind === "unavailable";
52664
53114
  const selectedCourseWithPricing = selectedCourse && selectedCoursePricing ? {
52665
53115
  course: selectedCourse,
@@ -52752,6 +53202,14 @@ function CourseRegistrationWizard(props2) {
52752
53202
  onSelect: setSelectedPricingOptionId
52753
53203
  }
52754
53204
  ),
53205
+ /* @__PURE__ */ jsx141(
53206
+ CourseSlidingScaleInput,
53207
+ {
53208
+ pricing: selectedCourseWithPricing.pricing,
53209
+ value: selectedSlidingAmountInput,
53210
+ onChange: setSelectedSlidingAmountInput
53211
+ }
53212
+ ),
52755
53213
  showSpamProtection && /* @__PURE__ */ jsx141("div", { className: "cr-altcha", children: /* @__PURE__ */ jsx141(
52756
53214
  AltchaWidget,
52757
53215
  {
@@ -52789,7 +53247,7 @@ function CourseRegistrationWizard(props2) {
52789
53247
  {
52790
53248
  type: "submit",
52791
53249
  className: buttonClass,
52792
- disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || pricingUnavailable,
53250
+ disabled: courseSoldOut || eligibilityLoading || pricingSelectionMissing || slidingScaleMessage !== null || pricingUnavailable,
52793
53251
  children: eligibilityLoading ? "Checking availability\u2026" : courseSoldOut ? "Sold out" : detailsSubmitLabel
52794
53252
  }
52795
53253
  )
@@ -52799,6 +53257,7 @@ function CourseRegistrationWizard(props2) {
52799
53257
  ),
52800
53258
  courseSoldOut && /* @__PURE__ */ jsx141("p", { className: "cr-text cr-text--muted", children: "This course is fully booked and there is no waitlist available right now." }),
52801
53259
  pricingSelectionMissing && /* @__PURE__ */ jsx141("p", { className: "cr-text cr-text--muted", children: "Choose a pricing option to continue." }),
53260
+ slidingScaleMessage && /* @__PURE__ */ jsx141("p", { className: "cr-text cr-text--muted", children: slidingScaleMessage }),
52802
53261
  pricingUnavailable && /* @__PURE__ */ jsx141("p", { className: "cr-text cr-text--muted", children: "Pricing is not available for this course right now." }),
52803
53262
  eligibilityError && /* @__PURE__ */ jsx141("p", { className: "cr-text cr-text--error", children: eligibilityError })
52804
53263
  ] })
@@ -62952,6 +63411,21 @@ function fieldPlanToZod(plan, options = {}) {
62952
63411
  }
62953
63412
  });
62954
63413
  }
63414
+ function fieldPresenceForPlan(plan) {
63415
+ return plan.required ? { kind: "required" } : { kind: "optional" };
63416
+ }
63417
+ function fieldPlanToObjectPropertyZod(plan, options = {}) {
63418
+ const schema = fieldPlanToZod(plan, options);
63419
+ const presence = fieldPresenceForPlan(plan);
63420
+ switch (presence.kind) {
63421
+ case "required":
63422
+ return schema;
63423
+ case "optional":
63424
+ return schema.optional();
63425
+ default:
63426
+ return assertNever4(presence);
63427
+ }
63428
+ }
62955
63429
  function addFieldIssueToZodContext(zodCtx, validationIssue, rootPath) {
62956
63430
  const message = fieldIssueToMessage(validationIssue);
62957
63431
  const path = relativeZodIssuePath(validationIssue.path, rootPath);
@@ -63164,7 +63638,7 @@ function createManifestValidator(manifestOrFields, options = {}) {
63164
63638
  function deriveContentZodFromPlans(plans, options) {
63165
63639
  const shape = {};
63166
63640
  for (const plan of plans) {
63167
- shape[plan.fieldId] = fieldPlanToZod(plan, options);
63641
+ shape[plan.fieldId] = fieldPlanToObjectPropertyZod(plan, options);
63168
63642
  }
63169
63643
  return z7.object(shape).catchall(z7.unknown()).passthrough();
63170
63644
  }
@@ -97086,6 +97560,7 @@ var CANONICAL_REFUND_CONFLICT_CODES = [
97086
97560
  ];
97087
97561
  var CANONICAL_REFUND_PLANNER_ERROR_CODES = [
97088
97562
  ...CANONICAL_REFUND_CONFLICT_CODES,
97563
+ "invalid_amount",
97089
97564
  "amount_exceeds_remaining",
97090
97565
  "pass_revoke_requires_full_refund",
97091
97566
  "refund_failed"
@@ -97112,6 +97587,7 @@ var CUSTOMER_MEMBERSHIP_PAYMENT_REFUND_FAILED_CODES = [
97112
97587
  // ../api/src/bookingRefundOutcomes.ts
97113
97588
  var STANDARD_LINE_ITEM_REFUND_INVALID_CODES = [
97114
97589
  "missing_amount",
97590
+ "invalid_amount",
97115
97591
  "amount_exceeds_remaining",
97116
97592
  "credit_refunds_disabled",
97117
97593
  "missing_credit_customer",
@@ -97126,6 +97602,7 @@ var EVENT_ATTENDEE_REFUND_INVALID_CODES = [
97126
97602
  var MANAGED_PRODUCT_REFUND_INVALID_CODES = [
97127
97603
  "credit_refunds_disabled",
97128
97604
  "not_refundable",
97605
+ "invalid_amount",
97129
97606
  "amount_exceeds_remaining"
97130
97607
  ];
97131
97608
  var CUSTOMER_PASS_REFUND_INVALID_CODES = [
@@ -99098,7 +99575,7 @@ var SimpleCache = class {
99098
99575
  };
99099
99576
 
99100
99577
  // src/version.ts
99101
- var SDK_VERSION = "0.91.0";
99578
+ var SDK_VERSION = "0.92.0";
99102
99579
 
99103
99580
  // src/client/error.ts
99104
99581
  var RiverbankApiError = class _RiverbankApiError extends Error {