@wix/himalaya-cli 0.1583.0 → 0.1585.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 (313) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/app-module.d.ts +7 -4
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/ts/src/bindings.d.ts +7 -6
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/blog-v3/src/extensions.d.ts +2 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/comments-v1/src/extensions.d.ts +2 -0
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/index.d.ts +3 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/groups/src/extensions.d.ts +15 -0
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/loyalty/src/extensions.d.ts +13 -0
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/members-v1/src/index.d.ts +19 -4
  9. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/core.mjs +2 -2
  10. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/sdui.mjs +8 -2
  11. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-comments.mjs +3 -0
  12. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +4 -2
  13. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-groups.mjs +5 -0
  14. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-loyalty.mjs +4 -0
  15. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-members.mjs +10 -3
  16. package/dist/cli.mjs +2201 -1716
  17. package/dist/preview-assets.generated.bin +0 -0
  18. package/fixtures/wix-canned.json +25 -0
  19. package/guides/branded-lite-site-binding.md +7 -1
  20. package/guides/designing-himi-screens.md +47 -7
  21. package/guides/himi-skill.md +16 -0
  22. package/guides/index.json +1 -0
  23. package/guides/one-app-to-himi-app.md +52 -1
  24. package/guides/sdk-reference.json +37 -9
  25. package/guides/tier-5-add-a-screen.md +9 -0
  26. package/guides/wix-api-usage.md +9 -0
  27. package/package.json +2 -2
  28. package/templates/beauty-salon/config.ts +7 -0
  29. package/templates/beauty-salon/dev/index.ts +758 -352
  30. package/templates/beauty-salon/himalaya.content.json +3 -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-branded-lite-category/index.ts +3 -2
  38. package/templates/beauty-salon/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  39. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  40. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +14 -3
  41. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  42. package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  43. package/templates/beauty-salon-owner/dev/index.ts +129 -66
  44. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +19 -3
  45. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  46. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  47. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  48. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  49. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  50. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  51. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  52. package/templates/branded-lite/config.ts +9 -0
  53. package/templates/branded-lite/dev/index.ts +4988 -3304
  54. package/templates/branded-lite/himalaya.content.json +3 -0
  55. package/templates/branded-lite/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  56. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +19 -3
  57. package/templates/branded-lite/tier5-src/_shared/event-registration.ts +86 -0
  58. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +2 -1
  59. package/templates/branded-lite/tier5-src/_shared/owner-editor-value.ts +164 -0
  60. package/templates/branded-lite/tier5-src/_shared/store-product-info.ts +50 -0
  61. package/templates/branded-lite/tier5-src/himi-blog-post/index.ts +21 -8
  62. package/templates/branded-lite/tier5-src/himi-branded-lite-badges/index.ts +2 -1
  63. package/templates/branded-lite/tier5-src/himi-branded-lite-category/index.ts +3 -2
  64. package/templates/branded-lite/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  65. package/templates/branded-lite/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  66. package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +115 -49
  67. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  68. package/templates/branded-lite/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  69. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  70. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  71. package/templates/branded-lite/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  72. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +21 -11
  73. package/templates/branded-lite/tier5-src/himi-branded-lite-video/index.ts +2 -1
  74. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +12 -86
  75. package/templates/branded-lite/tier5-src/himi-events-list/index.ts +47 -6
  76. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +13 -3
  77. package/templates/branded-lite/tier5-src/wix-spaces-forum-post/index.ts +3 -2
  78. package/templates/branded-lite/tier5-src/wix-spaces-member-detail/index.ts +53 -37
  79. package/templates/branded-lite/tier5-src/wix-spaces-members/index.ts +100 -34
  80. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  81. package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  82. package/templates/community-club/config.ts +16 -0
  83. package/templates/community-club/dev/index.ts +1930 -401
  84. package/templates/community-club/himalaya.content.json +6 -0
  85. package/templates/community-club/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  86. package/templates/community-club/tier5-src/_shared/collection-contract.ts +19 -3
  87. package/templates/community-club/tier5-src/_shared/event-registration.ts +86 -0
  88. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +2 -1
  89. package/templates/community-club/tier5-src/himi-blog-post/index.ts +21 -8
  90. package/templates/community-club/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  91. package/templates/community-club/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  92. package/templates/community-club/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  93. package/templates/community-club/tier5-src/himi-events-detail/index.ts +12 -86
  94. package/templates/community-club/tier5-src/himi-events-list/index.ts +47 -6
  95. package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  96. package/templates/community-club-owner/dev/index.ts +53 -1
  97. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +19 -3
  98. package/templates/community-club-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  99. package/templates/community-club-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  100. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  101. package/templates/community-club-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  102. package/templates/community-club-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  103. package/templates/community-club-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  104. package/templates/community-club-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  105. package/templates/events-meetups/config.ts +16 -0
  106. package/templates/events-meetups/dev/index.ts +1930 -401
  107. package/templates/events-meetups/himalaya.content.json +6 -0
  108. package/templates/events-meetups/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  109. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +19 -3
  110. package/templates/events-meetups/tier5-src/_shared/event-registration.ts +86 -0
  111. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +2 -1
  112. package/templates/events-meetups/tier5-src/himi-blog-post/index.ts +21 -8
  113. package/templates/events-meetups/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  114. package/templates/events-meetups/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  115. package/templates/events-meetups/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  116. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +12 -86
  117. package/templates/events-meetups/tier5-src/himi-events-list/index.ts +47 -6
  118. package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  119. package/templates/events-meetups-owner/dev/index.ts +6 -0
  120. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +19 -3
  121. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  122. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  123. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  124. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  125. package/templates/gym-fitness/config.ts +7 -0
  126. package/templates/gym-fitness/dev/index.ts +1115 -278
  127. package/templates/gym-fitness/himalaya.content.json +3 -0
  128. package/templates/gym-fitness/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  129. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +19 -3
  130. package/templates/gym-fitness/tier5-src/_shared/event-registration.ts +86 -0
  131. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +2 -1
  132. package/templates/gym-fitness/tier5-src/_shared/owner-editor-value.ts +164 -0
  133. package/templates/gym-fitness/tier5-src/_shared/store-product-info.ts +50 -0
  134. package/templates/gym-fitness/tier5-src/himi-blog-post/index.ts +21 -8
  135. package/templates/gym-fitness/tier5-src/himi-branded-lite-category/index.ts +3 -2
  136. package/templates/gym-fitness/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  137. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  138. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +12 -86
  139. package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +47 -6
  140. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +14 -3
  141. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  142. package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  143. package/templates/gym-fitness-owner/dev/index.ts +132 -66
  144. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +19 -3
  145. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  146. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  147. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  148. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  149. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  150. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  151. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  152. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  153. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  154. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  155. package/templates/nonprofit-cause/config.ts +16 -0
  156. package/templates/nonprofit-cause/dev/index.ts +1925 -396
  157. package/templates/nonprofit-cause/himalaya.content.json +6 -0
  158. package/templates/nonprofit-cause/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  159. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +19 -3
  160. package/templates/nonprofit-cause/tier5-src/_shared/event-registration.ts +86 -0
  161. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +2 -1
  162. package/templates/nonprofit-cause/tier5-src/himi-blog-post/index.ts +21 -8
  163. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  164. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-group-about/index.ts +243 -0
  165. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  166. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  167. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +12 -86
  168. package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +47 -6
  169. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  170. package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  171. package/templates/nonprofit-cause-owner/dev/index.ts +85 -65
  172. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +19 -3
  173. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  174. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  175. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  176. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  177. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  178. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  179. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  180. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  181. package/templates/owner-lite/dev/index.ts +132 -66
  182. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +19 -3
  183. package/templates/owner-lite/tier5-src/himi-owner-lite-event/index.ts +16 -8
  184. package/templates/owner-lite/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  185. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +4 -2
  186. package/templates/owner-lite/tier5-src/himi-owner-lite-post/index.ts +7 -0
  187. package/templates/owner-lite/tier5-src/himi-owner-lite-product/index.ts +39 -6
  188. package/templates/owner-lite/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  189. package/templates/owner-lite/tier5-src/himi-owner-lite-products/index.ts +7 -4
  190. package/templates/owner-lite/tier5-src/himi-owner-lite-service/index.ts +38 -7
  191. package/templates/owner-lite/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  192. package/templates/owner-lite/tier5-src/himi-owner-lite-services/index.ts +4 -2
  193. package/templates/podcast-creator/config.ts +7 -0
  194. package/templates/podcast-creator/dev/index.ts +1051 -158
  195. package/templates/podcast-creator/himalaya.content.json +3 -0
  196. package/templates/podcast-creator/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  197. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +19 -3
  198. package/templates/podcast-creator/tier5-src/_shared/event-registration.ts +86 -0
  199. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +2 -1
  200. package/templates/podcast-creator/tier5-src/himi-blog-post/index.ts +21 -8
  201. package/templates/podcast-creator/tier5-src/himi-branded-lite-feed/index.ts +4 -3
  202. package/templates/podcast-creator/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  203. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  204. package/templates/podcast-creator/tier5-src/himi-branded-lite-podcasts/index.ts +2 -1
  205. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +12 -86
  206. package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +47 -6
  207. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  208. package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  209. package/templates/podcast-creator-owner/dev/index.ts +53 -1
  210. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +19 -3
  211. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  212. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  213. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  214. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  215. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  216. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  217. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  218. package/templates/portfolio-creative/config.ts +7 -0
  219. package/templates/portfolio-creative/dev/index.ts +1305 -468
  220. package/templates/portfolio-creative/himalaya.content.json +3 -0
  221. package/templates/portfolio-creative/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  222. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +19 -3
  223. package/templates/portfolio-creative/tier5-src/_shared/event-registration.ts +86 -0
  224. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +2 -1
  225. package/templates/portfolio-creative/tier5-src/_shared/owner-editor-value.ts +164 -0
  226. package/templates/portfolio-creative/tier5-src/_shared/store-product-info.ts +50 -0
  227. package/templates/portfolio-creative/tier5-src/himi-blog-post/index.ts +21 -8
  228. package/templates/portfolio-creative/tier5-src/himi-branded-lite-category/index.ts +3 -2
  229. package/templates/portfolio-creative/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  230. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -1
  231. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -2
  232. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +12 -86
  233. package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +47 -6
  234. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +14 -3
  235. package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  236. package/templates/portfolio-creative-owner/dev/index.ts +129 -66
  237. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +19 -3
  238. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  239. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  240. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  241. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  242. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  243. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  244. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  245. package/templates/restaurant/config.ts +7 -0
  246. package/templates/restaurant/dev/index.ts +1051 -158
  247. package/templates/restaurant/himalaya.content.json +3 -0
  248. package/templates/restaurant/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  249. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +19 -3
  250. package/templates/restaurant/tier5-src/_shared/event-registration.ts +86 -0
  251. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +2 -1
  252. package/templates/restaurant/tier5-src/himi-blog-post/index.ts +21 -8
  253. package/templates/restaurant/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  254. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +12 -86
  255. package/templates/restaurant/tier5-src/himi-events-list/index.ts +47 -6
  256. package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  257. package/templates/restaurant-owner/dev/index.ts +132 -66
  258. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +19 -3
  259. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  260. package/templates/restaurant-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  261. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  262. package/templates/restaurant-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  263. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  264. package/templates/restaurant-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  265. package/templates/restaurant-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  266. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  267. package/templates/restaurant-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  268. package/templates/restaurant-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
  269. package/templates/retail-store/config.ts +7 -0
  270. package/templates/retail-store/dev/index.ts +597 -191
  271. package/templates/retail-store/himalaya.content.json +3 -0
  272. package/templates/retail-store/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  273. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +19 -3
  274. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +2 -1
  275. package/templates/retail-store/tier5-src/_shared/owner-editor-value.ts +164 -0
  276. package/templates/retail-store/tier5-src/_shared/store-product-info.ts +50 -0
  277. package/templates/retail-store/tier5-src/himi-blog-post/index.ts +21 -8
  278. package/templates/retail-store/tier5-src/himi-branded-lite-category/index.ts +3 -2
  279. package/templates/retail-store/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  280. package/templates/retail-store/tier5-src/himi-store-product/index.ts +13 -3
  281. package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  282. package/templates/retail-store-owner/dev/index.ts +50 -1
  283. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +19 -3
  284. package/templates/retail-store-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  285. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product/index.ts +39 -6
  286. package/templates/retail-store-owner/tier5-src/himi-owner-lite-product-form/index.ts +14 -8
  287. package/templates/retail-store-owner/tier5-src/himi-owner-lite-products/index.ts +7 -4
  288. package/templates/yoga-studio/config.ts +7 -0
  289. package/templates/yoga-studio/dev/index.ts +1107 -110
  290. package/templates/yoga-studio/himalaya.content.json +3 -0
  291. package/templates/yoga-studio/tier5-src/_shared/branded-lite-hosted.ts +1 -1
  292. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +19 -3
  293. package/templates/yoga-studio/tier5-src/_shared/event-registration.ts +86 -0
  294. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +2 -1
  295. package/templates/yoga-studio/tier5-src/himi-blog-post/index.ts +21 -8
  296. package/templates/yoga-studio/tier5-src/himi-branded-lite-notifsettings/index.ts +173 -0
  297. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +2 -1
  298. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/facts.ts +52 -0
  299. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +21 -11
  300. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +12 -86
  301. package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +47 -6
  302. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +2 -1
  303. package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +2 -1
  304. package/templates/yoga-studio-owner/dev/index.ts +85 -65
  305. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +19 -3
  306. package/templates/yoga-studio-owner/tier5-src/_shared/owner-rich-guard.ts +27 -0
  307. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event/index.ts +16 -8
  308. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-event-form/index.ts +16 -7
  309. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +4 -2
  310. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-post/index.ts +7 -0
  311. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service/index.ts +38 -7
  312. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-service-form/index.ts +53 -7
  313. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-services/index.ts +4 -2
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  import { defineService } from "@himalaya/state";
2
3
  import { LOADING, loaded, loadFailed, needsSignIn, type LoadFlags } from "../_shared/branded-load-state.js";
3
4
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
@@ -54,7 +55,7 @@ const svc = defineService<S, { reset: (s: S) => S; load: (s: S, arg?: unknown) =
54
55
  if (!channel || !video || video.status !== "VISIBLE") next = unavailable(next, "unavailable", copyParam("video.unavailableText", "This video is unavailable."));
55
56
  else {
56
57
  next.title = video.title ?? "";
57
- next.description = video.description ?? "";
58
+ next.description = descriptionBody(video.description).bodyText;
58
59
  if (video.content?.status !== "READY") next = isVideoProcessing(video) ?
59
60
  unavailable(next, "processing", copyParam("video.processingText", "Video is processing. Try again later.")) :
60
61
  unavailable(next, "unavailable", copyParam("video.unavailableText", "This video is unavailable."));
@@ -35,8 +35,10 @@ import {
35
35
  type EventRow,
36
36
  } from "../_shared/himi-events";
37
37
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
38
+ import { eventPageUrlOf, eventStatus, registrationKindOf, registrationUi, registrationUrlOf, type RegistrationKind } from "../_shared/event-registration.js";
39
+ export { eventPageUrlOf, registrationKindOf, registrationUrlOf, type RegistrationKind };
38
40
  import { openRichLink } from "../_shared/rich-content-link.js";
39
- import { richBody } from "../_shared/rich-body.js";
41
+ import { descriptionBody } from "../_shared/rich-body.js";
40
42
  import { mayUseRegularTicketCheckout } from "../_shared/iap-attendance.js";
41
43
  import { ATTENDANCE_INITIAL, ATTENDANCE_NOT_HYDRATED, createAttendancePurchase, type AttendancePurchaseFields } from "../_shared/iap-attendance-purchase.js";
42
44
  import { resetUnhydratedKeysOnOlderRuntime } from "../_shared/iap-worker.js";
@@ -88,9 +90,6 @@ interface TicketTierRow {
88
90
  notAvailable?: boolean;
89
91
  }
90
92
 
91
- /** Registration CTA kind derived from the event's registration/eventType. */
92
- export type RegistrationKind = "rsvp" | "tickets" | "external" | "none";
93
-
94
93
  // Offline-first (data-offline): the event detail is a read-through single-item read —
95
94
  // it shares the `events` entity written by the list, and also write-throughs its own
96
95
  // fetched row so a deep-linked detail is cached for the next offline open. On a failed
@@ -302,7 +301,7 @@ function loadedTiers(defs: WixAvailableTicket[]): { rows: TicketTierRow[]; ticke
302
301
  const ticket = tickets.get(row.ticketDefinitionId ?? row.id);
303
302
  if (!ticket) return { ...row, quantity: 0, description: "", feeNote: "", policy: "", noPolicy: true, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, maxQuantity: 0 };
304
303
  const policy = (ticket as unknown as { policy?: string; policyText?: string }).policyText || (ticket as unknown as { policy?: string }).policy || "";
305
- return { ...row, quantity: 0, description: ticket.description || "", feeNote: ((ticket as unknown as { wixFeeConfig?: { type?: string }; feeType?: string }).wixFeeConfig?.type ?? (ticket as unknown as { feeType?: string }).feeType) === "FEE_ADDED_AT_CHECKOUT" ? detailCopy("event.serviceFee", "+ ticket service fee") : "", policy, noPolicy: !policy, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, priceAmount: row.priceAmount ?? Number(ticket.price?.amount ?? 0), rowLimit: row.available ? ticket.limitPerCheckout ?? 99 : 0, maxQuantity: row.available ? ticket.limitPerCheckout ?? 99 : 0 };
304
+ return { ...row, quantity: 0, description: descriptionBody(ticket.description).bodyText, feeNote: ((ticket as unknown as { wixFeeConfig?: { type?: string }; feeType?: string }).wixFeeConfig?.type ?? (ticket as unknown as { feeType?: string }).feeType) === "FEE_ADDED_AT_CHECKOUT" ? detailCopy("event.serviceFee", "+ ticket service fee") : "", policy, noPolicy: !policy, notAvailable: !row.available, notGuestPrice: !row.isGuestPrice, priceAmount: row.priceAmount ?? Number(ticket.price?.amount ?? 0), rowLimit: row.available ? ticket.limitPerCheckout ?? 99 : 0, maxQuantity: row.available ? ticket.limitPerCheckout ?? 99 : 0 };
306
305
  });
307
306
  return { rows, tickets };
308
307
  }
@@ -451,29 +450,6 @@ function calendarFallbackUrl(s: S, end: number): string {
451
450
  * shortDescription: REGISTRATION fills eventType, which skips the query enrichment that used to supply them. */
452
451
  export const EVENT_DETAIL_FIELDSETS: WixEventFieldset[] = ["DETAILS", "TEXTS", "REGISTRATION", "URLS", "FORM", "AGENDA"];
453
452
 
454
- /** Map registration status and decoded eventType onto the CTA variant. The decoded
455
- * `eventType` already folds registration.type / registration.initialType /
456
- * the legacy flat `type` (decodeEvent, #478). Without a recognized type or
457
- * status there is no registration action to offer. */
458
- export function registrationKindOf(e: WixEvent): RegistrationKind {
459
- const raw = e as unknown as { registration?: { type?: string; initialType?: string; status?: string } };
460
- const value = (raw.registration?.type ?? e.eventType ?? raw.registration?.initialType ?? "").toUpperCase();
461
- if (value === "NONE" || value.includes("NO_REGISTRATION")) return "none";
462
- // Open statuses select the footer action, so they must also select the
463
- // inventory read and action guards when type metadata is absent or stale.
464
- switch (raw.registration?.status?.toUpperCase()) {
465
- case "OPEN_TICKETS": return "tickets";
466
- case "OPEN_EXTERNAL": return "external";
467
- case "OPEN_RSVP":
468
- case "OPEN_RSVP_WAITLIST_ONLY":
469
- case "SCHEDULED_RSVP": return "rsvp";
470
- }
471
- if (value.includes("EXTERNAL")) return "external";
472
- if (value.includes("TICKET")) return "tickets";
473
- if (value.includes("RSVP")) return "rsvp";
474
- return "none";
475
- }
476
-
477
453
  function detailCopy(key: string, fallback: string): string { return copyParam(key, fallback); }
478
454
 
479
455
  function detailDate(e: WixEvent): { line: string; zone: string; start: number; end: number } {
@@ -536,69 +512,17 @@ function detailPlace(e: WixEvent): { value: string; mapUrl: string; hideMapTap:
536
512
  return { value, mapUrl: mapped, hideMapTap: !mapped, icon: "location" };
537
513
  }
538
514
 
539
- function eventStatus(e: WixEvent): string {
540
- const raw = e as unknown as { status?: string };
541
- return (raw.status ?? e.eventStatus ?? "").toUpperCase();
542
- }
543
-
544
- function registrationUi(e: WixEvent, now: number): { state: string; label: string; action: string; caption: string; notice: string } {
545
- const raw = e as unknown as { registration?: { status?: string; registrationPaused?: boolean; registrationDisabled?: boolean; allowedGuestTypes?: string; rsvp?: { startDate?: string }; tickets?: { soldOut?: boolean }; external?: { url?: string } }; form?: { messages?: { registrationClosed?: { message?: string; exploreEventsActionLabel?: string }; ticketsUnavailable?: { message?: string; exploreEventsActionLabel?: string } } } };
546
- const r = raw.registration ?? {};
547
- const status = eventStatus(e);
548
- const type = registrationKindOf(e);
549
- if (status === "CANCELED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.canceledNotice", "This event was canceled.") };
550
- if (status === "ENDED") return { state: "notice", label: "", action: "", caption: "", notice: detailCopy("event.endedNotice", "This event has ended.") };
551
- if (type === "none" || r.registrationDisabled) return { state: "none", label: "", action: "", caption: "", notice: "" };
552
- const ownerClosed = raw.form?.messages?.registrationClosed?.message;
553
- const ownerSold = raw.form?.messages?.ticketsUnavailable?.message;
554
- if (["CLOSED_AUTOMATICALLY", "CLOSED_MANUALLY"].includes(r.status || "") || r.registrationPaused) return { state: "notice", label: "", action: "", caption: "", notice: ownerClosed || detailCopy("event.closedNotice", "Registration is closed.") };
555
- if (type === "tickets" && r.tickets?.soldOut) return { state: "notice", label: "", action: "", caption: "", notice: ownerSold || detailCopy("event.soldOutNotice", "Tickets are sold out.") };
556
- if (r.status === "SCHEDULED_RSVP") return { state: "scheduled", label: detailCopy("event.rsvpCta", "RSVP"), action: "", caption: detailCopy("event.opensCaption", "RSVP opens {date}.").replace("{date}", r.rsvp?.startDate ? formatWeekdayDateTime(new Date(r.rsvp.startDate)) : ""), notice: "" };
557
- if (r.status === "OPEN_RSVP_WAITLIST_ONLY") return { state: "waitlist", label: detailCopy("event.waitlistCta", "Join Waitlist"), action: "himi.open", caption: detailCopy("event.fullCaption", "This event is full."), notice: "" };
558
- if (r.status === "OPEN_TICKETS" || type === "tickets") return { state: "tickets", label: detailCopy("event.ticketsCta", "Buy Tickets"), action: "openTickets", caption: "", notice: "" };
559
- if (r.status === "OPEN_EXTERNAL" || type === "external") return registrationUrlOf(e)
560
- ? { state: "external", label: detailCopy("event.externalCta", "Register"), action: "openRegistration", caption: "", notice: "" }
561
- : { state: "none", label: "", action: "", caption: "", notice: "" };
562
- if (type === "rsvp" && r.status === "OPEN_RSVP") return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
563
- if (r.status === "OPEN_RSVP" || (type === "rsvp" && !r.status)) return { state: "rsvp", label: detailCopy("event.rsvpCta", "RSVP"), action: "himi.open", caption: "", notice: "" };
564
- void now;
565
- return { state: "none", label: "", action: "", caption: "", notice: "" };
566
- }
567
-
568
- /** External registration URL / event page URL from the raw payload ("" when absent). */
569
- export function registrationUrlOf(e: WixEvent): string {
570
- const raw = e as unknown as {
571
- registration?: { external?: { registration?: string; url?: string } };
572
- eventPageUrl?: { base?: string; path?: string } | string;
573
- };
574
- const external = raw.registration?.external?.registration ?? raw.registration?.external?.url;
575
- if (typeof external === "string" && /^https?:\/\//i.test(external)) return external;
576
- return eventPageUrlOf(e);
577
- }
578
-
579
515
  /** Event slug for createRedirectSession({ eventsCheckout }) — "" when absent. */
580
516
  export function eventSlugOf(e: WixEvent): string {
581
517
  const slug = e.slug ?? (e as unknown as { slug?: string }).slug;
582
518
  return typeof slug === "string" ? slug.trim() : "";
583
519
  }
584
520
 
585
- /** The event's public page URL when the payload carries one ("" otherwise). */
586
- export function eventPageUrlOf(e: WixEvent): string {
587
- const raw = e as unknown as { eventPageUrl?: { base?: string; path?: string } | string };
588
- const page = raw.eventPageUrl;
589
- if (typeof page === "string" && /^https?:\/\//i.test(page)) return page;
590
- if (page && typeof page === "object" && typeof page.base === "string" && page.base) {
591
- const path = typeof page.path === "string" ? page.path : "";
592
- return `${page.base.replace(/\/$/, "")}${path && !path.startsWith("/") ? "/" : ""}${path}`;
593
- }
594
- return "";
595
- }
596
-
597
521
  /** The description pair: the v3 Ricos `description` for RichContent, and its plain text. A string description
598
522
  * (or none) keeps `eventDescription`'s legacy read, with no document. */
599
523
  function eventBody(e: WixEvent): { description: string; descriptionJson: string } {
600
- const legacy = eventDescription(e);
601
- const { bodyJson, bodyText } = richBody((e as unknown as { description?: unknown }).description, legacy);
524
+ const legacy = eventDescription({ ...e, description: undefined });
525
+ const { bodyJson, bodyText } = descriptionBody((e as unknown as { description?: unknown }).description, descriptionBody(legacy).bodyText);
602
526
  return { description: bodyText, descriptionJson: bodyJson };
603
527
  }
604
528
 
@@ -1132,6 +1056,8 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1132
1056
  }).sort((a, b) => (eventStartMillis(a) ?? 0) - (eventStartMillis(b) ?? 0)).map((item) => ({ id: item.id ?? "", date: eventWhen(item) })).filter((item) => !!item.id);
1133
1057
  } catch { moreDates = []; optionalReadFailed = true; }
1134
1058
  }
1059
+ const body = eventBody(e);
1060
+ const shortDescription = descriptionBody(rawEvent.shortDescription).bodyText;
1135
1061
  const readyState = derive({
1136
1062
  ...s,
1137
1063
  status: "ready",
@@ -1153,9 +1079,9 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1153
1079
  price,
1154
1080
  zone: dates.zone,
1155
1081
  mapUrl: place.mapUrl,
1156
- shortDescription: rawEvent.shortDescription || "",
1157
- noShortDescription: !rawEvent.shortDescription || rawEvent.shortDescription === eventBody(e).description,
1158
- noDescription: !eventBody(e).description && !eventBody(e).descriptionJson,
1082
+ shortDescription,
1083
+ noShortDescription: !shortDescription || shortDescription === body.description,
1084
+ noDescription: !body.description && !body.descriptionJson,
1159
1085
  noDate: !dates.line,
1160
1086
  registrationState: registration.state,
1161
1087
  footerLabel: registration.label,
@@ -1177,7 +1103,7 @@ const svc: ReturnType<typeof defineService<S, DetailActions>> = defineService<S,
1177
1103
  noMoreDates: moreDates.length === 0,
1178
1104
  // The Ricos description when the event has one (its own text is the plain copy); the legacy
1179
1105
  // string fields otherwise.
1180
- ...eventBody(e),
1106
+ ...body,
1181
1107
  image,
1182
1108
  hasImage: !!image,
1183
1109
  noImage: !image,
@@ -5,6 +5,8 @@ import { getAll, upsert, type DbEntity } from "@himalaya/local-db";
5
5
  import { normalizeEvent, type EventRow } from "../_shared/himi-events";
6
6
  import { offsetPage } from "../_shared/offset-page.js";
7
7
  import { copyParam } from "../_shared/himi-copy.js";
8
+ import { eventPageUrlOf, registrationUi } from "../_shared/event-registration.js";
9
+ import { share as shareSheet } from "@himalaya/share";
8
10
 
9
11
  // Offline-first (data-offline): the public events list is a read-through catalog —
10
12
  // paint the last-synced rows from the on-device store first (instant, works offline),
@@ -13,8 +15,37 @@ import { copyParam } from "../_shared/himi-copy.js";
13
15
  // (+ declared the `events` entity in its dataSchema) just falls back to today's
14
16
  // online-only path — the try/catch is the seam that keeps this shared bundle safe.
15
17
  const ENTITY = "events";
16
- async function readCached(): Promise<EventRow[]> {
17
- try { return (await getAll(ENTITY)) as unknown as EventRow[]; } catch { return []; }
18
+ interface ListEventRow extends EventRow {
19
+ dateBadge: string; noDateBadge: boolean;
20
+ /** This card's verb (Register / RSVP / Buy Tickets / Join Waitlist) from the SAME rule the detail uses; hidden when none applies. */
21
+ actionLabel: string; noAction: boolean;
22
+ /** The event page URL (when the payload carries one) for Share. */
23
+ shareUrl: string;
24
+ }
25
+
26
+ // The day monogram is a title initial for undated events. Only an actual calendar
27
+ // date becomes a badge; derive it for old cached rows too, without a cache migration.
28
+ // The month must be the three-letter abbreviation: rows cached by an older worker on an
29
+ // Intl-less engine can hold a whole "11/10/2030, 12:00:00 PM" there.
30
+ function withDateBadge(row: EventRow): ListEventRow {
31
+ const dated = typeof row.month === "string" && /^[A-Z]{3}$/.test(row.month)
32
+ && /^(?:[1-9]|[12]\d|3[01])$/.test(row.day);
33
+ const known = row as Partial<ListEventRow>;
34
+ // Rows cached by an older build carry no registration data: they show no verb until a refresh, never a wrong one.
35
+ return { ...row, dateBadge: dated ? `${row.day}\n${row.month}` : "", noDateBadge: !dated,
36
+ actionLabel: known.actionLabel ?? "", noAction: known.noAction ?? true, shareUrl: known.shareUrl ?? "" };
37
+ }
38
+
39
+ /** The card's action, from the event's own registration (REGISTRATION + URLS fieldsets). Cancelled, ended,
40
+ * closed, sold-out and registration-less events offer none, so a card never promises "Register" the detail refuses. */
41
+ function withRegistration(raw: WixEvent, row: ListEventRow): ListEventRow {
42
+ const ui = registrationUi(raw, Date.now());
43
+ const offers = ui.state !== "notice" && ui.state !== "none" && ui.state !== "scheduled" && !!ui.label;
44
+ return { ...row, actionLabel: offers ? ui.label : "", noAction: !offers, shareUrl: eventPageUrlOf(raw) };
45
+ }
46
+
47
+ async function readCached(): Promise<ListEventRow[]> {
48
+ try { return ((await getAll(ENTITY)) as unknown as EventRow[]).map(withDateBadge); } catch { return []; }
18
49
  }
19
50
  async function writeCached(rows: EventRow[]): Promise<void> {
20
51
  try { for (const r of rows) await upsert(ENTITY, r as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
@@ -22,12 +53,12 @@ async function writeCached(rows: EventRow[]): Promise<void> {
22
53
 
23
54
  interface S {
24
55
  status: "loading" | "ready";
25
- query: string; all: EventRow[]; results: EventRow[]; emptyText: string; countLabel: string;
56
+ query: string; all: ListEventRow[]; results: ListEventRow[]; emptyText: string; countLabel: string;
26
57
  errored: boolean; notErrored: boolean; isLoading: boolean; notLoading: boolean; noEmpty: boolean;
27
58
  nextCursor?: string; complete?: boolean; total?: number; loadingMore?: boolean; pageError?: boolean; fromCache?: boolean;
28
59
  }
29
60
  let queryNow = new Date().toISOString();
30
- const svc = defineBackendCollection<S, EventRow>("himiEvents", {
61
+ const svc = defineBackendCollection<S, ListEventRow>("himiEvents", {
31
62
  restorePagesOnLoad: true,
32
63
  state: { status: "loading", query: "", all: [], results: [], emptyText: "", countLabel: "", errored: false, notErrored: true,
33
64
  isLoading: true, notLoading: false, noEmpty: true },
@@ -62,10 +93,10 @@ const svc = defineBackendCollection<S, EventRow>("himiEvents", {
62
93
  ];
63
94
  }
64
95
  if (criteria.filter === "value:Past") filter["dateAndTimeSettings.startDate"] = { $lt: queryNow };
65
- const res = await queryEvents({ limit: 50, offset: Number(cursor || 0), query: { filter,
96
+ const res = await queryEvents({ limit: 50, offset: Number(cursor || 0), fields: ["DETAILS", "REGISTRATION", "URLS"], query: { filter,
66
97
  sort: [{ fieldName: criteria.sort === "default" ? "dateAndTimeSettings.startDate" : "title", order: criteria.sort === "za" ? "DESC" : "ASC" }, { fieldName: "id", order: "ASC" }] } });
67
98
  const raw = res.events ?? [];
68
- const items = raw.filter(row => !!row.id).map(normalizeEvent);
99
+ const items = raw.filter(row => !!row.id).map(row => withRegistration(row, withDateBadge(normalizeEvent(row))));
69
100
  await writeCached(items);
70
101
  const metadata = (res as { pagingMetadata?: { total?: number } }).pagingMetadata;
71
102
  return { items, ...offsetPage(cursor, raw.length, 50, metadata), metadata: { fromCache: false } };
@@ -78,6 +109,16 @@ const svc = defineBackendCollection<S, EventRow>("himiEvents", {
78
109
  fromCache: page.metadata?.fromCache === true, errored, notErrored: !errored, isLoading: loading, notLoading: !loading,
79
110
  noEmpty: true, countLabel: "", emptyText: errored ? copyParam("events.errorText", "Events could not be loaded.") : "" };
80
111
  },
112
+ actions: {
113
+ // Share a card's event: its title and date line, plus the event page when the payload carried one.
114
+ share: async (s, arg) => {
115
+ const id = typeof arg === "string" ? arg : String((arg as { id?: unknown } | undefined)?.id ?? "");
116
+ const row = s.results.find(item => item.id === id);
117
+ if (!row) return undefined;
118
+ try { await shareSheet({ subject: row.title, text: [row.title, row.dateLine].filter(Boolean).join(" — "), ...(row.shareUrl ? { url: row.shareUrl } : {}) }); } catch { /* the sheet is best-effort: nothing to undo */ }
119
+ return undefined;
120
+ },
121
+ },
81
122
  });
82
123
  svc.load();
83
124
  export { svc };
@@ -20,6 +20,7 @@ import { isBridgeUnavailable } from "../_shared/owner-load-error.js";
20
20
  import { isProductSaved, toggleSavedProduct } from "../_shared/himi-store-saved.js";
21
21
  import { openRichLink } from "../_shared/rich-content-link.js";
22
22
  import { descriptionBody } from "../_shared/rich-body.js";
23
+ import { PRODUCT_DETAIL_FIELDS, productInfoSections, type ProductInfoSection } from "../_shared/store-product-info.js";
23
24
 
24
25
  /** Live cart size (sum of line-item quantities) for the top-bar cart badge.
25
26
  * Best-effort: a cart error just yields 0 (badge hidden), never fails the screen. */
@@ -201,6 +202,7 @@ interface S {
201
202
  wasPriceEmpty: boolean;
202
203
  noSku: boolean;
203
204
  skuLabel: string;
205
+ infoSections: ProductInfoSection[];
204
206
  returnPolicyBody: string;
205
207
  shippingInfoBody: string;
206
208
  noReturnPolicyBody: boolean;
@@ -535,6 +537,7 @@ const svc = onlineService<S, {
535
537
  wasPriceEmpty: false,
536
538
  skuLabel: "",
537
539
  noSku: true,
540
+ infoSections: [],
538
541
  returnPolicyBody: "",
539
542
  shippingInfoBody: "",
540
543
  noReturnPolicyBody: true,
@@ -625,7 +628,7 @@ const svc = onlineService<S, {
625
628
  try { saved = isProductSaved(id); } catch { /* save action reports a retryable error */ }
626
629
  s = { ...s, saved, saveHint: "",
627
630
  optionValidationTarget: "",
628
- ...(id !== lastLoadedId ? { adding: false, added: false, shareHint: "", addedSheetPresented: false, quantity: 1, showOptionErrors: false, optionValidationRequest: 0, galleryIndex: 0, returnPolicyExpanded: false, shippingInfoExpanded: false } : {}) };
631
+ ...(id !== lastLoadedId ? { adding: false, added: false, shareHint: "", addedSheetPresented: false, quantity: 1, showOptionErrors: false, optionValidationRequest: 0, galleryIndex: 0, returnPolicyExpanded: false, shippingInfoExpanded: false, infoSections: [] } : {}) };
629
632
  // A fresh navigation into the product resets any quantity left over
630
633
  // from a previous visit to the same worker instance.
631
634
  if (!id) {
@@ -642,8 +645,8 @@ const svc = onlineService<S, {
642
645
  void cartCount.then((n) => { cartKnown = n; }, () => {});
643
646
  // The live product read goes out FIRST too — the local-db read below must never delay it.
644
647
  // Settled into a value so a failure is handled where it is awaited, never unhandled.
645
- // Read both the Ricos description and choice-linked images; V3 omits these fields by default.
646
- const live = getStoreProduct(id, ["DESCRIPTION", "PRODUCT_CHOICES_DISPLAY_IMAGE"], readOptions(arg)).then(
648
+ // Read the Ricos description, the Product Info sections (V3) and choice-linked images; V3 omits them by default.
649
+ const live = getStoreProduct(id, [...PRODUCT_DETAIL_FIELDS, "PRODUCT_CHOICES_DISPLAY_IMAGE"], readOptions(arg)).then(
647
650
  (r) => ({ ok: true as const, r }),
648
651
  (error) => ({ ok: false as const, error }),
649
652
  );
@@ -702,6 +705,7 @@ const svc = onlineService<S, {
702
705
  ...sale,
703
706
  commercePrice: sale.price,
704
707
  skuLabel: skuOf(p) ? `SKU: ${skuOf(p)}` : "",
708
+ infoSections: productInfoSections(p, base.infoSections),
705
709
  returnPolicyBody: productPolicyBody(p, "returns") || copyParam("product.returnPolicyBody", "") || copyParam("product.policyUnavailable", "Policy details have not been provided by this store."),
706
710
  shippingInfoBody: productPolicyBody(p, "shipping") || copyParam("product.shippingInfoBody", "") || copyParam("product.policyUnavailable", "Policy details have not been provided by this store."),
707
711
  ...productBody(p),
@@ -721,6 +725,12 @@ const svc = onlineService<S, {
721
725
  const quantity = parseQuantity(argValue(arg, "value"));
722
726
  return quantity ? derive({ ...s, quantity }) : s;
723
727
  },
728
+ toggleInfoSection: (s, arg) => {
729
+ const id = argValue(arg, "id");
730
+ if (!s.infoSections.some(section => section.id === id && !section.noTitle)) return s;
731
+ return derive({ ...s, infoSections: s.infoSections.map(section => section.id === id
732
+ ? { ...section, expanded: !section.expanded, collapsed: section.expanded } : section) });
733
+ },
724
734
  toggleReturnPolicy: s => derive({ ...s, returnPolicyExpanded: !s.returnPolicyExpanded }),
725
735
  toggleShippingInfo: s => derive({ ...s, shippingInfoExpanded: !s.shippingInfoExpanded }),
726
736
  // One chip row per option group. `group` is the descriptor slot ("1".."3");
@@ -5,7 +5,7 @@ import { defineService } from "@himalaya/state";
5
5
  import { getPost, reactToPost, removePostReaction, createForumComment, forumPostContent, type WixPost } from "@himalaya/wix-forum";
6
6
  import { presence, presenceAfterError, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
7
7
  import { openRichLink } from "../_shared/rich-content-link.js";
8
- import { richBody } from "../_shared/rich-body.js";
8
+ import { richBody, descriptionBody } from "../_shared/rich-body.js";
9
9
 
10
10
  import { readAccess, assertReadAccess, readSignInFlags, NO_READ_SIGN_IN } from "../_shared/member-read.js";
11
11
 
@@ -62,7 +62,8 @@ const svc = defineService<S, {
62
62
  const doc = forumPostContent(p);
63
63
  // A string `content` is legacy HTML only when it is not a serialized Ricos document, which must never
64
64
  // surface as raw JSON text.
65
- const legacy = plain(pp.contentText || (typeof pp.content === "string" && !doc ? pp.content : ""));
65
+ const legacy = pp.contentText ? plain(pp.contentText)
66
+ : descriptionBody(typeof pp.content === "string" && !doc ? pp.content : "").bodyText;
66
67
  // The Ricos `content`, drawn by RichContent; bodyJson "" (none, or rejected) shows the plain body instead,
67
68
  // which is the document's own text when there is no contentText.
68
69
  const { bodyJson, bodyText } = richBody(doc, legacy);
@@ -14,7 +14,7 @@ import {
14
14
  } from "@himalaya/wix-members";
15
15
  import { presence, presenceAfterError, detailInputId, type PresenceFlags } from "../_shared/detail-presence.js";
16
16
 
17
- import { readAccess, assertReadAccess, readSignInFlags, NO_READ_SIGN_IN } from "../_shared/member-read.js";
17
+ import { readAccess, assertReadAccess, readSignInFlags, NO_READ_SIGN_IN, type ReadAccess } from "../_shared/member-read.js";
18
18
 
19
19
  interface S extends PresenceFlags {
20
20
  signInRequired: boolean; noSignIn: boolean;
@@ -27,8 +27,8 @@ interface S extends PresenceFlags {
27
27
  photo: string;
28
28
  followHint: string;
29
29
  isFollowing: boolean;
30
- followers: number;
31
- following: number;
30
+ followers: number | null;
31
+ following: number | null;
32
32
  followLabel: string;
33
33
  followCount: string;
34
34
  }
@@ -42,8 +42,8 @@ function name(m: WixMember): string {
42
42
  function followLabelFor(isFollowing: boolean): string {
43
43
  return isFollowing ? "Following ✓" : "Follow";
44
44
  }
45
- function followCountFor(followers: number, following: number): string {
46
- return followers + " Followers · " + following + " Following";
45
+ function followCountFor(followers: number | null, following: number | null): string {
46
+ return followers === null || following === null ? "" : followers + " Followers · " + following + " Following";
47
47
  }
48
48
 
49
49
  const INITIAL: S = {
@@ -56,22 +56,24 @@ const INITIAL: S = {
56
56
  photo: "",
57
57
  followHint: "",
58
58
  isFollowing: false,
59
- followers: 0,
60
- following: 0,
59
+ followers: null,
60
+ following: null,
61
61
  followLabel: "Follow",
62
- followCount: "0 Followers · 0 Following",
62
+ followCount: "",
63
63
  ...presence("loading"), ...NO_READ_SIGN_IN,
64
64
  };
65
65
  let loadVersion = 0;
66
+ let loadedAccess: ReadAccess | undefined;
67
+ const pendingFollows = new Set<string>();
66
68
 
67
69
  const svc = defineService<S, {
68
70
  prepare: (s: S) => S;
69
71
  load: (s: S) => Promise<S | undefined>;
70
- toggleFollow: (s: S) => Promise<S>;
72
+ toggleFollow: (s: S) => Promise<S | undefined>;
71
73
  }>("spacesMemberDetail", {
72
74
  state: INITIAL,
73
75
  actions: {
74
- prepare: (s) => ({ ...s, ...INITIAL }),
76
+ prepare: (s) => { loadedAccess = undefined; return { ...s, ...INITIAL }; },
75
77
  load: async (s): Promise<S | undefined> => {
76
78
  const version = ++loadVersion;
77
79
  await svc.prepare();
@@ -83,15 +85,15 @@ const svc = defineService<S, {
83
85
  const m = ((res as unknown as { member?: WixMember }).member) ?? (res as unknown as WixMember);
84
86
  const n = name(m);
85
87
 
86
- // Each relationship read is best-effort: a 403 (site privacy config) must
87
- // default rather than blank the screen.
88
- let followers = 0;
89
- let following = 0;
88
+ // A forbidden/missing aggregate is unknown, while the public profile
89
+ // remains readable. The descriptor collapses an empty count label.
90
+ let followers: number | null = null;
91
+ let following: number | null = null;
90
92
  try {
91
93
  const counts = await getFollowCounts(id);
92
94
  followers = counts.followers;
93
95
  following = counts.following;
94
- } catch { /* default 0 / 0 */ }
96
+ } catch { /* unavailable counts stay hidden */ }
95
97
 
96
98
  let isFollowing = false;
97
99
  try {
@@ -100,6 +102,7 @@ const svc = defineService<S, {
100
102
 
101
103
  await assertReadAccess(access);
102
104
  if (version !== loadVersion) return undefined;
105
+ loadedAccess = access;
103
106
  return {
104
107
  ...INITIAL, status: "ready",
105
108
  id,
@@ -125,35 +128,48 @@ const svc = defineService<S, {
125
128
  // adjusts the follower total, then recomputes the label + count strings.
126
129
  toggleFollow: async (s) => {
127
130
  if (!s.id) return { ...s, followHint: "No member selected." };
128
- if (s.isFollowing) {
129
- try {
130
- await unfollowMember(s.id);
131
- const followers = Math.max(0, s.followers - 1);
132
- return {
133
- ...s,
134
- isFollowing: false,
135
- followers,
136
- followLabel: followLabelFor(false),
137
- followCount: followCountFor(followers, s.following),
138
- followHint: "Unfollowed.",
139
- };
140
- } catch {
141
- return { ...s, followHint: "Couldn't unfollow — try again." };
142
- }
143
- }
131
+ const access = loadedAccess;
132
+ if (!access || detailInputId() !== s.id) return undefined;
133
+ const pendingKey = JSON.stringify([access.tenant, access.member.scope, access.member.email, s.id]);
134
+ if (pendingFollows.has(pendingKey)) return undefined;
135
+ const version = loadVersion;
136
+ const current = () => version === loadVersion && access === loadedAccess && detailInputId() === s.id;
137
+ // Serialize this reader's writes to one member within this worker, including after
138
+ // navigation back to it. Another member or reader/site can act while this request is
139
+ // pending. Separate worker instances are isolated contexts and cannot share this guard.
140
+ pendingFollows.add(pendingKey);
144
141
  try {
145
- await followMember(s.id);
146
- const followers = s.followers + 1;
142
+ await assertReadAccess(access);
143
+ if (!current()) return undefined;
144
+ if (s.isFollowing) await unfollowMember(s.id);
145
+ else await followMember(s.id);
146
+ await assertReadAccess(access);
147
+ if (!current()) {
148
+ // The write is confirmed, but a reload overlapped it and may have read the old
149
+ // relationship. Same reader and member: re-read instead of leaving a stale label.
150
+ if (detailInputId() === s.id) await svc.load();
151
+ return undefined;
152
+ }
153
+ const isFollowing = !s.isFollowing;
154
+ const followers = s.followers === null ? null : Math.max(0, s.followers + (isFollowing ? 1 : -1));
147
155
  return {
148
156
  ...s,
149
- isFollowing: true,
157
+ isFollowing,
150
158
  followers,
151
- followLabel: followLabelFor(true),
159
+ followLabel: followLabelFor(isFollowing),
152
160
  followCount: followCountFor(followers, s.following),
153
- followHint: "Following ✓",
161
+ followHint: isFollowing ? "Following ✓" : "Unfollowed.",
154
162
  };
155
163
  } catch {
156
- return { ...s, followHint: "Couldn't follow — try again." };
164
+ // A failed write belongs only to the reader who loaded this profile.
165
+ // Compare explicitly: a context change discards this completion, while a
166
+ // failure for the same reader produces the retry hint below.
167
+ const now = await readAccess();
168
+ if (now.member.email !== access.member.email || now.member.scope !== access.member.scope || now.tenant !== access.tenant) return undefined;
169
+ if (!current()) return undefined;
170
+ return { ...s, followHint: s.isFollowing ? "Couldn't unfollow — try again." : "Couldn't follow — try again." };
171
+ } finally {
172
+ pendingFollows.delete(pendingKey);
157
173
  }
158
174
  },
159
175
  },