@riverbankcms/sdk 0.135.1 → 0.137.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 (220) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +4 -8
  3. package/dist/{PreviewEditorSidebar-D4p_5TdY.mjs → PreviewEditorSidebar-Bn6cONPa.mjs} +8 -8
  4. package/dist/{PreviewEditorUI-D0cRoTX1.mjs → PreviewEditorUI-LAwXZPSx.mjs} +3 -3
  5. package/dist/{PreviewQueryProvider-DcgaESkm.mjs → PreviewQueryProvider-ztQUdsNo.mjs} +1 -1
  6. package/dist/{SdkPreviewModeRuntime-BKLtM3Nz.mjs → SdkPreviewModeRuntime-BbbGsI_v.mjs} +621 -238
  7. package/dist/{SiteChromeCustomizeContext-61X2m7QV.mjs → SiteChromeCustomizeContext-BbkNSt5a.mjs} +5041 -2407
  8. package/dist/_dts/ai/src/contracts/proposals.d.ts +18 -850
  9. package/dist/_dts/ai/src/contracts/siteStyleProposalSchemas.d.ts +412 -2316
  10. package/dist/_dts/ai/src/designer/themePatch.d.ts +5 -10
  11. package/dist/_dts/api/src/accessAdmin.d.ts +1 -9
  12. package/dist/_dts/api/src/accountApi.d.ts +49 -2
  13. package/dist/_dts/api/src/contentRuntime.d.ts +7 -4
  14. package/dist/_dts/api/src/domainConnection.d.ts +41 -0
  15. package/dist/_dts/api/src/endpoints.d.ts +201 -181
  16. package/dist/_dts/api/src/index.d.ts +5 -3
  17. package/dist/_dts/api/src/media.d.ts +19 -2
  18. package/dist/_dts/api/src/publicEventEndpointDefinitions.d.ts +7 -0
  19. package/dist/_dts/api/src/publicExperience/eventEndpoints.d.ts +26 -0
  20. package/dist/_dts/api/src/sdkBlockContentAdmission.d.ts +5 -0
  21. package/dist/_dts/api/src/sdkContracts.d.ts +2 -15
  22. package/dist/_dts/api/src/sdkMediaContracts.d.ts +40 -0
  23. package/dist/_dts/api/src/sdkMediaEndpointDefinitions.d.ts +61 -0
  24. package/dist/_dts/api/src/sdkThemeWire.d.ts +21 -436
  25. package/dist/_dts/api/src/siteInfrastructure.d.ts +17 -23
  26. package/dist/_dts/api/src/siteManagementEndpoints.d.ts +7 -21
  27. package/dist/_dts/api/src/sitePlatformEndpoints.d.ts +27 -3
  28. package/dist/_dts/api/src/siteRuntimeEndpoints.d.ts +37 -17
  29. package/dist/_dts/api/src/types.d.ts +1 -1
  30. package/dist/_dts/block-form/src/blockDraftSaveability.d.ts +2 -3
  31. package/dist/_dts/block-form/src/utils/slug.d.ts +6 -0
  32. package/dist/_dts/blocks/src/PageRenderer.d.ts +13 -7
  33. package/dist/_dts/blocks/src/contracts/proof-block-islands.d.ts +48 -0
  34. package/dist/_dts/blocks/src/definitions.d.ts +14 -0
  35. package/dist/_dts/blocks/src/index.d.ts +4 -3
  36. package/dist/_dts/blocks/src/pageRenderAdmission.d.ts +49 -0
  37. package/dist/_dts/blocks/src/pageRendererBlockSupport.d.ts +75 -0
  38. package/dist/_dts/blocks/src/pageRendererBoundaries.d.ts +2 -0
  39. package/dist/_dts/blocks/src/server.d.ts +2 -2
  40. package/dist/_dts/blocks/src/system/blocks/event-registration.d.ts +1 -1
  41. package/dist/_dts/blocks/src/system/data/index.d.ts +2 -0
  42. package/dist/_dts/blocks/src/system/data/publicLoaderCachePolicy.d.ts +78 -0
  43. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +2 -18
  44. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +103 -16
  45. package/dist/_dts/blocks/src/system/node/validation-constants.d.ts +1 -1
  46. package/dist/_dts/blocks/src/system/runtime/api/client.d.ts +4 -2
  47. package/dist/_dts/blocks/src/system/runtime/api/endpoints.d.ts +8 -0
  48. package/dist/_dts/blocks/src/system/runtime/api/index.d.ts +1 -1
  49. package/dist/_dts/blocks/src/system/runtime/api/types/events.d.ts +19 -0
  50. package/dist/_dts/blocks/src/system/runtime/components/registry.client.d.ts +52 -0
  51. package/dist/_dts/blocks/src/system/runtime/components/runtime-backed-blocks.d.ts +24 -0
  52. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.d.ts +47 -21
  53. package/dist/_dts/blocks/src/system/runtime/hooks/useCalendarEvents.state.d.ts +38 -9
  54. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/ErrorStep.d.ts +3 -5
  55. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/VerifyingTimeoutStep.d.ts +3 -1
  56. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +2 -0
  57. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.planning.d.ts +1 -1
  58. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/hooks.d.ts +2 -0
  59. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorPresentation.d.ts +5 -0
  60. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationErrorRecovery.d.ts +25 -0
  61. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/registrationWizardContext.d.ts +4 -0
  62. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationStripeReturn.d.ts +8 -3
  63. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationSubmit.d.ts +2 -0
  64. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useEventRegistrationWizard.d.ts +5 -2
  65. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/useWaitlistClaimRestoration.d.ts +2 -0
  66. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.client.d.ts +2 -2
  67. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCalendar.interactive.d.ts +40 -0
  68. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.client.d.ts +2 -0
  69. package/dist/_dts/blocks/src/system/runtime/nodes/events/EventCombined.interactive.d.ts +43 -0
  70. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/capacity.d.ts +164 -0
  71. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/cta.d.ts +29 -0
  72. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/dates.d.ts +47 -0
  73. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/index.d.ts +14 -0
  74. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/people.d.ts +6 -0
  75. package/dist/_dts/blocks/src/system/runtime/nodes/events/domain/pricing.d.ts +31 -0
  76. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarDayCell.d.ts +3 -1
  77. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/CalendarNavigationChevron.d.ts +4 -0
  78. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/DayEventsModal.d.ts +2 -1
  79. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/EventModalContext.d.ts +3 -2
  80. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/OfferingTitle.d.ts +11 -0
  81. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.d.ts +4 -1
  82. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekBoardView.d.ts +31 -3
  83. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/WeekTimetableView.d.ts +3 -1
  84. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCollectionContract.d.ts +9 -1
  85. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/index.d.ts +3 -1
  86. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/listingLayout.d.ts +23 -0
  87. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/timetableModel.d.ts +4 -3
  88. package/dist/_dts/blocks/src/system/transforms/registry/layout.d.ts +0 -1
  89. package/dist/_dts/calendar/src/grid.d.ts +24 -66
  90. package/dist/_dts/calendar/src/index.d.ts +2 -2
  91. package/dist/_dts/calendar/src/timetable.d.ts +5 -9
  92. package/dist/_dts/commerce/src/purchase/allocation.d.ts +11 -0
  93. package/dist/_dts/commerce/src/purchase/event-registration-errors.d.ts +57 -0
  94. package/dist/_dts/commerce/src/purchase/index.d.ts +3 -0
  95. package/dist/_dts/commerce/src/purchase/settlement.d.ts +33 -0
  96. package/dist/_dts/commerce/src/purchase/stripeCheckoutReturnUrl.d.ts +8 -0
  97. package/dist/_dts/content-model/src/templates/defaults.d.ts +6 -0
  98. package/dist/_dts/core/src/slug.d.ts +20 -5
  99. package/dist/_dts/db/src/generated/supabase/database.types.d.ts +8 -40
  100. package/dist/_dts/db/src/index.d.ts +1 -0
  101. package/dist/_dts/db/src/utils/postgrestFilter.d.ts +2 -0
  102. package/dist/_dts/media-core/src/bulkDeletePlanning.d.ts +4 -0
  103. package/dist/_dts/media-core/src/fileDomain.d.ts +3 -0
  104. package/dist/_dts/preview-next/src/client/blocks/EditablePageRenderer.d.ts +1 -1
  105. package/dist/_dts/preview-next/src/client/preview/PreviewShell.d.ts +1 -0
  106. package/dist/_dts/preview-next/src/client/preview/PreviewShellLayout.d.ts +1 -0
  107. package/dist/_dts/preview-next/src/client/preview/SiteChromeCustomizeContext.d.ts +3 -2
  108. package/dist/_dts/preview-next/src/client/preview/headerInteractionPaletteSurfaceState.d.ts +2 -1
  109. package/dist/_dts/preview-next/src/client/preview/siteChromeBrandPreferences.d.ts +1 -1
  110. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlHistoryState.d.ts +2 -0
  111. package/dist/_dts/preview-next/src/client/preview/siteChromeQuickControlSaveRequest.d.ts +51 -0
  112. package/dist/_dts/preview-next/src/client/preview/siteChromeSavePlan.d.ts +24 -2
  113. package/dist/_dts/preview-next/src/client/preview/siteStylePreviewPlanner.d.ts +23 -1
  114. package/dist/_dts/preview-next/src/client/preview/usePersistedHeaderPreferences.d.ts +8 -0
  115. package/dist/_dts/sdk/src/client/publicLoaderCacheAccess.d.ts +6 -0
  116. package/dist/_dts/sdk/src/public-api/contracts.d.ts +7 -2
  117. package/dist/_dts/sdk/src/rendering/blocks-page-renderer.d.ts +10 -6
  118. package/dist/_dts/sdk/src/rendering/components/Block.shared.d.ts +0 -1
  119. package/dist/_dts/sdk/src/rendering/components/pageShared.d.ts +4 -4
  120. package/dist/_dts/sdk/src/rendering/helpers/footerPageOutline.d.ts +3 -0
  121. package/dist/_dts/sdk/src/rendering/helpers/loadContent.d.ts +3 -0
  122. package/dist/_dts/sdk/src/rendering/helpers/loadPage.d.ts +1 -1
  123. package/dist/_dts/sdk/src/rendering/helpers/pageRuntimeSupport.d.ts +17 -0
  124. package/dist/_dts/sdk/src/rendering/helpers/templateContent.d.ts +9 -3
  125. package/dist/_dts/sdk/src/rendering/hooks/usePage.d.ts +1 -1
  126. package/dist/_dts/sdk/src/rendering/index.d.ts +2 -2
  127. package/dist/_dts/sdk/src/rendering/overrideResolution.d.ts +7 -4
  128. package/dist/_dts/sdk/src/version.d.ts +1 -1
  129. package/dist/_dts/theme-core/src/cards/types.d.ts +8 -8
  130. package/dist/_dts/theme-core/src/data.d.ts +12 -0
  131. package/dist/_dts/theme-core/src/generated/containerResponsiveThemeCss.d.ts +1 -1
  132. package/dist/_dts/theme-core/src/gradients/types.d.ts +5 -0
  133. package/dist/_dts/theme-core/src/header/interactionPalette.d.ts +25 -2
  134. package/dist/_dts/theme-core/src/header/legacyHeaderVariant.d.ts +2 -6
  135. package/dist/_dts/theme-core/src/mock-themes/all.d.ts +9 -810
  136. package/dist/_dts/theme-core/src/schema.d.ts +460 -1285
  137. package/dist/_dts/theme-core/src/site-styles/compilationPlan.d.ts +43 -3
  138. package/dist/_dts/theme-core/src/site-styles/curatedSiteStyles.d.ts +223 -1
  139. package/dist/_dts/theme-core/src/site-styles/headerBrandPresentation.d.ts +63 -0
  140. package/dist/_dts/theme-core/src/site-styles/headerBytesComparison.d.ts +30 -0
  141. package/dist/_dts/theme-core/src/site-styles/headerInteractionPalette.d.ts +50 -1
  142. package/dist/_dts/theme-core/src/site-styles/headerLayoutCatalog.d.ts +48 -38
  143. package/dist/_dts/theme-core/src/site-styles/headerLayoutStyles.d.ts +10 -0
  144. package/dist/_dts/theme-core/src/site-styles/headerLayoutTypes.d.ts +8 -5
  145. package/dist/_dts/theme-core/src/site-styles/headerOpeningForeground.d.ts +4 -4
  146. package/dist/_dts/theme-core/src/site-styles/headerPreferences.d.ts +162 -0
  147. package/dist/_dts/theme-core/src/site-styles/headerPreferencesPersistence.d.ts +73 -0
  148. package/dist/_dts/theme-core/src/site-styles/headerRecompile.d.ts +118 -0
  149. package/dist/_dts/theme-core/src/site-styles/headerStyleCompiler.d.ts +25 -2
  150. package/dist/_dts/theme-core/src/site-styles/headerStyleDefaults.d.ts +465 -0
  151. package/dist/_dts/theme-core/src/site-styles/headerStyleRecipe.d.ts +168 -0
  152. package/dist/_dts/theme-core/src/site-styles/index.d.ts +5 -0
  153. package/dist/_dts/theme-core/src/site-styles/previewCompiler.d.ts +1 -1
  154. package/dist/_dts/theme-core/src/site-styles/siteChromeBackgrounds.d.ts +20 -1
  155. package/dist/_dts/theme-core/src/site-styles/siteChromeNavPreferences.d.ts +43 -2
  156. package/dist/client/{BookingConsentAgreement-Bz9znbpn.mjs → BookingConsentAgreement--_fUCZe-.mjs} +2 -2
  157. package/dist/client/{DiscountCodeDisclosure-35Ru8yB9.mjs → DiscountCodeDisclosure-BMipSoDJ.mjs} +1 -1
  158. package/dist/client/{EventCombined.client-BKB0ZxRR.mjs → EventCombined.client-CwJOxn7A.mjs} +274 -278
  159. package/dist/client/{EventListing.client-Dx8I21ok.mjs → EventListing.client-eVwiylPW.mjs} +2 -2
  160. package/dist/client/{LeafletStaticMap-CEWx-Bhe.mjs → LeafletStaticMap-IKenna5u.mjs} +1 -1
  161. package/dist/client/{accordion-KIDsKTwS.mjs → accordion-MGAO3PuE.mjs} +1 -1
  162. package/dist/client/{bookingForm-D96Rmp-5.mjs → bookingForm-CudlQPUu.mjs} +5 -5
  163. package/dist/client/bookings.mjs +476 -98
  164. package/dist/client/capacity-BW00PO7s.mjs +271 -0
  165. package/dist/client/{carousel-YwUgyL66.mjs → carousel-BrbGJ2Jl.mjs} +1 -1
  166. package/dist/client/{cart-BbwuNrUT.mjs → cart-Bq5eU0xZ.mjs} +3 -3
  167. package/dist/client/{checkout-hp1ucmEY.mjs → checkout-BAFVRdrK.mjs} +3 -3
  168. package/dist/client/client.mjs +4641 -2721
  169. package/dist/client/{context-DljQn8L5.mjs → context-DCkiHVdD.mjs} +20 -5
  170. package/dist/client/dates-2RPXHQy4.mjs +264 -0
  171. package/dist/client/{eventCalendar-DU4iNLpo.mjs → eventCalendar-B0O-hPF7.mjs} +261 -126
  172. package/dist/client/{eventCombined-CZuupW8m.mjs → eventCombined-DCkDaoDI.mjs} +3 -3
  173. package/dist/client/{eventDetails-DPQWwCIi.mjs → eventDetails-D8GG2dnD.mjs} +7 -4
  174. package/dist/client/{eventListing-C7oMidwS.mjs → eventListing-3ssV_bxz.mjs} +3 -3
  175. package/dist/client/{eventRegistration-zEiunlUs.mjs → eventRegistration-bPQo3kVw.mjs} +610 -313
  176. package/dist/client/{form-D2KIyCVJ.mjs → form-qMv8RDkk.mjs} +2 -2
  177. package/dist/client/{headerSection-CK5uChUK.mjs → headerSection-CH0Sc5hX.mjs} +2 -2
  178. package/dist/client/hooks.mjs +2514 -308
  179. package/dist/client/{locationMap-DLQNH1ln.mjs → locationMap-0_xlBnNI.mjs} +2 -2
  180. package/dist/client/{newsletterForm-ZuhIG7fZ.mjs → newsletterForm-NUW7m9RI.mjs} +2 -2
  181. package/dist/client/{productDetail-7hIcGeae.mjs → productDetail-FBEOM7jW.mjs} +3 -3
  182. package/dist/client/{productList-T4Cii1ot.mjs → productList-R0OSGhnn.mjs} +3 -3
  183. package/dist/client/{propsDecoder-mB0aC_Fi.mjs → propsDecoder-pYvx_Srl.mjs} +179 -401
  184. package/dist/client/rendering/client.mjs +3496 -2223
  185. package/dist/client/rendering/islands.mjs +17 -17
  186. package/dist/client/rendering.mjs +4400 -2510
  187. package/dist/client/{scrollToBlock-Du5mHgvL.mjs → scrollToBlock-ByQv96M2.mjs} +1 -1
  188. package/dist/client/{shop-B5zAcKZn.mjs → shop-RUG9DmRE.mjs} +6 -6
  189. package/dist/client/{shop-commerce-BKBZB6l5.mjs → shop-commerce-B76Jz23B.mjs} +5 -5
  190. package/dist/client/{shop-commerce-store-Cq4qlNtQ.mjs → shop-commerce-store-QuQNdDsD.mjs} +1 -1
  191. package/dist/client/{shopShared-DYGzEs9D.mjs → shopShared-vXU1omwJ.mjs} +3 -3
  192. package/dist/client/{useEventFilters-Bphkda8m.mjs → useEventFilters-ByuU6HLA.mjs} +33 -18
  193. package/dist/preview-next/before-render.mjs +86 -51
  194. package/dist/preview-next/client/runtime.mjs +4 -4
  195. package/dist/preview-next/keepAlive.mjs +86 -51
  196. package/dist/preview-next/middleware.mjs +86 -51
  197. package/dist/{previewAuthStore-C7ksxt5b.mjs → previewAuthStore-L_cKW8Sb.mjs} +86 -51
  198. package/dist/{sdk-runtime-BbIC8G4J.mjs → sdk-runtime-sUMr5d70.mjs} +2 -2
  199. package/dist/{separator-CadhAtI7.mjs → separator-BIHkie5i.mjs} +1 -1
  200. package/dist/server/components.mjs +4397 -2960
  201. package/dist/server/config-validation.mjs +354 -145
  202. package/dist/server/config.mjs +354 -145
  203. package/dist/server/data.mjs +354 -144
  204. package/dist/server/index.mjs +201 -55
  205. package/dist/server/manifest.mjs +0 -1
  206. package/dist/server/next.mjs +4046 -2611
  207. package/dist/server/page-converter.mjs +225 -122
  208. package/dist/server/prebuild.mjs +1 -1
  209. package/dist/server/rendering/server.mjs +4105 -2636
  210. package/dist/server/rendering.mjs +4522 -3087
  211. package/dist/server/routing.mjs +3289 -1637
  212. package/dist/server/server.mjs +23175 -21079
  213. package/dist/server/theme-bridge.mjs +708 -272
  214. package/dist/server/theme.mjs +29 -25
  215. package/dist/styles/index.css +624 -181
  216. package/package.json +1 -1
  217. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/eventCapacity.d.ts +0 -48
  218. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +0 -17
  219. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +0 -100
  220. /package/dist/_dts/blocks/src/system/runtime/nodes/events/{shared → domain}/destination.d.ts +0 -0
@@ -244,6 +244,11 @@ const DEFAULT_BOOKING_DATE_TIME_FORMAT = {
244
244
  hour: "numeric",
245
245
  minute: "2-digit"
246
246
  };
247
+ const START_OF_DAY = {
248
+ hour: 0,
249
+ minute: 0,
250
+ second: 0
251
+ };
247
252
  const LOCAL_DATE_PATTERN = /^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/u;
248
253
  const DATE_TIME_FORMATTER_CACHE_LIMIT = 100;
249
254
  const dateTimeFormatterCache = /* @__PURE__ */ new Map();
@@ -447,6 +452,9 @@ function formatBookingDateParts(input, options) {
447
452
  function getBookingLocalDateKey(input) {
448
453
  return formatDateKey(getLocalScheduleDate(new Date(input.startsAt), input.timeZone));
449
454
  }
455
+ function compareLocalScheduleDates(left, right) {
456
+ return Date.UTC(left.year, left.month - 1, left.day) - Date.UTC(right.year, right.month - 1, right.day);
457
+ }
450
458
  function parseBookingLocalDate(value) {
451
459
  const match = LOCAL_DATE_PATTERN.exec(value.trim());
452
460
  if (match === null || match.groups === void 0) throw new Error(`Invalid booking date format: "${value}" (expected YYYY-MM-DD)`);
@@ -461,6 +469,53 @@ function parseBookingLocalDate(value) {
461
469
  day
462
470
  };
463
471
  }
472
+ function parseBookingUtcDateWindow(input) {
473
+ const timeZone = resolveBookingTimeZoneId(input.timeZone);
474
+ let startLocalDate;
475
+ try {
476
+ startLocalDate = parseBookingLocalDate(input.startDate);
477
+ } catch {
478
+ return {
479
+ ok: false,
480
+ field: "startDate",
481
+ reason: "invalid_date_format",
482
+ value: input.startDate
483
+ };
484
+ }
485
+ let endLocalDate;
486
+ try {
487
+ endLocalDate = parseBookingLocalDate(input.endDate);
488
+ } catch {
489
+ return {
490
+ ok: false,
491
+ field: "endDate",
492
+ reason: "invalid_date_format",
493
+ value: input.endDate
494
+ };
495
+ }
496
+ if (compareLocalScheduleDates(startLocalDate, endLocalDate) > 0) return {
497
+ ok: false,
498
+ field: "endDate",
499
+ reason: "invalid_date_range",
500
+ value: input.endDate
501
+ };
502
+ return {
503
+ ok: true,
504
+ value: {
505
+ timeZone,
506
+ startLocalDate,
507
+ endLocalDate,
508
+ startAt: localScheduleDateTimeToUtcDate(startLocalDate, START_OF_DAY, timeZone),
509
+ endExclusive: localScheduleDateTimeToUtcDate(addLocalScheduleDays(endLocalDate, 1), START_OF_DAY, timeZone)
510
+ }
511
+ };
512
+ }
513
+ function buildBookingUtcDateWindow(input) {
514
+ const result = parseBookingUtcDateWindow(input);
515
+ if (result.ok) return result.value;
516
+ if (result.reason === "invalid_date_range") throw new Error(`Invalid booking date range: startDate "${input.startDate}" must be on or before endDate "${input.endDate}"`);
517
+ throw new Error(`Invalid booking date format: "${result.value}" (expected YYYY-MM-DD)`);
518
+ }
464
519
  function getLocalScheduleDate(date, timeZone) {
465
520
  const parts = getZonedDateTimeParts(date, getSafeTimeZone(timeZone));
466
521
  return {
@@ -480,6 +535,22 @@ function addLocalScheduleDays(date, days) {
480
535
  day: utcDate.getUTCDate()
481
536
  };
482
537
  }
538
+ function localScheduleDateTimeToUtcDate(localDate, localTime, timeZone) {
539
+ const resolvedTimeZone = getSafeTimeZone(timeZone);
540
+ const targetLocalMs = Date.UTC(localDate.year, localDate.month - 1, localDate.day, localTime.hour, localTime.minute, localTime.second, 0);
541
+ let guess = targetLocalMs;
542
+ const seenGuesses = /* @__PURE__ */ new Set();
543
+ for (let iteration = 0; iteration < 4; iteration += 1) {
544
+ seenGuesses.add(guess);
545
+ const actual = getZonedDateTimeParts(new Date(guess), resolvedTimeZone);
546
+ const diff = Date.UTC(actual.year, actual.month - 1, actual.day, actual.hour, actual.minute, actual.second, 0) - targetLocalMs;
547
+ if (diff === 0) return new Date(guess);
548
+ const nextGuess = guess - diff;
549
+ if (seenGuesses.has(nextGuess)) return new Date(Math.max(guess, nextGuess));
550
+ guess = nextGuess;
551
+ }
552
+ return new Date(guess);
553
+ }
483
554
  //#endregion
484
555
  //#region ../core/src/newsletter-signup-destination.ts
485
556
  function parseNewsletterSignupDestinationProvider(value) {
@@ -4480,6 +4551,11 @@ const gradientConfigSchema = z.object({
4480
4551
  position: z.string().optional(),
4481
4552
  stops: z.array(gradientStopSchema).min(2)
4482
4553
  });
4554
+ const gradientsSchema = z.object({
4555
+ button: z.string().min(1).nullable().optional(),
4556
+ hero: z.string().min(1).nullable().optional(),
4557
+ background: z.string().min(1).nullable().optional()
4558
+ });
4483
4559
  //#endregion
4484
4560
  //#region ../theme-core/src/interactive/baseSchema.ts
4485
4561
  /**
@@ -5084,12 +5160,8 @@ const legacyHeaderVariantValues = Object.keys(legacyHeaderVariants);
5084
5160
  * `@riverbankcms/api` is `z.input<typeof themeSchema>`, so a widened input here
5085
5161
  * would leak straight into a public type.
5086
5162
  *
5087
- * Not every read of a persisted theme goes through the schema:
5088
- * `resolveRuntimeTheme` in
5089
- * `apps/dashboard/src/server/services/sites/runtimeSiteResponse.ts` returns the
5090
- * raw database JSON asserted as `Theme` with no parse, so a theme still
5091
- * carrying a legacy spelling reaches the theme editor un-aliased. That gap
5092
- * predates this alias and is harmless while persisted usage is zero.
5163
+ * Persisted runtime themes are admitted through `parseThemeForPersistence`
5164
+ * before reaching consumers, so legacy spellings normalize at that boundary.
5093
5165
  */
5094
5166
  const headerVariantWithLegacyAliasSchema = z.enum([...headerVariantValues, ...legacyHeaderVariantValues]).transform((value) => value in legacyHeaderVariants ? legacyHeaderVariants[value] : value);
5095
5167
  const BREAKPOINT_LG = 1024;
@@ -5141,11 +5213,30 @@ const headerContainerSchema = z.object({
5141
5213
  tint: z.string().nullable().optional(),
5142
5214
  opacity: z.number().min(0).max(1).default(.12)
5143
5215
  }).optional();
5144
- const headerInteractionPaletteSchema = z.enum([
5216
+ //#endregion
5217
+ //#region ../theme-core/src/header/interactionPalette.ts
5218
+ /**
5219
+ * The palettes that fill the nav link with a colour on hover and focus. Each
5220
+ * one names a background/foreground token pair.
5221
+ */
5222
+ const headerInteractionPillPaletteValues = [
5145
5223
  "primary",
5146
5224
  "secondary",
5147
5225
  "accent"
5148
- ]);
5226
+ ];
5227
+ /**
5228
+ * "No pill": the nav link keeps whatever hover treatment its recipe compiled,
5229
+ * and the palette step writes nothing at all (#1318 Phase 4c decision 2).
5230
+ *
5231
+ * It exists because "a colour change on hover, no filled background" was not
5232
+ * expressible in the vocabulary, and it is what
5233
+ * `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
5234
+ * the palette existed. Without it those two rows can only be classified as a
5235
+ * conscious visual change.
5236
+ */
5237
+ const headerInteractionQuietPalette = "quiet";
5238
+ const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
5239
+ const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
5149
5240
  //#endregion
5150
5241
  //#region ../theme-core/src/boxBackgroundSchema.ts
5151
5242
  const boxBackgroundOverlaySchema = z.preprocess(normalizeAuthoringOpacity, z.discriminatedUnion("type", [
@@ -5548,7 +5639,6 @@ const navFontWeight = z.enum([
5548
5639
  "bold"
5549
5640
  ]);
5550
5641
  const headerMaxWidth = z.enum(["container", "full"]);
5551
- const headerBarFrame = z.enum(["full-bleed", "inset-shell"]);
5552
5642
  const headerBorderSchema = componentBorderSchema.extend({ position: z.enum([
5553
5643
  "bottom",
5554
5644
  "top",
@@ -5577,12 +5667,6 @@ const headerLogoTextSchema = z.object({
5577
5667
  ]).optional(),
5578
5668
  hideOnShrink: z.boolean().optional()
5579
5669
  }).optional();
5580
- const navEffectsSchema = z.object({
5581
- underlineGradient: z.boolean().default(false),
5582
- glow: z.boolean().default(false),
5583
- glowColor: z.string().optional(),
5584
- neumorphic: z.boolean().default(false)
5585
- }).optional();
5586
5670
  const dropdownStyleSchema = z.object({
5587
5671
  background: themeColorTokenSchema.default("surface"),
5588
5672
  textColor: themeColorTokenSchema.default("text"),
@@ -5673,10 +5757,8 @@ const headerSchema = z.object({
5673
5757
  variant: headerVariantWithLegacyAliasSchema,
5674
5758
  positioning: headerPositioning,
5675
5759
  shrinkOnScroll: z.boolean(),
5676
- maxWidth: headerMaxWidth,
5677
- barFrame: headerBarFrame.optional(),
5760
+ maxWidth: headerMaxWidth.default("container"),
5678
5761
  scrollTreatment: headerScrollTreatmentSchema,
5679
- logoOverride: mediaSchema.nullable().optional(),
5680
5762
  background: headerBackgroundSchema.default({
5681
5763
  type: "color",
5682
5764
  color: "surface"
@@ -5688,13 +5770,11 @@ const headerSchema = z.object({
5688
5770
  navFontRole: z.enum(["body", "heading"]).optional(),
5689
5771
  navTextSize: navLinkStyleSchema.shape.textSize,
5690
5772
  interactionPalette: headerInteractionPaletteSchema.optional(),
5691
- navPlacement: z.enum(["end", "center"]).optional(),
5692
5773
  container: headerContainerSchema,
5693
5774
  border: headerBorderSchema,
5694
5775
  shadow: componentShadowSchema.optional(),
5695
5776
  logo: logoStyleSchema,
5696
5777
  logoText: headerLogoTextSchema,
5697
- navEffects: navEffectsSchema,
5698
5778
  navContainer: navContainerSchema,
5699
5779
  navLinkStyle: navLinkStyleSchema.optional(),
5700
5780
  ctaGap: headerCtaGapSchema.optional(),
@@ -5876,11 +5956,6 @@ const bodyTextSchema = z.object({ intro: z.object({
5876
5956
  paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
5877
5957
  headingSize: bodyTextIntroTypographySizeSchema.optional()
5878
5958
  }).optional() }).optional();
5879
- const gradientsSchema = z.object({
5880
- button: z.string().min(1).nullable().optional(),
5881
- hero: z.string().min(1).nullable().optional(),
5882
- background: z.string().min(1).nullable().optional()
5883
- });
5884
5959
  /**
5885
5960
  * Testimonial quote-mark treatment, owned by the site style (analogous to the
5886
5961
  * accordion icon). `style` is the coarse selector; the optional fields fine-tune
@@ -6613,193 +6688,7 @@ function normalizeEventStaffMemberId(value) {
6613
6688
  return trimmed ? trimmed : null;
6614
6689
  }
6615
6690
  //#endregion
6616
- //#region ../calendar/src/timezone.ts
6617
- /**
6618
- * Timezone boundary helpers — internal.
6619
- *
6620
- * Both functions read an ISO instant in a given IANA timezone and project it
6621
- * onto the wall clock of that zone. They use Intl.DateTimeFormat with
6622
- * hourCycle: 'h23' to avoid 12/24-hour ambiguity. Used internally by the
6623
- * timetable model and `groupItemsByDate`; not exported from the package's
6624
- * public surface.
6625
- */
6626
- const MINUTES_PER_HOUR = 60;
6627
- /**
6628
- * Local minute-of-day [0, 24*60) for an ISO instant in the given zone.
6629
- * Returns NaN if the inputs cannot be parsed (caller should drop the item).
6630
- */
6631
- function getLocalMinuteOfDay(isoTimestamp, timeZone) {
6632
- const date = new Date(isoTimestamp);
6633
- if (Number.isNaN(date.getTime())) return NaN;
6634
- if (!isValidTimeZone(timeZone)) return NaN;
6635
- const parts = new Intl.DateTimeFormat("en-GB", {
6636
- timeZone,
6637
- hour: "2-digit",
6638
- minute: "2-digit",
6639
- hourCycle: "h23"
6640
- }).formatToParts(date);
6641
- const hourPart = Number(parts.find((part) => part.type === "hour")?.value);
6642
- const minute = Number(parts.find((part) => part.type === "minute")?.value);
6643
- const hour = hourPart === 24 ? 0 : hourPart;
6644
- if (Number.isNaN(hour) || Number.isNaN(minute)) return NaN;
6645
- return hour * MINUTES_PER_HOUR + minute;
6646
- }
6647
- /**
6648
- * Local YYYY-MM-DD for an ISO instant in the given zone.
6649
- * Returns null if the inputs cannot be parsed.
6650
- */
6651
- function getLocalDateKey(isoTimestamp, timeZone) {
6652
- const date = new Date(isoTimestamp);
6653
- if (Number.isNaN(date.getTime())) return null;
6654
- if (!isValidTimeZone(timeZone)) return null;
6655
- const parts = new Intl.DateTimeFormat("en-CA", {
6656
- timeZone,
6657
- year: "numeric",
6658
- month: "2-digit",
6659
- day: "2-digit"
6660
- }).formatToParts(date);
6661
- const year = parts.find((part) => part.type === "year")?.value;
6662
- const month = parts.find((part) => part.type === "month")?.value;
6663
- const day = parts.find((part) => part.type === "day")?.value;
6664
- if (year === void 0 || year.length === 0) return null;
6665
- if (month === void 0 || month.length === 0) return null;
6666
- if (day === void 0 || day.length === 0) return null;
6667
- return `${year}-${month}-${day}`;
6668
- }
6669
- const validTimeZoneCache = /* @__PURE__ */ new Map();
6670
- function isValidTimeZone(timeZone) {
6671
- if (typeof timeZone !== "string" || timeZone.length === 0) return false;
6672
- const cached = validTimeZoneCache.get(timeZone);
6673
- if (cached !== void 0) return cached;
6674
- try {
6675
- new Intl.DateTimeFormat("en-US", { timeZone });
6676
- validTimeZoneCache.set(timeZone, true);
6677
- return true;
6678
- } catch {
6679
- validTimeZoneCache.set(timeZone, false);
6680
- return false;
6681
- }
6682
- }
6683
- //#endregion
6684
- //#region ../calendar/src/group.ts
6685
- /**
6686
- * Group items by their local date (YYYY-MM-DD in the item's own timezone).
6687
- *
6688
- * Generic over `T`. Items with un-parseable startsAt or invalid timezone are
6689
- * silently dropped — call sites that need diagnostics should validate upstream.
6690
- *
6691
- * The output map preserves insertion order of dates (ES2015 spec) and the
6692
- * relative order of items within each date matches input order.
6693
- */
6694
- function groupItemsByDate(items, accessors) {
6695
- const grouped = /* @__PURE__ */ new Map();
6696
- for (const item of items) {
6697
- const dateKey = getLocalDateKey(accessors.getStartsAt(item), accessors.getTimeZone(item));
6698
- if (dateKey == null) continue;
6699
- const existing = grouped.get(dateKey);
6700
- if (existing !== void 0) existing.push(item);
6701
- else grouped.set(dateKey, [item]);
6702
- }
6703
- return grouped;
6704
- }
6705
- /**
6706
- * Canonical responsive ladder for a user-configured column count: 1 column at
6707
- * base, 2 from `sm`, 3 from `lg` (when the count allows), and the full count
6708
- * from `xl`. This matches the events blocks' COLUMN_CLASSES ladder — the two
6709
- * are derived from this single function.
6710
- */
6711
- function gridUserColumnLadder(count) {
6712
- const capped = Number.isFinite(count) ? Math.max(1, Math.min(4, Math.floor(count))) : 3;
6713
- const ladder = { base: 1 };
6714
- if (capped >= 2) ladder.sm = 2;
6715
- if (capped >= 3) ladder.lg = 3;
6716
- if (capped >= 4) ladder.xl = capped;
6717
- return ladder;
6718
- }
6719
- /** The ladder as a space-separated `rb-*` utility class string. */
6720
- function gridUserColumnClasses(count) {
6721
- const ladder = gridUserColumnLadder(count);
6722
- return GRID_BREAKPOINTS.flatMap((bp) => {
6723
- const value = ladder[bp];
6724
- return typeof value === "number" ? [COL_COUNT_CLASS[bp](value)] : [];
6725
- }).join(" ");
6726
- }
6727
- /** Utility class for a numeric column COUNT at a given breakpoint. */
6728
- const COL_COUNT_CLASS = {
6729
- base: (n) => `rb-grid-cols-${n}`,
6730
- sm: (n) => `rb-sm-grid-cols-${n}`,
6731
- md: (n) => `rb-md-grid-cols-${n}`,
6732
- lg: (n) => `rb-lg-grid-cols-${n}`,
6733
- xl: (n) => `rb-xl-grid-cols-${n}`
6734
- };
6735
- const GRID_BREAKPOINTS = [
6736
- "base",
6737
- "sm",
6738
- "md",
6739
- "lg",
6740
- "xl"
6741
- ];
6742
- //#endregion
6743
- //#region ../blocks/src/system/runtime/nodes/events/shared/eventCapacity.ts
6744
- /**
6745
- * Compute remaining spots for an event from `availableSpots` with a fallback
6746
- * to `capacity - registeredCount`. Returns `{ available: null, isFull: false }`
6747
- * for uncapped events so call sites can render no capacity indicator at all.
6748
- *
6749
- * The single source of truth for capacity math across event cards, agenda
6750
- * rows, and timetable cells — keep wording variations in the call sites.
6751
- */
6752
- function getEventAvailability(event) {
6753
- if (event.capacity == null) return {
6754
- available: null,
6755
- isFull: false
6756
- };
6757
- const available = event.availableSpots ?? event.capacity - event.registeredCount;
6758
- if (available <= 0) return {
6759
- available: 0,
6760
- isFull: true
6761
- };
6762
- return {
6763
- available,
6764
- isFull: false
6765
- };
6766
- }
6767
- /**
6768
- * Derive the three-state availability status (available / filling / waitlist)
6769
- * plus a short pill label. Builds on {@link getEventAvailability} so the
6770
- * capacity math (availableSpots with a `capacity - registeredCount` fallback)
6771
- * stays consistent with the binary helper used elsewhere.
6772
- */
6773
- function getEventAvailabilityStatus(event, options) {
6774
- const threshold = options?.fillingThreshold ?? 2;
6775
- const { available, isFull } = getEventAvailability(event);
6776
- if (available == null) return {
6777
- status: "available",
6778
- available: null,
6779
- isFull: false,
6780
- label: null
6781
- };
6782
- if (isFull) return {
6783
- status: "waitlist",
6784
- available: 0,
6785
- isFull: true,
6786
- label: event.waitlistEnabled ? "Waitlist" : "Full"
6787
- };
6788
- if (available <= threshold) return {
6789
- status: "filling",
6790
- available,
6791
- isFull: false,
6792
- label: `${available} left`
6793
- };
6794
- return {
6795
- status: "available",
6796
- available,
6797
- isFull: false,
6798
- label: `${available} places`
6799
- };
6800
- }
6801
- //#endregion
6802
- //#region ../blocks/src/system/runtime/nodes/events/shared/destination.ts
6691
+ //#region ../blocks/src/system/runtime/nodes/events/domain/destination.ts
6803
6692
  function isRecord$2(value) {
6804
6693
  return typeof value === "object" && value !== null && !Array.isArray(value);
6805
6694
  }
@@ -6817,7 +6706,7 @@ function resolveRuntimeEventDestination(event) {
6817
6706
  };
6818
6707
  }
6819
6708
  //#endregion
6820
- //#region ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
6709
+ //#region ../blocks/src/system/runtime/nodes/events/domain/pricing.ts
6821
6710
  const DEFAULT_EVENT_CURRENCY = "USD";
6822
6711
  function resolveRuntimeEventPricing(event) {
6823
6712
  if (isPublicEventPricing(event.pricing)) return event.pricing;
@@ -6984,10 +6873,66 @@ function readCoursePriceDisplay(value, currency) {
6984
6873
  function readAmount(value) {
6985
6874
  return typeof value === "number" && Number.isFinite(value) ? value : null;
6986
6875
  }
6876
+ /**
6877
+ * Format event price for display.
6878
+ * @returns "Free", "£25", "£12 - £25", "Free / £12 - £25", "Course (From £40)", etc.
6879
+ *
6880
+ * Events render compact amounts (no trailing ".00"); the actual ADT→string
6881
+ * mapping lives in `formatBookingPriceDisplay` in @riverbankcms/core so all
6882
+ * surfaces stay consistent.
6883
+ */
6884
+ function formatEventPrice(event) {
6885
+ return formatEventPricing(resolveRuntimeEventPricing(event), { decimals: "auto" });
6886
+ }
6887
+ /**
6888
+ * Format an event's price directly from its pricing value (without the full
6889
+ * event). Same output as {@link formatEventPrice}.
6890
+ */
6891
+ function formatEventPriceDisplay(pricing) {
6892
+ return formatEventPricing(pricing, { decimals: "auto" });
6893
+ }
6894
+ /**
6895
+ * Canonical responsive ladder for a user-configured column count: 1 column at
6896
+ * base, 2 from `sm`, 3 from `lg` (when the count allows), and the full count
6897
+ * from `xl`. This matches the events blocks' COLUMN_CLASSES ladder — the two
6898
+ * are derived from this single function.
6899
+ */
6900
+ function gridUserColumnLadder(count) {
6901
+ const capped = Number.isFinite(count) ? Math.max(1, Math.min(4, Math.floor(count))) : 3;
6902
+ const ladder = { base: 1 };
6903
+ if (capped >= 2) ladder.sm = 2;
6904
+ if (capped >= 3) ladder.lg = 3;
6905
+ if (capped >= 4) ladder.xl = capped;
6906
+ return ladder;
6907
+ }
6908
+ /** The ladder as a space-separated `rb-*` utility class string. */
6909
+ function gridUserColumnClasses(count) {
6910
+ const ladder = gridUserColumnLadder(count);
6911
+ return GRID_BREAKPOINTS.flatMap((bp) => {
6912
+ const value = ladder[bp];
6913
+ return typeof value === "number" ? [COL_COUNT_CLASS[bp](value)] : [];
6914
+ }).join(" ");
6915
+ }
6916
+ /** Utility class for a numeric column COUNT at a given breakpoint. */
6917
+ const COL_COUNT_CLASS = {
6918
+ base: (n) => `rb-grid-cols-${n}`,
6919
+ sm: (n) => `rb-sm-grid-cols-${n}`,
6920
+ md: (n) => `rb-md-grid-cols-${n}`,
6921
+ lg: (n) => `rb-lg-grid-cols-${n}`,
6922
+ xl: (n) => `rb-xl-grid-cols-${n}`
6923
+ };
6924
+ const GRID_BREAKPOINTS = [
6925
+ "base",
6926
+ "sm",
6927
+ "md",
6928
+ "lg",
6929
+ "xl"
6930
+ ];
6987
6931
  //#endregion
6988
- //#region ../blocks/src/system/runtime/nodes/events/shared/utils.ts
6932
+ //#region ../blocks/src/system/runtime/nodes/events/shared/listingLayout.ts
6989
6933
  /**
6990
- * Shared utility functions for event blocks
6934
+ * Layout classes for event listing containers and cards. Presentation
6935
+ * plumbing rather than event domain logic, so it lives beside the components.
6991
6936
  */
6992
6937
  /**
6993
6938
  * Column class mappings for grid layouts, derived from the canonical
@@ -7022,187 +6967,6 @@ function getCardOrientation(layout) {
7022
6967
  case "compact": return "vertical";
7023
6968
  }
7024
6969
  }
7025
- /**
7026
- * Format date for display.
7027
- * Returns: "Monday, January 15, 2025"
7028
- */
7029
- function formatDate(isoTimestamp, options) {
7030
- return formatBookingDate(toScheduledBookingDateTime({
7031
- startsAt: isoTimestamp,
7032
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7033
- locale: options?.locale ?? "en-US"
7034
- }), {
7035
- weekday: "long",
7036
- year: "numeric",
7037
- month: "long",
7038
- day: "numeric"
7039
- });
7040
- }
7041
- /**
7042
- * Format time for display.
7043
- * Returns: "10:00 AM"
7044
- */
7045
- function formatTime(isoTimestamp, options) {
7046
- return formatBookingTime(toScheduledBookingDateTime({
7047
- startsAt: isoTimestamp,
7048
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7049
- locale: options?.locale ?? "en-US"
7050
- }), {
7051
- hour: "numeric",
7052
- minute: "2-digit",
7053
- hour12: true
7054
- });
7055
- }
7056
- function formatEventDateParts(args) {
7057
- const scheduledRange = toScheduledBookingDateRange({
7058
- startsAt: args.startsAt,
7059
- endsAt: args.endsAt,
7060
- timeZone: toBookingTimeZone(args.timeZone ?? "UTC"),
7061
- locale: args.locale ?? "en-US"
7062
- });
7063
- const { date, time: start } = formatBookingDateParts(scheduledRange, { dateFormat: {
7064
- weekday: "long",
7065
- year: "numeric",
7066
- month: "long",
7067
- day: "numeric"
7068
- } });
7069
- return {
7070
- date,
7071
- timeRange: `${start} – ${formatBookingTime(toScheduledBookingDateTime({
7072
- startsAt: args.endsAt,
7073
- timeZone: scheduledRange.timeZone,
7074
- locale: args.locale ?? "en-US"
7075
- }))}`
7076
- };
7077
- }
7078
- function formatEventCompactDateRail(isoTimestamp, options) {
7079
- const dateTime = toScheduledBookingDateTime({
7080
- startsAt: isoTimestamp,
7081
- timeZone: toBookingTimeZone(options?.timeZone ?? "UTC"),
7082
- locale: options?.locale ?? "en-US"
7083
- });
7084
- const weekday = formatBookingDate(dateTime, { weekday: "short" }).toLocaleUpperCase(options?.locale ?? "en-US");
7085
- const month = formatBookingDate(dateTime, { month: "short" }).toLocaleUpperCase(options?.locale ?? "en-US");
7086
- return {
7087
- weekday,
7088
- day: formatBookingDate(dateTime, { day: "numeric" }),
7089
- month,
7090
- label: formatBookingDate(dateTime, {
7091
- weekday: "long",
7092
- year: "numeric",
7093
- month: "long",
7094
- day: "numeric"
7095
- })
7096
- };
7097
- }
7098
- function formatTeamMemberNames(names) {
7099
- const normalized = names.map((name) => name.trim()).filter(Boolean);
7100
- if (normalized.length === 0) return null;
7101
- const [firstName, secondName] = normalized;
7102
- if (!firstName) return null;
7103
- if (normalized.length === 1) return firstName;
7104
- if (normalized.length === 2 && secondName) return `${firstName} and ${secondName}`;
7105
- const finalName = normalized[normalized.length - 1] ?? "";
7106
- return `${normalized.slice(0, -1).join(", ")}, and ${finalName}`;
7107
- }
7108
- function formatEventTeacherLine(event) {
7109
- const names = formatTeamMemberNames((event.teamMembers ?? []).map((teamMember) => teamMember.name));
7110
- return names ? `with ${names}` : null;
7111
- }
7112
- /**
7113
- * Group events by their local date (YYYY-MM-DD key in each event's own
7114
- * timezone). Thin domain wrapper over `groupItemsByDate` from the calendar
7115
- * package; preserves the historical `Map<string, PublicEvent[]>` shape.
7116
- */
7117
- function groupEventsByDate(events) {
7118
- const grouped = groupItemsByDate(events, {
7119
- getStartsAt: (event) => event.startsAt,
7120
- getTimeZone: (event) => event.timeZone ?? "UTC"
7121
- });
7122
- const map = /* @__PURE__ */ new Map();
7123
- for (const [dateKey, bucket] of grouped) map.set(dateKey, [...bucket]);
7124
- return map;
7125
- }
7126
- /**
7127
- * Format event price for display.
7128
- * @returns "Free", "£25", "£12 - £25", "Free / £12 - £25", "Course (From £40)", etc.
7129
- *
7130
- * Events render compact amounts (no trailing ".00"); the actual ADT→string
7131
- * mapping lives in `formatBookingPriceDisplay` in @riverbankcms/core so all
7132
- * surfaces stay consistent.
7133
- */
7134
- function formatEventPrice(event) {
7135
- return formatEventPricing(resolveRuntimeEventPricing(event), { decimals: "auto" });
7136
- }
7137
- /**
7138
- * Format an event's price directly from its pricing value (without the full
7139
- * event). Same output as {@link formatEventPrice}.
7140
- */
7141
- function formatEventPriceDisplay(pricing) {
7142
- return formatEventPricing(pricing, { decimals: "auto" });
7143
- }
7144
- function closedEventBookingLabel(reason) {
7145
- switch (reason) {
7146
- case "occurrence_cancelled":
7147
- case "series_cancelled": return "Cancelled";
7148
- case "occurrence_completed":
7149
- case "series_completed": return "Completed";
7150
- case "series_draft":
7151
- case "series_archived":
7152
- case "parent_archived":
7153
- case "parent_inconsistent":
7154
- case "not_public":
7155
- case "already_started":
7156
- case "enrollment_closed": return "Registration closed";
7157
- default: return assertNever$2(reason);
7158
- }
7159
- }
7160
- function resolveEventHref(event) {
7161
- const destination = resolveRuntimeEventDestination(event);
7162
- return destination.kind === "external" ? destination.url : destination.path;
7163
- }
7164
- function resolveEventLinkProps(event) {
7165
- const href = resolveEventHref(event);
7166
- return resolveRuntimeEventDestination(event).kind === "external" ? {
7167
- href,
7168
- target: "_blank",
7169
- rel: "noopener noreferrer"
7170
- } : { href };
7171
- }
7172
- /**
7173
- * Resolve the CTA state for an event.
7174
- * Shared by EventCard and EventCompactRow to keep sold-out, registration-closed,
7175
- * and course-specific defaults consistent.
7176
- */
7177
- function resolveEventCta(event, buttonText) {
7178
- const { isFull: isSoldOut } = getEventAvailability(event);
7179
- const destination = resolveRuntimeEventDestination(event);
7180
- const isExternal = destination.kind === "external";
7181
- const defaultButtonText = isExternal ? destination.label : event.bookingScope === "whole_series" ? "Book course" : "Book";
7182
- const effectiveButtonText = buttonText ?? defaultButtonText;
7183
- const href = resolveEventHref(event);
7184
- if (event.bookingAvailability.kind === "closed") {
7185
- const linksToClosedWholeSeriesDetails = event.bookingAvailability.reason === "enrollment_closed" && !isExternal;
7186
- return {
7187
- label: closedEventBookingLabel(event.bookingAvailability.reason),
7188
- href,
7189
- external: isExternal,
7190
- hidden: !linksToClosedWholeSeriesDetails
7191
- };
7192
- }
7193
- if (!isExternal && isSoldOut) return {
7194
- label: "Sold out",
7195
- href,
7196
- external: false,
7197
- hidden: true
7198
- };
7199
- return {
7200
- label: effectiveButtonText,
7201
- href,
7202
- external: isExternal,
7203
- hidden: false
7204
- };
7205
- }
7206
6970
  //#endregion
7207
6971
  //#region ../blocks/src/system/constants/events.ts
7208
6972
  const eventCalendarDisplayModeValues = [
@@ -7392,7 +7156,8 @@ const eventCollectionSourceSchemaShape = {
7392
7156
  buttonVariant: eventButtonVariantSchema.optional(),
7393
7157
  buttonText: z.string().optional(),
7394
7158
  emptyMessage: z.string().optional(),
7395
- className: z.string().optional()
7159
+ className: z.string().optional(),
7160
+ bookingTimeZone: z.string().optional()
7396
7161
  };
7397
7162
  const eventCollectionDisplaySchemaShape = {
7398
7163
  events: z.array(publicEventSchema),
@@ -7427,7 +7192,15 @@ const eventCollectionHydrationSchema = z.object({
7427
7192
  prefetchedEvents: z.boolean(),
7428
7193
  eventSurface: eventSurfaceScopeSchema$1,
7429
7194
  filterEventPresets: z.string().nullable(),
7430
- scheduleScope: eventScheduleScopeSchema$1
7195
+ scheduleScope: eventScheduleScopeSchema$1,
7196
+ calendarContext: z.object({
7197
+ timeZone: z.string().transform(toBookingTimeZone),
7198
+ initialToday: z.object({
7199
+ year: z.number().int(),
7200
+ month: z.number().int().min(1).max(12),
7201
+ day: z.number().int().min(1).max(31)
7202
+ })
7203
+ })
7431
7204
  });
7432
7205
  function buildEventCollectionDisplay(source) {
7433
7206
  return {
@@ -7472,14 +7245,19 @@ function buildEventCollectionPresentationDisplay(source) {
7472
7245
  emptyMessage: source.emptyMessage ?? "No upcoming events scheduled."
7473
7246
  };
7474
7247
  }
7475
- function buildEventCollectionHydration(source) {
7248
+ function buildEventCollectionHydration(source, now = /* @__PURE__ */ new Date()) {
7249
+ const timeZone = toBookingTimeZone(source.bookingTimeZone);
7476
7250
  return {
7477
7251
  siteId: source.siteId,
7478
7252
  stage: source.stage,
7479
7253
  prefetchedEvents: source.events != null,
7480
7254
  eventSurface: normalizeEventSurfaceScope(source.eventSurface),
7481
7255
  filterEventPresets: normalizeEventSurfacePresetsCsv(source.filterEventPresets),
7482
- scheduleScope: normalizeEventScheduleScope(source.scheduleScope)
7256
+ scheduleScope: normalizeEventScheduleScope(source.scheduleScope),
7257
+ calendarContext: {
7258
+ timeZone,
7259
+ initialToday: getLocalScheduleDate(now, timeZone)
7260
+ }
7483
7261
  };
7484
7262
  }
7485
7263
  function buildEventCollectionListingDisplay(display, prefetchedEvents, className) {
@@ -12413,4 +12191,4 @@ function defineIslandPropsDecoder(kind) {
12413
12191
  };
12414
12192
  }
12415
12193
  //#endregion
12416
- export { getLocalMinuteOfDay as $, formatCurrencyAmount as $t, isSemanticSpacing as A, assertNever$1 as At, formatEventPrice as B, formatBookingInstant as Bt, getStepNumber as C, toSelectedCollectionOptionKind as Ct, staticHeaderChromeRuntime as D, isBookingRequirementConsentFieldId as Dt, buildEventCalendarListingRender as E, BOOKING_FORM_DATA_RESERVED_KEYS as Et, normalizeCsvString as F, reportWorkflowError as Ft, getContainerClass as G, resolveBookingTimeZoneId as Gt, formatEventTeacherLine as H, formatLocalScheduleDate as Ht, normalizeOptionalId as I, reportWorkflowSuccess as It, resolveEventLinkProps as J, publicEventPricingCurrency as Jt, groupEventsByDate as K, toBookingTimeZone as Kt, formatDate as L, addLocalScheduleDays as Lt, buildEventRuntimeFilters as M, err as Mt, getEventRuntimeFiltersKey as N, ok as Nt, resolveEventRegistrationScopedEvents as O, RichTextDocSchema as Ot, normalizeCsvIds as P, okWith as Pt, getLocalDateKey as Q, publicEventTicketTypes as Qt, formatEventCompactDateRail as R, formatBookingDate as Rt, WIZARD_STEPS as S, resolveDefaultBookingServiceId as St, planEventRegistrationSurface as T, resolveSelectedAppointmentConsent as Tt, formatTime as U, getBookingLocalDateKey as Ut, formatEventPriceDisplay as V, formatBookingTime as Vt, getCardOrientation as W, parseBookingLocalDate as Wt, getEventAvailability as X, publicEventPricingType as Xt, resolveRuntimeEventPricing as Y, publicEventPricingMode as Yt, getEventAvailabilityStatus as Z, publicEventScalarPrice as Zt, findEventByOccurrenceId as _, parseBookingCurrencyInputToCents as _t, buildProductDetailDisplay as a, resolveOnColorForThemeToken as at, resolveEventRequiresPayment as b, resolveBookingPaymentStep as bt, EMPTY_COMMERCE_CART_STATE as c, normalizeCardRole as ct, deriveCommerceCheckoutState as d, formatBookingCentsAsCurrencyInput as dt, assertNever$2 as en, formatWholeSeriesPriceAmount as et, parseStoredCommerceCartState as f, formatBookingSubmissionPreparationError as ft, toWholeSeriesRegistrationEvents as g, isBookingServiceSlidingAmountInRange as gt, toRegistrationEvents as h, getBookingSuccessMessage as ht, buildProductCardDisplay as i, isCssColorLiteral as it, resolveSpacing as j, clearWorkflowFeedback as jt, SEMANTIC_SPACING_VALUES as k, isRecord$5 as kt, buildPassCartItem as l, logger as lt, reduceCommerceCart as m, getBookingServiceSlidingScale as mt, getShopDisplayForItems as n, resolveWholeSeriesPricingState as nt, formatCommercePrice as o, resolveTokenOrCssColorValue as ot, productItemKey as p, getBookingRedirectStateFromLocation as pt, resolveEventCta as q, toScheduledBookingDateTime as qt, buildCartDisplay as r, boxBackgroundSchema as rt, formatInterval as s, themeButtonClassName as st, defineIslandPropsDecoder as t, formatWholeSeriesPriceDisplay as tt, buildProductCartItem as u, deriveBookingFormViewState as ut, findPreSelectedEvent as v, prepareBookingSubmission as vt, getWizardStepsForContext as w, projectSelectedAppointmentConsentIntoForm as wt, resolveRegistrationTarget as x, resolveBookingSiteId as xt, resolveDiscountContext as y, resolveAppointmentCheckoutVerificationState as yt, formatEventDateParts as z, formatBookingDateTime as zt };
12194
+ export { deriveBookingFormViewState as $, isSemanticSpacing as A, formatBookingDateTime as At, formatEventPriceDisplay as B, toScheduledBookingDateTime as Bt, getStepNumber as C, okWith as Ct, staticHeaderChromeRuntime as D, buildBookingUtcDateWindow as Dt, buildEventCalendarListingRender as E, addLocalScheduleDays as Et, normalizeCsvString as F, getLocalScheduleDate as Ft, resolveWholeSeriesPricingState as G, publicEventTicketTypes as Gt, resolveRuntimeEventDestination as H, publicEventPricingMode as Ht, normalizeOptionalId as I, parseBookingLocalDate as It, resolveOnColorForThemeToken as J, boxBackgroundSchema as K, formatCurrencyAmount as Kt, getCardOrientation as L, resolveBookingTimeZoneId as Lt, buildEventRuntimeFilters as M, formatBookingTime as Mt, getEventRuntimeFiltersKey as N, formatLocalScheduleDate as Nt, resolveEventRegistrationScopedEvents as O, formatBookingDate as Ot, normalizeCsvIds as P, getBookingLocalDateKey as Pt, logger as Q, getContainerClass as R, toBookingTimeZone as Rt, WIZARD_STEPS as S, ok as St, planEventRegistrationSurface as T, reportWorkflowSuccess as Tt, formatWholeSeriesPriceAmount as U, publicEventPricingType as Ut, resolveRuntimeEventPricing as V, publicEventPricingCurrency as Vt, formatWholeSeriesPriceDisplay as W, publicEventScalarPrice as Wt, themeButtonClassName as X, resolveTokenOrCssColorValue as Y, normalizeCardRole as Z, findEventByOccurrenceId as _, RichTextDocSchema as _t, buildProductDetailDisplay as a, isBookingServiceSlidingAmountInRange as at, resolveEventRequiresPayment as b, clearWorkflowFeedback as bt, EMPTY_COMMERCE_CART_STATE as c, resolveAppointmentCheckoutVerificationState as ct, deriveCommerceCheckoutState as d, resolveDefaultBookingServiceId as dt, formatBookingCentsAsCurrencyInput as et, parseStoredCommerceCartState as f, toSelectedCollectionOptionKind as ft, toWholeSeriesRegistrationEvents as g, isBookingRequirementConsentFieldId as gt, toRegistrationEvents as h, BOOKING_FORM_DATA_RESERVED_KEYS as ht, buildProductCardDisplay as i, getBookingSuccessMessage as it, resolveSpacing as j, formatBookingInstant as jt, SEMANTIC_SPACING_VALUES as k, formatBookingDateParts as kt, buildPassCartItem as l, resolveBookingPaymentStep as lt, reduceCommerceCart as m, resolveSelectedAppointmentConsent as mt, getShopDisplayForItems as n, getBookingRedirectStateFromLocation as nt, formatCommercePrice as o, parseBookingCurrencyInputToCents as ot, productItemKey as p, projectSelectedAppointmentConsentIntoForm as pt, isCssColorLiteral as q, assertNever$2 as qt, buildCartDisplay as r, getBookingServiceSlidingScale as rt, formatInterval as s, prepareBookingSubmission as st, defineIslandPropsDecoder as t, formatBookingSubmissionPreparationError as tt, buildProductCartItem as u, resolveBookingSiteId as ut, findPreSelectedEvent as v, isRecord$5 as vt, getWizardStepsForContext as w, reportWorkflowError as wt, resolveRegistrationTarget as x, err as xt, resolveDiscountContext as y, assertNever$1 as yt, formatEventPrice as z, toScheduledBookingDateRange as zt };