@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
@@ -2,7 +2,7 @@
2
2
  // The public project row is resolved again from Wix, then its ordered project
3
3
  // items supply the actual image/video/text story. No sample project is invented.
4
4
  import { defineService } from "@himalaya/state";
5
- import { listProjectItems, listProjects, type WixPortfolioProjectItem } from "@himalaya/wix-portfolio";
5
+ import { listProjectItems, listProjects, portfolioCoverRef, type WixPortfolioProjectItem } from "@himalaya/wix-portfolio";
6
6
  import { wixMediaUrl, WIX_DETAIL_IMAGE } from "@himalaya/wix-media";
7
7
  import { copyParam, stashCopy } from "../_shared/himi-copy.js";
8
8
  import { presence, type PresenceFlags } from "../_shared/detail-presence.js";
@@ -48,7 +48,7 @@ function inputId(): string {
48
48
  }
49
49
 
50
50
  function imageOf(item: WixPortfolioProjectItem): string {
51
- return wixMediaUrl(item.image?.imageInfo?.url ?? item.image?.imageInfo?.id, WIX_DETAIL_IMAGE);
51
+ return wixMediaUrl(portfolioCoverRef(item.image), WIX_DETAIL_IMAGE);
52
52
  }
53
53
 
54
54
  function row(item: WixPortfolioProjectItem, index: number): MediaRow {
@@ -133,7 +133,7 @@ const svc = defineService<State, { load: (state: State, arg?: unknown) => Promis
133
133
  title: (project.title || copyParam("portfolioProject.projectFallbackName", "Project")).trim(),
134
134
  initial: ((project.title || "P").trim()[0] || "P").toUpperCase(),
135
135
  description: (project.description ?? "").trim(),
136
- coverImage: wixMediaUrl(project.coverImage || "", WIX_DETAIL_IMAGE),
136
+ coverImage: wixMediaUrl(portfolioCoverRef(project.coverImage), WIX_DETAIL_IMAGE),
137
137
  items,
138
138
  emptyText: items.length ? "" : copyParam("portfolioProject.noMediaText", "This project has no published media yet."),
139
139
  ...presence("found"),
@@ -124,7 +124,7 @@ function priceOf(p: WixProgram): string {
124
124
  if (raw == null || raw === "" || Number.isNaN(Number(raw))) return "";
125
125
  if (Number(raw) === 0) return "";
126
126
  try {
127
- return formatCurrency(Number(raw), price?.currency || "USD", "en", Number(raw) % 1 === 0 ? 0 : 2);
127
+ return formatCurrency(Number(raw), price?.currency || "USD", "en");
128
128
  } catch {
129
129
  return `${raw}`;
130
130
  }
@@ -26,7 +26,8 @@ import {
26
26
  reserveReservation,
27
27
  type WixReservationLocation,
28
28
  } from "@himalaya/wix-table-reservations";
29
- import { addDays, dayKey, dayLabel, hhmm } from "@himalaya/time";
29
+ import { addDays, dayKey, dayLabel } from "@himalaya/time";
30
+ import { formatClock } from "../_shared/engine-intl.js";
30
31
  import { copyParam, copyTemplate, stashCopy } from "../_shared/himi-copy.js";
31
32
  import { formatWeekdayDateTime } from "../_shared/engine-intl.js";
32
33
 
@@ -128,7 +129,8 @@ function slotsFor(day: string, now: Date): Option[] {
128
129
  for (let minutes = SERVICE_START_HOUR * 60; minutes <= SERVICE_END_HOUR * 60; minutes += SLOT_STEP_MINUTES) {
129
130
  const at = new Date(base.getFullYear(), base.getMonth(), base.getDate(), Math.floor(minutes / 60), minutes % 60, 0, 0);
130
131
  if (at.getTime() <= now.getTime()) continue;
131
- out.push({ id: at.toISOString(), label: hhmm(at) });
132
+ // 12-hour like every other customer time (#3079 — the chips read "20:00").
133
+ out.push({ id: at.toISOString(), label: formatClock(at) });
132
134
  }
133
135
  return out;
134
136
  }
@@ -153,7 +155,7 @@ function chosen(arg: unknown): string {
153
155
  * the strip does not silently discard the diner's 19:30. */
154
156
  function withDay(state: State, day: string, now = new Date()): State {
155
157
  const slots = slotsFor(day, now);
156
- const keepTime = state.selectedSlot ? hhmm(state.selectedSlot) : "";
158
+ const keepTime = state.selectedSlot ? formatClock(new Date(state.selectedSlot)) : "";
157
159
  const match = keepTime ? slots.find((s) => s.label === keepTime) : undefined;
158
160
  const slot = match ?? slots[0];
159
161
  const when = slot ? new Date(slot.id) : undefined;
@@ -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
 
@@ -26,7 +26,7 @@ function fromCategory(c: StoreCategory): Row {
26
26
  function fromCollection(c: WixCollection): Row {
27
27
  const cc = c as unknown as { id?: string; name?: string; numberOfProducts?: number };
28
28
  const n = cc.numberOfProducts;
29
- return { id: cc.id || cc.name || "collection", name: cc.name || "Collection", subtitle: n != null ? `${n} ${n === 1 ? "product" : "products"}` : "" };
29
+ return { id: cc.id || cc.name || "collection", name: cc.name || "Collection", subtitle: n != null && n > 0 ? `${n} ${n === 1 ? "product" : "products"}` : "" };
30
30
  }
31
31
 
32
32
  type Loaded = { ok: true; rows: Row[] } | { ok: false; error: string };
@@ -16,6 +16,8 @@ interface Row {
16
16
  counts: string; // "(likeCount) likes · (commentCount) comments"
17
17
  photo: string;
18
18
  initial: string;
19
+ /** True when there is neither an author name nor a photo to show. */
20
+ noAvatar: boolean;
19
21
  }
20
22
  interface S { status: "loading" | "ready"; all: Row[]; posts: Row[]; emptyText: string }
21
23
 
@@ -73,7 +75,11 @@ function norm(p: WixPost): Row {
73
75
  excerpt,
74
76
  counts,
75
77
  photo,
76
- initial: ((author || title || "#")[0] || "#").toUpperCase(),
78
+ // The monogram is the AUTHOR's: it used to fall back to the post title, so an anonymous
79
+ // post wore a "W" for "Welcome to the Forum" ([branded-lite-qa] #3077). No author → no
80
+ // avatar (`noAvatar`), rather than a letter that belongs to nobody.
81
+ initial: (author[0] || "").toUpperCase(),
82
+ noAvatar: !author && !photo,
77
83
  };
78
84
  }
79
85
 
@@ -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" }));
@@ -3224,17 +3224,6 @@ const SCREENS = runtimeSetting({
3224
3224
  }
3225
3225
  }
3226
3226
  },
3227
- {
3228
- "id": "",
3229
- "body": {
3230
- "package": "himalaya",
3231
- "type": "Text",
3232
- "props": {
3233
- "text": "__HIMI_APP_NAME__",
3234
- "style": "bodyEmphasis"
3235
- }
3236
- }
3237
- },
3238
3227
  {
3239
3228
  "id": "",
3240
3229
  "body": {
@@ -4868,16 +4857,64 @@ const SCREENS = runtimeSetting({
4868
4857
  }
4869
4858
  },
4870
4859
  {
4871
- "id": "",
4860
+ "id": "himi_whats_new.empty",
4872
4861
  "body": {
4873
4862
  "package": "himalaya",
4874
- "type": "Text",
4863
+ "type": "VStack",
4875
4864
  "props": {
4876
- "text": "$service:spacesWhatsNew.emptyText",
4877
- "style": "body",
4878
- "color": "textSecondary",
4879
- "collapseWhenEmpty": true
4865
+ "spacing": 8,
4866
+ "alignment": "leading",
4867
+ "children": [
4868
+ {
4869
+ "id": "",
4870
+ "body": {
4871
+ "package": "himalaya",
4872
+ "type": "Icon",
4873
+ "props": {
4874
+ "name": "newspaper",
4875
+ "size": 28,
4876
+ "tint": "textTertiary"
4877
+ }
4878
+ },
4879
+ "modifiers": {
4880
+ "accessibilityRole": "decorative"
4881
+ }
4882
+ },
4883
+ {
4884
+ "id": "",
4885
+ "body": {
4886
+ "package": "himalaya",
4887
+ "type": "Text",
4888
+ "props": {
4889
+ "text": "$service:spacesWhatsNew.emptyText",
4890
+ "style": "body",
4891
+ "color": "textSecondary",
4892
+ "collapseWhenEmpty": true
4893
+ }
4894
+ }
4895
+ }
4896
+ ]
4880
4897
  }
4898
+ },
4899
+ "modifiers": {
4900
+ "background": {
4901
+ "token": "surfaceElevated"
4902
+ },
4903
+ "border": {
4904
+ "width": 1,
4905
+ "color": {
4906
+ "token": "border"
4907
+ }
4908
+ },
4909
+ "cornerRadius": 16,
4910
+ "padding": {
4911
+ "leading": 16,
4912
+ "trailing": 16,
4913
+ "top": 16,
4914
+ "bottom": 16
4915
+ },
4916
+ "fillMaxWidth": true,
4917
+ "hiddenPath": "$service:spacesWhatsNew.noEmpty"
4881
4918
  }
4882
4919
  }
4883
4920
  ]
@@ -5899,7 +5936,7 @@ const SCREENS = runtimeSetting({
5899
5936
  "package": "himalaya",
5900
5937
  "type": "SearchField",
5901
5938
  "props": {
5902
- "placeholder": "Exact event title",
5939
+ "placeholder": "Search events",
5903
5940
  "debounceMs": 200,
5904
5941
  "onChange": {
5905
5942
  "actionId": "tier5.himiEvents.collectionSearch"
@@ -7092,20 +7129,6 @@ const SCREENS = runtimeSetting({
7092
7129
  }
7093
7130
  }
7094
7131
  },
7095
- {
7096
- "id": "btn_tier5_himievents_refresh",
7097
- "body": {
7098
- "package": "himalaya",
7099
- "type": "Button",
7100
- "props": {
7101
- "title": "Refresh",
7102
- "style": "plain",
7103
- "action": {
7104
- "actionId": "tier5.himiEvents.refresh"
7105
- }
7106
- }
7107
- }
7108
- },
7109
7132
  {
7110
7133
  "id": "himi_events.list.collection.more",
7111
7134
  "body": {
@@ -7781,20 +7804,6 @@ const SCREENS = runtimeSetting({
7781
7804
  }
7782
7805
  }
7783
7806
  },
7784
- {
7785
- "id": "btn_tier5_himievents_refresh",
7786
- "body": {
7787
- "package": "himalaya",
7788
- "type": "Button",
7789
- "props": {
7790
- "title": "Refresh",
7791
- "style": "plain",
7792
- "action": {
7793
- "actionId": "tier5.himiEvents.refresh"
7794
- }
7795
- }
7796
- }
7797
- },
7798
7807
  {
7799
7808
  "id": "himi_events.list.collection.compact-more",
7800
7809
  "body": {
@@ -9267,7 +9276,7 @@ const SCREENS = runtimeSetting({
9267
9276
  "package": "himalaya",
9268
9277
  "type": "SearchField",
9269
9278
  "props": {
9270
- "placeholder": "Search loaded items",
9279
+ "placeholder": "Search",
9271
9280
  "debounceMs": 200,
9272
9281
  "onChange": {
9273
9282
  "actionId": "tier5.himiBlogFeed.collectionSearch"
@@ -10379,20 +10388,6 @@ const SCREENS = runtimeSetting({
10379
10388
  }
10380
10389
  }
10381
10390
  },
10382
- {
10383
- "id": "btn_tier5_himiblogfeed_retry",
10384
- "body": {
10385
- "package": "himalaya",
10386
- "type": "Button",
10387
- "props": {
10388
- "title": "Refresh",
10389
- "style": "plain",
10390
- "action": {
10391
- "actionId": "tier5.himiBlogFeed.retry"
10392
- }
10393
- }
10394
- }
10395
- },
10396
10391
  {
10397
10392
  "id": "himi_blog.list.collection.more",
10398
10393
  "body": {
@@ -11030,20 +11025,6 @@ const SCREENS = runtimeSetting({
11030
11025
  }
11031
11026
  }
11032
11027
  },
11033
- {
11034
- "id": "btn_tier5_himiblogfeed_retry",
11035
- "body": {
11036
- "package": "himalaya",
11037
- "type": "Button",
11038
- "props": {
11039
- "title": "Refresh",
11040
- "style": "plain",
11041
- "action": {
11042
- "actionId": "tier5.himiBlogFeed.retry"
11043
- }
11044
- }
11045
- }
11046
- },
11047
11028
  {
11048
11029
  "id": "himi_blog.list.collection.compact-more",
11049
11030
  "body": {
@@ -11170,7 +11151,7 @@ const SCREENS = runtimeSetting({
11170
11151
  "package": "himalaya",
11171
11152
  "type": "Text",
11172
11153
  "props": {
11173
- "text": "Sort loaded items",
11154
+ "text": "Sort",
11174
11155
  "style": "bodyEmphasis"
11175
11156
  }
11176
11157
  }
@@ -11874,7 +11855,7 @@ const SCREENS = runtimeSetting({
11874
11855
  "package": "himalaya",
11875
11856
  "type": "Text",
11876
11857
  "props": {
11877
- "text": "Posts saved on this member device.",
11858
+ "text": "Posts you have saved to read later.",
11878
11859
  "style": "body",
11879
11860
  "color": "textSecondary"
11880
11861
  }
@@ -12432,7 +12413,7 @@ const SCREENS = runtimeSetting({
12432
12413
  "package": "himalaya",
12433
12414
  "type": "SearchField",
12434
12415
  "props": {
12435
- "placeholder": "Search loaded items",
12416
+ "placeholder": "Search",
12436
12417
  "autoCapitalization": "none",
12437
12418
  "submitLabel": "search",
12438
12419
  "onChange": {
@@ -14177,7 +14158,7 @@ const SCREENS = runtimeSetting({
14177
14158
  "package": "himalaya",
14178
14159
  "type": "Text",
14179
14160
  "props": {
14180
- "text": "Sort loaded items",
14161
+ "text": "Sort",
14181
14162
  "style": "bodyEmphasis"
14182
14163
  }
14183
14164
  }
@@ -14950,7 +14931,7 @@ const SCREENS = runtimeSetting({
14950
14931
  "package": "himalaya",
14951
14932
  "type": "SearchField",
14952
14933
  "props": {
14953
- "placeholder": "Search loaded items",
14934
+ "placeholder": "Search",
14954
14935
  "autoCapitalization": "none",
14955
14936
  "submitLabel": "search",
14956
14937
  "onChange": {
@@ -16528,7 +16509,7 @@ const SCREENS = runtimeSetting({
16528
16509
  "package": "himalaya",
16529
16510
  "type": "Text",
16530
16511
  "props": {
16531
- "text": "Sort loaded items",
16512
+ "text": "Sort",
16532
16513
  "style": "bodyEmphasis"
16533
16514
  }
16534
16515
  }
@@ -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
+ }