@wix/himalaya-cli 0.1035.0 → 0.1037.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 (276) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/core/dev-server/src/builder.d.ts +7 -1
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/events/src/display.d.ts +13 -1
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/portfolio/src/index.d.ts +46 -9
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-events.mjs +28 -6
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-portfolio.mjs +14 -2
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores-collections.mjs +1 -1
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/wix-stores.mjs +7 -1
  8. package/dist/cli.mjs +482 -113
  9. package/dist/preview-assets.generated.bin +0 -0
  10. package/fixtures/wix-canned.json +51 -2
  11. package/guides/sdk-reference.json +32 -14
  12. package/guides/wix-business-context.md +13 -1
  13. package/guides/wix-site-to-himi-app.md +10 -1
  14. package/package.json +2 -2
  15. package/templates/beauty-salon/dev/index.ts +72 -175
  16. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +16 -9
  17. package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +19 -5
  18. package/templates/beauty-salon/tier5-src/_shared/engine-intl.ts +51 -0
  19. package/templates/beauty-salon/tier5-src/_shared/himi-restaurants.ts +4 -4
  20. package/templates/beauty-salon/tier5-src/_shared/owner-load-error.ts +49 -0
  21. package/templates/beauty-salon/tier5-src/_shared/wix-service-price.ts +1 -1
  22. package/templates/beauty-salon/tier5-src/himi-booking-bookings/index.ts +14 -8
  23. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +12 -1
  24. package/templates/beauty-salon/tier5-src/himi-branded-lite-info/index.ts +5 -2
  25. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  26. package/templates/beauty-salon/tier5-src/wix-spaces-collections/index.ts +1 -1
  27. package/templates/beauty-salon/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  28. package/templates/beauty-salon-owner/dev/index.ts +84 -599
  29. package/templates/beauty-salon-owner/tier5-src/_shared/aria-chat.ts +22 -6
  30. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +16 -9
  31. package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +19 -5
  32. package/templates/beauty-salon-owner/tier5-src/_shared/engine-intl.ts +51 -0
  33. package/templates/beauty-salon-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  34. package/templates/beauty-salon-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  35. package/templates/beauty-salon-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  36. package/templates/branded-lite/dev/index.ts +178 -264
  37. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +16 -9
  38. package/templates/branded-lite/tier5-src/_shared/collection-service.ts +19 -5
  39. package/templates/branded-lite/tier5-src/_shared/engine-intl.ts +51 -0
  40. package/templates/branded-lite/tier5-src/_shared/himi-restaurants.ts +4 -4
  41. package/templates/branded-lite/tier5-src/_shared/owner-load-error.ts +49 -0
  42. package/templates/branded-lite/tier5-src/_shared/wix-service-price.ts +1 -1
  43. package/templates/branded-lite/tier5-src/himi-booking-bookings/index.ts +14 -8
  44. package/templates/branded-lite/tier5-src/himi-branded-lite-cms/index.ts +5 -5
  45. package/templates/branded-lite/tier5-src/himi-branded-lite-coupons/index.ts +33 -7
  46. package/templates/branded-lite/tier5-src/himi-branded-lite-donations/index.ts +3 -3
  47. package/templates/branded-lite/tier5-src/himi-branded-lite-forms/index.ts +11 -10
  48. package/templates/branded-lite/tier5-src/himi-branded-lite-giftcards/index.ts +3 -3
  49. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +12 -1
  50. package/templates/branded-lite/tier5-src/himi-branded-lite-info/index.ts +5 -2
  51. package/templates/branded-lite/tier5-src/himi-branded-lite-invoices/index.ts +3 -3
  52. package/templates/branded-lite/tier5-src/himi-branded-lite-loyalty/index.ts +1 -1
  53. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  54. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio/index.ts +2 -2
  55. package/templates/branded-lite/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -3
  56. package/templates/branded-lite/tier5-src/himi-branded-lite-program/index.ts +1 -1
  57. package/templates/branded-lite/tier5-src/himi-branded-lite-reserve/index.ts +5 -3
  58. package/templates/branded-lite/tier5-src/himi-events-detail/index.ts +1 -1
  59. package/templates/branded-lite/tier5-src/himi-events-list/index.ts +20 -4
  60. package/templates/branded-lite/tier5-src/himi-events-my-events/index.ts +2 -2
  61. package/templates/branded-lite/tier5-src/wix-spaces-collections/index.ts +1 -1
  62. package/templates/branded-lite/tier5-src/wix-spaces-forum/index.ts +7 -1
  63. package/templates/branded-lite/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  64. package/templates/community-club/dev/index.ts +62 -81
  65. package/templates/community-club/tier5-src/_shared/collection-contract.ts +16 -9
  66. package/templates/community-club/tier5-src/_shared/collection-service.ts +19 -5
  67. package/templates/community-club/tier5-src/_shared/engine-intl.ts +51 -0
  68. package/templates/community-club/tier5-src/_shared/himi-restaurants.ts +4 -4
  69. package/templates/community-club/tier5-src/_shared/owner-load-error.ts +49 -0
  70. package/templates/community-club/tier5-src/_shared/wix-service-price.ts +1 -1
  71. package/templates/community-club/tier5-src/himi-branded-lite-cms/index.ts +5 -5
  72. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +12 -1
  73. package/templates/community-club/tier5-src/himi-branded-lite-info/index.ts +5 -2
  74. package/templates/community-club/tier5-src/himi-events-detail/index.ts +1 -1
  75. package/templates/community-club/tier5-src/himi-events-list/index.ts +20 -4
  76. package/templates/community-club/tier5-src/himi-events-my-events/index.ts +2 -2
  77. package/templates/community-club/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  78. package/templates/community-club-owner/dev/index.ts +84 -599
  79. package/templates/community-club-owner/tier5-src/_shared/aria-chat.ts +22 -6
  80. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +16 -9
  81. package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +19 -5
  82. package/templates/community-club-owner/tier5-src/_shared/engine-intl.ts +51 -0
  83. package/templates/community-club-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  84. package/templates/community-club-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  85. package/templates/community-club-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  86. package/templates/events-meetups/dev/index.ts +60 -79
  87. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +16 -9
  88. package/templates/events-meetups/tier5-src/_shared/collection-service.ts +19 -5
  89. package/templates/events-meetups/tier5-src/_shared/engine-intl.ts +51 -0
  90. package/templates/events-meetups/tier5-src/_shared/himi-restaurants.ts +4 -4
  91. package/templates/events-meetups/tier5-src/_shared/owner-load-error.ts +49 -0
  92. package/templates/events-meetups/tier5-src/_shared/wix-service-price.ts +1 -1
  93. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +12 -1
  94. package/templates/events-meetups/tier5-src/himi-branded-lite-info/index.ts +5 -2
  95. package/templates/events-meetups/tier5-src/himi-events-detail/index.ts +1 -1
  96. package/templates/events-meetups/tier5-src/himi-events-list/index.ts +20 -4
  97. package/templates/events-meetups/tier5-src/himi-events-my-events/index.ts +2 -2
  98. package/templates/events-meetups/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  99. package/templates/events-meetups-owner/dev/index.ts +84 -543
  100. package/templates/events-meetups-owner/tier5-src/_shared/aria-chat.ts +22 -6
  101. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +16 -9
  102. package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +19 -5
  103. package/templates/events-meetups-owner/tier5-src/_shared/engine-intl.ts +51 -0
  104. package/templates/events-meetups-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  105. package/templates/events-meetups-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  106. package/templates/events-meetups-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  107. package/templates/gym-fitness/dev/index.ts +73 -204
  108. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +16 -9
  109. package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +19 -5
  110. package/templates/gym-fitness/tier5-src/_shared/engine-intl.ts +51 -0
  111. package/templates/gym-fitness/tier5-src/_shared/himi-restaurants.ts +4 -4
  112. package/templates/gym-fitness/tier5-src/_shared/owner-load-error.ts +49 -0
  113. package/templates/gym-fitness/tier5-src/_shared/wix-service-price.ts +1 -1
  114. package/templates/gym-fitness/tier5-src/himi-booking-bookings/index.ts +14 -8
  115. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +12 -1
  116. package/templates/gym-fitness/tier5-src/himi-branded-lite-info/index.ts +5 -2
  117. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  118. package/templates/gym-fitness/tier5-src/himi-events-detail/index.ts +1 -1
  119. package/templates/gym-fitness/tier5-src/himi-events-list/index.ts +20 -4
  120. package/templates/gym-fitness/tier5-src/himi-events-my-events/index.ts +2 -2
  121. package/templates/gym-fitness/tier5-src/wix-spaces-collections/index.ts +1 -1
  122. package/templates/gym-fitness/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  123. package/templates/gym-fitness-owner/dev/index.ts +84 -627
  124. package/templates/gym-fitness-owner/tier5-src/_shared/aria-chat.ts +22 -6
  125. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +16 -9
  126. package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +19 -5
  127. package/templates/gym-fitness-owner/tier5-src/_shared/engine-intl.ts +51 -0
  128. package/templates/gym-fitness-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  129. package/templates/gym-fitness-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  130. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  131. package/templates/gym-fitness-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  132. package/templates/himi-chat/tier5-src/_shared/owner-load-error.ts +49 -0
  133. package/templates/nonprofit-cause/dev/index.ts +71 -90
  134. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +16 -9
  135. package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +19 -5
  136. package/templates/nonprofit-cause/tier5-src/_shared/engine-intl.ts +51 -0
  137. package/templates/nonprofit-cause/tier5-src/_shared/himi-restaurants.ts +4 -4
  138. package/templates/nonprofit-cause/tier5-src/_shared/owner-load-error.ts +49 -0
  139. package/templates/nonprofit-cause/tier5-src/_shared/wix-service-price.ts +1 -1
  140. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +12 -1
  141. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-info/index.ts +5 -2
  142. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  143. package/templates/nonprofit-cause/tier5-src/himi-events-detail/index.ts +1 -1
  144. package/templates/nonprofit-cause/tier5-src/himi-events-list/index.ts +20 -4
  145. package/templates/nonprofit-cause/tier5-src/himi-events-my-events/index.ts +2 -2
  146. package/templates/nonprofit-cause/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  147. package/templates/nonprofit-cause-owner/dev/index.ts +84 -571
  148. package/templates/nonprofit-cause-owner/tier5-src/_shared/aria-chat.ts +22 -6
  149. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +16 -9
  150. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +19 -5
  151. package/templates/nonprofit-cause-owner/tier5-src/_shared/engine-intl.ts +51 -0
  152. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  153. package/templates/nonprofit-cause-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  154. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  155. package/templates/nonprofit-cause-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  156. package/templates/owner-lite/dev/index.ts +84 -627
  157. package/templates/owner-lite/tier5-src/_shared/aria-chat.ts +22 -6
  158. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +16 -9
  159. package/templates/owner-lite/tier5-src/_shared/collection-service.ts +19 -5
  160. package/templates/owner-lite/tier5-src/_shared/engine-intl.ts +51 -0
  161. package/templates/owner-lite/tier5-src/_shared/owner-load-error.ts +49 -0
  162. package/templates/owner-lite/tier5-src/_shared/owner-module-data.ts +2 -2
  163. package/templates/owner-lite/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  164. package/templates/owner-lite/tier5-src/himi-owner-lite-events/index.ts +1 -0
  165. package/templates/podcast-creator/dev/index.ts +71 -90
  166. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +16 -9
  167. package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +19 -5
  168. package/templates/podcast-creator/tier5-src/_shared/engine-intl.ts +51 -0
  169. package/templates/podcast-creator/tier5-src/_shared/himi-restaurants.ts +4 -4
  170. package/templates/podcast-creator/tier5-src/_shared/owner-load-error.ts +49 -0
  171. package/templates/podcast-creator/tier5-src/_shared/wix-service-price.ts +1 -1
  172. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +12 -1
  173. package/templates/podcast-creator/tier5-src/himi-branded-lite-info/index.ts +5 -2
  174. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  175. package/templates/podcast-creator/tier5-src/himi-events-detail/index.ts +1 -1
  176. package/templates/podcast-creator/tier5-src/himi-events-list/index.ts +20 -4
  177. package/templates/podcast-creator/tier5-src/himi-events-my-events/index.ts +2 -2
  178. package/templates/podcast-creator/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  179. package/templates/podcast-creator-owner/dev/index.ts +84 -599
  180. package/templates/podcast-creator-owner/tier5-src/_shared/aria-chat.ts +22 -6
  181. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +16 -9
  182. package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +19 -5
  183. package/templates/podcast-creator-owner/tier5-src/_shared/engine-intl.ts +51 -0
  184. package/templates/podcast-creator-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  185. package/templates/podcast-creator-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  186. package/templates/podcast-creator-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  187. package/templates/portfolio-creative/dev/index.ts +62 -137
  188. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +16 -9
  189. package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +19 -5
  190. package/templates/portfolio-creative/tier5-src/_shared/engine-intl.ts +51 -0
  191. package/templates/portfolio-creative/tier5-src/_shared/himi-restaurants.ts +4 -4
  192. package/templates/portfolio-creative/tier5-src/_shared/owner-load-error.ts +49 -0
  193. package/templates/portfolio-creative/tier5-src/_shared/wix-service-price.ts +1 -1
  194. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +12 -1
  195. package/templates/portfolio-creative/tier5-src/himi-branded-lite-info/index.ts +5 -2
  196. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio/index.ts +2 -2
  197. package/templates/portfolio-creative/tier5-src/himi-branded-lite-portfolio-project/index.ts +3 -3
  198. package/templates/portfolio-creative/tier5-src/himi-events-detail/index.ts +1 -1
  199. package/templates/portfolio-creative/tier5-src/himi-events-list/index.ts +20 -4
  200. package/templates/portfolio-creative/tier5-src/himi-events-my-events/index.ts +2 -2
  201. package/templates/portfolio-creative/tier5-src/wix-spaces-collections/index.ts +1 -1
  202. package/templates/portfolio-creative/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  203. package/templates/portfolio-creative-owner/dev/index.ts +84 -599
  204. package/templates/portfolio-creative-owner/tier5-src/_shared/aria-chat.ts +22 -6
  205. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +16 -9
  206. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +19 -5
  207. package/templates/portfolio-creative-owner/tier5-src/_shared/engine-intl.ts +51 -0
  208. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  209. package/templates/portfolio-creative-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  210. package/templates/portfolio-creative-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  211. package/templates/restaurant/dev/index.ts +124 -101
  212. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +16 -9
  213. package/templates/restaurant/tier5-src/_shared/collection-service.ts +19 -5
  214. package/templates/restaurant/tier5-src/_shared/engine-intl.ts +51 -0
  215. package/templates/restaurant/tier5-src/_shared/himi-restaurants.ts +4 -4
  216. package/templates/restaurant/tier5-src/_shared/owner-load-error.ts +49 -0
  217. package/templates/restaurant/tier5-src/_shared/wix-service-price.ts +1 -1
  218. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +12 -1
  219. package/templates/restaurant/tier5-src/himi-branded-lite-info/index.ts +5 -2
  220. package/templates/restaurant/tier5-src/himi-branded-lite-reserve/index.ts +5 -3
  221. package/templates/restaurant/tier5-src/himi-events-detail/index.ts +1 -1
  222. package/templates/restaurant/tier5-src/himi-events-list/index.ts +20 -4
  223. package/templates/restaurant/tier5-src/himi-events-my-events/index.ts +2 -2
  224. package/templates/restaurant/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  225. package/templates/restaurant-owner/dev/index.ts +84 -627
  226. package/templates/restaurant-owner/tier5-src/_shared/aria-chat.ts +22 -6
  227. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +16 -9
  228. package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +19 -5
  229. package/templates/restaurant-owner/tier5-src/_shared/engine-intl.ts +51 -0
  230. package/templates/restaurant-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  231. package/templates/restaurant-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  232. package/templates/restaurant-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  233. package/templates/restaurant-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
  234. package/templates/retail-store/dev/index.ts +59 -106
  235. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +16 -9
  236. package/templates/retail-store/tier5-src/_shared/collection-service.ts +19 -5
  237. package/templates/retail-store/tier5-src/_shared/engine-intl.ts +51 -0
  238. package/templates/retail-store/tier5-src/_shared/himi-restaurants.ts +4 -4
  239. package/templates/retail-store/tier5-src/_shared/owner-load-error.ts +49 -0
  240. package/templates/retail-store/tier5-src/_shared/wix-service-price.ts +1 -1
  241. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +12 -1
  242. package/templates/retail-store/tier5-src/himi-branded-lite-info/index.ts +5 -2
  243. package/templates/retail-store/tier5-src/wix-spaces-collections/index.ts +1 -1
  244. package/templates/retail-store/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  245. package/templates/retail-store-owner/dev/index.ts +84 -571
  246. package/templates/retail-store-owner/tier5-src/_shared/aria-chat.ts +22 -6
  247. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +16 -9
  248. package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +19 -5
  249. package/templates/retail-store-owner/tier5-src/_shared/engine-intl.ts +51 -0
  250. package/templates/retail-store-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  251. package/templates/retail-store-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  252. package/templates/yoga-studio/dev/index.ts +73 -148
  253. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +16 -9
  254. package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +19 -5
  255. package/templates/yoga-studio/tier5-src/_shared/engine-intl.ts +51 -0
  256. package/templates/yoga-studio/tier5-src/_shared/himi-restaurants.ts +4 -4
  257. package/templates/yoga-studio/tier5-src/_shared/owner-load-error.ts +49 -0
  258. package/templates/yoga-studio/tier5-src/_shared/wix-service-price.ts +1 -1
  259. package/templates/yoga-studio/tier5-src/himi-booking-bookings/index.ts +14 -8
  260. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +12 -1
  261. package/templates/yoga-studio/tier5-src/himi-branded-lite-info/index.ts +5 -2
  262. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +1 -1
  263. package/templates/yoga-studio/tier5-src/himi-branded-lite-program/index.ts +1 -1
  264. package/templates/yoga-studio/tier5-src/himi-events-detail/index.ts +1 -1
  265. package/templates/yoga-studio/tier5-src/himi-events-list/index.ts +20 -4
  266. package/templates/yoga-studio/tier5-src/himi-events-my-events/index.ts +2 -2
  267. package/templates/yoga-studio/tier5-src/wix-spaces-whats-new/index.ts +4 -4
  268. package/templates/yoga-studio-owner/dev/index.ts +84 -571
  269. package/templates/yoga-studio-owner/tier5-src/_shared/aria-chat.ts +22 -6
  270. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +16 -9
  271. package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +19 -5
  272. package/templates/yoga-studio-owner/tier5-src/_shared/engine-intl.ts +51 -0
  273. package/templates/yoga-studio-owner/tier5-src/_shared/owner-load-error.ts +49 -0
  274. package/templates/yoga-studio-owner/tier5-src/_shared/owner-module-data.ts +2 -2
  275. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-appointments/index.ts +4 -1
  276. package/templates/yoga-studio-owner/tier5-src/himi-owner-lite-events/index.ts +1 -0
@@ -26,6 +26,13 @@ export interface CollectionConfig {
26
26
  reviewSelectedAction?: string;
27
27
  /** Optional comfortable grid media height for immersive visual catalogs. */
28
28
  gridImageHeight?: number;
29
+ /** What the count summary counts — ["dish", "dishes"] reads "12 dishes". Default "item(s)". */
30
+ countNoun?: [string, string];
31
+ /** Phone grid columns (default 2). A card carrying a paragraph of benefits and a CTA — a
32
+ * pricing plan — is unreadable at half width, so it opts into one. */
33
+ gridColumns?: number;
34
+ /** Lines of the meta caption a comfortable row may use (default 2). */
35
+ metaMaxLines?: number;
29
36
  /** The card's primary verb, shown as a visible button beside the overflow.
30
37
  * `screenId` opens that screen with the row id; without it the verb reuses the
31
38
  * row's own tap action (the detail screen that owns the verb). */
@@ -38,28 +45,28 @@ export interface CollectionConfig {
38
45
  * page, the list/grid toggle, Refine, chips, search and count are noise. */
39
46
  export const COLLECTION_CHROME_MIN_ITEMS = 4;
40
47
  export const COLLECTIONS: Record<string, CollectionConfig> = {
41
- himiStoreShop: { rows: 'results', title: 'name', subtitle: 'subtitle', image: 'image', view: 'grid', backendQuery: true },
42
- himiBookingServices: { rows: 'results', title: 'name', subtitle: 'subtitle', image: 'image', filter: 'category', view: 'grid', backendQuery: true, searchPlaceholder: 'Service name starts with…', primaryVerb: { label: 'Book', screenId: 'himi_time' } },
48
+ himiStoreShop: { rows: 'results', title: 'name', subtitle: 'subtitle', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
49
+ himiBookingServices: { rows: 'results', title: 'name', subtitle: 'subtitle', image: 'image', filter: 'category', view: 'grid', backendQuery: true, searchPlaceholder: 'Search services', countNoun: ['service', 'services'], primaryVerb: { label: 'Book', screenId: 'himi_time' } },
43
50
  himiBookingBookings: { rows: 'rows', title: 'title', subtitle: 'subtitle', metadata: ['status'], view: 'list', backendQuery: true,
44
- defaultFilter: 'value:Upcoming', searchPlaceholder: 'Exact booking ID', filterValues: ['Upcoming', 'Past', 'Cancelled'],
51
+ defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search by booking ID', countNoun: ['booking', 'bookings'], filterValues: ['Upcoming', 'Past', 'Cancelled'],
45
52
  sortLabels: { default: 'Soonest', az: 'Oldest', za: 'Latest' } },
46
- himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, searchPlaceholder: 'Exact event title', filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
53
+ himiEvents: { rows: 'results', title: 'title', subtitle: 'dateLine', image: 'image', view: 'grid', backendQuery: true, defaultFilter: 'value:Upcoming', searchPlaceholder: 'Search events', countNoun: ['event', 'events'], filterValues: ['Upcoming', 'Past'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' }, primaryVerb: { label: 'Register' } },
47
54
  himiBlogFeed: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', filter: 'category', filterValuesFrom: 'categories', view: 'grid', narrowedBy: 'selectedCategoryId' },
48
- himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true },
55
+ himiOwnerLiteProducts: { rows: 'products', title: 'name', subtitle: 'price', image: 'image', view: 'grid', backendQuery: true, countNoun: ['product', 'products'] },
49
56
  himiOwnerModules: { instanceInput: 'module', rows: 'rows', title: 'title', subtitle: 'subtitle', image: 'image', filter: 'statusLabel', view: 'list', paged: true, reviewAction: 'prepareRecord', reviewSelectedAction: 'prepareSelected' },
50
57
  himiOwnerLiteServices: { rows: 'services', title: 'name', subtitle: 'subtitle', view: 'list', backendQuery: true, searchPlaceholder: 'Service name starts with…' },
51
58
  himiOwnerLiteEvents: { rows: 'events', title: 'title', subtitle: 'dateLine', view: 'grid', backendQuery: true, searchPlaceholder: 'Exact event title', filterValues: ['Upcoming'], sortLabels: { default: 'Date', az: 'Title A–Z', za: 'Title Z–A' } },
52
59
  himiOwnerLitePosts: { rows: 'posts', title: 'title', subtitle: 'excerpt', image: 'image', view: 'grid' },
53
- himiOwnerLiteOrders: { rows: 'orders', title: 'buyer', subtitle: 'meta', filter: 'statusLabel', view: 'list', backendQuery: true,
60
+ himiOwnerLiteOrders: { rows: 'orders', title: 'buyer', subtitle: 'meta', filter: 'statusLabel', view: 'list', backendQuery: true, countNoun: ['order', 'orders'],
54
61
  searchPlaceholder: 'Customer name, email or order #', sortLabels: { default: 'Newest', az: 'Customer A–Z', za: 'Customer Z–A' },
55
62
  filterValues: ['Recent 7 days', 'Paid', 'Unpaid', 'Partially paid', 'Pending', 'Refunded', 'Partly refunded'] },
56
63
  himiBrandedGroups: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
57
64
  himiBrandedCms: { rows: 'rows', title: 'name', subtitle: 'subtitle', view: 'grid' },
58
65
  himiBrandedCmsCollection: { instanceInput: 'collection', rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list' },
59
66
  himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
60
- searchPlaceholder: 'Exact order number', filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
67
+ searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
61
68
  sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
62
- himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid' },
69
+ himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
63
70
  himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
64
71
  himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
65
72
  himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
@@ -75,7 +82,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
75
82
  himiBrandedForms: { rows: 'forms', title: 'name', subtitle: 'description', view: 'list' },
76
83
  himiBrandedGalleries: { rows: 'galleries', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
77
84
  himiBrandedInbox: { rows: 'conversations', title: 'title', subtitle: 'preview', view: 'list' },
78
- himiBrandedMenus: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', sectioned: true, filter: 'collectionCategory', metadata: ['collectionCategory', 'priceLine', 'label1', 'label2', 'label3'], view: 'grid' },
85
+ himiBrandedMenus: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', sectioned: true, filter: 'collectionCategory', metadata: ['collectionCategory', 'priceLine', 'label1', 'label2', 'label3'], view: 'grid', searchPlaceholder: 'Search the menu', countNoun: ['dish', 'dishes'] },
79
86
  };
80
87
  export interface CollectionPreferences {
81
88
  view: 'list' | 'grid';
@@ -18,6 +18,20 @@ type Reducer = (s: State, ...args: unknown[]) => State | undefined | Promise<Sta
18
18
  const object = (x: unknown): State => x && typeof x === 'object' && !Array.isArray(x) ? x as State : {};
19
19
  const str = (arg: unknown, key: string): string => typeof arg === 'string' ? arg : String(object(arg)[key] ?? '');
20
20
 
21
+ /** The toolbar count line — "12 dishes", or "3 of 12 dishes" while a filter narrows the list.
22
+ *
23
+ * It used to read "12 / 12 loaded" and "2 / 2 results" ([branded-lite-qa] #3002): the numbers
24
+ * described the implementation (a client-side filter over a fetched page) rather than what the
25
+ * customer is looking at, and the slash-and-suffix wrapped onto two lines on web. When the
26
+ * backend total is unknown the count shown is simply what is shown; the "Load more" button is
27
+ * what says there is more. */
28
+ export function collectionSummary(o: { shown: number; loaded: number; total?: number; backend?: boolean; noun?: [string, string] }): string {
29
+ const [one, many] = o.noun ?? [copyParam('collection.itemOne', 'item'), copyParam('collection.itemMany', 'items')];
30
+ const whole = o.backend ? (typeof o.total === 'number' ? o.total : o.shown) : o.loaded;
31
+ const noun = (whole === 1 ? one : many);
32
+ return o.shown < whole ? `${o.shown} ${copyParam('collection.of', 'of')} ${whole} ${noun}` : `${o.shown} ${o.shown === 1 ? one : many}`;
33
+ }
34
+
21
35
  /** Opt-in decorator. Domain reducers remain authoritative; collection preferences
22
36
  * survive reducers which replace their entire state, including async refreshes. */
23
37
  export function decorateCollection(name: string, def: { state: State; actions?: Record<string, Reducer> }, saving?: CollectionSaving, backend?: BackendCollection) {
@@ -179,11 +193,11 @@ export function decorateCollection(name: string, def: { state: State; actions?:
179
193
  filtersPresented, previewPresented: !!previewId, preview: projected.items.filter(row => row.collectionId === previewId),
180
194
  persistenceLabel: persisted.available ? copyParam('collection.onDevice', 'Saved on this device') : copyParam('collection.sessionOnly', 'Choices last for this session'),
181
195
  noEmpty: loading || errored || sourceEmpty || domainNarrowed || projected.items.length > 0,
182
- summary: backend && config.backendQuery
183
- ? (s.fromCache ? copyParam('collection.cached', 'Saved offline · may be outdated')
184
- : typeof s.total === 'number' ? `${projected.items.length} / ${s.total} ${copyParam('collection.results', 'results')}`
185
- : `${projected.items.length} ${s.complete ? copyParam('collection.results', 'results') : copyParam('collection.loaded', 'loaded')}`)
186
- : `${projected.items.length} / ${projected.loaded} ${copyParam('collection.loaded', 'loaded')}`,
196
+ summary: backend && config.backendQuery && s.fromCache
197
+ ? copyParam('collection.cached', 'Saved offline · may be outdated')
198
+ : collectionSummary({ shown: projected.items.length, loaded: projected.loaded, backend: !!(backend && config.backendQuery),
199
+ total: typeof s.total === 'number' ? s.total : undefined, noun: config.countNoun
200
+ ? [copyParam(`collection.${name}.countOne`, config.countNoun[0]), copyParam(`collection.${name}.countMany`, config.countNoun[1])] : undefined }),
187
201
  selectedSummary: `${prefs.selectedIds.length} ${copyParam('collection.selected', 'selected')}`,
188
202
  filterChoices,
189
203
  chips: config.filterValues ? filterChoices.filter(choice => ['all', 'saved', prefs.filter].includes(choice.id)) : filterChoices,
@@ -352,3 +352,54 @@ export function compareDateDesc(a: string, b: string): number {
352
352
  if (bBad) return -1;
353
353
  return tb - ta;
354
354
  }
355
+
356
+ // --- [branded-lite-qa] #3079: one pattern per fact ---------------------------------------
357
+ //
358
+ // Before these existed, four screens formatted the same money four ways — Shop "69.00 ₪" (Wix's
359
+ // pre-formatted string), Services "₪70" (0 decimals), Menu "₪32.00", Plans "$24" — and two
360
+ // screens formatted the same clock two ways (Events "9:00 PM-11:00 PM", Reserve "20:00").
361
+ // Customers read that as sloppiness, and a whole "₪70" beside "₪69.50" reads as a different
362
+ // kind of number. So: one money pattern, one clock, one range.
363
+
364
+ // Money uses `formatCurrency(amount, currency, "en")` — the currency's NATURAL decimals
365
+ // ("₪70.00", "¥500"). Passing 0 fraction digits for whole amounts is what made a list read
366
+ // "₪70 · ₪69.50".
367
+
368
+ const KNOWN_SYMBOLS = new Set(Object.values(CURRENCY_SYMBOL));
369
+
370
+ /** Wix hands some prices over PRE-formatted in the site's regional style — "69.00 ₪" with the
371
+ * symbol trailing, often wrapped in bidi marks. Reorder those to the house pattern ("₪69.00") so
372
+ * they match every price we format ourselves. Only an en-shaped number ("69.00", "1,234.50") with
373
+ * a known trailing symbol is reordered: a comma-decimal or space-grouped string ("69,00 €",
374
+ * "1 234,56 €") is kept as Wix wrote it, because half-converting it would be worse. Anything else
375
+ * ("Market price") is returned trimmed. The store SDK's product price applies the same rule. */
376
+ export function normalizeFormattedPrice(formatted: string): string {
377
+ const raw = (formatted || "").replace(/[‎‏]/g, "").trim();
378
+ const m = /^(-?)(\d{1,3}(?:,\d{3})*(?:\.\d+)?|\d+(?:\.\d+)?)[\s  ]*(\S+)$/.exec(raw);
379
+ if (!m || !KNOWN_SYMBOLS.has(m[3])) return raw;
380
+ return `${m[1]}${m[3]}${m[2]}`;
381
+ }
382
+
383
+ function clockParts(d: Date, locale: string): { time: string; meridiem: string } {
384
+ const full = hasIntl()
385
+ ? new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", hour12: true }).format(d)
386
+ : clock12(d);
387
+ // ICU puts a narrow no-break space before the meridiem; normalize so callers can split on it.
388
+ const clean = full.replace(/[  ]/g, " ");
389
+ const m = /^(.*?)\s*([AP]\.?M\.?)$/i.exec(clean);
390
+ return m ? { time: m[1], meridiem: m[2] } : { time: clean, meridiem: "" };
391
+ }
392
+
393
+ /** "8:00 PM" — the one clock every customer screen uses (12-hour, en-US shape). */
394
+ export function formatClock(date: Date, locale = "en-US"): string {
395
+ const { time, meridiem } = clockParts(date, locale);
396
+ return meridiem ? `${time} ${meridiem}` : time;
397
+ }
398
+
399
+ /** Is this ISO instant before `now`? A missing or unparseable value is NOT past — "no expiry"
400
+ * must never classify an offer as expired. */
401
+ export function isPastDate(iso: string | undefined | null, now: number = Date.now()): boolean {
402
+ if (!iso) return false;
403
+ const t = Date.parse(iso);
404
+ return !Number.isNaN(t) && t < now;
405
+ }
@@ -11,7 +11,7 @@ import { wixMediaUrl, type WixImageSize } from "@himalaya/wix-media";
11
11
  // copier recognizes + carries the dependency; resolves to the same file here
12
12
  // and in the scaffolded tier5-src/_shared/ layout.
13
13
  import { cleanTitle, initials } from "../_shared/himi-text.js";
14
- import { formatCurrency } from "../_shared/engine-intl.js";
14
+ import { formatCurrency, normalizeFormattedPrice } from "../_shared/engine-intl.js";
15
15
 
16
16
  /** One dish tile/row: the media-XOR-initials card contract every himi list
17
17
  * uses, plus a formatted price line and the templated detail screen id. */
@@ -97,13 +97,13 @@ export function itemPriceLine(item: WixItem): string {
97
97
  | { price?: unknown; currency?: unknown; formattedPrice?: unknown }
98
98
  | undefined;
99
99
  const formatted = typeof info?.formattedPrice === "string" ? info.formattedPrice.trim() : "";
100
- if (formatted) return formatted;
100
+ if (formatted) return normalizeFormattedPrice(formatted);
101
101
  const price = info?.price;
102
102
  if (price == null || price === "" || Number.isNaN(Number(price))) return "";
103
103
  const currency = typeof info?.currency === "string" ? info.currency : "";
104
104
  if (!currency) return `${price}`;
105
105
  try {
106
- return formatCurrency(Number(price), currency, "en", Number(price) % 1 === 0 ? 0 : 2);
106
+ return formatCurrency(Number(price), currency, "en");
107
107
  } catch {
108
108
  return `${price}`;
109
109
  }
@@ -149,7 +149,7 @@ export function itemPriceVariants(item: WixItem): Array<{ id: string; name: stri
149
149
  let priceLine = `${price}`;
150
150
  if (currency) {
151
151
  try {
152
- priceLine = formatCurrency(Number(price), currency, "en", Number(price) % 1 === 0 ? 0 : 2);
152
+ priceLine = formatCurrency(Number(price), currency, "en");
153
153
  } catch { /* keep the bare amount */ }
154
154
  }
155
155
  rows.push({ id: typeof node.id === "string" && node.id ? node.id : `variant-${index}`, name: name || "Option", priceLine });
@@ -74,6 +74,54 @@ const SITE_CONTEXT_CODES = ["MISSING_METASITE_ID", "MISSING_REQUEST_SITE_CONTEXT
74
74
  * message and starts being a payload dump. */
75
75
  const MAX_RAW = 200;
76
76
 
77
+ /**
78
+ * A TRANSPORT failure: the request never got an HTTP answer at all.
79
+ *
80
+ * Each host describes one in its own platform's words, and every one of them is a
81
+ * diagnostic dump rather than a message. iOS's JSWorker hands a worker the Swift
82
+ * description of the `NSError` —
83
+ * `Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server."
84
+ * UserInfo={… NSErrorFailingURLStringKey=http://127.0.0.1:8178/__dev/wix/proxy, …}`
85
+ * — which Aria painted verbatim: the domain, the code, and the local server's
86
+ * own URL. Android's bridge carries the `java.net` exception text, and a browser's
87
+ * fetch rejects with "Failed to fetch" / "Load failed" / "NetworkError …".
88
+ *
89
+ * Only a message with NO HTTP status qualifies: a status means Wix answered, and
90
+ * that case keeps its own reductions below.
91
+ */
92
+ const TRANSPORT = [
93
+ /\bError Domain=\w+/, // any Cocoa NSError description
94
+ /\bNSURLErrorDomain\b/,
95
+ /\bjava\.(?:net|io)\.\w+(?:Exception|Error)\b/,
96
+ /\b(?:UnknownHost|Connect|SocketTimeout|NoRouteToHost|SSLHandshake)Exception\b/,
97
+ // Android's WebViewJSWorker bridges `e.message ?: e.javaClass.simpleName`, so
98
+ // the class name is usually absent and only OkHttp's own message arrives.
99
+ /^Failed to connect to \S/,
100
+ /^Unable to resolve host\b/,
101
+ /\b(?:Connection refused|Network is unreachable|Software caused connection abort)\b/,
102
+ /^(?:timeout|Read timed out|connect timed out)$/i,
103
+ /\bFailed to fetch\b/,
104
+ /\bNetworkError when attempting to fetch\b/,
105
+ /^(?:TypeError: )?Load failed$/,
106
+ /\bnet::ERR_[A-Z_]+/,
107
+ ];
108
+
109
+ /** Did this failure happen below HTTP — no answer from Wix at all? */
110
+ export function isTransportFailure(e: unknown): boolean {
111
+ const msg = String((e as Error)?.message ?? e ?? "");
112
+ if (STATUS.test(msg)) return false;
113
+ return TRANSPORT.some((re) => re.test(msg));
114
+ }
115
+
116
+ /** The one sentence for a transport failure. Copy-as-configuration: the key is
117
+ * under `ownerCommon.`, which every owner screen (Aria included) forwards. */
118
+ export function ownerTransportFailureText(): string {
119
+ return copyParam(
120
+ "ownerCommon.networkUnreachable",
121
+ "Couldn’t reach Wix. Check your connection and try again.",
122
+ );
123
+ }
124
+
77
125
  /**
78
126
  * Reduce a wire failure to something a PERSON can read.
79
127
  *
@@ -85,6 +133,7 @@ const MAX_RAW = 200;
85
133
  * markup; a long non-HTML body is truncated for the same reason.
86
134
  */
87
135
  function humanWireError(msg: string): string {
136
+ if (isTransportFailure(msg)) return ownerTransportFailureText();
88
137
  const status = STATUS.exec(msg)?.[1];
89
138
  // A JSON ENVELOPE or a NAMED REASON CODE is the machine's word for it, and the
90
139
  // owner gets neither. Both reductions used to live in `ownerDisplayError` only,
@@ -119,7 +119,7 @@ export function formatServicePrice(service: unknown): string {
119
119
  if (amount == null) return "";
120
120
  const currency = serviceCurrency(service);
121
121
  try {
122
- return formatCurrency(amount, currency, "en", 0);
122
+ return formatCurrency(amount, currency, "en");
123
123
  } catch {
124
124
  return `${amount}`;
125
125
  }
@@ -388,6 +388,10 @@ function resolveEffectiveLayout(arg: unknown, productCount: number): LayoutId {
388
388
  });
389
389
  }
390
390
 
391
+ /** The time-of-day greeting. Read at REFRESH time only, never at boot: the release build hashes
392
+ * the initial state, so a greeting read off the build machine's clock made every build of an
393
+ * unchanged template a different release (#3268, the `nondeterministic-initial-state` lint).
394
+ * The initial state carries `neutralWelcome()` instead, and the first refresh swaps this in. */
391
395
  function welcome(): string {
392
396
  const hour = new Date().getHours();
393
397
  if (hour < 12) return copyParam("home.greetingMorning", "Good morning");
@@ -395,6 +399,11 @@ function welcome(): string {
395
399
  return copyParam("home.greetingEvening", "Good evening");
396
400
  }
397
401
 
402
+ /** The boot-time greeting: clock-free, so the baked initial state is the same on every build. */
403
+ function neutralWelcome(): string {
404
+ return copyParam("home.greetingNeutral", "Welcome");
405
+ }
406
+
398
407
  function feature(p: StoreProduct): ProductFeature {
399
408
  const name = cleanTitle(p.name) || copyParam("home.productFallbackName", "Featured item");
400
409
  const image = productImage(p);
@@ -871,7 +880,9 @@ function baseState(): State {
871
880
  hideHeroInitial: true,
872
881
  hideHeroFallback: true,
873
882
  hideFeaturedSkeleton: false,
874
- welcome: welcome(),
883
+ // Not welcome(): that reads the clock, and this state is baked into the release. A neutral
884
+ // greeting keeps the hero pill painted (same size, no reflow) until the first refresh.
885
+ welcome: neutralWelcome(),
875
886
  refreshedAt: "",
876
887
  effectiveLayout: resolveEffectiveLayout(undefined, 0),
877
888
  accentColor: resolveAccent(undefined),
@@ -112,7 +112,10 @@ const svc = defineService<State, {
112
112
  // Probe before revealing anything picker-shaped (spec R8) — on a customer build this
113
113
  // stays false and the whole subtree is hidden.
114
114
  const canPick = await pickerAvailable();
115
- next = { ...next, pickerAvailable: canPick, pickerHidden: !canPick };
115
+ // The runtime line names the internal platform ("HimalayaCore 0.0.1") — a number for
116
+ // whoever debugs the build, not for a white-label app's customer ([branded-lite-qa]
117
+ // #3002/#3083). It shows only on a build that opted into the QA picker.
118
+ next = { ...next, pickerAvailable: canPick, pickerHidden: !canPick, runtimeLine: canPick ? next.runtimeLine : "" };
116
119
  if (canPick) {
117
120
  // `active()` is more precise than app-info's releasePinned: it also says WHICH pin
118
121
  // (user vs baked) is holding the release.
@@ -122,7 +125,7 @@ const svc = defineService<State, {
122
125
  }
123
126
  }
124
127
  } catch {
125
- next = { ...next, pickerAvailable: false, pickerHidden: true };
128
+ next = { ...next, pickerAvailable: false, pickerHidden: true, runtimeLine: "" };
126
129
  }
127
130
  return next;
128
131
  },
@@ -78,7 +78,7 @@ function money(plan: WixPlan): string {
78
78
  const raw = price?.value;
79
79
  if (raw == null || raw === "" || Number.isNaN(Number(raw))) return "";
80
80
  try {
81
- return formatCurrency(Number(raw), price?.currency || "USD", "en", Number(raw) % 1 === 0 ? 0 : 2);
81
+ return formatCurrency(Number(raw), price?.currency || "USD", "en");
82
82
  } catch {
83
83
  return `${raw}`;
84
84
  }
@@ -162,7 +162,7 @@ function tierPrice(t: WixAvailableTicket): string {
162
162
  // as "25" — a price that reads as a quantity, and nothing downstream could tell it was a
163
163
  // formatting failure. `formatCurrency` uses real Intl where it exists and a symbol-carrying
164
164
  // fallback where it does not, diffed against ECMA-402 in engine-intl-fallback.test.ts.
165
- return formatCurrency(Number(raw), t.price?.currency || "USD", "en", Number(raw) % 1 === 0 ? 0 : 2);
165
+ return formatCurrency(Number(raw), t.price?.currency || "USD", "en");
166
166
  }
167
167
 
168
168
  function tierAvailable(t: WixAvailableTicket): boolean {
@@ -35,16 +35,32 @@ const svc = defineBackendCollection<S, EventRow>("himiEvents", {
35
35
  id: row => row.id,
36
36
  errorMessage: () => copyParam("events.errorText", "Events could not be loaded."),
37
37
  async cached(criteria) {
38
- if (criteria.query || criteria.filter !== "all" || criteria.sort !== "default") return undefined;
39
- return { items: await readCached(), nextCursor: "", metadata: { fromCache: true } };
38
+ // The list now OPENS on Upcoming, so the default view must keep its offline-first paint:
39
+ // serve the cache for it too, minus the rows that have finished.
40
+ const upcoming = criteria.filter === "value:Upcoming";
41
+ if (criteria.query || (criteria.filter !== "all" && !upcoming) || criteria.sort !== "default") return undefined;
42
+ const rows = await readCached();
43
+ return { items: upcoming ? rows.filter((row) => row.statusLabel !== "Past") : rows, nextCursor: "", metadata: { fromCache: true } };
40
44
  },
41
45
  async fetch(criteria, cursor) {
42
46
  if (!cursor) queryNow = new Date().toISOString();
43
47
  if (criteria.filter === "saved" && !criteria.savedIds.length) return { items: [], nextCursor: "" };
44
48
  const filter: Record<string, unknown> = { status: { $ne: "DRAFT" } };
45
- if (criteria.query) filter.title = { $eq: criteria.query };
49
+ // Prefix, not exact: the placeholder says "Search events", and an exact-title match returned
50
+ // nothing for "Harvest" when the event is "Harvest Tasting".
51
+ if (criteria.query) filter.title = { $startsWith: criteria.query };
46
52
  if (criteria.filter === "saved") filter.id = { $in: criteria.savedIds };
47
- if (criteria.filter === "value:Upcoming") filter["dateAndTimeSettings.startDate"] = { $gte: queryNow };
53
+ // Upcoming = not started yet OR not finished yet: an event in progress (a festival on its
54
+ // second day) is still on. The start-date arm is what keeps a START-ONLY event (no endDate
55
+ // — valid in Wix Events) in the list; a filter on endDate alone dropped it from both views.
56
+ // Past stays "has started", so an in-progress event may appear under both; its row is only
57
+ // marked "Past" once it has actually finished (`eventIsPast`).
58
+ if (criteria.filter === "value:Upcoming") {
59
+ filter.$or = [
60
+ { "dateAndTimeSettings.startDate": { $gte: queryNow } },
61
+ { "dateAndTimeSettings.endDate": { $gte: queryNow } },
62
+ ];
63
+ }
48
64
  if (criteria.filter === "value:Past") filter["dateAndTimeSettings.startDate"] = { $lt: queryNow };
49
65
  const res = await queryEvents({ limit: 50, offset: Number(cursor || 0), query: { filter,
50
66
  sort: [{ fieldName: criteria.sort === "default" ? "dateAndTimeSettings.startDate" : "title", order: criteria.sort === "za" ? "DESC" : "ASC" }, { fieldName: "id", order: "ASC" }] } });
@@ -63,9 +63,9 @@ async function identityLine(): Promise<string> {
63
63
  const identity = await dispatch("himi.wix.memberIdentity") as { email?: string } | null;
64
64
  return identity?.email
65
65
  ? copyTemplate("myEvents.signedInAs", "Signed in as {email}", { email: identity.email })
66
- : copyParam("myEvents.identityGuest", "RSVPs saved on this device — not yet your full Wix Events history.");
66
+ : copyParam("myEvents.identityGuest", "RSVPs you confirm on this device appear here.");
67
67
  } catch {
68
- return copyParam("myEvents.identityGuest", "RSVPs saved on this device — not yet your full Wix Events history.");
68
+ return copyParam("myEvents.identityGuest", "RSVPs you confirm on this device appear here.");
69
69
  }
70
70
  }
71
71
 
@@ -4,7 +4,7 @@ import { withEmptyFlags } from "../_shared/empty-flags.js";
4
4
  import { listAnnouncements, type WixAnnouncement } from "@himalaya/wix-new-releases";
5
5
 
6
6
  interface Row { id: string; name: string; subtitle: string }
7
- interface S { status: "loading" | "ready"; items: Row[]; emptyText: string }
7
+ interface S { status: "loading" | "ready"; items: Row[]; emptyText: string; /** hiddenWhen hides on true: the empty card shows only once loaded AND empty. */ noEmpty: boolean }
8
8
 
9
9
  function norm(a: WixAnnouncement, i: number): Row {
10
10
  const aa = a as unknown as { id?: string; title?: string; bodyText?: string; description?: string };
@@ -12,14 +12,14 @@ function norm(a: WixAnnouncement, i: number): Row {
12
12
  }
13
13
 
14
14
  const svc = defineService<S, { load: (s: S) => Promise<S> }>("spacesWhatsNew", withEmptyFlags({
15
- state: { status: "loading", items: [], emptyText: "" },
15
+ state: { status: "loading", items: [], emptyText: "", noEmpty: true },
16
16
  actions: {
17
17
  load: async (s) => {
18
18
  try {
19
19
  const r = await listAnnouncements(20);
20
20
  const items = (((r as unknown as { announcements?: WixAnnouncement[] }).announcements) ?? []).map(norm);
21
- return { status: "ready", items, emptyText: items.length ? "" : "Nothing new right now" };
22
- } catch { return { ...s, status: "ready", items: [], emptyText: "Nothing new right now" }; }
21
+ return { status: "ready", items, emptyText: items.length ? "" : "Nothing new right now", noEmpty: items.length > 0 };
22
+ } catch { return { ...s, status: "ready", items: [], emptyText: "Nothing new right now", noEmpty: false }; }
23
23
  },
24
24
  },
25
25
  }, { items: "noItems" }));