@salla.sa/twilight-components 3.0.18 → 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 (376) hide show
  1. package/dist/cjs/{Helper-D2VNt6eb.js → Helper-DqKJCL8G.js} +1 -1
  2. package/dist/cjs/{filepond-DkCudLhG.js → filepond-Byu0lc8z.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-poster-C-uxFWfW.js → filepond-plugin-file-poster-qwx7lYZ-.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-size-BikXTQW2.js → filepond-plugin-file-validate-size-C-n0P4TT.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-file-validate-type-Drs7PouU.js → filepond-plugin-file-validate-type-C8VOOXdT.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-edit-hxr0h9kt.js → filepond-plugin-image-edit-B9KG763Y.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-exif-orientation-CVZmWU_4.js → filepond-plugin-image-exif-orientation-Czt4pYzU.js} +1 -1
  8. package/dist/cjs/{filepond-plugin-image-preview-CBjPKx-d.js → filepond-plugin-image-preview-BFm1Zl0o.js} +1 -1
  9. package/dist/cjs/helpers-NV7wgP50.js +602 -0
  10. package/dist/cjs/{index-CTYg2zC6.js → index-CFW798jX.js} +2 -2
  11. package/dist/cjs/{intlTelInput-C9X0hKXV.js → intlTelInput-CLa2B89b.js} +1 -1
  12. package/dist/cjs/loader.cjs.js +2 -2
  13. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  14. package/dist/cjs/salla-accordion_6.cjs.entry.js +2 -2
  15. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +46 -3
  16. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  17. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  19. package/dist/cjs/salla-applied-filters.cjs.entry.js +2 -2
  20. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  21. package/dist/cjs/salla-attachment-view_9.cjs.entry.js +11 -11
  22. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  23. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  24. package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +425 -119
  25. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  26. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  28. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  29. package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
  30. package/dist/cjs/salla-comment-form_9.cjs.entry.js +2 -2
  31. package/dist/cjs/salla-conditional-offer.cjs.entry.js +1 -1
  32. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  36. package/dist/cjs/salla-delivery-promise.cjs.entry.js +2 -2
  37. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  38. package/dist/cjs/salla-filters-widget.cjs.entry.js +2 -2
  39. package/dist/cjs/salla-filters.cjs.entry.js +2 -2
  40. package/dist/cjs/salla-free-shipping-banner.cjs.entry.js +1 -1
  41. package/dist/cjs/salla-free-shipping-tag.cjs.entry.js +1 -1
  42. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  43. package/dist/cjs/salla-gift-card-redeem_2.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  46. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +2 -2
  47. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  48. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-login-modal.cjs.entry.js +7 -2
  51. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  52. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-notification-item_2.cjs.entry.js +2 -2
  66. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  67. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-order-items.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-order-payment.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-order-rewards.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-orders.cjs.entry.js +2 -2
  84. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  85. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  86. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  87. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  88. package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  90. package/dist/cjs/salla-products-list.cjs.entry.js +9 -2
  91. package/dist/cjs/salla-products-slider.cjs.entry.js +2 -2
  92. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  93. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +2 -2
  94. package/dist/cjs/salla-rating-modal.cjs.entry.js +2 -2
  95. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +1 -1
  96. package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
  97. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  98. package/dist/cjs/salla-search-product-card.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-search.cjs.entry.js +2 -2
  100. package/dist/cjs/salla-skeleton.cjs.entry.js +1 -1
  101. package/dist/cjs/salla-social-share.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-social.cjs.entry.js +1 -1
  103. package/dist/cjs/salla-tab-content_3.cjs.entry.js +1 -1
  104. package/dist/cjs/salla-thank-you-header.cjs.entry.js +1 -1
  105. package/dist/cjs/salla-thank-you-page.cjs.entry.js +1 -1
  106. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  107. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  108. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  109. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  110. package/dist/cjs/salla-user-settings.cjs.entry.js +1 -1
  111. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  112. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  113. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +1 -1
  114. package/dist/cjs/salla-wishlist-summary.cjs.entry.js +2 -2
  115. package/dist/cjs/twilight.cjs.js +2 -2
  116. package/dist/cjs/{vanilla-picker-BsLm8rHT.js → vanilla-picker-Vx_7G7bs.js} +1 -1
  117. package/dist/collection/components/salla-bullet-delivery/api-service.js +156 -25
  118. package/dist/collection/components/salla-bullet-delivery/helpers.js +143 -15
  119. package/dist/collection/components/salla-bullet-delivery/icons.js +17 -0
  120. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.css +490 -52
  121. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.js +417 -90
  122. package/dist/collection/components/salla-login-modal/salla-login-modal.js +6 -1
  123. package/dist/collection/components/salla-products-list/salla-products-list.js +7 -0
  124. package/dist/collection/components/salla-quick-buy/salla-quick-buy.js +44 -1
  125. package/dist/components/helpers.js +299 -40
  126. package/dist/components/index.js +2 -2
  127. package/dist/components/salla-bullet-delivery.js +430 -118
  128. package/dist/components/salla-login-modal2.js +6 -1
  129. package/dist/components/salla-products-list2.js +7 -0
  130. package/dist/components/salla-quick-buy2.js +44 -1
  131. package/dist/docs.json +1 -1
  132. package/dist/esm/{Helper-C1T-oD4J.js → Helper-I0CAizv1.js} +1 -1
  133. package/dist/esm/{filepond-BoWJzHeN.js → filepond-DTUYXV0J.js} +1 -1
  134. package/dist/esm/{filepond-plugin-file-poster-B6MkMtuC.js → filepond-plugin-file-poster-DydZjneT.js} +1 -1
  135. package/dist/esm/{filepond-plugin-file-validate-size-Dds_462B.js → filepond-plugin-file-validate-size-nbsCU2t9.js} +1 -1
  136. package/dist/esm/{filepond-plugin-file-validate-type-DrfGXNay.js → filepond-plugin-file-validate-type-Cl5B_DIy.js} +1 -1
  137. package/dist/esm/{filepond-plugin-image-edit-CN7eGg-7.js → filepond-plugin-image-edit-bzskFyFR.js} +1 -1
  138. package/dist/esm/{filepond-plugin-image-exif-orientation-CyhxK_6R.js → filepond-plugin-image-exif-orientation-MBDuxLaX.js} +1 -1
  139. package/dist/esm/{filepond-plugin-image-preview-DdKqAzT9.js → filepond-plugin-image-preview-N6wzEryW.js} +1 -1
  140. package/dist/esm/helpers-Cnh7Qptl.js +568 -0
  141. package/dist/esm/{index-DgUQRYr2.js → index-C9zdeQFI.js} +2 -2
  142. package/dist/esm/{intlTelInput-SwlavcFB.js → intlTelInput-B5L9nYtO.js} +1 -1
  143. package/dist/esm/loader.js +3 -3
  144. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  145. package/dist/esm/salla-accordion_6.entry.js +2 -2
  146. package/dist/esm/salla-add-product-button_5.entry.js +46 -3
  147. package/dist/esm/salla-advertisement.entry.js +1 -1
  148. package/dist/esm/salla-alert_2.entry.js +1 -1
  149. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  150. package/dist/esm/salla-applied-filters.entry.js +2 -2
  151. package/dist/esm/salla-apps-icons.entry.js +1 -1
  152. package/dist/esm/salla-attachment-view_9.entry.js +11 -11
  153. package/dist/esm/salla-badge.entry.js +1 -1
  154. package/dist/esm/salla-basket-gap.entry.js +1 -1
  155. package/dist/esm/salla-bullet-delivery_3.entry.js +425 -119
  156. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  157. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  158. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  159. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  160. package/dist/esm/salla-comment-factors.entry.js +1 -1
  161. package/dist/esm/salla-comment-form_9.entry.js +2 -2
  162. package/dist/esm/salla-conditional-offer.entry.js +1 -1
  163. package/dist/esm/salla-contacts.entry.js +1 -1
  164. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  165. package/dist/esm/salla-count-down.entry.js +1 -1
  166. package/dist/esm/salla-custom-fields.entry.js +1 -1
  167. package/dist/esm/salla-delivery-promise.entry.js +2 -2
  168. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  169. package/dist/esm/salla-filters-widget.entry.js +2 -2
  170. package/dist/esm/salla-filters.entry.js +2 -2
  171. package/dist/esm/salla-free-shipping-banner.entry.js +1 -1
  172. package/dist/esm/salla-free-shipping-tag.entry.js +1 -1
  173. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  174. package/dist/esm/salla-gift-card-redeem_2.entry.js +1 -1
  175. package/dist/esm/salla-gifting.entry.js +1 -1
  176. package/dist/esm/salla-hook.entry.js +1 -1
  177. package/dist/esm/salla-infinite-scroll.entry.js +2 -2
  178. package/dist/esm/salla-installment.entry.js +1 -1
  179. package/dist/esm/salla-list-tile.entry.js +1 -1
  180. package/dist/esm/salla-localization-modal.entry.js +1 -1
  181. package/dist/esm/salla-login-modal.entry.js +7 -2
  182. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  183. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  184. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  185. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  186. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  187. package/dist/esm/salla-loyalty.entry.js +1 -1
  188. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  189. package/dist/esm/salla-map_3.entry.js +1 -1
  190. package/dist/esm/salla-menu.entry.js +1 -1
  191. package/dist/esm/salla-metadata.entry.js +1 -1
  192. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  193. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  194. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  195. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  196. package/dist/esm/salla-notification-item_2.entry.js +2 -2
  197. package/dist/esm/salla-offer-modal.entry.js +1 -1
  198. package/dist/esm/salla-offer.entry.js +1 -1
  199. package/dist/esm/salla-order-branch.entry.js +1 -1
  200. package/dist/esm/salla-order-cancel.entry.js +1 -1
  201. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  202. package/dist/esm/salla-order-details-options.entry.js +1 -1
  203. package/dist/esm/salla-order-details.entry.js +1 -1
  204. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  205. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  206. package/dist/esm/salla-order-edit.entry.js +1 -1
  207. package/dist/esm/salla-order-items.entry.js +1 -1
  208. package/dist/esm/salla-order-options-modal.entry.js +1 -1
  209. package/dist/esm/salla-order-payment.entry.js +1 -1
  210. package/dist/esm/salla-order-rewards.entry.js +1 -1
  211. package/dist/esm/salla-order-shipments.entry.js +1 -1
  212. package/dist/esm/salla-order-summary.entry.js +1 -1
  213. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  214. package/dist/esm/salla-orders.entry.js +2 -2
  215. package/dist/esm/salla-payments.entry.js +1 -1
  216. package/dist/esm/salla-placeholder.entry.js +1 -1
  217. package/dist/esm/salla-price-range.entry.js +1 -1
  218. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  219. package/dist/esm/salla-product-card.entry.js +1 -1
  220. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  221. package/dist/esm/salla-products-list.entry.js +9 -2
  222. package/dist/esm/salla-products-slider.entry.js +2 -2
  223. package/dist/esm/salla-quick-order.entry.js +1 -1
  224. package/dist/esm/salla-rating-modal-v2.entry.js +2 -2
  225. package/dist/esm/salla-rating-modal.entry.js +2 -2
  226. package/dist/esm/salla-review-factors-tags.entry.js +1 -1
  227. package/dist/esm/salla-review-order-item.entry.js +1 -1
  228. package/dist/esm/salla-scopes.entry.js +1 -1
  229. package/dist/esm/salla-search-product-card.entry.js +1 -1
  230. package/dist/esm/salla-search.entry.js +2 -2
  231. package/dist/esm/salla-skeleton.entry.js +1 -1
  232. package/dist/esm/salla-social-share.entry.js +1 -1
  233. package/dist/esm/salla-social.entry.js +1 -1
  234. package/dist/esm/salla-tab-content_3.entry.js +1 -1
  235. package/dist/esm/salla-thank-you-header.entry.js +1 -1
  236. package/dist/esm/salla-thank-you-page.entry.js +1 -1
  237. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  238. package/dist/esm/salla-trust-badges.entry.js +1 -1
  239. package/dist/esm/salla-user-menu.entry.js +1 -1
  240. package/dist/esm/salla-user-profile.entry.js +1 -1
  241. package/dist/esm/salla-user-settings.entry.js +1 -1
  242. package/dist/esm/salla-verify.entry.js +1 -1
  243. package/dist/esm/salla-wallet.entry.js +1 -1
  244. package/dist/esm/salla-wishlist-actions.entry.js +1 -1
  245. package/dist/esm/salla-wishlist-summary.entry.js +2 -2
  246. package/dist/esm/twilight.js +3 -3
  247. package/dist/esm/{vanilla-picker-Buy50D7t.js → vanilla-picker-CAeu4f87.js} +1 -1
  248. package/dist/twilight/{p-bed54154.entry.js → p-01d5cd61.entry.js} +1 -1
  249. package/dist/twilight/{p-b3ada3d0.entry.js → p-0213fde4.entry.js} +1 -1
  250. package/dist/twilight/{p-d0ff9a3e.entry.js → p-036fbfc0.entry.js} +1 -1
  251. package/dist/twilight/{p-53735934.entry.js → p-0add41ce.entry.js} +1 -1
  252. package/dist/twilight/{p-bb8b2c85.entry.js → p-0b0d4942.entry.js} +1 -1
  253. package/dist/twilight/{p-d1121b3f.entry.js → p-17bcbf9b.entry.js} +1 -1
  254. package/dist/twilight/{p-6b5de500.entry.js → p-1867f7d4.entry.js} +1 -1
  255. package/dist/twilight/{p-ee65c489.entry.js → p-194ff38c.entry.js} +1 -1
  256. package/dist/twilight/{p-4ab5ee99.entry.js → p-20f9c62f.entry.js} +1 -1
  257. package/dist/twilight/{p-5e8d019e.entry.js → p-252748d9.entry.js} +1 -1
  258. package/dist/twilight/{p-6c3084c9.entry.js → p-290ce0bc.entry.js} +1 -1
  259. package/dist/twilight/{p-5694edf1.entry.js → p-2b930157.entry.js} +1 -1
  260. package/dist/twilight/{p-7fd9dc36.entry.js → p-2e4e15c5.entry.js} +1 -1
  261. package/dist/twilight/{p-b9d67694.entry.js → p-2e6537a3.entry.js} +1 -1
  262. package/dist/twilight/{p-7f97335a.entry.js → p-30d64bd1.entry.js} +1 -1
  263. package/dist/twilight/{p-516e4edc.entry.js → p-32495dd2.entry.js} +1 -1
  264. package/dist/twilight/{p-e9b03e9c.entry.js → p-32d9da31.entry.js} +1 -1
  265. package/dist/twilight/{p-9d15f22d.entry.js → p-32f100f8.entry.js} +1 -1
  266. package/dist/twilight/{p-6dccd40f.entry.js → p-386b3dcd.entry.js} +1 -1
  267. package/dist/twilight/{p-d1093a59.entry.js → p-3a4e86d1.entry.js} +1 -1
  268. package/dist/twilight/{p-f7a58656.entry.js → p-3b766a79.entry.js} +1 -1
  269. package/dist/twilight/{p-361f6033.entry.js → p-3b7a45ec.entry.js} +1 -1
  270. package/dist/twilight/{p-9cf1cfd4.entry.js → p-3bb0e184.entry.js} +1 -1
  271. package/dist/twilight/{p-b0d2e31f.entry.js → p-3cc6ca6c.entry.js} +1 -1
  272. package/dist/twilight/{p-d4fb9921.entry.js → p-4070f808.entry.js} +1 -1
  273. package/dist/twilight/{p-af49191f.entry.js → p-412fb721.entry.js} +1 -1
  274. package/dist/twilight/{p-5f6fee0e.entry.js → p-427c13b6.entry.js} +1 -1
  275. package/dist/twilight/{p-be6f8551.entry.js → p-45e0a5d0.entry.js} +1 -1
  276. package/dist/twilight/{p-c4cf0b4d.entry.js → p-4d615f9f.entry.js} +1 -1
  277. package/dist/twilight/{p-fddf1997.entry.js → p-4d7fc363.entry.js} +1 -1
  278. package/dist/twilight/{p-9e042cce.entry.js → p-5009f654.entry.js} +1 -1
  279. package/dist/twilight/{p-d9a1bf08.entry.js → p-50853b76.entry.js} +1 -1
  280. package/dist/twilight/{p-eaf0c514.entry.js → p-550ad1a6.entry.js} +1 -1
  281. package/dist/twilight/{p-3f842f32.entry.js → p-575014c4.entry.js} +1 -1
  282. package/dist/twilight/{p-66ad7a30.entry.js → p-5aee5e44.entry.js} +1 -1
  283. package/dist/twilight/{p-8c1d14f0.entry.js → p-619c709c.entry.js} +1 -1
  284. package/dist/twilight/{p-cdeb9bcc.entry.js → p-6d572fe0.entry.js} +1 -1
  285. package/dist/twilight/{p-12a25415.entry.js → p-6f84c662.entry.js} +1 -1
  286. package/dist/twilight/{p-b5a45f87.entry.js → p-703b9253.entry.js} +1 -1
  287. package/dist/twilight/{p-a5b732b3.entry.js → p-73dea22d.entry.js} +1 -1
  288. package/dist/twilight/{p-a44670a9.entry.js → p-74285af9.entry.js} +1 -1
  289. package/dist/twilight/{p-b1f808f5.entry.js → p-777d28f6.entry.js} +1 -1
  290. package/dist/twilight/{p-027cbda6.entry.js → p-7db6ccfe.entry.js} +1 -1
  291. package/dist/twilight/{p-0418c2df.entry.js → p-7db8d545.entry.js} +1 -1
  292. package/dist/twilight/{p-da0df10b.entry.js → p-7fa4d0b6.entry.js} +1 -1
  293. package/dist/twilight/{p-78a56dc6.entry.js → p-825c5b73.entry.js} +1 -1
  294. package/dist/twilight/{p-4b70e6c4.entry.js → p-8262e669.entry.js} +1 -1
  295. package/dist/twilight/p-86d55a6b.entry.js +4 -0
  296. package/dist/twilight/{p-259c995a.entry.js → p-8712f7c7.entry.js} +1 -1
  297. package/dist/twilight/{p-08a15c81.entry.js → p-8948b58c.entry.js} +1 -1
  298. package/dist/twilight/{p-aa35cf98.entry.js → p-8b8cf457.entry.js} +1 -1
  299. package/dist/twilight/{p-d0aeeb7f.entry.js → p-8c2992e7.entry.js} +1 -1
  300. package/dist/twilight/{p-79822d99.entry.js → p-8c548259.entry.js} +1 -1
  301. package/dist/twilight/{p-6af8a7b6.entry.js → p-8eb3fd92.entry.js} +1 -1
  302. package/dist/twilight/p-908716fb.entry.js +4 -0
  303. package/dist/twilight/{p-70dfb2b4.entry.js → p-90a59027.entry.js} +1 -1
  304. package/dist/twilight/{p-37192614.entry.js → p-9683535f.entry.js} +1 -1
  305. package/dist/twilight/{p-f9e5ed10.entry.js → p-982e9124.entry.js} +1 -1
  306. package/dist/twilight/{p-0d795b58.entry.js → p-995b898f.entry.js} +1 -1
  307. package/dist/twilight/{p-d1204965.entry.js → p-99cb3dba.entry.js} +1 -1
  308. package/dist/twilight/{p-071aa0e7.entry.js → p-9a0218a6.entry.js} +1 -1
  309. package/dist/twilight/{p-eefced26.entry.js → p-9cf31ff4.entry.js} +1 -1
  310. package/dist/twilight/{p-4891c565.entry.js → p-9d1a5007.entry.js} +1 -1
  311. package/dist/twilight/{p-f9587127.entry.js → p-9e429047.entry.js} +1 -1
  312. package/dist/twilight/p-9e542951.entry.js +4 -0
  313. package/dist/twilight/{p-BxQMWsr9.js → p-BcT9tEVW.js} +1 -1
  314. package/dist/twilight/{p-Y2ZIN9Ok.js → p-Bgu5gtOF.js} +1 -1
  315. package/dist/twilight/{p-DgUQRYr2.js → p-C9zdeQFI.js} +2 -2
  316. package/dist/twilight/{p-CCwsxMbZ.js → p-CSGj1pY-.js} +1 -1
  317. package/dist/twilight/{p-Ca_SZn0u.js → p-CWWgpQ8Q.js} +1 -1
  318. package/dist/twilight/{p-CgNJWeaK.js → p-Cfz9xit3.js} +1 -1
  319. package/dist/twilight/{p-BK9YeXWp.js → p-CmYSalnM.js} +1 -1
  320. package/dist/twilight/p-Cnh7Qptl.js +4 -0
  321. package/dist/twilight/{p-De1tLqQF.js → p-Dav_R_d2.js} +1 -1
  322. package/dist/twilight/{p-DMvhNbgN.js → p-JGEddAgs.js} +1 -1
  323. package/dist/twilight/{p-CBLWEyM9.js → p-RflSBQNB.js} +1 -1
  324. package/dist/twilight/{p-NqQdbGu6.js → p-Xgbueas7.js} +1 -1
  325. package/dist/twilight/{p-ba6dd59c.entry.js → p-a070cce0.entry.js} +1 -1
  326. package/dist/twilight/{p-1960020c.entry.js → p-a0bfedad.entry.js} +1 -1
  327. package/dist/twilight/{p-f7ab5825.entry.js → p-a22d9931.entry.js} +1 -1
  328. package/dist/twilight/{p-a95b0dc1.entry.js → p-a3959219.entry.js} +1 -1
  329. package/dist/twilight/p-a3ca61af.entry.js +4 -0
  330. package/dist/twilight/{p-e457e68f.entry.js → p-a55b5c44.entry.js} +1 -1
  331. package/dist/twilight/{p-23772523.entry.js → p-a5fc8358.entry.js} +1 -1
  332. package/dist/twilight/{p-3a632c69.entry.js → p-aabcd09f.entry.js} +1 -1
  333. package/dist/twilight/{p-d8955835.entry.js → p-ad00021e.entry.js} +1 -1
  334. package/dist/twilight/{p-481f52b1.entry.js → p-ade9a68c.entry.js} +1 -1
  335. package/dist/twilight/{p-4a3fa153.entry.js → p-b5e0065a.entry.js} +1 -1
  336. package/dist/twilight/{p-bed68e52.entry.js → p-bbb09d92.entry.js} +1 -1
  337. package/dist/twilight/{p-9e65451e.entry.js → p-c2df20f8.entry.js} +1 -1
  338. package/dist/twilight/{p-1af9db24.entry.js → p-c4274188.entry.js} +1 -1
  339. package/dist/twilight/{p-bfd5e263.entry.js → p-c5e2c781.entry.js} +1 -1
  340. package/dist/twilight/{p-e38a6793.entry.js → p-c64b2b22.entry.js} +1 -1
  341. package/dist/twilight/{p-3d8b77f7.entry.js → p-c92cc92f.entry.js} +1 -1
  342. package/dist/twilight/{p-ac736b30.entry.js → p-cb54adac.entry.js} +1 -1
  343. package/dist/twilight/{p-5bbf17dd.entry.js → p-ce1e45a3.entry.js} +1 -1
  344. package/dist/twilight/{p-fcb1009f.entry.js → p-d0acd7ff.entry.js} +1 -1
  345. package/dist/twilight/{p-92fd20a0.entry.js → p-d0f903ac.entry.js} +1 -1
  346. package/dist/twilight/{p-26d2b28a.entry.js → p-d57b49ff.entry.js} +1 -1
  347. package/dist/twilight/{p-8766e236.entry.js → p-db3efb27.entry.js} +1 -1
  348. package/dist/twilight/{p-0f25fedd.entry.js → p-db73d0bf.entry.js} +1 -1
  349. package/dist/twilight/{p-6e52b649.entry.js → p-e20d5e75.entry.js} +1 -1
  350. package/dist/twilight/{p-254cff9a.entry.js → p-e223beca.entry.js} +1 -1
  351. package/dist/twilight/{p-8c40278d.entry.js → p-e37da59f.entry.js} +1 -1
  352. package/dist/twilight/{p-84b0e7a9.entry.js → p-e3991836.entry.js} +1 -1
  353. package/dist/twilight/{p-cb48a5ef.entry.js → p-e455709f.entry.js} +1 -1
  354. package/dist/twilight/{p-9e0a9a5a.entry.js → p-e921b2d1.entry.js} +1 -1
  355. package/dist/twilight/{p-0ba159db.entry.js → p-ec356e5a.entry.js} +1 -1
  356. package/dist/twilight/{p-34148827.entry.js → p-eeefc285.entry.js} +1 -1
  357. package/dist/twilight/{p-6ba3808a.entry.js → p-f3b12754.entry.js} +1 -1
  358. package/dist/twilight/{p-419d6d71.entry.js → p-f6279a13.entry.js} +1 -1
  359. package/dist/twilight/{p-487b4411.entry.js → p-f83efc62.entry.js} +1 -1
  360. package/dist/twilight/{p-7b5f97e7.entry.js → p-fa622715.entry.js} +1 -1
  361. package/dist/twilight/{p-ee479819.entry.js → p-faf00404.entry.js} +1 -1
  362. package/dist/twilight/twilight.esm.js +1 -1
  363. package/dist/types/components/salla-bullet-delivery/api-service.d.ts +23 -7
  364. package/dist/types/components/salla-bullet-delivery/helpers.d.ts +61 -6
  365. package/dist/types/components/salla-bullet-delivery/icons.d.ts +14 -0
  366. package/dist/types/components/salla-bullet-delivery/interfaces.d.ts +36 -1
  367. package/dist/types/components/salla-bullet-delivery/salla-bullet-delivery.d.ts +73 -2
  368. package/dist/types/components/salla-quick-buy/salla-quick-buy.d.ts +6 -0
  369. package/package.json +5 -5
  370. package/dist/cjs/helpers-8bMfm_mm.js +0 -329
  371. package/dist/esm/helpers-DqzaeXrL.js +0 -309
  372. package/dist/twilight/p-76584a5b.entry.js +0 -4
  373. package/dist/twilight/p-DqzaeXrL.js +0 -4
  374. package/dist/twilight/p-bac25e34.entry.js +0 -4
  375. package/dist/twilight/p-d346dabd.entry.js +0 -4
  376. package/dist/twilight/p-e24fbbb4.entry.js +0 -4
@@ -2,22 +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";
5
+ import { bulletDeliveryAPI, CACHE_TTL, clearApiCache, clearBranchCache, isSaudiArabia } from "./api-service";
6
6
  import { SelectorTracker, allocatedBranchIds, toCloseReason, } from "./selector-tracker";
7
- import { filterBranches, findNearestBranch, getGeolocationErrorMessage, getIntentBranchId, getIntentCityId, getIntentCountryCode, getIntentCountryId, getIntentDistrictId, getIntentLatitude, getIntentLongitude, getIntentRegionId, getIntentSubtitle, hasSessionAddressIntent, isBranchSelectable, isMapOriginIntent, requireRegionAndDistrictForSA, } from "./helpers";
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";
8
8
  /* Location permission notice */
9
9
  import { isLocationPermissionMissing, LocationPermissionNotice, requestLocationPermission, watchLocationPermission, } from "./location-permission-notice";
10
10
  import { calculateLocationDistanceKm, LocationDistanceWarning, } from "./location-distance-warning";
11
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;
12
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;
13
24
  /** Final fallback center used only if `user.default_geo_location` is absent (Riyadh — matches checkout map-page). */
14
25
  const DEFAULT_MAP_CENTER = { lat: 24.7756042, lng: 46.7360729 };
15
26
  import GetDirections from "../../assets/svg/get-directions.svg";
16
- import GPS from "../../assets/svg/gps.svg";
17
27
  import Location from "../../assets/svg/location.svg";
18
28
  import MiniMap from "../../assets/svg/mini-map.svg";
19
29
  import Search from "../../assets/svg/search.svg";
20
- import Store from "../../assets/svg/store3.svg";
30
+ import { BranchLocationIcon, LightningIcon, NotesIcon, PickupAvailabilityIcon, WorkingHoursIcon } from "./icons";
21
31
  const DEBUG_KEY = 'salla-bullet-delivery-debug';
22
32
  const OVERRIDE_IP_KEY = 'salla-bullet-delivery-override-ip';
23
33
  function log(message, data) {
@@ -33,6 +43,8 @@ export class SallaBulletDelivery {
33
43
  this.pendingCartSubmitResolver = null;
34
44
  this.pendingCartSubmitPromise = null;
35
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 = '';
36
48
  this.branchSearchDebounceTimer = null;
37
49
  this.countrySearchTimer = null;
38
50
  this.regionSearchTimer = null;
@@ -80,6 +92,8 @@ export class SallaBulletDelivery {
80
92
  /** Identifies the latest reverse-geocode call so a resolved-but-superseded callback (same
81
93
  * coordinates re-picked, or the map left) can be told apart from the current one. */
82
94
  this.geocodeRequestId = 0;
95
+ /** Identifies the latest pickup-offer fetch so one from an earlier modal open is dropped. */
96
+ this.pickupDiscountRequestId = 0;
83
97
  // Core state
84
98
  this.activeTab = "address";
85
99
  this.isLoggedIn = false;
@@ -102,9 +116,27 @@ export class SallaBulletDelivery {
102
116
  this.selectedSessionAddress = false;
103
117
  // Pickup state
104
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;
105
123
  this.filteredBranches = [];
106
124
  this.selectedBranch = null;
107
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;
108
140
  // Loading states
109
141
  this.loadingCountries = false;
110
142
  this.loadingCities = false;
@@ -223,6 +255,13 @@ export class SallaBulletDelivery {
223
255
  this.displayedDistricts = this.districts;
224
256
  this.searchingDistricts = false;
225
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
+ };
226
265
  this.handleMapLocationChanged = (event) => {
227
266
  const detail = event.detail;
228
267
  if (!detail?.position?.lat || !detail?.position?.lng)
@@ -447,6 +486,7 @@ export class SallaBulletDelivery {
447
486
  }
448
487
  // Reset state before opening
449
488
  this.resetState();
489
+ void this.loadPickupDiscountRange();
450
490
  if (!silent) {
451
491
  // Open modal immediately — fields render right away with inline loading
452
492
  this.modal.open();
@@ -498,20 +538,18 @@ export class SallaBulletDelivery {
498
538
  }
499
539
  }
500
540
  // Load branches only when branch tab is active (lazy tab data)
501
- // Use lat/lng from session storage if available for more accurate results
502
541
  if (this.activeTab === "branch") {
503
- const intentLat = getIntentLatitude(savedIntent);
504
- const intentLng = getIntentLongitude(savedIntent);
505
- if (intentLat != null && intentLng != null) {
506
- await this.loadBranchesWithLocation(intentLat, intentLng);
507
- }
508
- else {
509
- await this.loadBranches();
510
- }
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;
511
547
  // Auto-select preselected branch if provided
512
548
  if (this.preselectedBranchId) {
513
- this.preselectBranch(this.preselectedBranchId);
549
+ await this.preselectBranch(this.preselectedBranchId);
514
550
  }
551
+ if (request !== this.branchesRequestId || this.activeTab !== 'branch' || this.branchSearchQuery.trim())
552
+ return;
515
553
  }
516
554
  // Prefill form data from session storage
517
555
  await this.prefillFromSessionStorage();
@@ -564,6 +602,17 @@ export class SallaBulletDelivery {
564
602
  });
565
603
  }
566
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;
567
616
  this.viewMode = "main";
568
617
  this.mapLocationData = null;
569
618
  this.mapCountryCode = null;
@@ -580,11 +629,16 @@ export class SallaBulletDelivery {
580
629
  this.selectedSessionAddress = false;
581
630
  this.selectedBranch = null;
582
631
  this.branchSearchQuery = "";
632
+ this.branchCities = [];
633
+ this.selectedBranchCity = null;
634
+ this.expandedScheduleBranchId = null;
583
635
  this.regions = [];
584
636
  this.cities = [];
585
637
  this.districts = [];
586
638
  this.branches = [];
587
639
  this.filteredBranches = [];
640
+ this.pickupDiscountRange = null;
641
+ this.pickupDiscountResolved = false;
588
642
  this.newAddressForm = { ...SallaBulletDelivery.INITIAL_ADDRESS_FORM };
589
643
  this.showCartWillBeClearedBanner = false;
590
644
  this.allocationOutOfCoverageMessage = null;
@@ -988,16 +1042,63 @@ export class SallaBulletDelivery {
988
1042
  });
989
1043
  }
990
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();
991
1048
  this.loadingBranches = true;
992
1049
  try {
993
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1050
+ const branches = await bulletDeliveryAPI.getBranches({
994
1051
  country_id: this.selectedCountry?.id,
995
- }));
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();
996
1063
  }
997
1064
  finally {
998
- this.loadingBranches = false;
1065
+ if (request === this.branchesRequestId)
1066
+ this.loadingBranches = false;
999
1067
  }
1000
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
+ }
1001
1102
  /**
1002
1103
  * Store a freshly fetched branch list and reconcile the current selection with it.
1003
1104
  * `selectedBranch` is a snapshot from selection time: when the refreshed list carries the
@@ -1014,8 +1115,33 @@ export class SallaBulletDelivery {
1014
1115
  }
1015
1116
  this.filterBranches();
1016
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
+ }
1017
1140
  filterBranches() {
1018
- 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);
1019
1145
  }
1020
1146
  /**
1021
1147
  * Prefill form fields from session storage
@@ -1040,7 +1166,7 @@ export class SallaBulletDelivery {
1040
1166
  if (country) {
1041
1167
  this.selectedCountry = country;
1042
1168
  this.updateNewAddressForm({ country_id: Number(country.id) });
1043
- this.preselectBranch(getIntentBranchId(intent));
1169
+ await this.preselectBranch(getIntentBranchId(intent));
1044
1170
  }
1045
1171
  }
1046
1172
  }
@@ -1079,7 +1205,16 @@ export class SallaBulletDelivery {
1079
1205
  }
1080
1206
  }
1081
1207
  applyCountryChange(newCountry) {
1208
+ ++this.branchesRequestId;
1209
+ this.allBranchCities = null;
1210
+ this.loadingBranches = false;
1211
+ this.branches = [];
1212
+ this.filteredBranches = [];
1082
1213
  this.selectedCountry = newCountry;
1214
+ this.selectedBranchCity = null;
1215
+ this.branchCities = [];
1216
+ this.branchCitySearchQuery = '';
1217
+ this.selectedBranch = null;
1083
1218
  this.selectedRegion = null;
1084
1219
  this.regions = [];
1085
1220
  this.displayedRegions = [];
@@ -1199,19 +1334,11 @@ export class SallaBulletDelivery {
1199
1334
  void this.loadBranches();
1200
1335
  return;
1201
1336
  }
1202
- this.branchSearchDebounceTimer = setTimeout(async () => {
1203
- 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)
1204
1340
  return;
1205
- this.loadingBranches = true;
1206
- try {
1207
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1208
- country_id: this.selectedCountry?.id,
1209
- query,
1210
- }));
1211
- }
1212
- finally {
1213
- this.loadingBranches = false;
1214
- }
1341
+ void this.loadBranches();
1215
1342
  }, BRANCH_SEARCH_DEBOUNCE_MS);
1216
1343
  }
1217
1344
  clearSearchTimers() {
@@ -1238,13 +1365,23 @@ export class SallaBulletDelivery {
1238
1365
  this.clearSearchTimers();
1239
1366
  }
1240
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;
1241
1372
  this.loadingBranches = true;
1242
1373
  try {
1243
- this.applyLoadedBranches(await bulletDeliveryAPI.getBranches({
1374
+ const branches = await bulletDeliveryAPI.getBranches({
1244
1375
  country_id: this.selectedCountry?.id,
1376
+ city_id: this.selectedBranchCity?.id,
1245
1377
  lat,
1246
1378
  lng,
1247
- }));
1379
+ });
1380
+ if (request !== this.branchesRequestId)
1381
+ return false;
1382
+ this.branchesServerQuery = '';
1383
+ this.applyLoadedBranches(branches);
1384
+ void this.refreshBranchCities();
1248
1385
  // Auto-select country from first branch if no country is selected
1249
1386
  if (!this.selectedCountry && this.branches.length > 0) {
1250
1387
  const firstBranch = this.branches[0];
@@ -1258,9 +1395,11 @@ export class SallaBulletDelivery {
1258
1395
  }
1259
1396
  }
1260
1397
  }
1398
+ return true;
1261
1399
  }
1262
1400
  finally {
1263
- this.loadingBranches = false;
1401
+ if (request === this.branchesRequestId)
1402
+ this.loadingBranches = false;
1264
1403
  }
1265
1404
  }
1266
1405
  /** Locate nearest branch using device geolocation (branches tab only). */
@@ -1269,6 +1408,7 @@ export class SallaBulletDelivery {
1269
1408
  this.locationError = Salla.lang.get("pages.checkout.current_location_not_supported");
1270
1409
  return;
1271
1410
  }
1411
+ const request = ++this.nearestBranchRequestId;
1272
1412
  this.loadingNearestBranch = true;
1273
1413
  this.locationError = "";
1274
1414
  try {
@@ -1280,7 +1420,12 @@ export class SallaBulletDelivery {
1280
1420
  });
1281
1421
  });
1282
1422
  const { latitude, longitude } = position.coords;
1283
- 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;
1284
1429
  const nearestBranch = findNearestBranch(this.branches, latitude, longitude);
1285
1430
  if (nearestBranch) {
1286
1431
  this.selectedBranch = nearestBranch;
@@ -1289,26 +1434,56 @@ export class SallaBulletDelivery {
1289
1434
  }
1290
1435
  }
1291
1436
  catch (error) {
1437
+ if (request !== this.nearestBranchRequestId)
1438
+ return;
1292
1439
  const geoError = error;
1293
1440
  const code = typeof geoError?.code === "number" ? geoError.code : -1;
1294
- this.locationError = getGeolocationErrorMessage(code);
1441
+ this.locationError = this.geolocationErrorMessage(code);
1295
1442
  }
1296
1443
  finally {
1297
- this.loadingNearestBranch = false;
1444
+ if (request === this.nearestBranchRequestId)
1445
+ this.loadingNearestBranch = false;
1446
+ }
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.");
1298
1453
  }
1454
+ return Salla.lang.getWithDefault("pages.checkout.pickup_location_unavailable", this.isArabic ? "تعذَّر تحديد موقعك. حاول مرة أخرى." : "We couldn't detect your location. Try again.");
1299
1455
  }
1300
1456
  handleBranchSelect(branch) {
1301
1457
  this.selectedBranch = branch;
1302
1458
  this.allocationOutOfCoverageMessage = null;
1303
1459
  }
1460
+ toggleBranchSchedule(branchId) {
1461
+ this.expandedScheduleBranchId = this.expandedScheduleBranchId === branchId ? null : branchId;
1462
+ }
1304
1463
  /**
1305
1464
  * Restore a remembered / event-provided branch — but only one the list renders as selectable,
1306
1465
  * so a branch that has closed since it was remembered is never silently preselected (INV-1407).
1307
1466
  */
1308
- preselectBranch(branchId) {
1309
- if (branchId == null)
1467
+ async preselectBranch(branchId) {
1468
+ if (branchId == null || this.selectedBranch)
1310
1469
  return;
1311
- 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
+ }
1312
1487
  if (branch && isBranchSelectable(branch)) {
1313
1488
  this.selectedBranch = branch;
1314
1489
  }
@@ -1569,6 +1744,48 @@ export class SallaBulletDelivery {
1569
1744
  }
1570
1745
  /** Center shown before geolocation resolves — the customer's IP-based default (falls back to Riyadh). */
1571
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() {
1572
1789
  const geo = salla.config.get('user.default_geo_location');
1573
1790
  // Coerce defensively: config may arrive as strings, blank/whitespace, NaN, or out-of-range
1574
1791
  // values. Reject blanks before coercion (Number('') === 0) and validate lat/lng ranges so we
@@ -1577,7 +1794,7 @@ export class SallaBulletDelivery {
1577
1794
  const lng = this.toCoordinate(geo?.lng);
1578
1795
  const isValid = lat != null && lng != null &&
1579
1796
  lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180;
1580
- return isValid ? { lat, lng } : DEFAULT_MAP_CENTER;
1797
+ return isValid ? { lat, lng } : null;
1581
1798
  }
1582
1799
  /** Parse a config coordinate to a finite number, treating blank/whitespace strings as missing. */
1583
1800
  toCoordinate(value) {
@@ -1591,6 +1808,8 @@ export class SallaBulletDelivery {
1591
1808
  async handleTabChange(tab) {
1592
1809
  if (this.tabChanging)
1593
1810
  return;
1811
+ ++this.branchesRequestId;
1812
+ this.loadingBranches = false;
1594
1813
  this.tabChanging = true;
1595
1814
  try {
1596
1815
  this.activeTab = tab;
@@ -1622,24 +1841,25 @@ export class SallaBulletDelivery {
1622
1841
  this.selectedCountry = this.countries[0];
1623
1842
  }
1624
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
+ }
1625
1853
  if (tab === "address" && this.needsMap)
1626
1854
  this.fetchAddressToken();
1627
1855
  // Load appropriate data when switching tabs if country is selected
1628
1856
  if (this.selectedCountry) {
1629
1857
  if (tab === "branch") {
1630
1858
  // Load branches when switching to branch tab
1631
- // Check session storage for lat/lng to get nearby branches
1632
1859
  const savedIntent = this.getStoredIntent();
1633
- const intentLat = getIntentLatitude(savedIntent);
1634
- const intentLng = getIntentLongitude(savedIntent);
1635
- if (intentLat != null && intentLng != null) {
1636
- await this.loadBranchesWithLocation(intentLat, intentLng);
1637
- }
1638
- else {
1639
- await this.loadBranches();
1640
- }
1860
+ await this.loadBranches();
1641
1861
  // Preselect branch from session storage if available
1642
- this.preselectBranch(getIntentBranchId(savedIntent));
1862
+ void this.preselectBranch(getIntentBranchId(savedIntent));
1643
1863
  }
1644
1864
  else if (tab === "address") {
1645
1865
  // Lazy-load saved addresses when switching to address tab (if not loaded yet).
@@ -2143,10 +2363,17 @@ export class SallaBulletDelivery {
2143
2363
  (this.activeTab === "address"
2144
2364
  ? Salla.lang.get("pages.checkout.address_out_of_coverage")
2145
2365
  : Salla.lang.get("pages.checkout.failed_to_set_pickup"));
2146
- this.allocationOutOfCoverageMessage = `${errorMessage}.`;
2366
+ this.allocationOutOfCoverageMessage = /[.!؟?]$/.test(errorMessage.trim()) ? errorMessage : `${errorMessage}.`;
2147
2367
  this.trackSelectorResult("failed", previousIntent, []);
2148
2368
  this.confirmBtn?.stop();
2149
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
+ }
2150
2377
  return;
2151
2378
  }
2152
2379
  // Process successful allocation response
@@ -2312,6 +2539,19 @@ export class SallaBulletDelivery {
2312
2539
  wrapper.append(info, btn);
2313
2540
  return wrapper;
2314
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
+ }
2315
2555
  renderTabs() {
2316
2556
  // Show tabs only when pickup is supported; otherwise delivery-only view with no tabs.
2317
2557
  // The pickup tab is gated by this same `supportsPickup` check, while the delivery
@@ -2321,10 +2561,10 @@ export class SallaBulletDelivery {
2321
2561
  return (h("div", { class: "s-bullet-delivery-tabs" }, this.isSupportsDelivery && (h("button", { type: "button", class: {
2322
2562
  "s-bullet-delivery-tab": true,
2323
2563
  "s-bullet-delivery-tab--active": this.activeTab === "address",
2324
- }, 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: {
2325
2565
  "s-bullet-delivery-tab": true,
2326
2566
  "s-bullet-delivery-tab--active": this.activeTab === "branch",
2327
- }, 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())));
2328
2568
  }
2329
2569
  renderAlert(message) {
2330
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)));
@@ -2348,8 +2588,9 @@ export class SallaBulletDelivery {
2348
2588
  }
2349
2589
  return this.renderAlert(`${message} ${Salla.lang.getWithDefault('pages.cart.choose_another_address', 'Choose another address')}`);
2350
2590
  }
2351
- renderCountrySelect() {
2352
- 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() })));
2353
2594
  }
2354
2595
  /** Region select (SA only): shown when SA is selected; city and district disabled until region selected. */
2355
2596
  renderRegionSelect() {
@@ -2594,66 +2835,146 @@ export class SallaBulletDelivery {
2594
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'))))));
2595
2836
  }
2596
2837
  renderBranchSearch() {
2597
- 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: {
2598
2839
  "s-bullet-delivery-location-btn": true,
2599
2840
  "s-bullet-delivery-location-btn--full": true,
2600
2841
  "s-bullet-delivery-location-btn--loading": this.loadingNearestBranch,
2601
- }, 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))));
2602
- }
2603
- /** Renders a branch's `preparation_time_range` as a localized "from - to <unit>" label. */
2604
- getPreparationTimeLabel(range) {
2605
- if (!range)
2606
- return null;
2607
- const unitKeys = {
2608
- hour: 'pages.checkout.preparation_time_hours',
2609
- minute: 'pages.checkout.preparation_time_minutes',
2610
- day: 'pages.checkout.preparation_time_days',
2611
- };
2612
- const key = unitKeys[range.from.unit];
2613
- if (!key)
2614
- return null;
2615
- return Salla.lang
2616
- .getWithDefault(key, 'من {from} إلى {to} دقائق')
2617
- ?.replace('{from}', String(range.from.value))
2618
- ?.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))));
2619
2843
  }
2620
2844
  renderBranchList() {
2621
2845
  if (this.loadingBranches) {
2622
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" }))))))));
2623
2847
  }
2624
- if (this.filteredBranches.length === 0) {
2848
+ const branches = this.filteredBranches.filter(isBranchVisible);
2849
+ if (branches.length === 0) {
2625
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"))));
2626
2851
  }
2627
- 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 => {
2628
2853
  const selectable = isBranchSelectable(branch);
2629
- const readiness = branch?.pickup_readiness;
2854
+ const isSelected = this.selectedBranch?.id === branch.id;
2630
2855
  const locationText = [branch.country?.name, branch.city?.name, branch.district?.name]
2631
2856
  .filter(Boolean)
2632
- .join(', ');
2633
- 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);
2634
2870
  return (h("li", { key: branch.id, class: {
2635
2871
  's-bullet-delivery-branch-item': true,
2636
- 's-bullet-delivery-branch-item--selected': this.selectedBranch?.id === branch.id,
2872
+ 's-bullet-delivery-branch-item--selected': isSelected,
2637
2873
  's-bullet-delivery-branch-item--disabled': !selectable,
2638
- }, 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;
2639
2877
  if (e.key === 'Enter' || e.key === ' ') {
2640
2878
  e.preventDefault();
2641
2879
  if (selectable)
2642
2880
  this.handleBranchSelect(branch);
2643
2881
  }
2644
- } }, 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: {
2645
- width: 'full',
2646
- maxWidth: '50px',
2647
- marginInlineStart: 'auto',
2648
- } }, 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')))))));
2649
2887
  }))));
2650
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
+ }
2651
2961
  renderPickupTab() {
2652
2962
  return (h("div", { class: {
2653
2963
  "s-bullet-delivery-content": true,
2654
2964
  "s-bullet-delivery-content--active": this.activeTab === "branch",
2655
2965
  "s-hidden": this.activeTab !== "branch",
2656
- } }, 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
+ } })));
2657
2978
  }
2658
2979
  /** Add-address form: map + confirm card in one screen, or dropdown fallback. */
2659
2980
  renderAddAddressForm() {
@@ -2695,7 +3016,7 @@ export class SallaBulletDelivery {
2695
3016
  return (h("div", { class: "s-bullet-delivery-footer" }, h("salla-button", { ref: (btn) => {
2696
3017
  this.confirmBtn = btn;
2697
3018
  }, disabled: this.isConfirmDisabled(), onClick: () => this.handleConfirm(), "loader-position": "center", width: "wide" }, this.activeTab === "branch"
2698
- ? Salla.lang.get("pages.checkout.confirm_receipt_from_the_branch")
3019
+ ? Salla.lang.getWithDefault("pages.checkout.confirm_receipt_from_the_branch", "تأكيد الإستلام من الفرع")
2699
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")))));
2700
3021
  }
2701
3022
  renderMainView() {
@@ -2881,7 +3202,7 @@ export class SallaBulletDelivery {
2881
3202
  this.handleModalVisibility(Boolean(event.detail));
2882
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'
2883
3204
  ? Salla.lang.get('blocks.home.add_address_for_order_delivery')
2884
- : 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'
2885
3206
  ? Salla.lang.get('blocks.home.products_available_for_delivery_shown_while_shopping')
2886
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()))));
2887
3208
  }
@@ -3040,9 +3361,15 @@ export class SallaBulletDelivery {
3040
3361
  "selectedSavedAddress": {},
3041
3362
  "selectedSessionAddress": {},
3042
3363
  "branches": {},
3364
+ "pickupDiscountRange": {},
3365
+ "pickupDiscountResolved": {},
3043
3366
  "filteredBranches": {},
3044
3367
  "selectedBranch": {},
3045
3368
  "branchSearchQuery": {},
3369
+ "branchCities": {},
3370
+ "selectedBranchCity": {},
3371
+ "branchCitySearchQuery": {},
3372
+ "expandedScheduleBranchId": {},
3046
3373
  "loadingCountries": {},
3047
3374
  "loadingCities": {},
3048
3375
  "loadingDistricts": {},