@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
@@ -1,3 +1,5 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
2
+ import { openRichLink } from "../_shared/rich-content-link.js";
1
3
  // Tier-5 bundle: __HIMI_APP_NAME__ — Store product DETAIL + show/hide (visibility).
2
4
  // Inner admin page opened from the products list (himi_owner_lite_product_<id>).
3
5
  // Uses the Stores v1 client (the Apptestersite catalog is v1; v3 428s) — getProduct
@@ -6,7 +8,7 @@
6
8
 
7
9
  import { defineService } from "@himalaya/state";
8
10
  import { getProduct, updateProduct, deleteProduct, type WixProduct } from "@himalaya/wix-stores";
9
- import { plainText, productImage, wixMediaUrl, WIX_DETAIL_IMAGE, type StoreProduct } from "../_shared/himi-store.js";
11
+ import { productImage, wixMediaUrl, WIX_DETAIL_IMAGE, type StoreProduct } from "../_shared/himi-store.js";
10
12
  import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback, ownerWriteError, isNotFound } from "../_shared/owner-load-error.js";
11
13
  import { DEMO_PRODUCTS, DEMO_NOTE, DEMO_SAVE_NOTE } from "../_shared/owner-preview-demo.js";
12
14
  import { stashCopy, copyParam } from "../_shared/himi-copy.js";
@@ -33,6 +35,10 @@ interface ProductState {
33
35
  title: string;
34
36
  subtitle: string;
35
37
  summary: string;
38
+ bodyJson: string;
39
+ bodyText: string;
40
+ hasBodyJson: boolean;
41
+ noBodyJson: boolean;
36
42
  initial: string;
37
43
  image: string;
38
44
  // Info rows
@@ -133,6 +139,10 @@ function shapeFields(s: Partial<ProductState>): ProductState {
133
139
  title: s.title ?? "",
134
140
  subtitle: s.subtitle ?? "",
135
141
  summary: s.summary ?? "",
142
+ bodyJson: s.bodyJson ?? "",
143
+ bodyText: s.bodyText ?? "",
144
+ hasBodyJson: !!s.bodyJson,
145
+ noBodyJson: !s.bodyJson,
136
146
  initial: s.initial ?? "?",
137
147
  image,
138
148
  price: s.price ?? "—",
@@ -170,7 +180,9 @@ function demoDetail(s: ProductState, id: string): ProductState | null {
170
180
  status: "ready",
171
181
  title: d.name,
172
182
  subtitle: d.visible ? "Visible in store" : "Hidden from store",
173
- summary: d.description,
183
+ summary: "",
184
+ bodyText: d.description,
185
+ bodyJson: "",
174
186
  initial: (d.name[0] || "?").toUpperCase(),
175
187
  image: "",
176
188
  price: d.price,
@@ -190,7 +202,9 @@ function cacheRow(s: ProductState): DbEntity {
190
202
  name: s.title,
191
203
  title: s.title,
192
204
  subtitle: s.subtitle,
193
- summary: s.summary,
205
+ summary: s.bodyText,
206
+ bodyJson: s.bodyJson,
207
+ bodyText: s.bodyText,
194
208
  initial: s.initial,
195
209
  image: s.image,
196
210
  price: s.price,
@@ -204,6 +218,7 @@ function cacheRow(s: ProductState): DbEntity {
204
218
  /** Rehydrate a ready detail from a last-synced local row (offline read fallback). */
205
219
  function cachedDetail(s: ProductState, row: DbEntity): ProductState {
206
220
  const visible = row.visible !== "false";
221
+ const body = descriptionBody(row.bodyJson || row.description, descriptionBody(row.bodyText || row.summary).bodyText);
207
222
  const title = String(row.title ?? row.name ?? "");
208
223
  return shape({
209
224
  ...s,
@@ -211,7 +226,9 @@ function cachedDetail(s: ProductState, row: DbEntity): ProductState {
211
226
  status: "ready",
212
227
  title,
213
228
  subtitle: String(row.subtitle ?? (visible ? "Visible in store" : "Hidden from store")),
214
- summary: String(row.summary ?? ""),
229
+ summary: "",
230
+ bodyJson: body.bodyJson,
231
+ bodyText: body.bodyText,
215
232
  initial: String(row.initial ?? (title[0] || "?")).slice(0, 1).toUpperCase(),
216
233
  image: String(row.image ?? ""),
217
234
  price: String(row.price ?? "—"),
@@ -220,25 +237,29 @@ function cachedDetail(s: ProductState, row: DbEntity): ProductState {
220
237
  inventory: String(row.inventory ?? "—"),
221
238
  visibleRaw: visible,
222
239
  error: "",
240
+ actionMessage: "",
223
241
  demoNote: "",
224
242
  });
225
243
  }
226
244
 
227
245
  async function refresh(prev: ProductState, id: string, actionMessage = ""): Promise<ProductState> {
228
- const res = await getProduct(id);
246
+ const res = await getProduct(id, { richDescription: true });
229
247
  const p = (res.product ?? res) as WixProduct;
230
248
  if (!p || !(p as any).id) {
231
249
  return shape({ ...prev, id, status: "error", title: "", error: copyParam("ownerProduct.errorNotFound", "product not found"), actionMessage });
232
250
  }
233
251
  const name = ((p as any).name || "Untitled product").trim();
234
252
  const visible = (p as any).visible !== false;
253
+ const body = descriptionBody((p as { richDescription?: unknown }).richDescription ?? (p as any).description, descriptionBody((p as any).description || (p as any).plainDescription).bodyText);
235
254
  return shape({
236
255
  ...prev,
237
256
  id,
238
257
  status: "ready",
239
258
  title: name,
240
259
  subtitle: visible ? "Visible in store" : "Hidden from store",
241
- summary: plainText((p as any).description || ""),
260
+ summary: "",
261
+ bodyJson: body.bodyJson,
262
+ bodyText: body.bodyText,
242
263
  initial: (name[0] || "?").toUpperCase(),
243
264
  image: imageOf(p),
244
265
  price: priceOf(p),
@@ -300,6 +321,7 @@ const svc = defineService<ProductState, {
300
321
  show: (s: ProductState) => Promise<ProductState>;
301
322
  hide: (s: ProductState) => Promise<ProductState>;
302
323
  delete: (s: ProductState) => Promise<ProductState>;
324
+ openLink: (s: ProductState, arg?: unknown) => Promise<ProductState>;
303
325
  cancelDelete: (s: ProductState) => Promise<ProductState>;
304
326
  confirmDelete: (s: ProductState) => Promise<ProductState | undefined>;
305
327
  }>("himiOwnerLiteProduct", {
@@ -308,6 +330,12 @@ const svc = defineService<ProductState, {
308
330
  // perVisit: a cached embed's warm return starts this run fresh, as a cold boot does (`__resetRunState`).
309
331
  perVisit: [...DELETE_RUN_KEYS],
310
332
  actions: {
333
+ openLink: async (_state, arg): Promise<ProductState> => {
334
+ const opened = await openRichLink(arg);
335
+ const failed = copyParam("ownerProduct.linkOpenFailed", "This link could not be opened.");
336
+ const current = svc.getState();
337
+ return shape({ ...current, actionMessage: opened ? (current.actionMessage === failed ? "" : current.actionMessage) : failed });
338
+ },
311
339
  load: async (s0, arg) => {
312
340
  const s = disarm(s0);
313
341
  stashCopy(arg); // absorb the descriptor's onAppear copy-pack param
@@ -394,4 +422,9 @@ if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydra
394
422
  if (booted && typeof booted === "object") Object.assign(booted, { deletePending: false, noDeleteConfirmation: true });
395
423
  }
396
424
 
425
+ // Hydration shallow-merges older snapshots over defaults; derive both body gates from
426
+ // the restored document before the first paint, without waiting for a load action.
427
+ const bootedBody = svc.getState();
428
+ Object.assign(bootedBody, { hasBodyJson: !!bootedBody.bodyJson, noBodyJson: !bootedBody.bodyJson });
429
+
397
430
  export { svc };
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // Tier-5 bundle: __HIMI_APP_NAME__ — create/edit Store product (the CRUD "C" + "U").
2
3
  // Opened from the products list ("New product") or a product detail ("Edit").
3
4
  // The Apptestersite catalog is Stores v1 (v3 428s), so this uses the v1 client:
@@ -243,19 +244,23 @@ const svc = defineService<ProductFormState, {
243
244
  name: (p as any).name ?? "",
244
245
  price: priceOf(p),
245
246
  sku: (p as any).sku ?? "",
246
- ...((html: string, rich: unknown) => {
247
+ ...((raw: unknown, rich: unknown) => {
248
+ const html = typeof raw === "string" ? raw : "";
249
+ const preview = htmlPreviewText(html);
250
+ const description = descriptionBody(raw);
251
+ const legacyRich = !!parseRicosJson(raw) || (raw != null && typeof raw !== "string") || (!!preview.trim() && !description.bodyText);
247
252
  // `richDescription` is present only on a Catalog V3 site. V3 is edited in the RichContentEditor;
248
253
  // a V3 product whose Ricos document did not come back is edited only when its HTML is trivial
249
254
  // (seeded as paragraphs), else locked — fail closed. V1 keeps the plain/locked split: the
250
255
  // editor writes Ricos, and a V1 description is HTML.
251
256
  const doc = rich === undefined || rich === null ? null : parseRicosJson(rich);
252
257
  const hasDoc = !!doc && doc.nodes.length > 0;
253
- const text = htmlPreviewText(html);
254
- const descriptionMode = rich === undefined ? (htmlIsTrivial(html) ? "plain" as const : "locked" as const)
258
+ const text = descriptionBody(rich, legacyRich ? description.bodyText : preview).bodyText;
259
+ const descriptionMode = rich === undefined ? (!legacyRich && htmlIsTrivial(html) ? "plain" as const : "locked" as const)
255
260
  : hasDoc || htmlIsTrivial(html) ? "editor" as const : "locked" as const;
256
- const descriptionJson = descriptionMode !== "editor" ? "" : hasDoc ? JSON.stringify(rich) : text ? docJson(plainPostBody(text)) : "";
261
+ const descriptionJson = descriptionMode !== "editor" ? "" : hasDoc ? JSON.stringify(doc) : text ? docJson(plainPostBody(text)) : "";
257
262
  return { ...DESCRIPTION_LOADED, description: text, originalDescription: text, descriptionMode, descriptionJson };
258
- })(String((p as any).description ?? ""), (p as { richDescription?: unknown }).richDescription),
263
+ })((p as any).description, (p as { richDescription?: unknown }).richDescription),
259
264
  visible: (p as any).visible !== false ? "true" : "false",
260
265
  status: "idle",
261
266
  error: "",
@@ -265,12 +270,13 @@ const svc = defineService<ProductFormState, {
265
270
  // Offline: prefill from the last-synced local cache before the demo/error fallbacks.
266
271
  const cached = await cacheGet(id);
267
272
  if (cached) {
273
+ const description = descriptionBody(cached.descriptionJson || cached.bodyJson, descriptionBody(cached.description ?? cached.bodyText ?? cached.summary).bodyText).bodyText;
268
274
  return shape({
269
275
  ...s, mode: "edit", id, heading: "Edit product",
270
276
  name: String(cached.name ?? ""), price: editablePrice(String(cached.price ?? "")), sku: String(cached.sku ?? ""),
271
- description: String(cached.description ?? ""), originalDescription: String(cached.description ?? ""), ...DESCRIPTION_LOADED,
272
- // A row cached with its document reopens in the editor. An older row has none, so its verdict
273
- // decides: plain when it was plain, else locked (it carries no verdict either: fail closed).
277
+ description, originalDescription: description, ...DESCRIPTION_LOADED,
278
+ // Only the form's document establishes its editor mode. Detail bodyJson can also come
279
+ // from a V1 catalog, so it supplies the preview without guessing a V3 write contract.
274
280
  ...(cached.descriptionJson ? { descriptionMode: "editor" as const, descriptionJson: String(cached.descriptionJson) }
275
281
  : { descriptionMode: cached.descriptionProtected === "false" ? "plain" as const : "locked" as const, descriptionJson: "" }),
276
282
  visible: cached.visible === "false" ? "false" : "true",
@@ -1,3 +1,4 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
1
2
  // Tier-5 bundle: __HIMI_APP_NAME__ — Store products management list.
2
3
  // Owner/management view of the active business's Wix Stores catalog. Site-scoped
3
4
  // through the native wix-native (mode:owner) provider + WixApiCapability, which
@@ -6,11 +7,11 @@
6
7
 
7
8
  import { compareText } from "../_shared/engine-intl.js";
8
9
  import { defineBackendCollection } from "../_shared/collection-service.js";
9
- import { searchStoreProducts, productImage, productPrice, plainText, type StoreProduct } from "../_shared/himi-store.js";
10
+ import { searchStoreProducts, productImage, productPrice, type StoreProduct } from "../_shared/himi-store.js";
10
11
  import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback } from "../_shared/owner-load-error.js";
11
12
  import { DEMO_PRODUCTS, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
12
13
  import { copyParam } from "../_shared/himi-copy.js";
13
- import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
14
+ import { get, getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
14
15
 
15
16
  // Offline-first (data-offline): the owner's last-synced rows are the READ-THROUGH
16
17
  // cache for the `owner_products` entity — paint them instantly (works offline),
@@ -24,7 +25,9 @@ async function readCachedRows(): Promise<ProductRow[]> {
24
25
  try { return (await getAll(ENTITY)) as unknown as ProductRow[]; } catch { return []; }
25
26
  }
26
27
  async function writeCachedRows(rows: ProductRow[]): Promise<void> {
27
- try { for (const r of rows) await upsert(ENTITY, r as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
28
+ try {
29
+ for (const r of rows) await upsert(ENTITY, { ...await get(ENTITY, r.id), ...r, title: r.name } as unknown as DbEntity);
30
+ } catch { /* capability not granted → online-only */ }
28
31
  }
29
32
 
30
33
  interface ProductRow {
@@ -76,7 +79,7 @@ function normalize(raw: StoreProduct): ProductRow {
76
79
  const name = (raw.name || "Untitled product").trim();
77
80
  const image = productImage(raw);
78
81
  const price = productPrice(raw);
79
- const desc = plainText(raw.plainDescription || raw.description);
82
+ const desc = descriptionBody(raw.description, descriptionBody(raw.plainDescription).bodyText).bodyText;
80
83
  return {
81
84
  id: raw.id || "",
82
85
  name,
@@ -1,3 +1,5 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
2
+ import { openRichLink } from "../_shared/rich-content-link.js";
1
3
  // Tier-5 bundle: __HIMI_APP_NAME__ — Bookings service DETAIL (read view).
2
4
  // Inner admin page opened from the services list (himi_owner_lite_service_<id>).
3
5
  // Fetches the single Bookings v2 service and exposes a flat string state bound by
@@ -35,6 +37,10 @@ interface ServiceState {
35
37
  title: string;
36
38
  subtitle: string;
37
39
  summary: string;
40
+ bodyJson: string;
41
+ bodyText: string;
42
+ hasBodyJson: boolean;
43
+ noBodyJson: boolean;
38
44
  initial: string;
39
45
  image: string;
40
46
  typeLabel: string;
@@ -99,7 +105,7 @@ function imageOf(s: WixService): string {
99
105
 
100
106
  /** The fact rows this detail shows (the descriptor"s DETAIL_T5 rows): each publishes
101
107
  * `<key>Blank`, so a fact with no value hides instead of painting a dangling label. */
102
- const FACT_KEYS = ["typeLabel", "category", "capacity", "description"] as const;
108
+ const FACT_KEYS = ["typeLabel", "category", "capacity"] as const;
103
109
 
104
110
  function shape(s: Partial<ServiceState>): ServiceState {
105
111
  const out = shapeFields(s);
@@ -121,6 +127,10 @@ function shapeFields(s: Partial<ServiceState>): ServiceState {
121
127
  title: s.title ?? "",
122
128
  subtitle: s.subtitle ?? "",
123
129
  summary: s.summary ?? "",
130
+ bodyJson: s.bodyJson ?? "",
131
+ bodyText: s.bodyText ?? "",
132
+ hasBodyJson: !!s.bodyJson,
133
+ noBodyJson: !s.bodyJson,
124
134
  initial: s.initial ?? "?",
125
135
  image,
126
136
  typeLabel: s.typeLabel ?? "—",
@@ -152,7 +162,9 @@ function demoDetail(s: ServiceState, id: string): ServiceState | null {
152
162
  status: "ready",
153
163
  title: d.name,
154
164
  subtitle: [d.type, d.category].filter(Boolean).join(" · "),
155
- summary: d.description,
165
+ summary: "",
166
+ bodyText: d.description,
167
+ bodyJson: "",
156
168
  initial: (d.name[0] || "S").toUpperCase(),
157
169
  image: "",
158
170
  typeLabel: d.type,
@@ -169,20 +181,24 @@ function cachedDetail(s: ServiceState, row: DbEntity): ServiceState {
169
181
  const title = String(row.title ?? row.name ?? "");
170
182
  const type = String(row.typeLabel ?? "");
171
183
  const category = String(row.category ?? "");
184
+ const body = descriptionBody(row.bodyJson || row.description, descriptionBody(row.bodyText || row.summary).bodyText);
172
185
  return shape({
173
186
  ...s,
174
187
  id: String(row.id),
175
188
  status: "ready",
176
189
  title,
177
190
  subtitle: String(row.subtitle ?? [type, category].filter(Boolean).join(" · ")),
178
- summary: String(row.summary ?? row.description ?? ""),
191
+ summary: "",
192
+ bodyJson: body.bodyJson,
193
+ bodyText: body.bodyText,
179
194
  initial: String(row.initial ?? (title[0] || "S")).slice(0, 1).toUpperCase(),
180
195
  image: String(row.image ?? ""),
181
196
  typeLabel: type || "—",
182
197
  category: category || "—",
183
198
  capacity: String(row.capacity ?? "—"),
184
- description: String(row.description ?? "—"),
199
+ description: body.bodyText || "—",
185
200
  error: "",
201
+ actionMessage: "",
186
202
  demoNote: "",
187
203
  });
188
204
  }
@@ -197,21 +213,24 @@ async function refresh(prev: ServiceState, id: string): Promise<ServiceState> {
197
213
  const type = serviceTypeLabel((svc as any).type);
198
214
  const category = categoryName(svc) || "—";
199
215
  const cap = (svc as any).defaultCapacity;
200
- const desc = ((svc as any).description || (svc as any).tagLine || "").trim();
216
+ const body = descriptionBody((svc as any).description, descriptionBody((svc as any).tagLine).bodyText);
201
217
  return shape({
202
218
  ...prev,
203
219
  id,
204
220
  status: "ready",
205
221
  title: name,
206
222
  subtitle: [type, category !== "—" ? category : ""].filter(Boolean).join(" · "),
207
- summary: desc,
223
+ summary: "",
224
+ bodyJson: body.bodyJson,
225
+ bodyText: body.bodyText,
208
226
  initial: (name[0] || "S").toUpperCase(),
209
227
  image: imageOf(svc),
210
228
  typeLabel: type,
211
229
  category,
212
230
  capacity: cap != null ? `${cap} ${Number(cap) === 1 ? "guest" : "guests"}` : "—",
213
- description: desc || "—",
231
+ description: body.bodyText || "—",
214
232
  error: "",
233
+ actionMessage: "",
215
234
  demoNote: "",
216
235
  });
217
236
  }
@@ -220,9 +239,16 @@ const svc = defineService<ServiceState, {
220
239
  load: (s: ServiceState, arg?: unknown) => Promise<ServiceState>;
221
240
  retry: (s: ServiceState) => Promise<ServiceState>;
222
241
  delete: (s: ServiceState) => Promise<ServiceState>;
242
+ openLink: (s: ServiceState, arg?: unknown) => Promise<ServiceState>;
223
243
  }>("himiOwnerLiteService", {
224
244
  state: shape({ status: "loading" }),
225
245
  actions: {
246
+ openLink: async (_state, arg): Promise<ServiceState> => {
247
+ const opened = await openRichLink(arg);
248
+ const failed = copyParam("ownerService.linkOpenFailed", "This link could not be opened.");
249
+ const current = svc.getState();
250
+ return shape({ ...current, actionMessage: opened ? (current.actionMessage === failed ? "" : current.actionMessage) : failed });
251
+ },
226
252
  load: async (s, arg) => {
227
253
  stashCopy(arg); // absorb the descriptor's onAppear copy-pack param
228
254
  const id = idFrom(arg, s.id) || inputId();
@@ -284,4 +310,9 @@ const svc = defineService<ServiceState, {
284
310
  },
285
311
  });
286
312
 
313
+ // Hydration shallow-merges older snapshots over defaults; derive both body gates from
314
+ // the restored document before the first paint, without waiting for a load action.
315
+ const bootedBody = svc.getState();
316
+ Object.assign(bootedBody, { hasBodyJson: !!bootedBody.bodyJson, noBodyJson: !bootedBody.bodyJson });
317
+
287
318
  export { svc };
@@ -1,3 +1,6 @@
1
+ import { descriptionBody } from "../_shared/rich-body.js";
2
+ import { htmlIsTrivial, htmlPreviewText } from "../_shared/owner-rich-guard.js";
3
+ import { parseRicosJson } from "@himalaya/wix-rich-content";
1
4
  // Tier-5 bundle: __HIMI_APP_NAME__ — edit Bookings service (the CRUD "U").
2
5
  // Opened from the service detail ("Edit"). EDIT-ONLY — the Bookings v2
3
6
  // service-create needs a full nested structure the generated client doesn't
@@ -31,6 +34,10 @@ interface ServiceFormState {
31
34
  heading: string;
32
35
  name: string;
33
36
  description: string;
37
+ originalDescription: unknown;
38
+ descriptionProtected: boolean;
39
+ descriptionEditable: boolean;
40
+ descriptionNote: string;
34
41
  category: string;
35
42
  status: "idle" | "loading" | "saving" | "saved" | "error";
36
43
  writeBusy: boolean;
@@ -79,6 +86,16 @@ function categoryName(s: WixService): string {
79
86
  return "";
80
87
  }
81
88
 
89
+ function descriptionFields(value: unknown, known = true) {
90
+ const body = descriptionBody(value);
91
+ const protectedBody = !known || (value != null && (typeof value !== "string" || !!parseRicosJson(value)
92
+ || !htmlIsTrivial(value) || (!!htmlPreviewText(value).trim() && !body.bodyText)));
93
+ return { description: body.bodyText, originalDescription: known ? value ?? "" : undefined, descriptionProtected: protectedBody,
94
+ descriptionEditable: !protectedBody, descriptionNote: protectedBody
95
+ ? known ? copyParam("ownerServiceForm.richDescriptionNote", "This description has formatting or media. Edit it in your site dashboard to keep that content intact; the name can be changed here.")
96
+ : copyParam("ownerServiceForm.descriptionNotLoaded", "The description is not available yet. The name can be changed here.") : "" };
97
+ }
98
+
82
99
  function shape(s: Partial<ServiceFormState>): ServiceFormState {
83
100
  const id = typeof s.id === "string" ? s.id : "";
84
101
  const savedId = typeof s.savedId === "string" ? s.savedId : id;
@@ -89,6 +106,10 @@ function shape(s: Partial<ServiceFormState>): ServiceFormState {
89
106
  heading: s.heading ?? copyParam("ownerServiceForm.headingEdit", "Edit service"),
90
107
  name: s.name ?? "",
91
108
  description: s.description ?? "",
109
+ originalDescription: s.originalDescription,
110
+ descriptionProtected: s.descriptionProtected ?? true,
111
+ descriptionEditable: s.descriptionProtected === false,
112
+ descriptionNote: s.descriptionNote ?? copyParam("ownerServiceForm.descriptionNotLoaded", "The description is not available yet. The name can be changed here."),
92
113
  category: s.category ?? "",
93
114
  status: s.status ?? "idle",
94
115
  writeBusy: s.writeBusy ?? false,
@@ -117,6 +138,8 @@ const svc = defineService<ServiceFormState, {
117
138
  submit: (s: ServiceFormState, values?: Record<string, unknown>) => Promise<ServiceFormState>;
118
139
  }>("himiOwnerLiteServiceForm", {
119
140
  state: shape({ id: initialId }),
141
+ notHydrated: ["originalDescription", "descriptionProtected", "descriptionEditable", "descriptionNote"],
142
+ perVisit: ["originalDescription", "descriptionProtected", "descriptionEditable", "descriptionNote"],
120
143
  actions: {
121
144
  load: async (s, arg): Promise<ServiceFormState | undefined> => landsOnLive(svc, s, await (async () => {
122
145
  stashCopy(arg); // absorb the descriptor's onAppear copy-pack param
@@ -130,7 +153,7 @@ const svc = defineService<ServiceFormState, {
130
153
  if (d) {
131
154
  return shape({
132
155
  ...s, id, heading: copyParam("ownerServiceForm.headingEdit", "Edit service"),
133
- name: d.name, description: d.description, category: d.category,
156
+ name: d.name, ...descriptionFields(d.description), category: d.category,
134
157
  status: "idle", error: "", savedMessage: "", demoNote: copyParam("ownerCommon.demoNote", DEMO_NOTE),
135
158
  });
136
159
  }
@@ -144,7 +167,7 @@ const svc = defineService<ServiceFormState, {
144
167
  ...s, id, heading: copyParam("ownerServiceForm.headingEdit", "Edit service"),
145
168
  revision: String((svcObj as any).revision ?? ""),
146
169
  name: (svcObj as any).name ?? "",
147
- description: (svcObj as any).description ?? "",
170
+ ...descriptionFields((svcObj as any).description),
148
171
  category: categoryName(svcObj),
149
172
  status: "idle", error: "", savedMessage: "",
150
173
  });
@@ -155,7 +178,7 @@ const svc = defineService<ServiceFormState, {
155
178
  return shape({
156
179
  ...s, id, heading: "Edit service",
157
180
  name: String(cached.name ?? cached.title ?? ""),
158
- description: String(cached.description ?? ""),
181
+ ...descriptionFields(cached.description, Object.hasOwn(cached, "description")),
159
182
  category: String(cached.category ?? ""),
160
183
  status: "idle", error: "", savedMessage: "",
161
184
  });
@@ -165,7 +188,7 @@ const svc = defineService<ServiceFormState, {
165
188
  if (d) {
166
189
  return shape({
167
190
  ...s, id, heading: copyParam("ownerServiceForm.headingEdit", "Edit service"),
168
- name: d.name, description: d.description, category: d.category,
191
+ name: d.name, ...descriptionFields(d.description), category: d.category,
169
192
  status: "idle", error: "", savedMessage: "", demoNote: copyParam("ownerCommon.demoNote", DEMO_NOTE),
170
193
  });
171
194
  }
@@ -177,25 +200,42 @@ const svc = defineService<ServiceFormState, {
177
200
  if (isOwnerPreviewDemo() || s.demoNote) return demoRefusal(s);
178
201
  const v = values ?? {};
179
202
  const targetId = s.id || (typeof v.id === "string" ? v.id : "");
203
+ const cached = !s.id && targetId ? await cacheGet(targetId) : null;
204
+ // Legacy queued payloads arrive without a loaded form. Read the current body before
205
+ // accepting their text, so replay cannot replace a rich description blindly.
206
+ if (!s.id && targetId) {
207
+ try {
208
+ const service = (await getService(targetId)).service;
209
+ if (!service?.id) return shape({ ...s, status: "error", error: copyParam("ownerServiceForm.errorNotFound", "Service not found") });
210
+ s = shape({ ...s, id: targetId, revision: String(service.revision ?? ""),
211
+ ...descriptionFields(service.description), category: categoryName(service) || String(cached?.category ?? "") });
212
+ } catch (error) {
213
+ return shape({ ...s, status: "error", error: ownerWriteError(error), savedMessage: "" });
214
+ }
215
+ }
180
216
  const name = String(v.name ?? s.name).trim();
181
217
  const description = String(v.description ?? s.description).trim();
218
+ const descriptionEdited = description !== descriptionBody(s.originalDescription).bodyText.trim();
219
+ if (s.descriptionProtected && descriptionEdited) return shape({ ...s, status: "error", error: s.descriptionNote, savedMessage: "" });
182
220
  if (!name) return shape({ ...s, status: "error", error: copyParam("ownerServiceForm.nameRequired", "Name is required"), savedMessage: "" });
183
221
  if (!targetId) return shape({ ...s, status: "error", error: copyParam("ownerServiceForm.errorNoService", "No service to edit."), savedMessage: "" });
184
222
 
185
- const cached = !s.id ? await cacheGet(targetId) : null;
186
- const localRow: DbEntity = { ...(cached ?? {}), id: targetId, name, title: name, description, category: s.category || String(cached?.category ?? "") };
223
+ const localRow: DbEntity = { ...(cached ?? {}), id: targetId, name, title: name,
224
+ ...(descriptionEdited || s.originalDescription !== undefined ? { description: descriptionEdited ? description : s.originalDescription } : {}),
225
+ category: s.category || String(cached?.category ?? "") };
187
226
  try {
188
227
  // v2 PATCH is a masked update requiring the current `revision` (optimistic
189
228
  // concurrency), captured from the getService on load. The response wraps
190
229
  // the UPDATED entity ({ service }) with its bumped revision — carry it
191
230
  // into state, or the SECOND consecutive save would send the stale
192
231
  // load-time revision and reject.
193
- const res = await updateService(targetId, { name, ...(description ? { description } : {}), ...(s.revision ? { revision: s.revision } : {}) });
232
+ const res = await updateService(targetId, { name, ...(!s.descriptionProtected && descriptionEdited ? { description } : {}), ...(s.revision ? { revision: s.revision } : {}) });
194
233
  const bumped = String((res?.service as { revision?: unknown } | undefined)?.revision ?? "");
195
234
  // The local row follows only what Wix confirmed.
196
235
  await cachePut(localRow);
197
236
  return shape({
198
237
  ...s, id: targetId, name, description,
238
+ originalDescription: descriptionEdited ? description : s.originalDescription,
199
239
  revision: bumped || s.revision,
200
240
  status: "saved", error: "", savedMessage: copyParam("ownerServiceForm.savedMessage", "Service saved."),
201
241
  savedId: targetId,
@@ -211,4 +251,10 @@ const svc = defineService<ServiceFormState, {
211
251
  },
212
252
  });
213
253
 
254
+ // Older binaries ignore notHydrated. Their restored preview must also stay read-only until load.
255
+ if (!(globalThis as { himalaya?: { hydrationOptOut?: boolean } }).himalaya?.hydrationOptOut) {
256
+ Object.assign(svc.getState(), { originalDescription: undefined, descriptionProtected: true, descriptionEditable: false,
257
+ descriptionNote: copyParam("ownerServiceForm.descriptionNotLoaded", "The description is not available yet. The name can be changed here.") });
258
+ }
259
+
214
260
  export { svc };
@@ -8,7 +8,7 @@ import { queryServices, type WixService } from "@himalaya/wix-bookings";
8
8
  import { ownerLoadError, isOwnerPreviewDemo, ownerDemoFallback } from "../_shared/owner-load-error.js";
9
9
  import { DEMO_SERVICES, DEMO_NOTE } from "../_shared/owner-preview-demo.js";
10
10
  import { copyParam } from "../_shared/himi-copy.js";
11
- import { getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
11
+ import { get, getAll, upsert, type DbEntity } from "../_shared/owner-local-db.js";
12
12
 
13
13
  // Last-synced rows are an explicitly incomplete fallback for an unfiltered
14
14
  // request during an outage. Filtered pages never claim this cache is complete.
@@ -17,8 +17,10 @@ const ENTITY = "owner_services";
17
17
  async function readCachedRows(): Promise<ServiceRow[]> {
18
18
  try { return (await getAll(ENTITY)) as unknown as ServiceRow[]; } catch { return []; }
19
19
  }
20
+ // A list row carries no description. Merge it over the stored row so a normal refresh keeps the
21
+ // rich body the service form cached, as Products and Events do.
20
22
  async function writeCachedRows(rows: ServiceRow[]): Promise<void> {
21
- try { for (const r of rows) await upsert(ENTITY, r as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
23
+ try { for (const r of rows) await upsert(ENTITY, { ...await get(ENTITY, r.id), ...r } as unknown as DbEntity); } catch { /* capability not granted → online-only */ }
22
24
  }
23
25
 
24
26
  interface ServiceRow {
@@ -589,6 +589,15 @@ export default defineApp({
589
589
  "parentScreenId": "himi_groups",
590
590
  "tabId": ""
591
591
  },
592
+ {
593
+ "screenId": "himi_group_about",
594
+ "isPublic": true,
595
+ "paramKeys": [
596
+ "id"
597
+ ],
598
+ "parentScreenId": "himi_group",
599
+ "tabId": ""
600
+ },
592
601
  {
593
602
  "screenId": "himi_cms",
594
603
  "isPublic": true,
@@ -1071,7 +1080,7 @@ export default defineApp({
1071
1080
  // Your on-device database. It must declare every entity a worker reads or writes,
1072
1081
  // or those calls fail closed — keep it in step with `local_database` above.
1073
1082
  dataSchema: {
1074
- "version": 2,
1083
+ "version": 3,
1075
1084
  "entities": [
1076
1085
  {
1077
1086
  "name": "products",
@@ -1152,6 +1161,25 @@ export default defineApp({
1152
1161
  }
1153
1162
  ]
1154
1163
  },
1164
+ {
1165
+ "name": "booking_prefs",
1166
+ "encrypted": false,
1167
+ "fields": [
1168
+ {
1169
+ "name": "id",
1170
+ "type": "string",
1171
+ "required": true
1172
+ },
1173
+ {
1174
+ "name": "locationId",
1175
+ "type": "string"
1176
+ },
1177
+ {
1178
+ "name": "locationLabel",
1179
+ "type": "string"
1180
+ }
1181
+ ]
1182
+ },
1155
1183
  {
1156
1184
  "name": "cms_collections",
1157
1185
  "encrypted": false,