@salla.sa/twilight-components 3.0.17 → 3.0.19

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 (408) hide show
  1. package/dist/cjs/{Helper-DSYelC4c.js → Helper-DqKJCL8G.js} +1 -1
  2. package/dist/cjs/{filepond-l9D0Yv7b.js → filepond-Byu0lc8z.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-poster-8SrbLptI.js → filepond-plugin-file-poster-qwx7lYZ-.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-size-DGD6oJy0.js → filepond-plugin-file-validate-size-C-n0P4TT.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-file-validate-type-BIvrhp3w.js → filepond-plugin-file-validate-type-C8VOOXdT.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-edit-4xKikdXG.js → filepond-plugin-image-edit-B9KG763Y.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-exif-orientation-C3BvwxQh.js → filepond-plugin-image-exif-orientation-Czt4pYzU.js} +1 -1
  8. package/dist/cjs/{filepond-plugin-image-preview-CBq_GY0V.js → filepond-plugin-image-preview-BFm1Zl0o.js} +1 -1
  9. package/dist/cjs/free-shipping-timer-6QBe5BNS.js +156 -0
  10. package/dist/cjs/helpers-NV7wgP50.js +602 -0
  11. package/dist/cjs/{index-BAwxpXnB.js → index-CFW798jX.js} +2 -2
  12. package/dist/cjs/{intlTelInput-xw-HaAEN.js → intlTelInput-CLa2B89b.js} +1 -1
  13. package/dist/cjs/loader.cjs.js +2 -2
  14. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  15. package/dist/cjs/salla-accordion_6.cjs.entry.js +2 -2
  16. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +46 -3
  17. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  19. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  20. package/dist/cjs/salla-applied-filters.cjs.entry.js +2 -2
  21. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  22. package/dist/cjs/salla-attachment-view_9.cjs.entry.js +11 -11
  23. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  24. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  25. package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +590 -134
  26. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  28. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  29. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  30. package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
  31. package/dist/cjs/salla-comment-form_9.cjs.entry.js +2 -2
  32. package/dist/cjs/salla-conditional-offer.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  36. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  37. package/dist/cjs/salla-delivery-promise.cjs.entry.js +2 -2
  38. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  39. package/dist/cjs/salla-filters-widget.cjs.entry.js +2 -2
  40. package/dist/cjs/salla-filters.cjs.entry.js +2 -2
  41. package/dist/cjs/salla-free-shipping-banner.cjs.entry.js +95 -9
  42. package/dist/cjs/salla-free-shipping-tag.cjs.entry.js +18 -5
  43. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-gift-card-redeem_2.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-gifting.cjs.entry.js +13 -6
  46. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  47. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +2 -2
  48. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  51. package/dist/cjs/salla-login-modal.cjs.entry.js +7 -2
  52. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  66. package/dist/cjs/salla-notification-item_2.cjs.entry.js +2 -2
  67. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-order-items.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-order-payment.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-order-rewards.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  84. package/dist/cjs/salla-orders.cjs.entry.js +2 -2
  85. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  86. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  87. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  88. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
  90. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  91. package/dist/cjs/salla-products-list.cjs.entry.js +13 -5
  92. package/dist/cjs/salla-products-slider.cjs.entry.js +2 -2
  93. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  94. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +7 -4
  95. package/dist/cjs/salla-rating-modal.cjs.entry.js +2 -2
  96. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +1 -1
  97. package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
  98. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-search-product-card.cjs.entry.js +1 -1
  100. package/dist/cjs/salla-search.cjs.entry.js +2 -2
  101. package/dist/cjs/salla-skeleton.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-social-share.cjs.entry.js +1 -1
  103. package/dist/cjs/salla-social.cjs.entry.js +1 -1
  104. package/dist/cjs/salla-tab-content_3.cjs.entry.js +1 -1
  105. package/dist/cjs/salla-thank-you-header.cjs.entry.js +1 -1
  106. package/dist/cjs/salla-thank-you-page.cjs.entry.js +1 -1
  107. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  108. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  109. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  110. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  111. package/dist/cjs/salla-user-settings.cjs.entry.js +1 -1
  112. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  113. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  114. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +1 -1
  115. package/dist/cjs/salla-wishlist-summary.cjs.entry.js +2 -2
  116. package/dist/cjs/twilight.cjs.js +2 -2
  117. package/dist/cjs/{vanilla-picker-Cm_uNdps.js → vanilla-picker-Vx_7G7bs.js} +1 -1
  118. package/dist/collection/components/salla-bullet-delivery/api-service.js +156 -25
  119. package/dist/collection/components/salla-bullet-delivery/helpers.js +143 -15
  120. package/dist/collection/components/salla-bullet-delivery/icons.js +17 -0
  121. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.css +490 -52
  122. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.js +489 -106
  123. package/dist/collection/components/salla-bullet-delivery/selector-tracker.js +106 -0
  124. package/dist/collection/components/salla-free-shipping-banner/salla-free-shipping-banner.css +18 -4
  125. package/dist/collection/components/salla-free-shipping-banner/salla-free-shipping-banner.js +94 -11
  126. package/dist/collection/components/salla-free-shipping-tag/salla-free-shipping-tag.css +6 -0
  127. package/dist/collection/components/salla-free-shipping-tag/salla-free-shipping-tag.js +18 -2
  128. package/dist/collection/components/salla-gifting/salla-gifting.css +16 -1
  129. package/dist/collection/components/salla-gifting/salla-gifting.js +11 -4
  130. package/dist/collection/components/salla-login-modal/salla-login-modal.js +6 -1
  131. package/dist/collection/components/salla-products-list/salla-products-list.js +11 -3
  132. package/dist/collection/components/salla-quick-buy/salla-quick-buy.js +44 -1
  133. package/dist/collection/components/salla-rating-modal/salla-rating-modal-v2.js +5 -2
  134. package/dist/collection/shared/free-shipping-timer.js +130 -0
  135. package/dist/components/free-shipping-timer.js +150 -0
  136. package/dist/components/helpers.js +299 -40
  137. package/dist/components/index.js +2 -2
  138. package/dist/components/salla-bullet-delivery.js +595 -133
  139. package/dist/components/salla-free-shipping-banner.js +95 -8
  140. package/dist/components/salla-free-shipping-tag.js +17 -3
  141. package/dist/components/salla-gifting.js +12 -5
  142. package/dist/components/salla-login-modal2.js +6 -1
  143. package/dist/components/salla-products-list2.js +11 -3
  144. package/dist/components/salla-quick-buy2.js +44 -1
  145. package/dist/components/salla-rating-modal-v22.js +5 -2
  146. package/dist/docs.json +25 -5
  147. package/dist/esm/{Helper-POCi7J93.js → Helper-I0CAizv1.js} +1 -1
  148. package/dist/esm/{filepond-Ct76xW02.js → filepond-DTUYXV0J.js} +1 -1
  149. package/dist/esm/{filepond-plugin-file-poster-C1OkzGRs.js → filepond-plugin-file-poster-DydZjneT.js} +1 -1
  150. package/dist/esm/{filepond-plugin-file-validate-size-gdd-69cO.js → filepond-plugin-file-validate-size-nbsCU2t9.js} +1 -1
  151. package/dist/esm/{filepond-plugin-file-validate-type-BaZ2QTzl.js → filepond-plugin-file-validate-type-Cl5B_DIy.js} +1 -1
  152. package/dist/esm/{filepond-plugin-image-edit-CsoNvf0i.js → filepond-plugin-image-edit-bzskFyFR.js} +1 -1
  153. package/dist/esm/{filepond-plugin-image-exif-orientation-BHDSe3nH.js → filepond-plugin-image-exif-orientation-MBDuxLaX.js} +1 -1
  154. package/dist/esm/{filepond-plugin-image-preview-CD_S17z3.js → filepond-plugin-image-preview-N6wzEryW.js} +1 -1
  155. package/dist/esm/free-shipping-timer-CnTwkF6y.js +150 -0
  156. package/dist/esm/helpers-Cnh7Qptl.js +568 -0
  157. package/dist/esm/{index-DmGAR42j.js → index-C9zdeQFI.js} +2 -2
  158. package/dist/esm/{intlTelInput-D76Q4XFx.js → intlTelInput-B5L9nYtO.js} +1 -1
  159. package/dist/esm/loader.js +3 -3
  160. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  161. package/dist/esm/salla-accordion_6.entry.js +2 -2
  162. package/dist/esm/salla-add-product-button_5.entry.js +46 -3
  163. package/dist/esm/salla-advertisement.entry.js +1 -1
  164. package/dist/esm/salla-alert_2.entry.js +1 -1
  165. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  166. package/dist/esm/salla-applied-filters.entry.js +2 -2
  167. package/dist/esm/salla-apps-icons.entry.js +1 -1
  168. package/dist/esm/salla-attachment-view_9.entry.js +11 -11
  169. package/dist/esm/salla-badge.entry.js +1 -1
  170. package/dist/esm/salla-basket-gap.entry.js +1 -1
  171. package/dist/esm/salla-bullet-delivery_3.entry.js +590 -134
  172. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  173. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  174. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  175. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  176. package/dist/esm/salla-comment-factors.entry.js +1 -1
  177. package/dist/esm/salla-comment-form_9.entry.js +2 -2
  178. package/dist/esm/salla-conditional-offer.entry.js +1 -1
  179. package/dist/esm/salla-contacts.entry.js +1 -1
  180. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  181. package/dist/esm/salla-count-down.entry.js +1 -1
  182. package/dist/esm/salla-custom-fields.entry.js +1 -1
  183. package/dist/esm/salla-delivery-promise.entry.js +2 -2
  184. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  185. package/dist/esm/salla-filters-widget.entry.js +2 -2
  186. package/dist/esm/salla-filters.entry.js +2 -2
  187. package/dist/esm/salla-free-shipping-banner.entry.js +94 -8
  188. package/dist/esm/salla-free-shipping-tag.entry.js +17 -4
  189. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  190. package/dist/esm/salla-gift-card-redeem_2.entry.js +1 -1
  191. package/dist/esm/salla-gifting.entry.js +13 -6
  192. package/dist/esm/salla-hook.entry.js +1 -1
  193. package/dist/esm/salla-infinite-scroll.entry.js +2 -2
  194. package/dist/esm/salla-installment.entry.js +1 -1
  195. package/dist/esm/salla-list-tile.entry.js +1 -1
  196. package/dist/esm/salla-localization-modal.entry.js +1 -1
  197. package/dist/esm/salla-login-modal.entry.js +7 -2
  198. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  199. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  200. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  201. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  202. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  203. package/dist/esm/salla-loyalty.entry.js +1 -1
  204. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  205. package/dist/esm/salla-map_3.entry.js +1 -1
  206. package/dist/esm/salla-menu.entry.js +1 -1
  207. package/dist/esm/salla-metadata.entry.js +1 -1
  208. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  209. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  210. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  211. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  212. package/dist/esm/salla-notification-item_2.entry.js +2 -2
  213. package/dist/esm/salla-offer-modal.entry.js +1 -1
  214. package/dist/esm/salla-offer.entry.js +1 -1
  215. package/dist/esm/salla-order-branch.entry.js +1 -1
  216. package/dist/esm/salla-order-cancel.entry.js +1 -1
  217. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  218. package/dist/esm/salla-order-details-options.entry.js +1 -1
  219. package/dist/esm/salla-order-details.entry.js +1 -1
  220. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  221. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  222. package/dist/esm/salla-order-edit.entry.js +1 -1
  223. package/dist/esm/salla-order-items.entry.js +1 -1
  224. package/dist/esm/salla-order-options-modal.entry.js +1 -1
  225. package/dist/esm/salla-order-payment.entry.js +1 -1
  226. package/dist/esm/salla-order-rewards.entry.js +1 -1
  227. package/dist/esm/salla-order-shipments.entry.js +1 -1
  228. package/dist/esm/salla-order-summary.entry.js +1 -1
  229. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  230. package/dist/esm/salla-orders.entry.js +2 -2
  231. package/dist/esm/salla-payments.entry.js +1 -1
  232. package/dist/esm/salla-placeholder.entry.js +1 -1
  233. package/dist/esm/salla-price-range.entry.js +1 -1
  234. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  235. package/dist/esm/salla-product-card.entry.js +1 -1
  236. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  237. package/dist/esm/salla-products-list.entry.js +13 -5
  238. package/dist/esm/salla-products-slider.entry.js +2 -2
  239. package/dist/esm/salla-quick-order.entry.js +1 -1
  240. package/dist/esm/salla-rating-modal-v2.entry.js +7 -4
  241. package/dist/esm/salla-rating-modal.entry.js +2 -2
  242. package/dist/esm/salla-review-factors-tags.entry.js +1 -1
  243. package/dist/esm/salla-review-order-item.entry.js +1 -1
  244. package/dist/esm/salla-scopes.entry.js +1 -1
  245. package/dist/esm/salla-search-product-card.entry.js +1 -1
  246. package/dist/esm/salla-search.entry.js +2 -2
  247. package/dist/esm/salla-skeleton.entry.js +1 -1
  248. package/dist/esm/salla-social-share.entry.js +1 -1
  249. package/dist/esm/salla-social.entry.js +1 -1
  250. package/dist/esm/salla-tab-content_3.entry.js +1 -1
  251. package/dist/esm/salla-thank-you-header.entry.js +1 -1
  252. package/dist/esm/salla-thank-you-page.entry.js +1 -1
  253. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  254. package/dist/esm/salla-trust-badges.entry.js +1 -1
  255. package/dist/esm/salla-user-menu.entry.js +1 -1
  256. package/dist/esm/salla-user-profile.entry.js +1 -1
  257. package/dist/esm/salla-user-settings.entry.js +1 -1
  258. package/dist/esm/salla-verify.entry.js +1 -1
  259. package/dist/esm/salla-wallet.entry.js +1 -1
  260. package/dist/esm/salla-wishlist-actions.entry.js +1 -1
  261. package/dist/esm/salla-wishlist-summary.entry.js +2 -2
  262. package/dist/esm/twilight.js +3 -3
  263. package/dist/esm/{vanilla-picker-BlIXJyvJ.js → vanilla-picker-CAeu4f87.js} +1 -1
  264. package/dist/twilight/{p-84d725d4.entry.js → p-01d5cd61.entry.js} +1 -1
  265. package/dist/twilight/{p-60f4357f.entry.js → p-0213fde4.entry.js} +1 -1
  266. package/dist/twilight/{p-8f0af317.entry.js → p-036fbfc0.entry.js} +1 -1
  267. package/dist/twilight/{p-90d4129f.entry.js → p-0add41ce.entry.js} +1 -1
  268. package/dist/twilight/{p-fed66bcb.entry.js → p-0b0d4942.entry.js} +1 -1
  269. package/dist/twilight/{p-86ca22d5.entry.js → p-17bcbf9b.entry.js} +1 -1
  270. package/dist/twilight/{p-b66df976.entry.js → p-1867f7d4.entry.js} +1 -1
  271. package/dist/twilight/p-194ff38c.entry.js +4 -0
  272. package/dist/twilight/{p-4057b692.entry.js → p-20f9c62f.entry.js} +1 -1
  273. package/dist/twilight/{p-783c6346.entry.js → p-252748d9.entry.js} +1 -1
  274. package/dist/twilight/{p-3d5e862c.entry.js → p-290ce0bc.entry.js} +1 -1
  275. package/dist/twilight/{p-d68625f7.entry.js → p-2b930157.entry.js} +1 -1
  276. package/dist/twilight/{p-184ea04c.entry.js → p-2e4e15c5.entry.js} +1 -1
  277. package/dist/twilight/p-2e6537a3.entry.js +4 -0
  278. package/dist/twilight/{p-2a3e2404.entry.js → p-30d64bd1.entry.js} +1 -1
  279. package/dist/twilight/{p-5d4fadd2.entry.js → p-32495dd2.entry.js} +1 -1
  280. package/dist/twilight/{p-cd7029fc.entry.js → p-32d9da31.entry.js} +1 -1
  281. package/dist/twilight/{p-7e854633.entry.js → p-32f100f8.entry.js} +1 -1
  282. package/dist/twilight/{p-dc3ff1f0.entry.js → p-386b3dcd.entry.js} +1 -1
  283. package/dist/twilight/{p-e8090773.entry.js → p-3a4e86d1.entry.js} +1 -1
  284. package/dist/twilight/{p-a0a57259.entry.js → p-3b766a79.entry.js} +1 -1
  285. package/dist/twilight/{p-a5d2ad5e.entry.js → p-3b7a45ec.entry.js} +1 -1
  286. package/dist/twilight/{p-33ff7967.entry.js → p-3bb0e184.entry.js} +1 -1
  287. package/dist/twilight/{p-8b39a9f7.entry.js → p-3cc6ca6c.entry.js} +1 -1
  288. package/dist/twilight/{p-0a794d6f.entry.js → p-4070f808.entry.js} +1 -1
  289. package/dist/twilight/{p-d2c1457e.entry.js → p-412fb721.entry.js} +1 -1
  290. package/dist/twilight/{p-fce39869.entry.js → p-427c13b6.entry.js} +1 -1
  291. package/dist/twilight/{p-49195cb6.entry.js → p-45e0a5d0.entry.js} +1 -1
  292. package/dist/twilight/{p-cd866be6.entry.js → p-4d615f9f.entry.js} +1 -1
  293. package/dist/twilight/{p-4f794057.entry.js → p-4d7fc363.entry.js} +1 -1
  294. package/dist/twilight/{p-9eb92980.entry.js → p-5009f654.entry.js} +1 -1
  295. package/dist/twilight/{p-f4411a04.entry.js → p-50853b76.entry.js} +1 -1
  296. package/dist/twilight/{p-b9695308.entry.js → p-550ad1a6.entry.js} +1 -1
  297. package/dist/twilight/{p-4839d408.entry.js → p-575014c4.entry.js} +1 -1
  298. package/dist/twilight/{p-f10b887a.entry.js → p-5aee5e44.entry.js} +1 -1
  299. package/dist/twilight/{p-622bfb21.entry.js → p-619c709c.entry.js} +1 -1
  300. package/dist/twilight/{p-74f206cf.entry.js → p-6d572fe0.entry.js} +1 -1
  301. package/dist/twilight/{p-26fec98c.entry.js → p-6f84c662.entry.js} +1 -1
  302. package/dist/twilight/{p-4a1aa85f.entry.js → p-703b9253.entry.js} +1 -1
  303. package/dist/twilight/{p-509ad773.entry.js → p-73dea22d.entry.js} +1 -1
  304. package/dist/twilight/{p-6c7cae8a.entry.js → p-74285af9.entry.js} +1 -1
  305. package/dist/twilight/{p-31260fae.entry.js → p-777d28f6.entry.js} +1 -1
  306. package/dist/twilight/{p-4702959a.entry.js → p-7db6ccfe.entry.js} +1 -1
  307. package/dist/twilight/{p-e89a1905.entry.js → p-7db8d545.entry.js} +1 -1
  308. package/dist/twilight/{p-0c6a024f.entry.js → p-7fa4d0b6.entry.js} +1 -1
  309. package/dist/twilight/{p-2e8f95d0.entry.js → p-825c5b73.entry.js} +1 -1
  310. package/dist/twilight/{p-e5545546.entry.js → p-8262e669.entry.js} +1 -1
  311. package/dist/twilight/{p-bab5d057.entry.js → p-86d55a6b.entry.js} +1 -1
  312. package/dist/twilight/{p-0225f396.entry.js → p-8712f7c7.entry.js} +1 -1
  313. package/dist/twilight/{p-a53e2c74.entry.js → p-8948b58c.entry.js} +1 -1
  314. package/dist/twilight/{p-37b68ef3.entry.js → p-8b8cf457.entry.js} +1 -1
  315. package/dist/twilight/{p-8e8e40bc.entry.js → p-8c2992e7.entry.js} +1 -1
  316. package/dist/twilight/{p-eb160c2e.entry.js → p-8c548259.entry.js} +1 -1
  317. package/dist/twilight/{p-5d775867.entry.js → p-8eb3fd92.entry.js} +1 -1
  318. package/dist/twilight/p-908716fb.entry.js +4 -0
  319. package/dist/twilight/{p-62ba0971.entry.js → p-90a59027.entry.js} +1 -1
  320. package/dist/twilight/{p-ae9c0abe.entry.js → p-9683535f.entry.js} +1 -1
  321. package/dist/twilight/{p-89cff0c8.entry.js → p-982e9124.entry.js} +1 -1
  322. package/dist/twilight/{p-a17784e6.entry.js → p-995b898f.entry.js} +1 -1
  323. package/dist/twilight/{p-3a3d4cb7.entry.js → p-99cb3dba.entry.js} +1 -1
  324. package/dist/twilight/{p-6757344a.entry.js → p-9a0218a6.entry.js} +1 -1
  325. package/dist/twilight/{p-7d337f25.entry.js → p-9cf31ff4.entry.js} +1 -1
  326. package/dist/twilight/{p-d495cce0.entry.js → p-9d1a5007.entry.js} +1 -1
  327. package/dist/twilight/{p-57b93850.entry.js → p-9e429047.entry.js} +1 -1
  328. package/dist/twilight/{p-40083f25.entry.js → p-9e542951.entry.js} +1 -1
  329. package/dist/twilight/{p-Dhq3oJAe.js → p-BcT9tEVW.js} +1 -1
  330. package/dist/twilight/{p-Dza6OBFg.js → p-Bgu5gtOF.js} +1 -1
  331. package/dist/twilight/p-C9zdeQFI.js +5 -0
  332. package/dist/twilight/{p-BbZqYBfp.js → p-CSGj1pY-.js} +1 -1
  333. package/dist/twilight/{p-Nyk6-1lq.js → p-CWWgpQ8Q.js} +1 -1
  334. package/dist/twilight/{p-BwzmoZpm.js → p-Cfz9xit3.js} +1 -1
  335. package/dist/twilight/{p-iCJI7qYe.js → p-CmYSalnM.js} +1 -1
  336. package/dist/twilight/p-CnTwkF6y.js +4 -0
  337. package/dist/twilight/p-Cnh7Qptl.js +4 -0
  338. package/dist/twilight/{p-DVWVl151.js → p-Dav_R_d2.js} +1 -1
  339. package/dist/twilight/{p-B7rn1taO.js → p-JGEddAgs.js} +1 -1
  340. package/dist/twilight/{p-CAc7tX2K.js → p-RflSBQNB.js} +1 -1
  341. package/dist/twilight/{p-De2wEjqS.js → p-Xgbueas7.js} +1 -1
  342. package/dist/twilight/{p-64d49f5d.entry.js → p-a070cce0.entry.js} +1 -1
  343. package/dist/twilight/p-a0bfedad.entry.js +4 -0
  344. package/dist/twilight/{p-bf29daf3.entry.js → p-a22d9931.entry.js} +1 -1
  345. package/dist/twilight/{p-2153c6fd.entry.js → p-a3959219.entry.js} +1 -1
  346. package/dist/twilight/p-a3ca61af.entry.js +4 -0
  347. package/dist/twilight/{p-0e1248c4.entry.js → p-a55b5c44.entry.js} +1 -1
  348. package/dist/twilight/{p-24423a8d.entry.js → p-a5fc8358.entry.js} +1 -1
  349. package/dist/twilight/{p-66f6d722.entry.js → p-aabcd09f.entry.js} +1 -1
  350. package/dist/twilight/{p-4c801f91.entry.js → p-ad00021e.entry.js} +1 -1
  351. package/dist/twilight/{p-fae2a8d0.entry.js → p-ade9a68c.entry.js} +1 -1
  352. package/dist/twilight/{p-83ccad19.entry.js → p-b5e0065a.entry.js} +1 -1
  353. package/dist/twilight/{p-2a8d6e7e.entry.js → p-bbb09d92.entry.js} +1 -1
  354. package/dist/twilight/{p-f034968b.entry.js → p-c2df20f8.entry.js} +1 -1
  355. package/dist/twilight/{p-b38c38fe.entry.js → p-c4274188.entry.js} +1 -1
  356. package/dist/twilight/{p-d63ce284.entry.js → p-c5e2c781.entry.js} +1 -1
  357. package/dist/twilight/{p-07450aed.entry.js → p-c64b2b22.entry.js} +1 -1
  358. package/dist/twilight/{p-14752d03.entry.js → p-c92cc92f.entry.js} +1 -1
  359. package/dist/twilight/{p-d6c5bf2a.entry.js → p-cb54adac.entry.js} +1 -1
  360. package/dist/twilight/{p-7750bace.entry.js → p-ce1e45a3.entry.js} +1 -1
  361. package/dist/twilight/{p-e4d90663.entry.js → p-d0acd7ff.entry.js} +1 -1
  362. package/dist/twilight/{p-f9b9af72.entry.js → p-d0f903ac.entry.js} +1 -1
  363. package/dist/twilight/{p-7df4ee74.entry.js → p-d57b49ff.entry.js} +1 -1
  364. package/dist/twilight/p-db3efb27.entry.js +4 -0
  365. package/dist/twilight/{p-e81c4521.entry.js → p-db73d0bf.entry.js} +1 -1
  366. package/dist/twilight/{p-bfb70998.entry.js → p-e20d5e75.entry.js} +1 -1
  367. package/dist/twilight/{p-1982520c.entry.js → p-e223beca.entry.js} +1 -1
  368. package/dist/twilight/{p-20939237.entry.js → p-e37da59f.entry.js} +1 -1
  369. package/dist/twilight/p-e3991836.entry.js +4 -0
  370. package/dist/twilight/{p-fe413496.entry.js → p-e455709f.entry.js} +1 -1
  371. package/dist/twilight/{p-cc2c52bb.entry.js → p-e921b2d1.entry.js} +1 -1
  372. package/dist/twilight/{p-acd48ae7.entry.js → p-ec356e5a.entry.js} +1 -1
  373. package/dist/twilight/{p-7a985c8c.entry.js → p-eeefc285.entry.js} +1 -1
  374. package/dist/twilight/{p-120b4694.entry.js → p-f3b12754.entry.js} +1 -1
  375. package/dist/twilight/{p-c3c99d9e.entry.js → p-f6279a13.entry.js} +1 -1
  376. package/dist/twilight/{p-b819de23.entry.js → p-f83efc62.entry.js} +1 -1
  377. package/dist/twilight/{p-8b43035f.entry.js → p-fa622715.entry.js} +1 -1
  378. package/dist/twilight/{p-b87ea253.entry.js → p-faf00404.entry.js} +1 -1
  379. package/dist/twilight/twilight.esm.js +1 -1
  380. package/dist/types/components/salla-bullet-delivery/api-service.d.ts +23 -7
  381. package/dist/types/components/salla-bullet-delivery/helpers.d.ts +61 -6
  382. package/dist/types/components/salla-bullet-delivery/icons.d.ts +14 -0
  383. package/dist/types/components/salla-bullet-delivery/interfaces.d.ts +36 -1
  384. package/dist/types/components/salla-bullet-delivery/salla-bullet-delivery.d.ts +82 -3
  385. package/dist/types/components/salla-bullet-delivery/selector-tracker.d.ts +30 -0
  386. package/dist/types/components/salla-free-shipping-banner/salla-free-shipping-banner.d.ts +19 -5
  387. package/dist/types/components/salla-free-shipping-tag/salla-free-shipping-tag.d.ts +6 -1
  388. package/dist/types/components/salla-gifting/salla-gifting.d.ts +1 -0
  389. package/dist/types/components/salla-quick-buy/salla-quick-buy.d.ts +6 -0
  390. package/dist/types/components/salla-rating-modal/salla-rating-modal-v2.d.ts +1 -0
  391. package/dist/types/components.d.ts +11 -25
  392. package/dist/types/shared/free-shipping-timer.d.ts +36 -0
  393. package/package.json +5 -5
  394. package/dist/cjs/free-shipping-DkIKD6ch.js +0 -31
  395. package/dist/cjs/helpers-8bMfm_mm.js +0 -329
  396. package/dist/components/free-shipping.js +0 -29
  397. package/dist/esm/free-shipping-Brg4wsgg.js +0 -29
  398. package/dist/esm/helpers-DqzaeXrL.js +0 -309
  399. package/dist/twilight/p-12a8aed8.entry.js +0 -4
  400. package/dist/twilight/p-4f443e36.entry.js +0 -4
  401. package/dist/twilight/p-5b81a3d9.entry.js +0 -4
  402. package/dist/twilight/p-93f26336.entry.js +0 -4
  403. package/dist/twilight/p-Brg4wsgg.js +0 -4
  404. package/dist/twilight/p-DmGAR42j.js +0 -5
  405. package/dist/twilight/p-DqzaeXrL.js +0 -4
  406. package/dist/twilight/p-c7c868b4.entry.js +0 -4
  407. package/dist/twilight/p-d5a2ba68.entry.js +0 -4
  408. package/dist/twilight/p-f8ff074f.entry.js +0 -4
@@ -2,21 +2,32 @@
2
2
  * Crafted with ❤ by Salla
3
3
  */
4
4
  import { h, Host, } from "@stencil/core";
5
- import { bulletDeliveryAPI, clearApiCache, isSaudiArabia } from "./api-service";
6
- import { filterBranches, findNearestBranch, getGeolocationErrorMessage, getIntentBranchId, getIntentCityId, getIntentCountryCode, getIntentCountryId, getIntentDistrictId, getIntentLatitude, getIntentLongitude, getIntentRegionId, getIntentSubtitle, hasSessionAddressIntent, isBranchSelectable, isMapOriginIntent, requireRegionAndDistrictForSA, } from "./helpers";
5
+ import { bulletDeliveryAPI, CACHE_TTL, clearApiCache, clearBranchCache, isSaudiArabia } from "./api-service";
6
+ import { SelectorTracker, allocatedBranchIds, toCloseReason, } from "./selector-tracker";
7
+ import { filterBranches, findNearestBranch, formatBranchClockTime, formatBranchDistance, getBranchWeekSchedule, getBranchCities, getBranchDirectionsHref, getBranchDiscount, getBranchDiscountRange, getBranchPhoneHref, getBranchReadinessLabel, getBranchReadyTodayTime, getBranchWhatsappHref, getIntentBranchId, getIntentCityId, getIntentCountryCode, getIntentCountryId, getIntentDistrictId, getIntentRegionId, getIntentSubtitle, groupBranchSchedule, hasSessionAddressIntent, isMapOriginIntent, isBranchAlwaysOpen, isBranchBusy, isBranchSelectable, isBranchVisible, requireRegionAndDistrictForSA, } from "./helpers";
7
8
  /* Location permission notice */
8
9
  import { isLocationPermissionMissing, LocationPermissionNotice, requestLocationPermission, watchLocationPermission, } from "./location-permission-notice";
9
10
  import { calculateLocationDistanceKm, LocationDistanceWarning, } from "./location-distance-warning";
10
11
  const BRANCH_SEARCH_DEBOUNCE_MS = 1000;
12
+ /** Shorter queries are filtered locally; the branches API ignores them. */
13
+ const BRANCH_SEARCH_MIN_LENGTH = 2;
11
14
  const GEOLOCATION_TIMEOUT = 10000;
15
+ /** GeolocationPositionError.PERMISSION_DENIED — inlined; the class is absent on the server. */
16
+ const GEO_PERMISSION_DENIED = 1;
17
+ /** Silent read for branch distances: a short wait, and a recent cached fix is good enough. */
18
+ const SILENT_GEOLOCATION_TIMEOUT = 5000;
19
+ const SILENT_GEOLOCATION_MAX_AGE = 10 * 60 * 1000;
20
+ /** Longest the branch list waits for that read; a later fix still serves the next list request. */
21
+ const BRANCH_LIST_LOCATION_WAIT = 1500;
22
+ /** Matches the `per_page` default of `bulletDeliveryAPI.getBranches`. */
23
+ const BRANCH_PAGE_SIZE = 20;
12
24
  /** Final fallback center used only if `user.default_geo_location` is absent (Riyadh — matches checkout map-page). */
13
25
  const DEFAULT_MAP_CENTER = { lat: 24.7756042, lng: 46.7360729 };
14
26
  import GetDirections from "../../assets/svg/get-directions.svg";
15
- import GPS from "../../assets/svg/gps.svg";
16
27
  import Location from "../../assets/svg/location.svg";
17
28
  import MiniMap from "../../assets/svg/mini-map.svg";
18
29
  import Search from "../../assets/svg/search.svg";
19
- import Store from "../../assets/svg/store3.svg";
30
+ import { BranchLocationIcon, LightningIcon, NotesIcon, PickupAvailabilityIcon, WorkingHoursIcon } from "./icons";
20
31
  const DEBUG_KEY = 'salla-bullet-delivery-debug';
21
32
  const OVERRIDE_IP_KEY = 'salla-bullet-delivery-override-ip';
22
33
  function log(message, data) {
@@ -32,6 +43,8 @@ export class SallaBulletDelivery {
32
43
  this.pendingCartSubmitResolver = null;
33
44
  this.pendingCartSubmitPromise = null;
34
45
  this.cartSubmitConfirmationPending = false;
46
+ /** Query the current branch list was searched with server-side; its results are not re-filtered locally. */
47
+ this.branchesServerQuery = '';
35
48
  this.branchSearchDebounceTimer = null;
36
49
  this.countrySearchTimer = null;
37
50
  this.regionSearchTimer = null;
@@ -63,6 +76,13 @@ export class SallaBulletDelivery {
63
76
  this.open();
64
77
  };
65
78
  this.closeHandler = null;
79
+ this.selectorTracker = new SelectorTracker();
80
+ this.openTrigger = "customer_requested";
81
+ this.pendingDismissReason = null;
82
+ this.modalCloseHandler = (detail) => {
83
+ this.pendingDismissReason = toCloseReason(detail?.reason);
84
+ queueMicrotask(() => (this.pendingDismissReason = null));
85
+ };
66
86
  this.bulletDeliveryMobileSelectHandler = null;
67
87
  /** True after saved addresses have been loaded (lazy: only when address tab is shown). */
68
88
  this.savedAddressesLoaded = false;
@@ -72,6 +92,8 @@ export class SallaBulletDelivery {
72
92
  /** Identifies the latest reverse-geocode call so a resolved-but-superseded callback (same
73
93
  * coordinates re-picked, or the map left) can be told apart from the current one. */
74
94
  this.geocodeRequestId = 0;
95
+ /** Identifies the latest pickup-offer fetch so one from an earlier modal open is dropped. */
96
+ this.pickupDiscountRequestId = 0;
75
97
  // Core state
76
98
  this.activeTab = "address";
77
99
  this.isLoggedIn = false;
@@ -94,9 +116,27 @@ export class SallaBulletDelivery {
94
116
  this.selectedSessionAddress = false;
95
117
  // Pickup state
96
118
  this.branches = [];
119
+ /** Pickup offer range across the store's branches (INV-1416); null shows the default tab badge. */
120
+ this.pickupDiscountRange = null;
121
+ /** False until the offer lookup settles, so the tab never flashes the default badge before an offer. */
122
+ this.pickupDiscountResolved = false;
97
123
  this.filteredBranches = [];
98
124
  this.selectedBranch = null;
99
125
  this.branchSearchQuery = "";
126
+ /** Cities that have branches in the selected country (from the unfiltered branch list). */
127
+ this.branchCities = [];
128
+ this.selectedBranchCity = null;
129
+ this.branchCitySearchQuery = '';
130
+ /** Branch whose weekly hours are expanded behind the chevron (one at a time). */
131
+ this.expandedScheduleBranchId = null;
132
+ /** Shopper's device location for branch distances (see getGrantedDeviceLocation). */
133
+ this.shopperLocation = null;
134
+ this.shopperLocationRequestId = 0;
135
+ this.nearestBranchRequestId = 0;
136
+ /** Bumped per branch-list request; overlapping city/search loads only apply the latest one. */
137
+ this.branchesRequestId = 0;
138
+ /** Shared country rows and city options expire with the API cache. */
139
+ this.allBranchCities = null;
100
140
  // Loading states
101
141
  this.loadingCountries = false;
102
142
  this.loadingCities = false;
@@ -215,6 +255,13 @@ export class SallaBulletDelivery {
215
255
  this.displayedDistricts = this.districts;
216
256
  this.searchingDistricts = false;
217
257
  };
258
+ this.handleBranchCitySelected = (item) => {
259
+ this.selectedBranchCity = item && item.id !== -1
260
+ ? (this.branchCities.find((c) => String(c.id) === String(item.id)) ?? item)
261
+ : null;
262
+ this.selectedBranch = null;
263
+ void this.loadBranches();
264
+ };
218
265
  this.handleMapLocationChanged = (event) => {
219
266
  const detail = event.detail;
220
267
  if (!detail?.position?.lat || !detail?.position?.lng)
@@ -329,6 +376,7 @@ export class SallaBulletDelivery {
329
376
  const details = this.buildIntentAddress();
330
377
  if (!details)
331
378
  return;
379
+ const previousIntent = this.getStoredIntent();
332
380
  this.setStoredIntent({
333
381
  type: "address",
334
382
  country_id: Number(this.selectedCountry?.id) || 0,
@@ -337,7 +385,7 @@ export class SallaBulletDelivery {
337
385
  });
338
386
  this.selectedSavedAddress = null;
339
387
  this.selectedSessionAddress = true;
340
- await this.handleConfirm();
388
+ await this.handleConfirm(previousIntent);
341
389
  };
342
390
  this.handleCountrySelected = (item) => {
343
391
  const country = this.countries.find(c => String(c.id) === String(item.id)) || item;
@@ -422,9 +470,10 @@ export class SallaBulletDelivery {
422
470
  * successful auto-confirm never flashes the saved-addresses list; the caller is
423
471
  * responsible for revealing the modal itself if it still needs the customer's input.
424
472
  */
425
- async open(silent = false) {
473
+ async open(silent = false, trigger = "customer_requested") {
426
474
  if (!this.isEnabled)
427
475
  return; // feature disabled — never open
476
+ this.openTrigger = trigger;
428
477
  // If mobile app, open the sheet
429
478
  if (this.isMobileApp) {
430
479
  Salla.event.dispatch('salla::bullet-delivery.open-sheet');
@@ -437,6 +486,7 @@ export class SallaBulletDelivery {
437
486
  }
438
487
  // Reset state before opening
439
488
  this.resetState();
489
+ void this.loadPickupDiscountRange();
440
490
  if (!silent) {
441
491
  // Open modal immediately — fields render right away with inline loading
442
492
  this.modal.open();
@@ -488,20 +538,18 @@ export class SallaBulletDelivery {
488
538
  }
489
539
  }
490
540
  // Load branches only when branch tab is active (lazy tab data)
491
- // Use lat/lng from session storage if available for more accurate results
492
541
  if (this.activeTab === "branch") {
493
- const intentLat = getIntentLatitude(savedIntent);
494
- const intentLng = getIntentLongitude(savedIntent);
495
- if (intentLat != null && intentLng != null) {
496
- await this.loadBranchesWithLocation(intentLat, intentLng);
497
- }
498
- else {
499
- await this.loadBranches();
500
- }
542
+ const loading = this.loadBranches();
543
+ const request = this.branchesRequestId;
544
+ await loading;
545
+ if (request !== this.branchesRequestId || this.activeTab !== 'branch' || this.branchSearchQuery.trim())
546
+ return;
501
547
  // Auto-select preselected branch if provided
502
548
  if (this.preselectedBranchId) {
503
- this.preselectBranch(this.preselectedBranchId);
549
+ await this.preselectBranch(this.preselectedBranchId);
504
550
  }
551
+ if (request !== this.branchesRequestId || this.activeTab !== 'branch' || this.branchSearchQuery.trim())
552
+ return;
505
553
  }
506
554
  // Prefill form data from session storage
507
555
  await this.prefillFromSessionStorage();
@@ -526,7 +574,45 @@ export class SallaBulletDelivery {
526
574
  this.bulletDeliveryClosed.emit();
527
575
  return this.modal?.close();
528
576
  }
577
+ selectorContext() {
578
+ return {
579
+ configured_required: this.isRequired,
580
+ effective_nonclosable: this.isRequired || this.shouldForceNonClosable(),
581
+ trigger_configured: this.openingType ?? null,
582
+ trigger_actual: this.openTrigger,
583
+ };
584
+ }
585
+ handleModalVisibility(visible) {
586
+ if (visible) {
587
+ this.selectorTracker.viewed(this.selectorContext());
588
+ return;
589
+ }
590
+ if (!this.pendingDismissReason)
591
+ return;
592
+ this.selectorTracker.dismissed(this.selectorContext(), this.pendingDismissReason, !!this.getStoredIntent()?.scope_allocated);
593
+ this.pendingDismissReason = null;
594
+ }
595
+ trackSelectorResult(outcome, previousIntent, newBranchIds, scopeId) {
596
+ this.selectorTracker.result(this.selectorContext(), {
597
+ outcome,
598
+ fulfilment_method: this.activeTab === "branch" ? "pickup" : "delivery",
599
+ previous_branch_ids: previousIntent?.scope_allocated ? allocatedBranchIds(previousIntent) : [],
600
+ new_branch_ids: newBranchIds,
601
+ scope_id: scopeId ?? null,
602
+ });
603
+ }
529
604
  resetState() {
605
+ clearBranchCache();
606
+ this.allBranchCities = null;
607
+ this.pickupDiscountRequestId++;
608
+ if (this.branchSearchDebounceTimer)
609
+ clearTimeout(this.branchSearchDebounceTimer);
610
+ ++this.branchesRequestId;
611
+ this.loadingBranches = false;
612
+ ++this.shopperLocationRequestId;
613
+ ++this.nearestBranchRequestId;
614
+ this.loadingNearestBranch = false;
615
+ this.shopperLocation = null;
530
616
  this.viewMode = "main";
531
617
  this.mapLocationData = null;
532
618
  this.mapCountryCode = null;
@@ -543,11 +629,16 @@ export class SallaBulletDelivery {
543
629
  this.selectedSessionAddress = false;
544
630
  this.selectedBranch = null;
545
631
  this.branchSearchQuery = "";
632
+ this.branchCities = [];
633
+ this.selectedBranchCity = null;
634
+ this.expandedScheduleBranchId = null;
546
635
  this.regions = [];
547
636
  this.cities = [];
548
637
  this.districts = [];
549
638
  this.branches = [];
550
639
  this.filteredBranches = [];
640
+ this.pickupDiscountRange = null;
641
+ this.pickupDiscountResolved = false;
551
642
  this.newAddressForm = { ...SallaBulletDelivery.INITIAL_ADDRESS_FORM };
552
643
  this.showCartWillBeClearedBanner = false;
553
644
  this.allocationOutOfCoverageMessage = null;
@@ -665,7 +756,7 @@ export class SallaBulletDelivery {
665
756
  if (!wasVisible) {
666
757
  // Load silently — a successful auto-confirm should never flash the
667
758
  // saved-addresses list; only reveal the modal below if it can't finish on its own.
668
- await this.open(true);
759
+ await this.open(true, "after_login");
669
760
  }
670
761
  // open() -> loadSavedAddresses() already sets selectedSessionAddress when a
671
762
  // session intent exists; bail if that didn't happen (e.g. address_id already set).
@@ -951,16 +1042,63 @@ export class SallaBulletDelivery {
951
1042
  });
952
1043
  }
953
1044
  async loadBranches() {
1045
+ const request = ++this.branchesRequestId;
1046
+ // A city change keeps the active search, so the server filters before picking the first page.
1047
+ const query = this.branchSearchQuery.trim();
954
1048
  this.loadingBranches = true;
955
1049
  try {
956
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1050
+ const branches = await bulletDeliveryAPI.getBranches({
957
1051
  country_id: this.selectedCountry?.id,
958
- }));
1052
+ city_id: this.selectedBranchCity?.id,
1053
+ query,
1054
+ ...(await this.getListLocation()),
1055
+ });
1056
+ if (request !== this.branchesRequestId)
1057
+ return;
1058
+ this.branchesServerQuery = query.length >= BRANCH_SEARCH_MIN_LENGTH ? query : '';
1059
+ this.applyLoadedBranches(branches);
1060
+ // Search results hold only the matching branches, which would shrink the city options.
1061
+ if (query.length < BRANCH_SEARCH_MIN_LENGTH)
1062
+ void this.refreshBranchCities();
959
1063
  }
960
1064
  finally {
961
- this.loadingBranches = false;
1065
+ if (request === this.branchesRequestId)
1066
+ this.loadingBranches = false;
962
1067
  }
963
1068
  }
1069
+ /** Reuse the country's city collection while keeping visible choices usable during loading. */
1070
+ refreshBranchCities() {
1071
+ if (!this.selectedBranchCity)
1072
+ this.branchCities = getBranchCities(this.branches);
1073
+ const countryId = this.selectedCountry?.id;
1074
+ if (!this.allBranchCities || this.allBranchCities.countryId !== countryId || Date.now() - this.allBranchCities.timestamp > CACHE_TTL) {
1075
+ const branches = bulletDeliveryAPI.getAllBranches(countryId, BRANCH_PAGE_SIZE);
1076
+ this.allBranchCities = {
1077
+ countryId,
1078
+ timestamp: Date.now(),
1079
+ branches,
1080
+ cities: branches.then((all) => getBranchCities(all)),
1081
+ };
1082
+ }
1083
+ const pending = this.allBranchCities;
1084
+ return pending.cities.then((cities) => {
1085
+ if (this.allBranchCities !== pending || this.selectedCountry?.id !== countryId)
1086
+ return;
1087
+ if (!cities.length) {
1088
+ this.allBranchCities = null;
1089
+ return;
1090
+ }
1091
+ this.branchCities = cities;
1092
+ }).catch((error) => {
1093
+ if (this.allBranchCities === pending) {
1094
+ this.allBranchCities = null;
1095
+ if (this.selectedCountry?.id === countryId && Array.isArray(error?.branches)) {
1096
+ this.branchCities = getBranchCities([...this.branches, ...error.branches]);
1097
+ }
1098
+ }
1099
+ console.error('[BulletDelivery] Error fetching branch cities', error);
1100
+ });
1101
+ }
964
1102
  /**
965
1103
  * Store a freshly fetched branch list and reconcile the current selection with it.
966
1104
  * `selectedBranch` is a snapshot from selection time: when the refreshed list carries the
@@ -977,8 +1115,33 @@ export class SallaBulletDelivery {
977
1115
  }
978
1116
  this.filterBranches();
979
1117
  }
1118
+ /** Shares a fresh branch collection with the picker on each modal open. */
1119
+ async loadPickupDiscountRange() {
1120
+ if (!this.supportsPickup)
1121
+ return;
1122
+ const requestId = ++this.pickupDiscountRequestId;
1123
+ const { branches, complete } = await bulletDeliveryAPI.getBranchCollection(undefined, 60, { requireComplete: true });
1124
+ if (requestId !== this.pickupDiscountRequestId)
1125
+ return;
1126
+ this.pickupDiscountRange = complete ? getBranchDiscountRange(branches) : null;
1127
+ this.pickupDiscountResolved = true;
1128
+ }
1129
+ /**
1130
+ * ar: "خصم 25%" / "خصم 5 – 25%" / "خصم 10 ر.س"; en: "25% discount" / "5 – 25% discount" / "10 SAR discount".
1131
+ * Money may carry the riyal-symbol SVG, so it renders as HTML.
1132
+ */
1133
+ getPickupDiscountLabel(type, min, max = min) {
1134
+ const format = (amount) => type === 'percentage' ? String(Math.round(amount * 100) / 100) : salla.money(amount);
1135
+ const value = min === max ? format(min) : `${format(min)} – ${format(max)}`;
1136
+ const amount = type === 'percentage' ? `${value}%` : `${value} `;
1137
+ const fallback = this.isArabic ? `خصم ${amount}` : ` ${amount} discount`;
1138
+ return Salla.lang.getWithDefault('pages.checkout.pickup_discount', fallback, { amount });
1139
+ }
980
1140
  filterBranches() {
981
- this.filteredBranches = filterBranches(this.branches, this.branchSearchQuery);
1141
+ const query = this.branchSearchQuery.trim();
1142
+ this.filteredBranches = query && query === this.branchesServerQuery
1143
+ ? this.branches
1144
+ : filterBranches(this.branches, query);
982
1145
  }
983
1146
  /**
984
1147
  * Prefill form fields from session storage
@@ -1003,7 +1166,7 @@ export class SallaBulletDelivery {
1003
1166
  if (country) {
1004
1167
  this.selectedCountry = country;
1005
1168
  this.updateNewAddressForm({ country_id: Number(country.id) });
1006
- this.preselectBranch(getIntentBranchId(intent));
1169
+ await this.preselectBranch(getIntentBranchId(intent));
1007
1170
  }
1008
1171
  }
1009
1172
  }
@@ -1042,7 +1205,16 @@ export class SallaBulletDelivery {
1042
1205
  }
1043
1206
  }
1044
1207
  applyCountryChange(newCountry) {
1208
+ ++this.branchesRequestId;
1209
+ this.allBranchCities = null;
1210
+ this.loadingBranches = false;
1211
+ this.branches = [];
1212
+ this.filteredBranches = [];
1045
1213
  this.selectedCountry = newCountry;
1214
+ this.selectedBranchCity = null;
1215
+ this.branchCities = [];
1216
+ this.branchCitySearchQuery = '';
1217
+ this.selectedBranch = null;
1046
1218
  this.selectedRegion = null;
1047
1219
  this.regions = [];
1048
1220
  this.displayedRegions = [];
@@ -1162,19 +1334,11 @@ export class SallaBulletDelivery {
1162
1334
  void this.loadBranches();
1163
1335
  return;
1164
1336
  }
1165
- this.branchSearchDebounceTimer = setTimeout(async () => {
1166
- if (query.trim().length < 2)
1337
+ this.branchSearchDebounceTimer = setTimeout(() => {
1338
+ // Below the minimum, only reload when the list still holds an earlier server search.
1339
+ if (query.trim().length < BRANCH_SEARCH_MIN_LENGTH && !this.branchesServerQuery)
1167
1340
  return;
1168
- this.loadingBranches = true;
1169
- try {
1170
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1171
- country_id: this.selectedCountry?.id,
1172
- query,
1173
- }));
1174
- }
1175
- finally {
1176
- this.loadingBranches = false;
1177
- }
1341
+ void this.loadBranches();
1178
1342
  }, BRANCH_SEARCH_DEBOUNCE_MS);
1179
1343
  }
1180
1344
  clearSearchTimers() {
@@ -1201,13 +1365,23 @@ export class SallaBulletDelivery {
1201
1365
  this.clearSearchTimers();
1202
1366
  }
1203
1367
  async loadBranchesWithLocation(lat, lng) {
1368
+ // Later list loads (city change, search) keep measuring from the device location.
1369
+ ++this.shopperLocationRequestId;
1370
+ this.shopperLocation = { lat, lng };
1371
+ const request = ++this.branchesRequestId;
1204
1372
  this.loadingBranches = true;
1205
1373
  try {
1206
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1374
+ const branches = await bulletDeliveryAPI.getBranches({
1207
1375
  country_id: this.selectedCountry?.id,
1376
+ city_id: this.selectedBranchCity?.id,
1208
1377
  lat,
1209
1378
  lng,
1210
- }));
1379
+ });
1380
+ if (request !== this.branchesRequestId)
1381
+ return false;
1382
+ this.branchesServerQuery = '';
1383
+ this.applyLoadedBranches(branches);
1384
+ void this.refreshBranchCities();
1211
1385
  // Auto-select country from first branch if no country is selected
1212
1386
  if (!this.selectedCountry && this.branches.length > 0) {
1213
1387
  const firstBranch = this.branches[0];
@@ -1221,9 +1395,11 @@ export class SallaBulletDelivery {
1221
1395
  }
1222
1396
  }
1223
1397
  }
1398
+ return true;
1224
1399
  }
1225
1400
  finally {
1226
- this.loadingBranches = false;
1401
+ if (request === this.branchesRequestId)
1402
+ this.loadingBranches = false;
1227
1403
  }
1228
1404
  }
1229
1405
  /** Locate nearest branch using device geolocation (branches tab only). */
@@ -1232,6 +1408,7 @@ export class SallaBulletDelivery {
1232
1408
  this.locationError = Salla.lang.get("pages.checkout.current_location_not_supported");
1233
1409
  return;
1234
1410
  }
1411
+ const request = ++this.nearestBranchRequestId;
1235
1412
  this.loadingNearestBranch = true;
1236
1413
  this.locationError = "";
1237
1414
  try {
@@ -1243,7 +1420,12 @@ export class SallaBulletDelivery {
1243
1420
  });
1244
1421
  });
1245
1422
  const { latitude, longitude } = position.coords;
1246
- await this.loadBranchesWithLocation(latitude, longitude);
1423
+ if (request !== this.nearestBranchRequestId)
1424
+ return;
1425
+ if (!await this.loadBranchesWithLocation(latitude, longitude))
1426
+ return;
1427
+ if (request !== this.nearestBranchRequestId)
1428
+ return;
1247
1429
  const nearestBranch = findNearestBranch(this.branches, latitude, longitude);
1248
1430
  if (nearestBranch) {
1249
1431
  this.selectedBranch = nearestBranch;
@@ -1252,26 +1434,56 @@ export class SallaBulletDelivery {
1252
1434
  }
1253
1435
  }
1254
1436
  catch (error) {
1437
+ if (request !== this.nearestBranchRequestId)
1438
+ return;
1255
1439
  const geoError = error;
1256
1440
  const code = typeof geoError?.code === "number" ? geoError.code : -1;
1257
- this.locationError = getGeolocationErrorMessage(code);
1441
+ this.locationError = this.geolocationErrorMessage(code);
1258
1442
  }
1259
1443
  finally {
1260
- this.loadingNearestBranch = false;
1444
+ if (request === this.nearestBranchRequestId)
1445
+ this.loadingNearestBranch = false;
1261
1446
  }
1262
1447
  }
1448
+ geolocationErrorMessage(code) {
1449
+ if (code === GEO_PERMISSION_DENIED) {
1450
+ return Salla.lang.getWithDefault("pages.checkout.allow_location_permission_blocked_hint", this.isArabic
1451
+ ? "مشاركة الموقع محجوبة لهذا المتجر في متصفحك. افتح إعدادات الموقع في المتصفح، اسمح بالوصول إلى الموقع، ثم أعد المحاولة."
1452
+ : "Location sharing is blocked for this store in your browser. Open your browser's location settings, allow location access, then try again.");
1453
+ }
1454
+ return Salla.lang.getWithDefault("pages.checkout.pickup_location_unavailable", this.isArabic ? "تعذَّر تحديد موقعك. حاول مرة أخرى." : "We couldn't detect your location. Try again.");
1455
+ }
1263
1456
  handleBranchSelect(branch) {
1264
1457
  this.selectedBranch = branch;
1265
1458
  this.allocationOutOfCoverageMessage = null;
1266
1459
  }
1460
+ toggleBranchSchedule(branchId) {
1461
+ this.expandedScheduleBranchId = this.expandedScheduleBranchId === branchId ? null : branchId;
1462
+ }
1267
1463
  /**
1268
1464
  * Restore a remembered / event-provided branch — but only one the list renders as selectable,
1269
1465
  * so a branch that has closed since it was remembered is never silently preselected (INV-1407).
1270
1466
  */
1271
- preselectBranch(branchId) {
1272
- if (branchId == null)
1467
+ async preselectBranch(branchId) {
1468
+ if (branchId == null || this.selectedBranch)
1273
1469
  return;
1274
- const branch = this.branches.find((b) => b.id === branchId);
1470
+ let branch = this.branches.find((b) => b.id === branchId);
1471
+ if (!branch && this.allBranchCities) {
1472
+ const pending = this.allBranchCities;
1473
+ const request = this.branchesRequestId;
1474
+ try {
1475
+ branch = (await pending.branches).find((b) => b.id === branchId);
1476
+ }
1477
+ catch {
1478
+ return;
1479
+ }
1480
+ if (request !== this.branchesRequestId || this.activeTab !== 'branch' || this.allBranchCities !== pending || pending.countryId !== this.selectedCountry?.id || this.selectedBranch || this.branchSearchQuery.trim())
1481
+ return;
1482
+ if (this.selectedBranchCity && String(branch?.city?.id) !== String(this.selectedBranchCity.id))
1483
+ return;
1484
+ if (branch && isBranchSelectable(branch))
1485
+ this.applyLoadedBranches([...this.branches, branch]);
1486
+ }
1275
1487
  if (branch && isBranchSelectable(branch)) {
1276
1488
  this.selectedBranch = branch;
1277
1489
  }
@@ -1532,6 +1744,48 @@ export class SallaBulletDelivery {
1532
1744
  }
1533
1745
  /** Center shown before geolocation resolves — the customer's IP-based default (falls back to Riyadh). */
1534
1746
  get mapInitialCenter() {
1747
+ return this.defaultGeoLocation ?? DEFAULT_MAP_CENTER;
1748
+ }
1749
+ /** Device location for a branch list request, waiting at most BRANCH_LIST_LOCATION_WAIT. */
1750
+ getListLocation() {
1751
+ const location = this.getGrantedDeviceLocation();
1752
+ if (this.shopperLocation || !navigator.geolocation || !navigator.permissions?.query)
1753
+ return location;
1754
+ return Promise.race([location, new Promise((resolve) => setTimeout(() => resolve({}), BRANCH_LIST_LOCATION_WAIT))]);
1755
+ }
1756
+ /**
1757
+ * Where branch distances (`distance_km`) are measured from: the shopper's real device location
1758
+ * only. It comes from "nearest to my location", or is read silently when location permission was
1759
+ * already granted (never prompting). Empty otherwise: `user.default_geo_location` and the Riyadh
1760
+ * map fallback are not where the shopper is, so they would show wrong distances.
1761
+ */
1762
+ async getGrantedDeviceLocation() {
1763
+ if (this.shopperLocation)
1764
+ return this.shopperLocation;
1765
+ if (!navigator.geolocation || !navigator.permissions?.query)
1766
+ return {};
1767
+ const request = ++this.shopperLocationRequestId;
1768
+ try {
1769
+ const permission = await navigator.permissions.query({ name: 'geolocation' });
1770
+ if (request !== this.shopperLocationRequestId)
1771
+ return this.shopperLocation ?? {};
1772
+ if (permission.state !== 'granted')
1773
+ return {};
1774
+ const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject, {
1775
+ timeout: SILENT_GEOLOCATION_TIMEOUT,
1776
+ maximumAge: SILENT_GEOLOCATION_MAX_AGE,
1777
+ }));
1778
+ if (request !== this.shopperLocationRequestId)
1779
+ return this.shopperLocation ?? {};
1780
+ this.shopperLocation = { lat: position.coords.latitude, lng: position.coords.longitude };
1781
+ return this.shopperLocation;
1782
+ }
1783
+ catch {
1784
+ return {};
1785
+ }
1786
+ }
1787
+ /** The customer's IP-based `user.default_geo_location`, or null when it is missing or invalid. */
1788
+ get defaultGeoLocation() {
1535
1789
  const geo = salla.config.get('user.default_geo_location');
1536
1790
  // Coerce defensively: config may arrive as strings, blank/whitespace, NaN, or out-of-range
1537
1791
  // values. Reject blanks before coercion (Number('') === 0) and validate lat/lng ranges so we
@@ -1540,7 +1794,7 @@ export class SallaBulletDelivery {
1540
1794
  const lng = this.toCoordinate(geo?.lng);
1541
1795
  const isValid = lat != null && lng != null &&
1542
1796
  lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
1543
- return isValid ? { lat, lng } : DEFAULT_MAP_CENTER;
1797
+ return isValid ? { lat, lng } : null;
1544
1798
  }
1545
1799
  /** Parse a config coordinate to a finite number, treating blank/whitespace strings as missing. */
1546
1800
  toCoordinate(value) {
@@ -1554,6 +1808,8 @@ export class SallaBulletDelivery {
1554
1808
  async handleTabChange(tab) {
1555
1809
  if (this.tabChanging)
1556
1810
  return;
1811
+ ++this.branchesRequestId;
1812
+ this.loadingBranches = false;
1557
1813
  this.tabChanging = true;
1558
1814
  try {
1559
1815
  this.activeTab = tab;
@@ -1585,24 +1841,25 @@ export class SallaBulletDelivery {
1585
1841
  this.selectedCountry = this.countries[0];
1586
1842
  }
1587
1843
  }
1844
+ if (String(previousCountryId ?? '') !== String(this.selectedCountry?.id ?? '')) {
1845
+ this.allBranchCities = null;
1846
+ this.selectedBranchCity = null;
1847
+ this.branchCities = [];
1848
+ this.branchCitySearchQuery = '';
1849
+ this.selectedBranch = null;
1850
+ this.branches = [];
1851
+ this.filteredBranches = [];
1852
+ }
1588
1853
  if (tab === "address" && this.needsMap)
1589
1854
  this.fetchAddressToken();
1590
1855
  // Load appropriate data when switching tabs if country is selected
1591
1856
  if (this.selectedCountry) {
1592
1857
  if (tab === "branch") {
1593
1858
  // Load branches when switching to branch tab
1594
- // Check session storage for lat/lng to get nearby branches
1595
1859
  const savedIntent = this.getStoredIntent();
1596
- const intentLat = getIntentLatitude(savedIntent);
1597
- const intentLng = getIntentLongitude(savedIntent);
1598
- if (intentLat != null && intentLng != null) {
1599
- await this.loadBranchesWithLocation(intentLat, intentLng);
1600
- }
1601
- else {
1602
- await this.loadBranches();
1603
- }
1860
+ await this.loadBranches();
1604
1861
  // Preselect branch from session storage if available
1605
- this.preselectBranch(getIntentBranchId(savedIntent));
1862
+ void this.preselectBranch(getIntentBranchId(savedIntent));
1606
1863
  }
1607
1864
  else if (tab === "address") {
1608
1865
  // Lazy-load saved addresses when switching to address tab (if not loaded yet).
@@ -1728,7 +1985,7 @@ export class SallaBulletDelivery {
1728
1985
  if (await this.tryAutoConfirmAfterLogin())
1729
1986
  return;
1730
1987
  if (!this.modal?.hasAttribute?.("visible") || !this.isLoggedIn) {
1731
- this.open();
1988
+ this.open(false, "after_login");
1732
1989
  }
1733
1990
  }
1734
1991
  buildIntentAddress() {
@@ -2007,13 +2264,14 @@ export class SallaBulletDelivery {
2007
2264
  }
2008
2265
  return raw;
2009
2266
  }
2010
- async handleConfirm() {
2267
+ async handleConfirm(intentBeforeDraft) {
2011
2268
  if (this.isConfirmDisabled() || this.confirming)
2012
2269
  return;
2013
2270
  this.allocationOutOfCoverageMessage = null;
2014
2271
  this.confirming = true;
2015
2272
  this.confirmBtn?.load();
2016
2273
  let scheduledRedirect = false;
2274
+ const previousIntent = intentBeforeDraft === undefined ? this.getStoredIntent() : intentBeforeDraft;
2017
2275
  try {
2018
2276
  // A guest map pin uses the pin's own country; the IP/selected country may differ from it.
2019
2277
  const country = this.activeTab === "address" && this.isGuestMapPick
@@ -2031,6 +2289,7 @@ export class SallaBulletDelivery {
2031
2289
  const savedAddress = await this.saveSessionAddressToProfile();
2032
2290
  if (!savedAddress) {
2033
2291
  Salla.notify?.error(Salla.lang.get("common.errors.error_occurred"));
2292
+ this.trackSelectorResult("failed", previousIntent, []);
2034
2293
  this.confirmBtn?.stop();
2035
2294
  return;
2036
2295
  }
@@ -2104,9 +2363,17 @@ export class SallaBulletDelivery {
2104
2363
  (this.activeTab === "address"
2105
2364
  ? Salla.lang.get("pages.checkout.address_out_of_coverage")
2106
2365
  : Salla.lang.get("pages.checkout.failed_to_set_pickup"));
2107
- this.allocationOutOfCoverageMessage = `${errorMessage}.`;
2366
+ this.allocationOutOfCoverageMessage = /[.!؟?]$/.test(errorMessage.trim()) ? errorMessage : `${errorMessage}.`;
2367
+ this.trackSelectorResult("failed", previousIntent, []);
2108
2368
  this.confirmBtn?.stop();
2109
2369
  this.confirming = false;
2370
+ // A rejected branch may have turned busy or closed: drop it (the reload may hide it) and
2371
+ // reload so the list shows its current state.
2372
+ if (this.activeTab === "branch") {
2373
+ this.selectedBranch = null;
2374
+ clearApiCache();
2375
+ void this.loadBranches();
2376
+ }
2110
2377
  return;
2111
2378
  }
2112
2379
  // Process successful allocation response
@@ -2141,6 +2408,7 @@ export class SallaBulletDelivery {
2141
2408
  const scopeApplied = await bulletDeliveryAPI.setDeliveryScope(allocatedScopeId);
2142
2409
  if (!scopeApplied) {
2143
2410
  this.allocationOutOfCoverageMessage = `${Salla.lang.get("common.errors.error_occurred")}.`;
2411
+ this.trackSelectorResult("failed", previousIntent, [], allocatedScopeId);
2144
2412
  this.confirmBtn?.stop();
2145
2413
  this.confirming = false;
2146
2414
  return;
@@ -2160,6 +2428,7 @@ export class SallaBulletDelivery {
2160
2428
  // After allocation success: persist latest intent so switch UI can always reflect
2161
2429
  // the selected address/branch once confirmed.
2162
2430
  this.setStoredIntent({ ...result, scope_allocated: true });
2431
+ this.trackSelectorResult("accepted", previousIntent, allocatedBranchIds(result), allocatedScopeId);
2163
2432
  const allocationConfirmedPayload = {
2164
2433
  ...result,
2165
2434
  address: this.buildAllocationConfirmedAddressData(selectedAddress),
@@ -2270,6 +2539,19 @@ export class SallaBulletDelivery {
2270
2539
  wrapper.append(info, btn);
2271
2540
  return wrapper;
2272
2541
  }
2542
+ /**
2543
+ * A branch pickup offer (INV-1416) replaces the default "save delivery fees" badge; only one shows,
2544
+ * and neither until the offer lookup settles.
2545
+ */
2546
+ renderPickupTabBadge() {
2547
+ if (!this.pickupDiscountResolved)
2548
+ return null;
2549
+ const range = this.pickupDiscountRange;
2550
+ if (range) {
2551
+ return (h("span", { class: "s-bullet-delivery-tab-badge s-bullet-delivery-discount-badge", dir: this.isArabic ? 'rtl' : 'ltr', innerHTML: this.getPickupDiscountLabel(range.type, range.min, range.max) }));
2552
+ }
2553
+ return (h("span", { class: "s-bullet-delivery-tab-badge" }, h("span", { class: "s-bullet-delivery-tab-badge-icon", innerHTML: LightningIcon, "aria-hidden": "true" }), Salla.lang.getWithDefault("pages.checkout.save_delivery_fees", "وفّر رسوم التوصيل")));
2554
+ }
2273
2555
  renderTabs() {
2274
2556
  // Show tabs only when pickup is supported; otherwise delivery-only view with no tabs.
2275
2557
  // The pickup tab is gated by this same `supportsPickup` check, while the delivery
@@ -2279,10 +2561,10 @@ export class SallaBulletDelivery {
2279
2561
  return (h("div", { class: "s-bullet-delivery-tabs" }, this.isSupportsDelivery && (h("button", { type: "button", class: {
2280
2562
  "s-bullet-delivery-tab": true,
2281
2563
  "s-bullet-delivery-tab--active": this.activeTab === "address",
2282
- }, onClick: () => this.handleTabChange("address") }, h("span", { class: "s-bullet-delivery-tab-icon", innerHTML: Location, "aria-hidden": "true" }), h("span", null, Salla.lang.get("mobile_app.strings.delivery")))), h("button", { type: "button", class: {
2564
+ }, onClick: () => this.handleTabChange("address") }, h("span", null, Salla.lang.get("mobile_app.strings.delivery")))), h("button", { type: "button", class: {
2283
2565
  "s-bullet-delivery-tab": true,
2284
2566
  "s-bullet-delivery-tab--active": this.activeTab === "branch",
2285
- }, onClick: () => this.handleTabChange("branch") }, h("span", { class: "s-bullet-delivery-tab-icon", innerHTML: Store, "aria-hidden": "true" }), h("span", null, Salla.lang.get("mobile_app.strings.pickup")))));
2567
+ }, onClick: () => this.handleTabChange("branch") }, h("span", null, Salla.lang.get("mobile_app.strings.pickup")), this.renderPickupTabBadge())));
2286
2568
  }
2287
2569
  renderAlert(message) {
2288
2570
  return (h("div", { class: "s-bullet-delivery-allocation-out-of-coverage", role: "alert" }, h("span", { class: "s-bullet-delivery-allocation-out-of-coverage-icon", "aria-hidden": "true" }, h("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", role: "img", "aria-hidden": "true" }, h("title", null, "Information"), h("circle", { cx: "10", cy: "10", r: "9", stroke: "currentColor", "stroke-width": "1.5", fill: "currentColor" }), h("text", { x: "10", y: "14", "text-anchor": "middle", fill: "white", "font-size": "12", "font-weight": "bold" }, "i"))), h("span", { class: "s-bullet-delivery-allocation-out-of-coverage-text", style: { textAlign: this.isRTL ? 'right' : 'left' } }, message)));
@@ -2306,8 +2588,9 @@ export class SallaBulletDelivery {
2306
2588
  }
2307
2589
  return this.renderAlert(`${message} ${Salla.lang.getWithDefault('pages.cart.choose_another_address', 'Choose another address')}`);
2308
2590
  }
2309
- renderCountrySelect() {
2310
- return (h("div", { class: "s-bullet-delivery-field" }, h("salla-searchable-dropdown", { label: Salla.lang.get("blocks.buy_as_gift.receiver_country"), placeholder: Salla.lang.get("pages.checkout.select_country"), items: this.displayedCountries, selectedItem: this.selectedCountry, loading: this.loadingCountries, searching: this.searchingCountries, disabled: this.countries.length <= 1, required: true, inputId: "bullet-delivery-country", searchQuery: this.countrySearchQuery, clientSearch: true, onItemSelected: (e) => this.handleCountrySelected(e.detail), onSearchInput: (e) => this.handleCountrySearch(e.detail), onDropdownClosed: () => this.handleCountryDropdownClosed() })));
2591
+ /** `showLabel` is off in the pickup filter panel, where the selects sit label-less side by side. */
2592
+ renderCountrySelect(showLabel = true) {
2593
+ return (h("div", { class: "s-bullet-delivery-field" }, h("salla-searchable-dropdown", { label: showLabel ? Salla.lang.get("blocks.buy_as_gift.receiver_country") : undefined, placeholder: Salla.lang.get("pages.checkout.select_country"), items: this.displayedCountries, selectedItem: this.selectedCountry, loading: this.loadingCountries, searching: this.searchingCountries, disabled: this.countries.length <= 1, required: true, inputId: "bullet-delivery-country", searchQuery: this.countrySearchQuery, clientSearch: true, onItemSelected: (e) => this.handleCountrySelected(e.detail), onSearchInput: (e) => this.handleCountrySearch(e.detail), onDropdownClosed: () => this.handleCountryDropdownClosed() })));
2311
2594
  }
2312
2595
  /** Region select (SA only): shown when SA is selected; city and district disabled until region selected. */
2313
2596
  renderRegionSelect() {
@@ -2552,66 +2835,146 @@ export class SallaBulletDelivery {
2552
2835
  return (h("form", { onSubmit: handleSubmit }, this.renderGuestDeliveryForm(), h("div", { class: "s-bullet-delivery-form-actions s-bullet-delivery-form-actions--single", style: { padding: '24px' } }, h("salla-button", { type: "submit", loading: this.confirming, disabled: !canSubmit || this.confirming, width: "wide" }, submitLabel), !this.isLoggedIn && (h("button", { type: "button", class: "s-bullet-delivery-login-prompt", onClick: () => this.handleLogin() }, Salla.lang.get('pages.checkout.login_to_use_saved_addresses'))))));
2553
2836
  }
2554
2837
  renderBranchSearch() {
2555
- return (h("div", { class: "s-bullet-delivery-branch-search" }, h("div", { class: "s-bullet-delivery-search-row" }, h("div", { class: "s-bullet-delivery-search s-bullet-delivery-branch-search-wrap" }, h("span", { class: "s-bullet-delivery-search-icon s-bullet-delivery-branch-search-icon", innerHTML: Search, "aria-hidden": "true" }), h("input", { type: "text", class: "s-bullet-delivery-branch-search-input form-input", placeholder: Salla.lang.get("pages.checkout.search_for_city_or_branch"), value: this.branchSearchQuery, onInput: (e) => this.handleBranchSearch(e), autocomplete: "off" })), h("button", { type: "button", class: {
2838
+ return (h("div", { class: "s-bullet-delivery-branch-search" }, h("div", { class: "s-bullet-delivery-search-row" }, h("div", { class: "s-bullet-delivery-search s-bullet-delivery-branch-search-wrap" }, h("span", { class: "s-bullet-delivery-search-icon s-bullet-delivery-branch-search-icon", innerHTML: Search, "aria-hidden": "true" }), h("input", { type: "text", class: "s-bullet-delivery-branch-search-input form-input", placeholder: Salla.lang.getWithDefault("pages.checkout.search_for_country_city_or_branch", this.isArabic ? "ابحث عن دولة أو مدينة أو فرع" : "Search for a country, city or branch"), value: this.branchSearchQuery, onInput: (e) => this.handleBranchSearch(e), autocomplete: "off" })), h("button", { type: "button", class: {
2556
2839
  "s-bullet-delivery-location-btn": true,
2557
2840
  "s-bullet-delivery-location-btn--full": true,
2558
2841
  "s-bullet-delivery-location-btn--loading": this.loadingNearestBranch,
2559
- }, onClick: () => this.handleFindNearestBranch(), disabled: this.loadingNearestBranch }, this.loadingNearestBranch ? (h("span", { class: "s-bullet-delivery-location-icon", "aria-hidden": "true" }, h("salla-skeleton", { height: "18px", width: "18px" }))) : (h("span", { class: "s-bullet-delivery-location-icon", innerHTML: GPS, "aria-hidden": "true" })), h("span", null, Salla.lang.get("pages.checkout.nearest_to_my_location")))), this.locationError && this.activeTab === "branch" && (h("span", { class: "s-bullet-delivery-error" }, this.locationError))));
2560
- }
2561
- /** Renders a branch's `preparation_time_range` as a localized "from - to <unit>" label. */
2562
- getPreparationTimeLabel(range) {
2563
- if (!range)
2564
- return null;
2565
- const unitKeys = {
2566
- hour: 'pages.checkout.preparation_time_hours',
2567
- minute: 'pages.checkout.preparation_time_minutes',
2568
- day: 'pages.checkout.preparation_time_days',
2569
- };
2570
- const key = unitKeys[range.from.unit];
2571
- if (!key)
2572
- return null;
2573
- return Salla.lang
2574
- .getWithDefault(key, 'من {from} إلى {to} دقائق')
2575
- ?.replace('{from}', String(range.from.value))
2576
- ?.replace('{to}', String(range.to.value));
2842
+ }, onClick: () => this.handleFindNearestBranch(), disabled: this.loadingNearestBranch, "aria-label": Salla.lang.get("pages.checkout.nearest_to_my_location") }, this.loadingNearestBranch ? (h("span", { class: "s-bullet-delivery-location-icon", "aria-hidden": "true" }, h("salla-skeleton", { height: "16px", width: "16px" }))) : (h("span", { class: "s-bullet-delivery-location-icon", innerHTML: Location, "aria-hidden": "true" })), h("span", { class: "s-bullet-delivery-location-label" }, Salla.lang.get("pages.checkout.nearest_to_my_location")))), this.locationError && this.activeTab === "branch" && (h("span", { class: "s-bullet-delivery-error" }, this.locationError))));
2577
2843
  }
2578
2844
  renderBranchList() {
2579
2845
  if (this.loadingBranches) {
2580
2846
  return (h("div", { class: "s-bullet-delivery-branches-wrap" }, h("div", { class: "s-bullet-delivery-branches-loading" }, [1, 2, 3, 4].map((v) => (h("div", { key: v, class: "s-bullet-delivery-branch-skeleton" }, h("salla-skeleton", { class: "s-bullet-delivery-branch-skeleton-radio", type: "circle", height: "16px", width: "16px" }), h("div", { class: "s-bullet-delivery-branch-skeleton-icon" }, h("salla-skeleton", { height: "34px", width: "34px" })), h("div", { class: "s-bullet-delivery-branch-skeleton-content" }, h("salla-skeleton", { class: "s-bullet-delivery-branch-skeleton-title", height: "12px", width: "124px" })), h("salla-skeleton", { class: "s-bullet-delivery-branch-skeleton-time", height: "12px", width: "88px" }), h("div", { class: "s-bullet-delivery-branch-skeleton-map" }, h("salla-skeleton", { height: "32px", width: "32px" }))))))));
2581
2847
  }
2582
- if (this.filteredBranches.length === 0) {
2848
+ const branches = this.filteredBranches.filter(isBranchVisible);
2849
+ if (branches.length === 0) {
2583
2850
  return (h("salla-placeholder", { alignment: "center", class: "s-bullet-delivery-placeholder" }, h("span", { slot: "title" }, Salla.lang.get("pages.checkout.no_pickup_branches_found"))));
2584
2851
  }
2585
- return (h("div", { class: "s-bullet-delivery-branches-wrap" }, h("ul", { class: "s-bullet-delivery-branches-list s-scrollbar" }, this.filteredBranches.map(branch => {
2852
+ return (h("div", { class: "s-bullet-delivery-branches-wrap" }, h("ul", { class: "s-bullet-delivery-branches-list s-scrollbar", role: "listbox" }, branches.map(branch => {
2586
2853
  const selectable = isBranchSelectable(branch);
2587
- const readiness = branch?.pickup_readiness;
2854
+ const isSelected = this.selectedBranch?.id === branch.id;
2588
2855
  const locationText = [branch.country?.name, branch.city?.name, branch.district?.name]
2589
2856
  .filter(Boolean)
2590
- .join(', ');
2591
- const preparationTimeLabel = this.getPreparationTimeLabel(branch.preparation_time_range);
2857
+ .join('، ');
2858
+ const distance = formatBranchDistance(branch.distance_km);
2859
+ // Closed but taking orders: the opening-time readiness replaces the range.
2860
+ const readinessLabel = getBranchReadinessLabel(branch) ?? this.getReadyTodayLabel(branch);
2861
+ const preparationLabel = readinessLabel ? null : this.getPreparationShortLabel(branch.preparation_time_range);
2862
+ const scheduleGroups = groupBranchSchedule(getBranchWeekSchedule(branch));
2863
+ // An always-open branch has no hours worth listing, so it gets no working-hours dropdown.
2864
+ const hasSchedule = scheduleGroups.length > 0 && !isBranchAlwaysOpen(branch);
2865
+ const scheduleOpen = hasSchedule && this.expandedScheduleBranchId === branch.id;
2866
+ const phoneHref = getBranchPhoneHref(branch);
2867
+ const whatsappHref = getBranchWhatsappHref(branch);
2868
+ const directionsHref = getBranchDirectionsHref(branch);
2869
+ const discount = getBranchDiscount(branch);
2592
2870
  return (h("li", { key: branch.id, class: {
2593
2871
  's-bullet-delivery-branch-item': true,
2594
- 's-bullet-delivery-branch-item--selected': this.selectedBranch?.id === branch.id,
2872
+ 's-bullet-delivery-branch-item--selected': isSelected,
2595
2873
  's-bullet-delivery-branch-item--disabled': !selectable,
2596
- }, tabIndex: selectable ? 0 : -1, role: "option", "aria-disabled": !selectable, onClick: () => selectable && this.handleBranchSelect(branch), onKeyDown: e => {
2874
+ }, tabIndex: selectable ? 0 : -1, role: "option", "aria-selected": isSelected, "aria-disabled": !selectable, onClick: () => selectable && this.handleBranchSelect(branch), onKeyDown: e => {
2875
+ if (e.target !== e.currentTarget)
2876
+ return;
2597
2877
  if (e.key === 'Enter' || e.key === ' ') {
2598
2878
  e.preventDefault();
2599
2879
  if (selectable)
2600
2880
  this.handleBranchSelect(branch);
2601
2881
  }
2602
- } }, h("input", { type: "radio", name: "branch", class: "s-bullet-delivery-radio", checked: this.selectedBranch?.id === branch.id, disabled: !selectable }), h("span", { class: "s-bullet-delivery-branch-icon", "aria-hidden": "true" }, h("span", { innerHTML: Store })), h("div", { class: "flex flex-col" }, h("div", { class: "flex items-center gap-1 mb-1" }, h("p", { class: "s-bullet-delivery-branch-name text-start" }, branch.name), readiness?.label && (h("p", { class: `branch-status-badge${readiness.color ? ` ${readiness.color}` : ''}` }, readiness.label))), locationText ? (h("span", { class: "s-bullet-delivery-branch-prep", style: { fontSize: '0.8rem', lineHeight: '1.1rem', textAlign: this.isRTL ? 'right' : 'left' } }, h("i", { class: "sicon-location" }), "\u00A0", h("span", { class: "first-letter-cap" }, locationText))) : null, preparationTimeLabel ? (h("span", { class: "s-bullet-delivery-branch-prep", style: { fontSize: '0.8rem', lineHeight: '1.1rem', textAlign: this.isRTL ? 'right' : 'left' } }, h("i", { class: "sicon-clock" }), "\u00A0", h("span", { class: "first-letter-cap" }, Salla.lang.get('pages.checkout.pickup_duration'), ": ", preparationTimeLabel))) : null, readiness?.detail_line && (h("span", { class: "s-bullet-delivery-branch-prep", style: { fontSize: '0.8rem', lineHeight: '1.1rem', textAlign: this.isRTL ? 'right' : 'left' } }, h("i", { class: "sicon-store" }), "\u00A0", readiness.detail_line))), h("div", { style: {
2603
- width: 'full',
2604
- maxWidth: '50px',
2605
- marginInlineStart: 'auto',
2606
- } }, branch.location?.lat != null && branch.location?.lng != null && (h("a", { href: `https://www.google.com/maps?q=${branch.location.lat},${branch.location.lng}`, target: "_blank", rel: "noopener noreferrer", class: "s-bullet-delivery-branch-map-link", title: Salla.lang.get('common.elements.open_in_google_maps'), onClick: e => e.stopPropagation() }, h("span", { class: "s-bullet-delivery-branch-map-link-icon", innerHTML: GetDirections.replace(/mask0_25437_34440/g, `mask0_branch_${branch.id}`), "aria-hidden": "true" }), h("span", { class: "sr-only" }, Salla.lang.get('common.elements.open_in_google_maps')))))));
2882
+ } }, h("input", { type: "radio", name: "branch", class: "s-bullet-delivery-radio s-bullet-delivery-branch-radio", checked: isSelected, disabled: !selectable, tabIndex: -1 }), h("div", { class: "s-bullet-delivery-branch-body" }, h("p", { class: "s-bullet-delivery-branch-name" }, h("span", null, branch.name), discount ? (h("span", { class: "s-bullet-delivery-discount-badge", dir: this.isArabic ? 'rtl' : 'ltr', innerHTML: this.getPickupDiscountLabel(discount.type, discount.amount) })) : null), locationText ? (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-location" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: BranchLocationIcon, "aria-hidden": "true" }), h("span", null, locationText))) : null, isSelected && branch.pickup_instructions ? (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-instructions" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: NotesIcon, "aria-hidden": "true" }), h("span", null, branch.pickup_instructions))) : null, h("div", { class: "s-bullet-delivery-branch-meta-row" }, distance || readinessLabel || preparationLabel ? (h("div", { class: "s-bullet-delivery-branch-pills" }, distance ? (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-pill s-bullet-delivery-branch-distance" }, h("i", { class: "sicon-location" }), h("span", null, this.renderDistanceLabel(distance)))) : null, readinessLabel ? (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-pill s-bullet-delivery-branch-prep" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: PickupAvailabilityIcon, "aria-hidden": "true" }), h("span", { class: "s-bullet-delivery-branch-meta-value" }, readinessLabel))) : preparationLabel ? (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-pill s-bullet-delivery-branch-prep" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: PickupAvailabilityIcon, "aria-hidden": "true" }), h("span", null, h("span", { class: "s-bullet-delivery-branch-meta-label" }, Salla.lang.getWithDefault('pages.checkout.pickup_branch_ready_in', 'Pickup available after:'), ' '), h("span", { class: "s-bullet-delivery-branch-meta-value" }, preparationLabel)))) : null)) : null, h("hr", { class: "s-bullet-delivery-branch-divider" }), this.renderBranchOpenStatus(branch, hasSchedule, scheduleOpen)), scheduleOpen && (h("div", { class: "s-bullet-delivery-branch-schedule" }, scheduleGroups.map((group, index) => (h("div", { key: index, class: "s-bullet-delivery-branch-schedule-day" }, h("span", { class: "s-bullet-delivery-branch-schedule-days" }, group.days.length > 1
2883
+ ? `${group.days[0]} - ${group.days[group.days.length - 1]}`
2884
+ : group.days[0]), h("span", { class: "s-bullet-delivery-branch-schedule-times" }, group.times
2885
+ .map(t => `${this.formatClock(t.start)} - ${this.formatClock(t.end)}`)
2886
+ .join(this.isArabic ? '، ' : ', ')))))))), h("div", { class: "s-bullet-delivery-branch-actions" }, phoneHref && (h("a", { href: phoneHref, class: "s-bullet-delivery-branch-action", title: Salla.lang.getWithDefault('pages.checkout.branch_call', 'اتصال'), onClick: e => e.stopPropagation() }, h("i", { class: "sicon-phone" }))), whatsappHref && (h("a", { href: whatsappHref, target: "_blank", rel: "noopener noreferrer", class: "s-bullet-delivery-branch-action", title: Salla.lang.getWithDefault('pages.checkout.branch_whatsapp', 'واتساب'), onClick: e => e.stopPropagation() }, h("i", { class: "sicon-whatsapp2" }))), directionsHref && (h("a", { href: directionsHref, target: "_blank", rel: "noopener noreferrer", class: "s-bullet-delivery-branch-action s-bullet-delivery-branch-map-link", title: Salla.lang.get('common.elements.open_in_google_maps'), onClick: e => e.stopPropagation() }, h("span", { class: "s-bullet-delivery-branch-map-link-icon", innerHTML: GetDirections.replace(/mask0_25437_34440/g, `mask0_branch_${branch.id}`), "aria-hidden": "true" }), h("span", { class: "sr-only" }, Salla.lang.get('common.elements.open_in_google_maps')))))));
2607
2887
  }))));
2608
2888
  }
2889
+ /** "يبعد عنك: ~" stays muted; the distance and its unit read as the value, as in checkout. */
2890
+ renderDistanceLabel(distance) {
2891
+ const template = Salla.lang.getWithDefault('pages.checkout.branch_distance_from_you', 'يبعد عنك: ~ {distance} كم');
2892
+ const index = template.indexOf('{distance}');
2893
+ if (index === -1)
2894
+ return template;
2895
+ return [
2896
+ h("span", { class: "s-bullet-delivery-branch-meta-label" }, template.slice(0, index)),
2897
+ h("span", { class: "s-bullet-delivery-branch-meta-value" }, template.slice(index).replace('{distance}', distance)),
2898
+ ];
2899
+ }
2900
+ get isArabic() {
2901
+ return (Salla.lang.getLocale?.() ?? document.documentElement.lang) === 'ar';
2902
+ }
2903
+ formatClock(time) {
2904
+ return formatBranchClockTime(time, this.isArabic ? 'ar' : 'en');
2905
+ }
2906
+ /** "جاهز اليوم 10:00 ص" for a branch that is closed now and opens later today. */
2907
+ getReadyTodayLabel(branch) {
2908
+ const time = getBranchReadyTodayTime(branch);
2909
+ return time
2910
+ ? `${Salla.lang.getWithDefault('pages.checkout.pickup_branch_ready_today_at', 'جاهز اليوم')} ${this.formatClock(time)}`
2911
+ : null;
2912
+ }
2913
+ /** "15-30 د" — compact preparation range for the card's meta row. */
2914
+ getPreparationShortLabel(range) {
2915
+ if (!range?.from || !range?.to)
2916
+ return null;
2917
+ const unitKeys = {
2918
+ minute: ['pages.checkout.preparation_time_minute_short', 'د'],
2919
+ hour: ['pages.checkout.preparation_time_hour_short', 'س'],
2920
+ day: ['pages.checkout.preparation_time_day_short', 'يوم'],
2921
+ };
2922
+ const unit = (u) => (unitKeys[u] ? Salla.lang.getWithDefault(unitKeys[u][0], unitKeys[u][1]) : null);
2923
+ const fromUnit = unit(range.from.unit);
2924
+ const toUnit = unit(range.to.unit);
2925
+ if (!fromUnit || !toUnit)
2926
+ return null;
2927
+ return range.from.unit === range.to.unit
2928
+ ? `${range.from.value}-${range.to.value} ${toUnit}`
2929
+ : `${range.from.value} ${fromUnit} - ${range.to.value} ${toUnit}`;
2930
+ }
2931
+ /**
2932
+ * Busy, always-open, or the backend's `open_status_line` ("مفتوح - يغلق عند 11:00 م"), which reads
2933
+ * the hours on the branch's clock. Without that line, `is_open` gives a bare open/closed label.
2934
+ */
2935
+ renderBranchOpenStatus(branch, hasSchedule, scheduleOpen) {
2936
+ const toggle = hasSchedule && (h("button", { type: "button", class: "s-bullet-delivery-branch-schedule-toggle", "aria-expanded": scheduleOpen ? 'true' : 'false', "aria-label": Salla.lang.getWithDefault('pages.checkout.pickup_show_weekly_hours', 'عرض ساعات العمل الأسبوعية'), onClick: e => {
2937
+ e.stopPropagation();
2938
+ this.toggleBranchSchedule(branch.id);
2939
+ } }, h("i", { class: { 'sicon-keyboard_arrow_down': true, 'is-expanded': scheduleOpen } })));
2940
+ if (isBranchBusy(branch)) {
2941
+ return (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-open-status" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: WorkingHoursIcon, "aria-hidden": "true" }), h("span", { class: "s-bullet-delivery-pickup-status--busy" }, Salla.lang.getWithDefault('pages.checkout.pickup_branch_busy', 'مشغول، لا يستقبل طلبات')), toggle));
2942
+ }
2943
+ const alwaysOpen = isBranchAlwaysOpen(branch);
2944
+ const line = branch.pickup_readiness?.open_status_line;
2945
+ if (!alwaysOpen && line) {
2946
+ return (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-open-status" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: WorkingHoursIcon, "aria-hidden": "true" }), h("span", null, line), toggle));
2947
+ }
2948
+ if (!alwaysOpen && !hasSchedule)
2949
+ return null;
2950
+ if (!alwaysOpen && typeof branch.is_open !== 'boolean') {
2951
+ return h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-open-status" }, toggle);
2952
+ }
2953
+ const isOpen = alwaysOpen || branch.is_open;
2954
+ const label = alwaysOpen
2955
+ ? Salla.lang.getWithDefault('pages.checkout.pickup_branch_always_open', 'مفتوح دائمًا 24/7')
2956
+ : isOpen
2957
+ ? Salla.lang.getWithDefault('pages.checkout.pickup_branch_open', 'مفتوح')
2958
+ : Salla.lang.getWithDefault('pages.checkout.pickup_branch_closed', 'مغلق');
2959
+ return (h("span", { class: "s-bullet-delivery-branch-meta s-bullet-delivery-branch-open-status" }, h("span", { class: "s-bullet-delivery-branch-meta-icon", innerHTML: WorkingHoursIcon, "aria-hidden": "true" }), h("span", { class: isOpen ? 's-bullet-delivery-pickup-status--open' : 's-bullet-delivery-pickup-status--closed' }, label), toggle));
2960
+ }
2609
2961
  renderPickupTab() {
2610
2962
  return (h("div", { class: {
2611
2963
  "s-bullet-delivery-content": true,
2612
2964
  "s-bullet-delivery-content--active": this.activeTab === "branch",
2613
2965
  "s-hidden": this.activeTab !== "branch",
2614
- } }, this.renderCountrySelect(), this.renderBranchSearch(), this.renderBranchList()));
2966
+ } }, h("div", { class: "s-bullet-delivery-pickup-filters" }, h("div", { class: "s-bullet-delivery-pickup-filters-row" }, this.renderCountrySelect(false), this.renderBranchCitySelect()), this.renderBranchSearch()), this.renderBranchList()));
2967
+ }
2968
+ /** City filter for the pickup list; options come from the branches themselves, so none is empty. */
2969
+ renderBranchCitySelect() {
2970
+ return (h("div", { class: "s-bullet-delivery-field" }, h("salla-searchable-dropdown", { placeholder: Salla.lang.get("pages.checkout.select_city"), items: [{ id: -1, name: Salla.lang.getWithDefault('pages.checkout.all_cities', this.isArabic ? 'كل المدن' : 'All cities') }, ...this.branchCities], selectedItem: this.selectedBranchCity, loading: this.loadingBranches && this.branchCities.length === 0, disabled: this.branchCities.length === 0 && !this.selectedBranchCity, inputId: "bullet-delivery-branch-city", searchQuery: this.branchCitySearchQuery, clientSearch: true, onItemSelected: (e) => {
2971
+ this.branchCitySearchQuery = '';
2972
+ this.handleBranchCitySelected(e.detail);
2973
+ }, onSearchInput: (e) => {
2974
+ this.branchCitySearchQuery = e.detail;
2975
+ }, onDropdownClosed: () => {
2976
+ this.branchCitySearchQuery = '';
2977
+ } })));
2615
2978
  }
2616
2979
  /** Add-address form: map + confirm card in one screen, or dropdown fallback. */
2617
2980
  renderAddAddressForm() {
@@ -2653,7 +3016,7 @@ export class SallaBulletDelivery {
2653
3016
  return (h("div", { class: "s-bullet-delivery-footer" }, h("salla-button", { ref: (btn) => {
2654
3017
  this.confirmBtn = btn;
2655
3018
  }, disabled: this.isConfirmDisabled(), onClick: () => this.handleConfirm(), "loader-position": "center", width: "wide" }, this.activeTab === "branch"
2656
- ? Salla.lang.get("pages.checkout.confirm_receipt_from_the_branch")
3019
+ ? Salla.lang.getWithDefault("pages.checkout.confirm_receipt_from_the_branch", "تأكيد الإستلام من الفرع")
2657
3020
  : Salla.lang.get("pages.checkout.confirm_address")), showAddAddressInFooter && (h("salla-button", { onClick: () => this.handleAddNewAddress(), width: "wide", shape: "link" }, Salla.lang.get("pages.checkout.add_new_address"))), !this.isLoggedIn && (h("salla-button", { shape: "link", onClick: () => this.handleLogin() }, Salla.lang.get("pages.checkout.login_to_use_saved_addresses")))));
2658
3021
  }
2659
3022
  renderMainView() {
@@ -2679,7 +3042,7 @@ export class SallaBulletDelivery {
2679
3042
  if (canOpenForIncompleteIntent) {
2680
3043
  this.markIncompleteIntentPrompted();
2681
3044
  }
2682
- this.open();
3045
+ this.open(false, canOpenForIncompleteIntent ? "incomplete_intent" : "after_add_to_cart");
2683
3046
  }
2684
3047
  overrideScopeSwitchUI() {
2685
3048
  const intent = this.getStoredIntent();
@@ -2732,7 +3095,7 @@ export class SallaBulletDelivery {
2732
3095
  // silently awaiting with nothing visible.
2733
3096
  if (!this.isGuestUser()) {
2734
3097
  if (!this.modal?.hasAttribute?.("visible")) {
2735
- this.open();
3098
+ this.open(false, "checkout_gate");
2736
3099
  }
2737
3100
  await this.pendingCartSubmitPromise;
2738
3101
  return;
@@ -2761,7 +3124,7 @@ export class SallaBulletDelivery {
2761
3124
  this.openGuestLogin();
2762
3125
  return;
2763
3126
  }
2764
- this.open();
3127
+ this.open(false, "checkout_gate");
2765
3128
  });
2766
3129
  await this.pendingCartSubmitPromise;
2767
3130
  };
@@ -2777,6 +3140,7 @@ export class SallaBulletDelivery {
2777
3140
  Salla.event.on("salla::bullet-delivery.modal.open.requested", this.bulletDeliveryOpenHandler);
2778
3141
  this.closeHandler = this.close.bind(this);
2779
3142
  Salla.event.on("salla::bullet-delivery.modal.close.requested", this.closeHandler);
3143
+ Salla.event.on("modal::close", this.modalCloseHandler);
2780
3144
  this.isRTL = Salla.config.get("theme.is_rtl");
2781
3145
  // Reading the permission never prompts, so it's safe to watch from here — the map view then
2782
3146
  // renders the notice (or not) on its first paint instead of a frame later.
@@ -2801,7 +3165,7 @@ export class SallaBulletDelivery {
2801
3165
  return;
2802
3166
  if (this.modal?.hasAttribute?.("visible"))
2803
3167
  return;
2804
- this.open();
3168
+ this.open(false, "after_login");
2805
3169
  };
2806
3170
  Salla.event.on("auth::logged.in", this.authLoggedInHandler);
2807
3171
  this.authLoggedOutHandler = () => {
@@ -2833,9 +3197,12 @@ export class SallaBulletDelivery {
2833
3197
  return null; // no-op when disabled
2834
3198
  return (h(Host, { class: "s-bullet-delivery" }, h("salla-modal", { ref: modal => {
2835
3199
  this.modal = modal;
2836
- }, isClosable: !this.isRequired && !this.shouldForceNonClosable(), class: "s-bullet-delivery-modal", width: "sm", "data-testid": "salla-bullet-delivery-modal" }, h("div", { class: "s-bullet-delivery-inner" }, h("div", { class: "s-bullet-delivery-header" }, h("h2", { class: "s-bullet-delivery-title" }, this.activeTab === 'address'
3200
+ }, isClosable: !this.isRequired && !this.shouldForceNonClosable(), onModalVisibilityChanged: (event) => {
3201
+ if (event.target === this.modal)
3202
+ this.handleModalVisibility(Boolean(event.detail));
3203
+ }, class: "s-bullet-delivery-modal", width: "sm", "data-testid": "salla-bullet-delivery-modal" }, h("div", { class: "s-bullet-delivery-inner" }, h("div", { class: "s-bullet-delivery-header" }, h("h2", { class: "s-bullet-delivery-title" }, this.activeTab === 'address'
2837
3204
  ? Salla.lang.get('blocks.home.add_address_for_order_delivery')
2838
- : Salla.lang.get('blocks.home.choose_branch_for_order_pickup')), h("p", { class: "s-bullet-delivery-subtitle" }, this.activeTab === 'address'
3205
+ : Salla.lang.getWithDefault('blocks.home.choose_pickup_branch_title', this.isArabic ? 'اختر فرع استلام طلبك' : 'Choose your pickup branch')), h("p", { class: "s-bullet-delivery-subtitle" }, this.activeTab === 'address'
2839
3206
  ? Salla.lang.get('blocks.home.products_available_for_delivery_shown_while_shopping')
2840
3207
  : Salla.lang.get('blocks.home.choose_branch_for_order_pickup_description'))), this.renderTabs(), this.viewMode === 'main' && this.renderMainView(), this.viewMode === 'add-address' && this.renderAddAddressForm()))));
2841
3208
  }
@@ -2869,7 +3236,7 @@ export class SallaBulletDelivery {
2869
3236
  // Handle pending open request (if event was emitted before component was ready)
2870
3237
  if (this.pendingOpen) {
2871
3238
  this.pendingOpen = false;
2872
- this.open();
3239
+ this.open(false, this.openTrigger);
2873
3240
  return;
2874
3241
  }
2875
3242
  // Strategy-based auto-open logic
@@ -2877,11 +3244,11 @@ export class SallaBulletDelivery {
2877
3244
  case "first_visit":
2878
3245
  // Open once per session when component mounts (per-session behaviour)
2879
3246
  if (!this.hasBeenShownThisSession()) {
2880
- this.open();
3247
+ this.open(false, "first_visit");
2881
3248
  }
2882
3249
  else if (this.shouldPromptIncompleteIntentThisSession()) {
2883
3250
  this.markIncompleteIntentPrompted();
2884
- this.open();
3251
+ this.open(false, "incomplete_intent");
2885
3252
  }
2886
3253
  break;
2887
3254
  case "on_cart_click":
@@ -2889,11 +3256,11 @@ export class SallaBulletDelivery {
2889
3256
  if (Salla.url.is_page("cart")) {
2890
3257
  const shown = this.getShownStorage().get(this.sessionShownKey) === true;
2891
3258
  if (!shown) {
2892
- this.open();
3259
+ this.open(false, "on_cart_click");
2893
3260
  }
2894
3261
  else if (this.shouldPromptIncompleteIntentThisSession()) {
2895
3262
  this.markIncompleteIntentPrompted();
2896
- this.open();
3263
+ this.open(false, "incomplete_intent");
2897
3264
  }
2898
3265
  }
2899
3266
  break;
@@ -2918,6 +3285,7 @@ export class SallaBulletDelivery {
2918
3285
  this.stopWatchingLocationPermission = null;
2919
3286
  }
2920
3287
  Salla.event.off("salla::bullet-delivery.modal.open.requested", this.bulletDeliveryOpenHandler);
3288
+ Salla.event.off("modal::close", this.modalCloseHandler);
2921
3289
  if (this.closeHandler) {
2922
3290
  Salla.event.off("salla::bullet-delivery.modal.close.requested", this.closeHandler);
2923
3291
  this.closeHandler = null;
@@ -2993,9 +3361,15 @@ export class SallaBulletDelivery {
2993
3361
  "selectedSavedAddress": {},
2994
3362
  "selectedSessionAddress": {},
2995
3363
  "branches": {},
3364
+ "pickupDiscountRange": {},
3365
+ "pickupDiscountResolved": {},
2996
3366
  "filteredBranches": {},
2997
3367
  "selectedBranch": {},
2998
3368
  "branchSearchQuery": {},
3369
+ "branchCities": {},
3370
+ "selectedBranchCity": {},
3371
+ "branchCitySearchQuery": {},
3372
+ "expandedScheduleBranchId": {},
2999
3373
  "loadingCountries": {},
3000
3374
  "loadingCities": {},
3001
3375
  "loadingDistricts": {},
@@ -3116,16 +3490,25 @@ export class SallaBulletDelivery {
3116
3490
  return {
3117
3491
  "open": {
3118
3492
  "complexType": {
3119
- "signature": "(silent?: boolean) => Promise<void>",
3493
+ "signature": "(silent?: boolean, trigger?: SelectorTrigger) => Promise<void>",
3120
3494
  "parameters": [{
3121
3495
  "name": "silent",
3122
3496
  "type": "boolean",
3123
3497
  "docs": ""
3498
+ }, {
3499
+ "name": "trigger",
3500
+ "type": "\"first_visit\" | \"after_add_to_cart\" | \"on_cart_click\" | \"incomplete_intent\" | \"checkout_gate\" | \"after_login\" | \"customer_requested\"",
3501
+ "docs": ""
3124
3502
  }],
3125
3503
  "references": {
3126
3504
  "Promise": {
3127
3505
  "location": "global",
3128
3506
  "id": "global::Promise"
3507
+ },
3508
+ "SelectorTrigger": {
3509
+ "location": "import",
3510
+ "path": "./selector-tracker",
3511
+ "id": "src/components/salla-bullet-delivery/selector-tracker.ts::SelectorTrigger"
3129
3512
  }
3130
3513
  },
3131
3514
  "return": "Promise<void>"