@wix/himalaya-cli 0.1582.0 → 0.1584.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 (336) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +8 -0
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/schema.d.ts +26 -2
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/validator.d.ts +18 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +2 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +5 -1
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +5 -1
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
  16. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
  17. package/dist/cli.mjs +1098 -702
  18. package/dist/preview-assets.generated.bin +0 -0
  19. package/fixtures/wix-canned.json +25 -0
  20. package/guides/component-reference.json +4 -3
  21. package/guides/component-reference.md +24 -2
  22. package/guides/one-app-to-himi-app.md +52 -1
  23. package/guides/sdk-reference.json +37 -9
  24. package/guides/wix-api-usage.md +9 -0
  25. package/package.json +2 -2
  26. package/templates/beauty-salon/config.ts +27 -1
  27. package/templates/beauty-salon/dev/index.ts +2285 -757
  28. package/templates/beauty-salon/himalaya.content.json +3 -0
  29. package/templates/beauty-salon/tier5-src/_shared/booking-location.ts +147 -0
  30. package/templates/beauty-salon/tier5-src/_shared/booking-session.ts +222 -0
  31. package/templates/beauty-salon/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  32. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +19 -3
  33. package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +2 -1
  34. package/templates/beauty-salon/tier5-src/_shared/owner-editor-value.ts +164 -0
  35. package/templates/beauty-salon/tier5-src/_shared/store-product-info.ts +50 -0
  36. package/templates/beauty-salon/tier5-src/himi-blog-post/index.ts +21 -8
  37. package/templates/beauty-salon/tier5-src/himi-booking-service/index.ts +277 -14
  38. package/templates/beauty-salon/tier5-src/himi-booking-services/index.ts +351 -92
  39. package/templates/beauty-salon/tier5-src/himi-booking-time/index.ts +25 -5
  40. package/templates/beauty-salon/tier5-src/himi-branded-lite-agenda/index.ts +219 -220
  41. package/templates/beauty-salon/tier5-src/himi-branded-lite-category/index.ts +3 -2
  42. package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  43. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  44. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
  45. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  46. package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  47. package/templates/beauty-salon-owner/dev/index.ts +129 -66
  48. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
  49. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  50. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  51. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  52. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  53. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  54. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  55. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  56. package/templates/branded-lite/config.ts +29 -1
  57. package/templates/branded-lite/dev/index.ts +8371 -5565
  58. package/templates/branded-lite/himalaya.content.json +3 -0
  59. package/templates/branded-lite/l10n/en.json +2 -2
  60. package/templates/branded-lite/tier5-src/_shared/booking-location.ts +147 -0
  61. package/templates/branded-lite/tier5-src/_shared/booking-session.ts +222 -0
  62. package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  63. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
  64. package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
  65. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
  66. package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
  67. package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
  68. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
  69. package/templates/branded-lite/tier5-src/himi-booking-service/index.ts +277 -14
  70. package/templates/branded-lite/tier5-src/himi-booking-services/index.ts +351 -92
  71. package/templates/branded-lite/tier5-src/himi-booking-time/index.ts +25 -5
  72. package/templates/branded-lite/tier5-src/himi-branded-lite-agenda/index.ts +219 -220
  73. package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
  74. package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
  75. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  76. package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  77. package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
  78. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  79. package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  80. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  81. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  82. package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  83. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
  84. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
  85. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
  86. package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
  87. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
  88. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
  89. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
  90. package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
  91. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  92. package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  93. package/templates/community-club/config.ts +36 -1
  94. package/templates/community-club/dev/index.ts +1930 -401
  95. package/templates/community-club/himalaya.content.json +6 -0
  96. package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  97. package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
  98. package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
  99. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
  100. package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
  101. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  102. package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  103. package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  104. package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
  105. package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
  106. package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  107. package/templates/community-club-owner/dev/index.ts +53 -1
  108. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
  109. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  110. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  111. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  112. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  113. package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  114. package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  115. package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  116. package/templates/events-meetups/config.ts +36 -1
  117. package/templates/events-meetups/dev/index.ts +1930 -401
  118. package/templates/events-meetups/himalaya.content.json +6 -0
  119. package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  120. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
  121. package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
  122. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
  123. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
  124. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  125. package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  126. package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  127. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
  128. package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
  129. package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  130. package/templates/events-meetups-owner/dev/index.ts +6 -0
  131. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
  132. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  133. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  134. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  135. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  136. package/templates/gym-fitness/config.ts +27 -1
  137. package/templates/gym-fitness/dev/index.ts +2803 -844
  138. package/templates/gym-fitness/himalaya.content.json +3 -0
  139. package/templates/gym-fitness/tier5-src/_shared/booking-location.ts +147 -0
  140. package/templates/gym-fitness/tier5-src/_shared/booking-session.ts +222 -0
  141. package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  142. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
  143. package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
  144. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
  145. package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
  146. package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
  147. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
  148. package/templates/gym-fitness/tier5-src/himi-booking-service/index.ts +277 -14
  149. package/templates/gym-fitness/tier5-src/himi-booking-services/index.ts +351 -92
  150. package/templates/gym-fitness/tier5-src/himi-booking-time/index.ts +25 -5
  151. package/templates/gym-fitness/tier5-src/himi-branded-lite-agenda/index.ts +219 -220
  152. package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
  153. package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  154. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  155. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
  156. package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
  157. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
  158. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  159. package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  160. package/templates/gym-fitness-owner/dev/index.ts +132 -66
  161. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
  162. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  163. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  164. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  165. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  166. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  167. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  168. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  169. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  170. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  171. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  172. package/templates/nonprofit-cause/config.ts +36 -1
  173. package/templates/nonprofit-cause/dev/index.ts +1925 -396
  174. package/templates/nonprofit-cause/himalaya.content.json +6 -0
  175. package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  176. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
  177. package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
  178. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
  179. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
  180. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  181. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  182. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  183. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  184. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
  185. package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
  186. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  187. package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  188. package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
  189. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
  190. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  191. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  192. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  193. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  194. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  195. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  196. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  197. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  198. package/templates/owner-lite/dev/index.ts +132 -66
  199. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
  200. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
  201. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  202. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
  203. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
  204. package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
  205. package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  206. package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
  207. package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
  208. package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  209. package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
  210. package/templates/podcast-creator/config.ts +27 -1
  211. package/templates/podcast-creator/dev/index.ts +1051 -158
  212. package/templates/podcast-creator/himalaya.content.json +3 -0
  213. package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  214. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
  215. package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
  216. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
  217. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
  218. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  219. package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  220. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  221. package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  222. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
  223. package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
  224. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  225. package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  226. package/templates/podcast-creator-owner/dev/index.ts +53 -1
  227. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
  228. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  229. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  230. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  231. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  232. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  233. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  234. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  235. package/templates/portfolio-creative/config.ts +27 -1
  236. package/templates/portfolio-creative/dev/index.ts +1305 -468
  237. package/templates/portfolio-creative/himalaya.content.json +3 -0
  238. package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  239. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
  240. package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
  241. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
  242. package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
  243. package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
  244. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
  245. package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
  246. package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  247. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  248. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  249. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
  250. package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
  251. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
  252. package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  253. package/templates/portfolio-creative-owner/dev/index.ts +129 -66
  254. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
  255. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  256. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  257. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  258. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  259. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  260. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  261. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  262. package/templates/restaurant/config.ts +27 -1
  263. package/templates/restaurant/dev/index.ts +1051 -158
  264. package/templates/restaurant/himalaya.content.json +3 -0
  265. package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  266. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
  267. package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
  268. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
  269. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
  270. package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  271. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
  272. package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
  273. package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  274. package/templates/restaurant-owner/dev/index.ts +132 -66
  275. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
  276. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  277. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  278. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  279. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  280. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  281. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  282. package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  283. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  284. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  285. package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  286. package/templates/retail-store/config.ts +27 -1
  287. package/templates/retail-store/dev/index.ts +597 -191
  288. package/templates/retail-store/himalaya.content.json +3 -0
  289. package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  290. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
  291. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
  292. package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
  293. package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
  294. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
  295. package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
  296. package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  297. package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
  298. package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  299. package/templates/retail-store-owner/dev/index.ts +50 -1
  300. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
  301. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  302. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  303. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  304. package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  305. package/templates/yoga-studio/config.ts +27 -1
  306. package/templates/yoga-studio/dev/index.ts +2996 -877
  307. package/templates/yoga-studio/himalaya.content.json +3 -0
  308. package/templates/yoga-studio/tier5-src/_shared/booking-location.ts +147 -0
  309. package/templates/yoga-studio/tier5-src/_shared/booking-session.ts +222 -0
  310. package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  311. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
  312. package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
  313. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
  314. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
  315. package/templates/yoga-studio/tier5-src/himi-booking-service/index.ts +277 -14
  316. package/templates/yoga-studio/tier5-src/himi-booking-services/index.ts +351 -92
  317. package/templates/yoga-studio/tier5-src/himi-booking-time/index.ts +25 -5
  318. package/templates/yoga-studio/tier5-src/himi-branded-lite-agenda/index.ts +219 -220
  319. package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  320. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  321. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  322. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
  323. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
  324. package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
  325. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  326. package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  327. package/templates/yoga-studio-owner/dev/index.ts +85 -65
  328. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
  329. package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  330. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  331. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  332. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  333. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  334. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  335. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  336. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
@@ -25,7 +25,7 @@ import { initials } from "../_shared/himi-text.js";
25
25
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
26
26
  import { formatMonthDay } from "../_shared/engine-intl.js";
27
27
  import { openRichLink } from "../_shared/rich-content-link.js";
28
- import { richBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
28
+ import { descriptionBody, ROW_IMAGE_WIDTH } from "../_shared/rich-body.js";
29
29
 
30
30
  interface FeedRow {
31
31
  id: string;
@@ -104,7 +104,7 @@ function feedText(item: WixFeedItem): string {
104
104
  if (joined) return joined;
105
105
  }
106
106
  const lean = item.entity?.body?.content;
107
- return typeof lean === "string" && lean.trim() ? lean.trim() : copyParam("feed.sharedUpdateFallback", "Shared an update.");
107
+ return descriptionBody(lean, copyParam("feed.sharedUpdateFallback", "Shared an update.")).bodyText;
108
108
  }
109
109
 
110
110
  function authorName(item: WixFeedItem, profiles: WixFeedProfile[]): string {
@@ -172,7 +172,8 @@ function feedRow(item: WixFeedItem, profiles: WixFeedProfile[]): FeedRow {
172
172
  const count = Array.isArray(item.reactionsList) ? item.reactionsList.length : 0;
173
173
  const avatar = authorAvatar(item, profiles);
174
174
  // A Ricos post's plain text is the whole document's (every node level), not feedText's one-level read.
175
- const { bodyJson, bodyText: text } = richBody(item.content, feedText(item), ROW_IMAGE_WIDTH);
175
+ const fallback = item.content == null ? copyParam("feed.sharedUpdateFallback", "Shared an update.") : feedText(item);
176
+ const { bodyJson, bodyText: text } = descriptionBody(item.content || item.entity?.body?.content, fallback, ROW_IMAGE_WIDTH);
176
177
  // A Ricos post draws its own images in RichContent, so the separate row image would repeat the first one.
177
178
  const image = bodyJson ? "" : contentImage(item);
178
179
  return {
@@ -0,0 +1,243 @@
1
+ import { defineService } from "@himalaya/state";
2
+ import { getGroup, listRules, type WixGroup, type WixGroupRule } from "@himalaya/wix-groups";
3
+ import { WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
4
+ import { copyParam, stashCopy } from "../_shared/himi-copy.js";
5
+ import { groupImage } from "../_shared/himi-media.js";
6
+ import { descriptionBody } from "../_shared/rich-body.js";
7
+ import { openRichLink } from "../_shared/rich-content-link.js";
8
+ import { formatDateMedium } from "../_shared/engine-intl.js";
9
+
10
+ // The About & rules page of ONE group. The group is `input.id`, never a fallback: a missing
11
+ // id is an error, not "the first group". The group read and the rules read are independent —
12
+ // the page renders the group's own facts when only the rules read failed, and says so in the
13
+ // rules region, so a rules outage is never shown as "this group has no rules".
14
+ //
15
+ // Read-only and online-only: no write exists on this page, so there is nothing to queue.
16
+
17
+ interface RuleRow {
18
+ id: string;
19
+ number: string;
20
+ title: string;
21
+ description: string;
22
+ }
23
+
24
+ type Failure = "forbidden" | "missing" | "failed";
25
+
26
+ interface State {
27
+ status: "loading" | "ready" | "error";
28
+ id: string;
29
+ name: string;
30
+ description: string;
31
+ descriptionJson: string;
32
+ hasDescriptionJson: boolean;
33
+ privacy: string;
34
+ createdDate: string;
35
+ memberCount: string;
36
+ image: string;
37
+ initial: string;
38
+ rules: RuleRow[];
39
+ /** The group read failed: the whole page says why. */
40
+ errorText: string;
41
+ /** The rules read alone failed: only the rules region says why. */
42
+ rulesErrorText: string;
43
+ hideLoading: boolean;
44
+ hideError: boolean;
45
+ hideContent: boolean;
46
+ hideImage: boolean;
47
+ hidePrivacy: boolean;
48
+ hideCreated: boolean;
49
+ hideMemberCount: boolean;
50
+ hideRules: boolean;
51
+ hideRulesEmpty: boolean;
52
+ hideRulesError: boolean;
53
+ }
54
+
55
+ const initial: State = {
56
+ status: "loading",
57
+ id: "",
58
+ name: "",
59
+ description: "",
60
+ descriptionJson: "",
61
+ hasDescriptionJson: false,
62
+ privacy: "",
63
+ createdDate: "",
64
+ memberCount: "",
65
+ image: "",
66
+ initial: "G",
67
+ rules: [],
68
+ errorText: "",
69
+ rulesErrorText: "",
70
+ hideLoading: false,
71
+ hideError: true,
72
+ hideContent: true,
73
+ hideImage: true,
74
+ hidePrivacy: true,
75
+ hideCreated: true,
76
+ hideMemberCount: true,
77
+ hideRules: true,
78
+ hideRulesEmpty: true,
79
+ hideRulesError: true,
80
+ };
81
+
82
+ /** Bumped by every group load, so a response that arrives after a newer load began is dropped. */
83
+ let loadSeq = 0;
84
+
85
+ function inputId(): string {
86
+ // `himalaya.input.id` is the contract: hosts thread the opener's `id` nav param
87
+ // (himi.open { screenId: "himi_group_about", id }) into worker input.
88
+ const input = (globalThis as { himalaya?: { input?: { id?: string } } }).himalaya?.input;
89
+ return typeof input?.id === "string" ? input.id : "";
90
+ }
91
+
92
+ function failureOf(e: unknown): Failure {
93
+ const message = String((e as Error)?.message ?? e);
94
+ if (/HTTP (401|403)\b/.test(message)) return "forbidden";
95
+ if (/HTTP 404\b/.test(message)) return "missing";
96
+ return "failed";
97
+ }
98
+
99
+ function groupFailureText(failure: Failure): string {
100
+ if (failure === "forbidden") return copyParam("groupAbout.forbiddenTitle", "You can’t view this group’s details.");
101
+ if (failure === "missing") return copyParam("groupAbout.missingTitle", "This group no longer exists.");
102
+ return copyParam("groupAbout.errorTitle", "This group’s details could not be loaded.");
103
+ }
104
+
105
+ function rulesFailureText(failure: Failure): string {
106
+ if (failure === "forbidden") return copyParam("groupAbout.rulesForbidden", "You can’t view this group’s rules.");
107
+ return copyParam("groupAbout.rulesError", "The rules could not be loaded.");
108
+ }
109
+
110
+ /** The rules the server sent, numbered from 1. `undefined` when the answer is not a rules list
111
+ * at all — a missing or non-array `rules` is a malformed answer, not an empty group. */
112
+ function ruleRowsOf(response: unknown): RuleRow[] | undefined {
113
+ const rules = (response as { rules?: unknown } | null | undefined)?.rules;
114
+ if (!Array.isArray(rules)) return undefined;
115
+ const rows: RuleRow[] = [];
116
+ for (const entry of rules as WixGroupRule[]) {
117
+ const title = typeof entry?.title === "string" ? entry.title.trim() : "";
118
+ const description = typeof entry?.description === "string" ? entry.description.trim() : "";
119
+ if (!title && !description) continue;
120
+ rows.push({ id: `rule_${rows.length + 1}`, number: String(rows.length + 1), title, description });
121
+ }
122
+ return rows;
123
+ }
124
+
125
+ function withRules(state: State, rules: RuleRow[] | undefined, failure: Failure | undefined): State {
126
+ if (failure || !rules) {
127
+ return {
128
+ ...state,
129
+ rules: [],
130
+ rulesErrorText: rulesFailureText(failure ?? "failed"),
131
+ hideRules: true,
132
+ hideRulesEmpty: true,
133
+ hideRulesError: false,
134
+ };
135
+ }
136
+ return {
137
+ ...state,
138
+ rules,
139
+ rulesErrorText: "",
140
+ hideRules: rules.length === 0,
141
+ hideRulesEmpty: rules.length > 0,
142
+ hideRulesError: true,
143
+ };
144
+ }
145
+
146
+ function capitalized(value: string): string {
147
+ const lower = value.trim().toLowerCase();
148
+ return lower ? lower[0].toUpperCase() + lower.slice(1) : "";
149
+ }
150
+
151
+ function createdLabel(value: unknown): string {
152
+ if (typeof value !== "string" || !value) return "";
153
+ const date = new Date(value);
154
+ return Number.isNaN(date.getTime()) ? "" : formatDateMedium(date);
155
+ }
156
+
157
+ function groupState(state: State, id: string, group: WixGroup): State {
158
+ const name = (group.name || (group as { title?: string }).title || copyParam("group.groupFallbackName", "Group")).trim();
159
+ const body = descriptionBody(group.description, descriptionBody(group.teaser, "").bodyText);
160
+ const privacy = capitalized(group.privacyStatus ?? "");
161
+ const created = createdLabel(group.createdDate);
162
+ const count = group.membersCount ?? group.memberCount;
163
+ const image = groupImage(group, WIX_DETAIL_IMAGE);
164
+ return {
165
+ ...state,
166
+ status: "ready",
167
+ id,
168
+ name,
169
+ description: body.bodyText,
170
+ descriptionJson: body.bodyJson,
171
+ hasDescriptionJson: body.bodyJson !== "",
172
+ privacy,
173
+ createdDate: created,
174
+ memberCount: typeof count === "number" ? `${count} ${count === 1 ? "member" : "members"}` : "",
175
+ image,
176
+ initial: (name[0] || "G").toUpperCase(),
177
+ errorText: "",
178
+ hideLoading: true,
179
+ hideError: true,
180
+ hideContent: false,
181
+ hideImage: !image,
182
+ hidePrivacy: !privacy,
183
+ hideCreated: !created,
184
+ hideMemberCount: typeof count !== "number",
185
+ };
186
+ }
187
+
188
+ function groupErrorState(id: string, failure: Failure): State {
189
+ return {
190
+ ...initial,
191
+ id,
192
+ status: "error",
193
+ errorText: groupFailureText(failure),
194
+ hideLoading: true,
195
+ hideError: false,
196
+ hideContent: true,
197
+ };
198
+ }
199
+
200
+ const svc = defineService<State, {
201
+ load: (state: State, arg?: unknown) => Promise<State | undefined>;
202
+ reloadRules: (state: State, arg?: unknown) => Promise<State | undefined>;
203
+ openLink: (state: State, arg?: unknown) => Promise<undefined>;
204
+ }>("himiBrandedGroupAbout", {
205
+ state: initial,
206
+ actions: {
207
+ load: async (state, arg) => {
208
+ stashCopy(arg);
209
+ const seq = ++loadSeq;
210
+ const id = inputId();
211
+ if (!id) return groupErrorState("", "missing");
212
+ const [groupResult, rulesResult] = await Promise.allSettled([getGroup(id), listRules(id)]);
213
+ // A newer load began while these reads were in flight: its answer owns the state.
214
+ if (seq !== loadSeq) return undefined;
215
+ if (groupResult.status === "rejected") return groupErrorState(id, failureOf(groupResult.reason));
216
+ // Social Groups wraps the entity as `{ group }`; the typed signature claims a bare group.
217
+ const group = ((groupResult.value as unknown as { group?: WixGroup }).group ?? groupResult.value) as WixGroup;
218
+ if (!group || typeof group !== "object" || (group.id && group.id !== id)) return groupErrorState(id, "failed");
219
+ const ready = groupState(state, id, group);
220
+ return rulesResult.status === "rejected"
221
+ ? withRules(ready, undefined, failureOf(rulesResult.reason))
222
+ : withRules(ready, ruleRowsOf(rulesResult.value), undefined);
223
+ },
224
+ // Retry for the rules region alone: the group's own facts stay on screen.
225
+ reloadRules: async (state, arg) => {
226
+ stashCopy(arg);
227
+ const id = state.id;
228
+ if (!id) return undefined;
229
+ const seq = loadSeq;
230
+ try {
231
+ const rules = ruleRowsOf(await listRules(id));
232
+ if (seq !== loadSeq || inputId() !== id) return undefined;
233
+ return withRules(state, rules, undefined);
234
+ } catch (e) {
235
+ if (seq !== loadSeq || inputId() !== id) return undefined;
236
+ return withRules(state, undefined, failureOf(e));
237
+ }
238
+ },
239
+ openLink: async (_state, arg) => { await openRichLink(arg); return undefined; },
240
+ },
241
+ });
242
+
243
+ export { svc };
@@ -1,69 +1,135 @@
1
- // himi-branded-lite-loyalty — reads the real Wix Loyalty program + rewards (getLoyaltyProgram + listLoyaltyRewards).
2
- // a failed query is an error with Retry (or Sign in when Wix refuses the
3
- // visitor) and an empty one is a real empty state — never sample rows presented as
4
- // the customer's own. Samples appear only in the host's explicit sample preview
5
- // (_shared/branded-load-state.ts).
1
+ // Public Loyalty offering plus the current member's separately verified balance.
6
2
  import { defineService } from "../_shared/collection-service.js";
7
3
  import { LOADING, loaded, loadFailed, type LoadCopy, type LoadFlags } from "../_shared/branded-load-state.js";
8
- import { getLoyaltyProgram, listLoyaltyRewards, type WixLoyaltyReward } from "@himalaya/wix-loyalty";
4
+ import { copyParam, copyTemplate } from "../_shared/himi-copy.js";
5
+ import { formatCurrency, formatNumber } from "../_shared/engine-intl.js";
6
+ import { memberAccess, assertMemberAccess } from "../_shared/member-access.js";
7
+ import { getLoyaltyProgram, listLoyaltyRewards, listLoyaltyEarningRules, getCurrentMemberLoyaltyAccount, type WixLoyaltyReward, type WixLoyaltyEarningRule } from "@himalaya/wix-loyalty";
9
8
 
10
9
  interface Row { id: string; name: string; subtitle: string }
11
10
  interface S extends LoadFlags {
12
- programName: string; unit: string;
13
- rewards: Row[]; emptyText: string;
14
- isMock: boolean; notMock: boolean; /** #2849 — hiddenWhen() hides on TRUE, so these are inverted: the empty-state banner
15
- * shows only when the site really has none, and the unavailable one only when the
16
- * query failed. A failed query must never render the sentence that claims the
17
- * customer has no content. */
18
- notMockEmpty: boolean; notMockUnavailable: boolean;
11
+ programName: string; unit: string; currency: string;
12
+ rewards: Row[]; earningRules: Row[]; emptyText: string; earningEmptyText: string;
13
+ hideCatalog: boolean; hideEarningHeading: boolean; inactiveText: string;
14
+ rewardError: string; earningError: string; hideRewardError: boolean; hideEarningError: boolean; hideRewardRows: boolean;
15
+ memberPoints: string; memberMessage: string;
16
+ hideMemberCard: boolean; hideMemberSignIn: boolean; hideMemberRetry: boolean;
19
17
  }
18
+ const PRIVATE_EMPTY = { memberPoints: "", memberMessage: "", hideMemberCard: true, hideMemberSignIn: true, hideMemberRetry: true };
19
+ const INITIAL: S = { ...LOADING, ...PRIVATE_EMPTY, programName: "", unit: "", currency: "", rewards: [], earningRules: [], emptyText: "", earningEmptyText: "", hideCatalog: false, hideEarningHeading: true, inactiveText: "", rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: false };
20
20
 
21
- /** "500 points" / "Free" from a reward's requiredPoints. */
22
- function pointsLabel(r: WixLoyaltyReward, unit: string): string {
23
- const pts = typeof r.requiredPoints === "number" ? r.requiredPoints : undefined;
24
- if (pts === undefined) return "";
25
- if (pts <= 0) return "Free";
26
- return `${pts.toLocaleString()} ${unit}`;
21
+ function amount(value: unknown): value is number { return typeof value === "number" && Number.isFinite(value) && value >= 0; }
22
+ function baseConfig<T extends { tierId?: string }>(configs: T[] | undefined): T | undefined {
23
+ const base = configs?.filter(config => !config.tierId);
24
+ return base?.length === 1 ? base[0] : undefined;
27
25
  }
28
-
29
- function norm(r: WixLoyaltyReward, unit: string): Row {
30
- return { id: r.id || r.name || "reward", name: r.name || "Reward", subtitle: pointsLabel(r, unit) };
26
+ function points(value: number, customUnit: string): string {
27
+ const unit = customUnit || copyParam(value === 1 ? "loyalty.pointOne" : "loyalty.pointMany", value === 1 ? "point" : "points");
28
+ return copyTemplate("loyalty.pointsLabel", "{points} {unit}", { points: formatNumber(value), unit });
29
+ }
30
+ function rewardLabel(reward: WixLoyaltyReward, customUnit: string): string {
31
+ const configs = reward.discountAmount?.configsByTier ?? reward.couponReward?.fixedAmount?.configsByTier
32
+ ?? reward.couponReward?.percentage?.configsByTier ?? reward.couponReward?.freeShipping?.configsByTier;
33
+ const cost = configs?.length ? baseConfig<{ tierId?: string; costInPoints?: number }>(configs)?.costInPoints : reward.requiredPoints;
34
+ if (!amount(cost)) return copyParam("loyalty.costUnavailable", "Points details unavailable");
35
+ return cost === 0 ? copyParam("loyalty.free", "Free") : points(cost, customUnit);
36
+ }
37
+ function earningLabel(rule: WixLoyaltyEarningRule, customUnit: string, currency: string): string {
38
+ if (rule.fixedAmount) {
39
+ const value = baseConfig(rule.fixedAmount.configs)?.points;
40
+ return amount(value) ? points(value, customUnit) : copyParam("loyalty.earningUnavailable", "Points details unavailable");
41
+ }
42
+ const rate = baseConfig(rule.conversionRate?.configs);
43
+ if (!rate || !amount(rate.points) || !amount(rate.moneyAmount) || rate.moneyAmount === 0 || !currency) {
44
+ return copyParam("loyalty.earningUnavailable", "Points details unavailable");
45
+ }
46
+ return copyTemplate("loyalty.pointsPerSpend", "{points} per {amount}", {
47
+ points: points(rate.points, customUnit), amount: formatCurrency(rate.moneyAmount, currency, "en", Number.isInteger(rate.moneyAmount) ? 0 : undefined),
48
+ });
49
+ }
50
+ function businessStamp(): unknown {
51
+ return (globalThis as { himalaya?: { input?: Record<string, unknown> } }).himalaya?.input?.__himiPreferenceBusiness;
52
+ }
53
+ function currencyFrom(s: S, arg: unknown): string {
54
+ if (!arg || typeof arg !== "object" || !("currency" in arg)) return s.currency;
55
+ const raw = (arg as { currency?: unknown }).currency;
56
+ return typeof raw === "string" && /^[A-Z]{3}$/.test(raw) ? raw : "";
31
57
  }
32
-
33
- const MOCK: Row[] = [
34
- { id: "sample-5off", name: "5% off your next order", subtitle: "500 points" },
35
- { id: "sample-ship", name: "Free shipping", subtitle: "300 points" },
36
- { id: "sample-15", name: "15% off any purchase", subtitle: "750 points" },
37
- { id: "sample-gift", name: "Free welcome gift", subtitle: "1,000 points" },
38
- ];
39
-
40
- /** sample rows reach the screen ONLY in the host's explicit sample preview;
41
- * a failed or empty query is an honest error / empty state (branded-load-state.ts). */
42
58
  const COPY: LoadCopy<Row> = {
43
- prefix: "loyalty",
44
- empty: "No rewards to redeem yet.",
45
- error: "The rewards program couldn't be loaded.",
46
- signIn: "Sign in to see your points and rewards.",
47
- sample: () => MOCK.map((r) => ({ ...r })),
59
+ prefix: "loyalty", empty: "No rewards to redeem yet.",
60
+ error: "The rewards program couldn't be loaded.", signIn: "The rewards program couldn't be loaded.",
61
+ sample: () => [{ id: "sample-reward", name: copyParam("loyalty.sampleReward", "5% off your next order"), subtitle: points(500, "") }],
48
62
  };
49
63
 
50
- const svc = defineService<S, { load: (s: S) => Promise<S> }>("himiBrandedLoyalty", {
51
- state: { ...LOADING, programName: "Rewards", unit: "points", rewards: [], emptyText: "" },
64
+ const svc = defineService<S, { prepare: (s: S) => S; load: (s: S, arg?: unknown) => Promise<S> }>("himiBrandedLoyalty", {
65
+ state: INITIAL,
66
+ notHydrated: ["memberPoints", "memberMessage", "hideMemberCard", "hideMemberSignIn", "hideMemberRetry"],
67
+ notPersisted: ["memberPoints", "memberMessage", "hideMemberCard", "hideMemberSignIn", "hideMemberRetry"],
52
68
  actions: {
53
- load: async (s) => {
69
+ prepare: s => ({ ...s, ...PRIVATE_EMPTY }),
70
+ load: async (s, arg) => {
71
+ const currency = currencyFrom(s, arg);
72
+ const business = businessStamp();
54
73
  try {
55
- const prog = await getLoyaltyProgram();
56
- const p = prog.loyaltyProgram ?? prog.program;
57
- const unit = (p?.pointDefinition?.customName || "points").trim() || "points";
58
- const programName = (p?.name || "Rewards").trim() || "Rewards";
59
- const r = await listLoyaltyRewards();
60
- const rewards = (r.rewards ?? []).filter((x) => x.active !== false).map((x) => norm(x, unit));
61
- return { ...s, programName, unit, ...loaded("rewards", rewards, COPY) };
62
- } catch (err) {
63
- return { ...s, ...loadFailed("rewards", err, COPY) };
74
+ const [response, member] = await Promise.all([getLoyaltyProgram(), memberAccess()]);
75
+ const program = response.loyaltyProgram ?? response.program;
76
+ if (!program || !["ACTIVE", "DRAFT", "PAUSED"].includes(program.status ?? "")) throw new Error("Loyalty program response is unavailable");
77
+ const customUnit = (program.pointDefinition?.customName ?? "").trim();
78
+ const common = { ...s, ...PRIVATE_EMPTY, currency, programName: program.name || copyParam("loyalty.programFallback", "Rewards"), unit: customUnit || copyParam("loyalty.pointMany", "points"), inactiveText: "", rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: true, hideEarningHeading: true };
79
+ if (program.status !== "ACTIVE") {
80
+ return { ...common, ...loaded("rewards", [], COPY), earningRules: [], earningEmptyText: "", hideCatalog: true,
81
+ inactiveText: copyParam("loyalty.inactive", "The rewards program isn't available right now.") };
82
+ }
83
+ // Balance failures cannot discard a public offering that loaded correctly.
84
+ const privateStateSupported = !!member.scope && ((globalThis as { himalaya?: { readLifecycleVersion?: number } }).himalaya?.readLifecycleVersion ?? 0) >= 1;
85
+ const accountRead = member.email && privateStateSupported
86
+ ? getCurrentMemberLoyaltyAccount().then(value => ({ value, failed: false })).catch(() => ({ value: undefined, failed: true }))
87
+ : Promise.resolve({ value: undefined, failed: false });
88
+ const [[rewardResult, earningResult], account] = await Promise.all([
89
+ Promise.allSettled([listLoyaltyRewards(), listLoyaltyEarningRules()]), accountRead,
90
+ ]);
91
+ const rewardResponse = rewardResult.status === "fulfilled" ? rewardResult.value : undefined;
92
+ const earningResponse = earningResult.status === "fulfilled" ? earningResult.value : undefined;
93
+ if (businessStamp() !== business) throw new Error("Business changed while Loyalty loaded");
94
+ if (rewardResult.status === "rejected" && earningResult.status === "rejected") throw new Error("Loyalty catalog is unavailable");
95
+ const rewards = (rewardResponse?.rewards ?? []).filter(reward => reward.active === true).map((reward, i) => ({
96
+ id: reward.id || `reward-${i}`, name: reward.name || copyParam("loyalty.rewardFallback", "Reward"), subtitle: rewardLabel(reward, customUnit),
97
+ }));
98
+ const earningRules = (earningResponse?.earningRules ?? []).filter(rule => rule.status === "ACTIVE").map((rule, i) => ({
99
+ id: rule.id || `earning-${i}`, name: rule.title || copyParam("loyalty.earnFallback", "Earn points"), subtitle: earningLabel(rule, customUnit, currency),
100
+ }));
101
+ let privateState = { ...PRIVATE_EMPTY };
102
+ if (member.email) {
103
+ try {
104
+ await assertMemberAccess(member);
105
+ const balance = account.value?.account?.points?.balance;
106
+ privateState = { ...PRIVATE_EMPTY, hideMemberCard: false,
107
+ memberPoints: typeof balance === "number" && Number.isFinite(balance) ? points(balance, customUnit) : "",
108
+ memberMessage: !privateStateSupported ? copyParam("loyalty.updateApp", "Update the app to see your points securely.") : account.failed ? copyParam("loyalty.pointsError", "Your points couldn't be loaded.")
109
+ : typeof balance !== "number" || !Number.isFinite(balance) ? copyParam("loyalty.pointsUnavailable", "Your points aren't available yet.") : "",
110
+ hideMemberRetry: !account.failed };
111
+ } catch {
112
+ const current = await memberAccess();
113
+ privateState = { ...PRIVATE_EMPTY, hideMemberCard: !current.email, hideMemberSignIn: !!current.email,
114
+ memberMessage: current.email ? copyParam("loyalty.pointsError", "Your points couldn't be loaded.") : "", hideMemberRetry: !current.email };
115
+ }
116
+ } else {
117
+ privateState.hideMemberSignIn = false;
118
+ }
119
+ return { ...common, ...loaded("rewards", rewards, COPY), ...privateState, earningRules, hideCatalog: false, hideEarningHeading: false,
120
+ rewardError: rewardResult.status === "rejected" ? copyParam("loyalty.rewardsError", "Rewards couldn't be loaded.") : "",
121
+ earningError: earningResult.status === "rejected" ? copyParam("loyalty.earningError", "Ways to earn points couldn't be loaded.") : "",
122
+ hideRewardError: rewardResult.status === "fulfilled", hideEarningError: earningResult.status === "fulfilled", hideRewardRows: rewardResult.status === "rejected",
123
+ emptyText: rewardResult.status === "rejected" ? "" : rewards.length ? "" : copyParam("loyalty.emptyText", COPY.empty),
124
+ earningEmptyText: earningResult.status === "rejected" || earningRules.length ? "" : copyParam("loyalty.earningEmpty", "No ways to earn points yet.") };
125
+ } catch (error) {
126
+ const failure = loadFailed("rewards", error, COPY);
127
+ return { ...s, ...PRIVATE_EMPTY, currency, earningRules: [], earningEmptyText: "", inactiveText: "", hideCatalog: !failure.isMock, hideEarningHeading: true, rewardError: "", earningError: "", hideRewardError: true, hideEarningError: true, hideRewardRows: !failure.isMock,
128
+ ...failure, signInRequired: false, noSignIn: true };
64
129
  }
65
130
  },
66
131
  },
67
132
  });
133
+ svc.prepare();
68
134
  svc.load();
69
135
  export { svc };
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  import { paymentSetupMessage } from "../_shared/payment-setup.js";
2
3
  import { offlineWriteMessage, offlineWritesEnabled, withOnlineWrites } from "../_shared/online-actions.js";
3
4
  import { connectivity } from "@himalaya/state";
@@ -140,7 +141,7 @@ function planRow(plan: WixPlan): PlanRow {
140
141
  return {
141
142
  id: plan.id || "",
142
143
  name,
143
- description: (plan.description ?? "").trim(),
144
+ description: descriptionBody(plan.description).bodyText,
144
145
  trialBadge,
145
146
  noTrialBadge: !trialBadge,
146
147
  priceLine: money(plan),
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // himi-branded-lite-podcasts — live RSS episodes with a persistent queue and
2
3
  // the shared native/web media playback transport.
3
4
  import { defineService } from "../_shared/collection-service.js";
@@ -99,7 +100,7 @@ function norm(episode: Episode): Row | null {
99
100
  title: (episode.title || "Episode").trim() || "Episode",
100
101
  subtitle: [dateLabel(episode.date), duration ? clock(duration) : ""].filter(Boolean).join(" · "),
101
102
  image: (episode.image || "").trim(),
102
- description: (episode.descriptionExcerpt || "").trim(),
103
+ description: descriptionBody(episode.descriptionExcerpt).bodyText,
103
104
  audioUrl,
104
105
  durationMs: duration,
105
106
  progressLabel: "",
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // himi-branded-lite-portfolio — real, public Wix Portfolio projects.
2
3
  // Empty and error states stay explicit; media IDs are normalized for every host.
3
4
  import { defineService } from "../_shared/collection-service.js";
@@ -28,7 +29,7 @@ function norm(project: WixPortfolioProject): Row {
28
29
  return {
29
30
  id: project.id || project.slug || title,
30
31
  title,
31
- subtitle: (project.description || "").trim(),
32
+ subtitle: descriptionBody(project.description).bodyText,
32
33
  image,
33
34
  initial: (title[0] || "#").toUpperCase(),
34
35
  hasImage: Boolean(image),
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // himi-branded-lite-portfolio-project — real Wix Portfolio project detail.
2
3
  // The public project row is resolved again from Wix, then its ordered project
3
4
  // items supply the actual image/video/text story. No sample project is invented.
@@ -59,7 +60,7 @@ function row(item: WixPortfolioProjectItem, index: number): MediaRow {
59
60
  return {
60
61
  id: item.id || `portfolio-item-${index}`,
61
62
  title: (item.title ?? "").trim(),
62
- description: (item.description ?? "").trim(),
63
+ description: descriptionBody(item.description).bodyText,
63
64
  image,
64
65
  videoUrl: hasVideo ? videoUrl : "",
65
66
  hasImage,
@@ -132,7 +133,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
132
133
  id,
133
134
  title: (project.title || copyParam("portfolioProject.projectFallbackName", "Project")).trim(),
134
135
  initial: ((project.title || "P").trim()[0] || "P").toUpperCase(),
135
- description: (project.description ?? "").trim(),
136
+ description: descriptionBody(project.description).bodyText,
136
137
  coverImage: wixMediaUrl(portfolioCoverRef(project.coverImage), WIX_DETAIL_IMAGE),
137
138
  items,
138
139
  emptyText: items.length ? "" : copyParam("portfolioProject.noMediaText", "This project has no published media yet."),
@@ -0,0 +1,52 @@
1
+ import type { WixProgramFull } from "@himalaya/wix-online-programs";
2
+ import { copyParam } from "../_shared/himi-copy.js";
3
+ import { formatDateMedium } from "../_shared/engine-intl.js";
4
+
5
+ export interface ProgramFact { id: string; icon: string; title: string; body: string }
6
+
7
+ /** Only explicit Program fields establish a fact. Missing access is never public. */
8
+ export function programFacts(program: WixProgramFull, locale: string): ProgramFact[] {
9
+ const facts: ProgramFact[] = [];
10
+ const timeline = program.timeline;
11
+ if (timeline?.selfPaced === true) facts.push({
12
+ id: "pace", icon: "clock",
13
+ title: copyParam("program.facts.selfPacedTitle", "Self-paced"),
14
+ body: copyParam("program.facts.selfPacedBody", "Complete steps at your own pace."),
15
+ });
16
+ else if (timeline?.selfPaced === false) facts.push({
17
+ id: "pace", icon: "clock",
18
+ title: copyParam("program.facts.scheduledTitle", "Scheduled"),
19
+ body: copyParam("program.facts.scheduledBody", "Follow the program schedule."),
20
+ });
21
+
22
+ const rawStart = timeline?.startDate;
23
+ // A date-only field is a calendar day, not midnight in the viewer's time zone. Build it at LOCAL noon and
24
+ // format it in the local zone: that reads the same day with ECMA-402 and on engines without it (watch/Wear),
25
+ // whose fallback ignores a timeZone argument and reads local getters.
26
+ const dateOnly = typeof rawStart === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(rawStart.trim()) : null;
27
+ const date = dateOnly ? new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]), 12)
28
+ : typeof rawStart === "string" && rawStart.trim() ? new Date(rawStart) : undefined;
29
+ if (date && Number.isFinite(date.getTime())) {
30
+ let body: string;
31
+ try { body = formatDateMedium(date, locale); }
32
+ catch { body = formatDateMedium(date, "en"); }
33
+ facts.push({ id: "start", icon: "calendar", title: copyParam("program.facts.fixedStartTitle", "Fixed start date"), body });
34
+ } else if (timeline?.selfPaced === true && (rawStart === undefined || rawStart === "")) facts.push({
35
+ id: "start", icon: "calendar",
36
+ title: copyParam("program.facts.flexibleStartTitle", "Flexible start date"),
37
+ body: copyParam("program.facts.flexibleStartBody", "Start whenever you're ready."),
38
+ });
39
+
40
+ const access = program.restrictions?.accessType?.toUpperCase();
41
+ if (access === "PUBLIC") facts.push({
42
+ id: "access", icon: "globe",
43
+ title: copyParam("program.facts.publicTitle", "Public"),
44
+ body: copyParam("program.facts.publicBody", "Anyone can join."),
45
+ });
46
+ else if (access === "PRIVATE") facts.push({
47
+ id: "access", icon: "lock",
48
+ title: copyParam("program.facts.privateTitle", "Private"),
49
+ body: copyParam("program.facts.privateBody", "Approval is required to join."),
50
+ });
51
+ return facts;
52
+ }