@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.
- package/dist/cjs/{Helper-D2VNt6eb.js → Helper-DqKJCL8G.js} +1 -1
- package/dist/cjs/{filepond-DkCudLhG.js → filepond-Byu0lc8z.js} +1 -1
- package/dist/cjs/{filepond-plugin-file-poster-C-uxFWfW.js → filepond-plugin-file-poster-qwx7lYZ-.js} +1 -1
- package/dist/cjs/{filepond-plugin-file-validate-size-BikXTQW2.js → filepond-plugin-file-validate-size-C-n0P4TT.js} +1 -1
- package/dist/cjs/{filepond-plugin-file-validate-type-Drs7PouU.js → filepond-plugin-file-validate-type-C8VOOXdT.js} +1 -1
- package/dist/cjs/{filepond-plugin-image-edit-hxr0h9kt.js → filepond-plugin-image-edit-B9KG763Y.js} +1 -1
- package/dist/cjs/{filepond-plugin-image-exif-orientation-CVZmWU_4.js → filepond-plugin-image-exif-orientation-Czt4pYzU.js} +1 -1
- package/dist/cjs/{filepond-plugin-image-preview-CBjPKx-d.js → filepond-plugin-image-preview-BFm1Zl0o.js} +1 -1
- package/dist/cjs/helpers-NV7wgP50.js +602 -0
- package/dist/cjs/{index-CTYg2zC6.js → index-CFW798jX.js} +2 -2
- package/dist/cjs/{intlTelInput-C9X0hKXV.js → intlTelInput-CLa2B89b.js} +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-accordion_6.cjs.entry.js +2 -2
- package/dist/cjs/salla-add-product-button_5.cjs.entry.js +46 -3
- package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
- package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
- package/dist/cjs/salla-applied-filters.cjs.entry.js +2 -2
- package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
- package/dist/cjs/salla-attachment-view_9.cjs.entry.js +11 -11
- package/dist/cjs/salla-badge.cjs.entry.js +1 -1
- package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
- package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +425 -119
- package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
- package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
- package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
- package/dist/cjs/salla-comment-form_9.cjs.entry.js +2 -2
- package/dist/cjs/salla-conditional-offer.cjs.entry.js +1 -1
- package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
- package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
- package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
- package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
- package/dist/cjs/salla-delivery-promise.cjs.entry.js +2 -2
- package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
- package/dist/cjs/salla-filters-widget.cjs.entry.js +2 -2
- package/dist/cjs/salla-filters.cjs.entry.js +2 -2
- package/dist/cjs/salla-free-shipping-banner.cjs.entry.js +1 -1
- package/dist/cjs/salla-free-shipping-tag.cjs.entry.js +1 -1
- package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
- package/dist/cjs/salla-gift-card-redeem_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
- package/dist/cjs/salla-hook.cjs.entry.js +1 -1
- package/dist/cjs/salla-infinite-scroll.cjs.entry.js +2 -2
- package/dist/cjs/salla-installment.cjs.entry.js +1 -1
- package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
- package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
- package/dist/cjs/salla-login-modal.cjs.entry.js +7 -2
- package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
- package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
- package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
- package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
- package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
- package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
- package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
- package/dist/cjs/salla-menu.cjs.entry.js +1 -1
- package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
- package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
- package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
- package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
- package/dist/cjs/salla-notification-item_2.cjs.entry.js +2 -2
- package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
- package/dist/cjs/salla-offer.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-items.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-payment.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-rewards.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
- package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-orders.cjs.entry.js +2 -2
- package/dist/cjs/salla-payments.cjs.entry.js +1 -1
- package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
- package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
- package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
- package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
- package/dist/cjs/salla-products-list.cjs.entry.js +9 -2
- package/dist/cjs/salla-products-slider.cjs.entry.js +2 -2
- package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
- package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +2 -2
- package/dist/cjs/salla-rating-modal.cjs.entry.js +2 -2
- package/dist/cjs/salla-review-factors-tags.cjs.entry.js +1 -1
- package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
- package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
- package/dist/cjs/salla-search-product-card.cjs.entry.js +1 -1
- package/dist/cjs/salla-search.cjs.entry.js +2 -2
- package/dist/cjs/salla-skeleton.cjs.entry.js +1 -1
- package/dist/cjs/salla-social-share.cjs.entry.js +1 -1
- package/dist/cjs/salla-social.cjs.entry.js +1 -1
- package/dist/cjs/salla-tab-content_3.cjs.entry.js +1 -1
- package/dist/cjs/salla-thank-you-header.cjs.entry.js +1 -1
- package/dist/cjs/salla-thank-you-page.cjs.entry.js +1 -1
- package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
- package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
- package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
- package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
- package/dist/cjs/salla-user-settings.cjs.entry.js +1 -1
- package/dist/cjs/salla-verify.cjs.entry.js +1 -1
- package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
- package/dist/cjs/salla-wishlist-actions.cjs.entry.js +1 -1
- package/dist/cjs/salla-wishlist-summary.cjs.entry.js +2 -2
- package/dist/cjs/twilight.cjs.js +2 -2
- package/dist/cjs/{vanilla-picker-BsLm8rHT.js → vanilla-picker-Vx_7G7bs.js} +1 -1
- package/dist/collection/components/salla-bullet-delivery/api-service.js +156 -25
- package/dist/collection/components/salla-bullet-delivery/helpers.js +143 -15
- package/dist/collection/components/salla-bullet-delivery/icons.js +17 -0
- package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.css +490 -52
- package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.js +417 -90
- package/dist/collection/components/salla-login-modal/salla-login-modal.js +6 -1
- package/dist/collection/components/salla-products-list/salla-products-list.js +7 -0
- package/dist/collection/components/salla-quick-buy/salla-quick-buy.js +44 -1
- package/dist/components/helpers.js +299 -40
- package/dist/components/index.js +2 -2
- package/dist/components/salla-bullet-delivery.js +430 -118
- package/dist/components/salla-login-modal2.js +6 -1
- package/dist/components/salla-products-list2.js +7 -0
- package/dist/components/salla-quick-buy2.js +44 -1
- package/dist/docs.json +1 -1
- package/dist/esm/{Helper-C1T-oD4J.js → Helper-I0CAizv1.js} +1 -1
- package/dist/esm/{filepond-BoWJzHeN.js → filepond-DTUYXV0J.js} +1 -1
- package/dist/esm/{filepond-plugin-file-poster-B6MkMtuC.js → filepond-plugin-file-poster-DydZjneT.js} +1 -1
- package/dist/esm/{filepond-plugin-file-validate-size-Dds_462B.js → filepond-plugin-file-validate-size-nbsCU2t9.js} +1 -1
- package/dist/esm/{filepond-plugin-file-validate-type-DrfGXNay.js → filepond-plugin-file-validate-type-Cl5B_DIy.js} +1 -1
- package/dist/esm/{filepond-plugin-image-edit-CN7eGg-7.js → filepond-plugin-image-edit-bzskFyFR.js} +1 -1
- package/dist/esm/{filepond-plugin-image-exif-orientation-CyhxK_6R.js → filepond-plugin-image-exif-orientation-MBDuxLaX.js} +1 -1
- package/dist/esm/{filepond-plugin-image-preview-DdKqAzT9.js → filepond-plugin-image-preview-N6wzEryW.js} +1 -1
- package/dist/esm/helpers-Cnh7Qptl.js +568 -0
- package/dist/esm/{index-DgUQRYr2.js → index-C9zdeQFI.js} +2 -2
- package/dist/esm/{intlTelInput-SwlavcFB.js → intlTelInput-B5L9nYtO.js} +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/esm/salla-accordion-body_2.entry.js +1 -1
- package/dist/esm/salla-accordion_6.entry.js +2 -2
- package/dist/esm/salla-add-product-button_5.entry.js +46 -3
- package/dist/esm/salla-advertisement.entry.js +1 -1
- package/dist/esm/salla-alert_2.entry.js +1 -1
- package/dist/esm/salla-app-install-alert.entry.js +1 -1
- package/dist/esm/salla-applied-filters.entry.js +2 -2
- package/dist/esm/salla-apps-icons.entry.js +1 -1
- package/dist/esm/salla-attachment-view_9.entry.js +11 -11
- package/dist/esm/salla-badge.entry.js +1 -1
- package/dist/esm/salla-basket-gap.entry.js +1 -1
- package/dist/esm/salla-bullet-delivery_3.entry.js +425 -119
- package/dist/esm/salla-cart-coupons.entry.js +1 -1
- package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
- package/dist/esm/salla-cart-summary-card.entry.js +1 -1
- package/dist/esm/salla-cashback-banner.entry.js +1 -1
- package/dist/esm/salla-comment-factors.entry.js +1 -1
- package/dist/esm/salla-comment-form_9.entry.js +2 -2
- package/dist/esm/salla-conditional-offer.entry.js +1 -1
- package/dist/esm/salla-contacts.entry.js +1 -1
- package/dist/esm/salla-cookies-bar.entry.js +1 -1
- package/dist/esm/salla-count-down.entry.js +1 -1
- package/dist/esm/salla-custom-fields.entry.js +1 -1
- package/dist/esm/salla-delivery-promise.entry.js +2 -2
- package/dist/esm/salla-edit-order-button.entry.js +1 -1
- package/dist/esm/salla-filters-widget.entry.js +2 -2
- package/dist/esm/salla-filters.entry.js +2 -2
- package/dist/esm/salla-free-shipping-banner.entry.js +1 -1
- package/dist/esm/salla-free-shipping-tag.entry.js +1 -1
- package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
- package/dist/esm/salla-gift-card-redeem_2.entry.js +1 -1
- package/dist/esm/salla-gifting.entry.js +1 -1
- package/dist/esm/salla-hook.entry.js +1 -1
- package/dist/esm/salla-infinite-scroll.entry.js +2 -2
- package/dist/esm/salla-installment.entry.js +1 -1
- package/dist/esm/salla-list-tile.entry.js +1 -1
- package/dist/esm/salla-localization-modal.entry.js +1 -1
- package/dist/esm/salla-login-modal.entry.js +7 -2
- package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
- package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
- package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
- package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
- package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
- package/dist/esm/salla-loyalty.entry.js +1 -1
- package/dist/esm/salla-maintenance-alert.entry.js +1 -1
- package/dist/esm/salla-map_3.entry.js +1 -1
- package/dist/esm/salla-menu.entry.js +1 -1
- package/dist/esm/salla-metadata.entry.js +1 -1
- package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
- package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
- package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
- package/dist/esm/salla-next-order-coupon.entry.js +1 -1
- package/dist/esm/salla-notification-item_2.entry.js +2 -2
- package/dist/esm/salla-offer-modal.entry.js +1 -1
- package/dist/esm/salla-offer.entry.js +1 -1
- package/dist/esm/salla-order-branch.entry.js +1 -1
- package/dist/esm/salla-order-cancel.entry.js +1 -1
- package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
- package/dist/esm/salla-order-details-options.entry.js +1 -1
- package/dist/esm/salla-order-details.entry.js +1 -1
- package/dist/esm/salla-order-edit-item.entry.js +1 -1
- package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
- package/dist/esm/salla-order-edit.entry.js +1 -1
- package/dist/esm/salla-order-items.entry.js +1 -1
- package/dist/esm/salla-order-options-modal.entry.js +1 -1
- package/dist/esm/salla-order-payment.entry.js +1 -1
- package/dist/esm/salla-order-rewards.entry.js +1 -1
- package/dist/esm/salla-order-shipments.entry.js +1 -1
- package/dist/esm/salla-order-summary.entry.js +1 -1
- package/dist/esm/salla-order-totals-card.entry.js +1 -1
- package/dist/esm/salla-orders.entry.js +2 -2
- package/dist/esm/salla-payments.entry.js +1 -1
- package/dist/esm/salla-placeholder.entry.js +1 -1
- package/dist/esm/salla-price-range.entry.js +1 -1
- package/dist/esm/salla-product-card-embed.entry.js +1 -1
- package/dist/esm/salla-product-card.entry.js +1 -1
- package/dist/esm/salla-product-size-guide.entry.js +1 -1
- package/dist/esm/salla-products-list.entry.js +9 -2
- package/dist/esm/salla-products-slider.entry.js +2 -2
- package/dist/esm/salla-quick-order.entry.js +1 -1
- package/dist/esm/salla-rating-modal-v2.entry.js +2 -2
- package/dist/esm/salla-rating-modal.entry.js +2 -2
- package/dist/esm/salla-review-factors-tags.entry.js +1 -1
- package/dist/esm/salla-review-order-item.entry.js +1 -1
- package/dist/esm/salla-scopes.entry.js +1 -1
- package/dist/esm/salla-search-product-card.entry.js +1 -1
- package/dist/esm/salla-search.entry.js +2 -2
- package/dist/esm/salla-skeleton.entry.js +1 -1
- package/dist/esm/salla-social-share.entry.js +1 -1
- package/dist/esm/salla-social.entry.js +1 -1
- package/dist/esm/salla-tab-content_3.entry.js +1 -1
- package/dist/esm/salla-thank-you-header.entry.js +1 -1
- package/dist/esm/salla-thank-you-page.entry.js +1 -1
- package/dist/esm/salla-tiered-offer.entry.js +1 -1
- package/dist/esm/salla-trust-badges.entry.js +1 -1
- package/dist/esm/salla-user-menu.entry.js +1 -1
- package/dist/esm/salla-user-profile.entry.js +1 -1
- package/dist/esm/salla-user-settings.entry.js +1 -1
- package/dist/esm/salla-verify.entry.js +1 -1
- package/dist/esm/salla-wallet.entry.js +1 -1
- package/dist/esm/salla-wishlist-actions.entry.js +1 -1
- package/dist/esm/salla-wishlist-summary.entry.js +2 -2
- package/dist/esm/twilight.js +3 -3
- package/dist/esm/{vanilla-picker-Buy50D7t.js → vanilla-picker-CAeu4f87.js} +1 -1
- package/dist/twilight/{p-bed54154.entry.js → p-01d5cd61.entry.js} +1 -1
- package/dist/twilight/{p-b3ada3d0.entry.js → p-0213fde4.entry.js} +1 -1
- package/dist/twilight/{p-d0ff9a3e.entry.js → p-036fbfc0.entry.js} +1 -1
- package/dist/twilight/{p-53735934.entry.js → p-0add41ce.entry.js} +1 -1
- package/dist/twilight/{p-bb8b2c85.entry.js → p-0b0d4942.entry.js} +1 -1
- package/dist/twilight/{p-d1121b3f.entry.js → p-17bcbf9b.entry.js} +1 -1
- package/dist/twilight/{p-6b5de500.entry.js → p-1867f7d4.entry.js} +1 -1
- package/dist/twilight/{p-ee65c489.entry.js → p-194ff38c.entry.js} +1 -1
- package/dist/twilight/{p-4ab5ee99.entry.js → p-20f9c62f.entry.js} +1 -1
- package/dist/twilight/{p-5e8d019e.entry.js → p-252748d9.entry.js} +1 -1
- package/dist/twilight/{p-6c3084c9.entry.js → p-290ce0bc.entry.js} +1 -1
- package/dist/twilight/{p-5694edf1.entry.js → p-2b930157.entry.js} +1 -1
- package/dist/twilight/{p-7fd9dc36.entry.js → p-2e4e15c5.entry.js} +1 -1
- package/dist/twilight/{p-b9d67694.entry.js → p-2e6537a3.entry.js} +1 -1
- package/dist/twilight/{p-7f97335a.entry.js → p-30d64bd1.entry.js} +1 -1
- package/dist/twilight/{p-516e4edc.entry.js → p-32495dd2.entry.js} +1 -1
- package/dist/twilight/{p-e9b03e9c.entry.js → p-32d9da31.entry.js} +1 -1
- package/dist/twilight/{p-9d15f22d.entry.js → p-32f100f8.entry.js} +1 -1
- package/dist/twilight/{p-6dccd40f.entry.js → p-386b3dcd.entry.js} +1 -1
- package/dist/twilight/{p-d1093a59.entry.js → p-3a4e86d1.entry.js} +1 -1
- package/dist/twilight/{p-f7a58656.entry.js → p-3b766a79.entry.js} +1 -1
- package/dist/twilight/{p-361f6033.entry.js → p-3b7a45ec.entry.js} +1 -1
- package/dist/twilight/{p-9cf1cfd4.entry.js → p-3bb0e184.entry.js} +1 -1
- package/dist/twilight/{p-b0d2e31f.entry.js → p-3cc6ca6c.entry.js} +1 -1
- package/dist/twilight/{p-d4fb9921.entry.js → p-4070f808.entry.js} +1 -1
- package/dist/twilight/{p-af49191f.entry.js → p-412fb721.entry.js} +1 -1
- package/dist/twilight/{p-5f6fee0e.entry.js → p-427c13b6.entry.js} +1 -1
- package/dist/twilight/{p-be6f8551.entry.js → p-45e0a5d0.entry.js} +1 -1
- package/dist/twilight/{p-c4cf0b4d.entry.js → p-4d615f9f.entry.js} +1 -1
- package/dist/twilight/{p-fddf1997.entry.js → p-4d7fc363.entry.js} +1 -1
- package/dist/twilight/{p-9e042cce.entry.js → p-5009f654.entry.js} +1 -1
- package/dist/twilight/{p-d9a1bf08.entry.js → p-50853b76.entry.js} +1 -1
- package/dist/twilight/{p-eaf0c514.entry.js → p-550ad1a6.entry.js} +1 -1
- package/dist/twilight/{p-3f842f32.entry.js → p-575014c4.entry.js} +1 -1
- package/dist/twilight/{p-66ad7a30.entry.js → p-5aee5e44.entry.js} +1 -1
- package/dist/twilight/{p-8c1d14f0.entry.js → p-619c709c.entry.js} +1 -1
- package/dist/twilight/{p-cdeb9bcc.entry.js → p-6d572fe0.entry.js} +1 -1
- package/dist/twilight/{p-12a25415.entry.js → p-6f84c662.entry.js} +1 -1
- package/dist/twilight/{p-b5a45f87.entry.js → p-703b9253.entry.js} +1 -1
- package/dist/twilight/{p-a5b732b3.entry.js → p-73dea22d.entry.js} +1 -1
- package/dist/twilight/{p-a44670a9.entry.js → p-74285af9.entry.js} +1 -1
- package/dist/twilight/{p-b1f808f5.entry.js → p-777d28f6.entry.js} +1 -1
- package/dist/twilight/{p-027cbda6.entry.js → p-7db6ccfe.entry.js} +1 -1
- package/dist/twilight/{p-0418c2df.entry.js → p-7db8d545.entry.js} +1 -1
- package/dist/twilight/{p-da0df10b.entry.js → p-7fa4d0b6.entry.js} +1 -1
- package/dist/twilight/{p-78a56dc6.entry.js → p-825c5b73.entry.js} +1 -1
- package/dist/twilight/{p-4b70e6c4.entry.js → p-8262e669.entry.js} +1 -1
- package/dist/twilight/p-86d55a6b.entry.js +4 -0
- package/dist/twilight/{p-259c995a.entry.js → p-8712f7c7.entry.js} +1 -1
- package/dist/twilight/{p-08a15c81.entry.js → p-8948b58c.entry.js} +1 -1
- package/dist/twilight/{p-aa35cf98.entry.js → p-8b8cf457.entry.js} +1 -1
- package/dist/twilight/{p-d0aeeb7f.entry.js → p-8c2992e7.entry.js} +1 -1
- package/dist/twilight/{p-79822d99.entry.js → p-8c548259.entry.js} +1 -1
- package/dist/twilight/{p-6af8a7b6.entry.js → p-8eb3fd92.entry.js} +1 -1
- package/dist/twilight/p-908716fb.entry.js +4 -0
- package/dist/twilight/{p-70dfb2b4.entry.js → p-90a59027.entry.js} +1 -1
- package/dist/twilight/{p-37192614.entry.js → p-9683535f.entry.js} +1 -1
- package/dist/twilight/{p-f9e5ed10.entry.js → p-982e9124.entry.js} +1 -1
- package/dist/twilight/{p-0d795b58.entry.js → p-995b898f.entry.js} +1 -1
- package/dist/twilight/{p-d1204965.entry.js → p-99cb3dba.entry.js} +1 -1
- package/dist/twilight/{p-071aa0e7.entry.js → p-9a0218a6.entry.js} +1 -1
- package/dist/twilight/{p-eefced26.entry.js → p-9cf31ff4.entry.js} +1 -1
- package/dist/twilight/{p-4891c565.entry.js → p-9d1a5007.entry.js} +1 -1
- package/dist/twilight/{p-f9587127.entry.js → p-9e429047.entry.js} +1 -1
- package/dist/twilight/p-9e542951.entry.js +4 -0
- package/dist/twilight/{p-BxQMWsr9.js → p-BcT9tEVW.js} +1 -1
- package/dist/twilight/{p-Y2ZIN9Ok.js → p-Bgu5gtOF.js} +1 -1
- package/dist/twilight/{p-DgUQRYr2.js → p-C9zdeQFI.js} +2 -2
- package/dist/twilight/{p-CCwsxMbZ.js → p-CSGj1pY-.js} +1 -1
- package/dist/twilight/{p-Ca_SZn0u.js → p-CWWgpQ8Q.js} +1 -1
- package/dist/twilight/{p-CgNJWeaK.js → p-Cfz9xit3.js} +1 -1
- package/dist/twilight/{p-BK9YeXWp.js → p-CmYSalnM.js} +1 -1
- package/dist/twilight/p-Cnh7Qptl.js +4 -0
- package/dist/twilight/{p-De1tLqQF.js → p-Dav_R_d2.js} +1 -1
- package/dist/twilight/{p-DMvhNbgN.js → p-JGEddAgs.js} +1 -1
- package/dist/twilight/{p-CBLWEyM9.js → p-RflSBQNB.js} +1 -1
- package/dist/twilight/{p-NqQdbGu6.js → p-Xgbueas7.js} +1 -1
- package/dist/twilight/{p-ba6dd59c.entry.js → p-a070cce0.entry.js} +1 -1
- package/dist/twilight/{p-1960020c.entry.js → p-a0bfedad.entry.js} +1 -1
- package/dist/twilight/{p-f7ab5825.entry.js → p-a22d9931.entry.js} +1 -1
- package/dist/twilight/{p-a95b0dc1.entry.js → p-a3959219.entry.js} +1 -1
- package/dist/twilight/p-a3ca61af.entry.js +4 -0
- package/dist/twilight/{p-e457e68f.entry.js → p-a55b5c44.entry.js} +1 -1
- package/dist/twilight/{p-23772523.entry.js → p-a5fc8358.entry.js} +1 -1
- package/dist/twilight/{p-3a632c69.entry.js → p-aabcd09f.entry.js} +1 -1
- package/dist/twilight/{p-d8955835.entry.js → p-ad00021e.entry.js} +1 -1
- package/dist/twilight/{p-481f52b1.entry.js → p-ade9a68c.entry.js} +1 -1
- package/dist/twilight/{p-4a3fa153.entry.js → p-b5e0065a.entry.js} +1 -1
- package/dist/twilight/{p-bed68e52.entry.js → p-bbb09d92.entry.js} +1 -1
- package/dist/twilight/{p-9e65451e.entry.js → p-c2df20f8.entry.js} +1 -1
- package/dist/twilight/{p-1af9db24.entry.js → p-c4274188.entry.js} +1 -1
- package/dist/twilight/{p-bfd5e263.entry.js → p-c5e2c781.entry.js} +1 -1
- package/dist/twilight/{p-e38a6793.entry.js → p-c64b2b22.entry.js} +1 -1
- package/dist/twilight/{p-3d8b77f7.entry.js → p-c92cc92f.entry.js} +1 -1
- package/dist/twilight/{p-ac736b30.entry.js → p-cb54adac.entry.js} +1 -1
- package/dist/twilight/{p-5bbf17dd.entry.js → p-ce1e45a3.entry.js} +1 -1
- package/dist/twilight/{p-fcb1009f.entry.js → p-d0acd7ff.entry.js} +1 -1
- package/dist/twilight/{p-92fd20a0.entry.js → p-d0f903ac.entry.js} +1 -1
- package/dist/twilight/{p-26d2b28a.entry.js → p-d57b49ff.entry.js} +1 -1
- package/dist/twilight/{p-8766e236.entry.js → p-db3efb27.entry.js} +1 -1
- package/dist/twilight/{p-0f25fedd.entry.js → p-db73d0bf.entry.js} +1 -1
- package/dist/twilight/{p-6e52b649.entry.js → p-e20d5e75.entry.js} +1 -1
- package/dist/twilight/{p-254cff9a.entry.js → p-e223beca.entry.js} +1 -1
- package/dist/twilight/{p-8c40278d.entry.js → p-e37da59f.entry.js} +1 -1
- package/dist/twilight/{p-84b0e7a9.entry.js → p-e3991836.entry.js} +1 -1
- package/dist/twilight/{p-cb48a5ef.entry.js → p-e455709f.entry.js} +1 -1
- package/dist/twilight/{p-9e0a9a5a.entry.js → p-e921b2d1.entry.js} +1 -1
- package/dist/twilight/{p-0ba159db.entry.js → p-ec356e5a.entry.js} +1 -1
- package/dist/twilight/{p-34148827.entry.js → p-eeefc285.entry.js} +1 -1
- package/dist/twilight/{p-6ba3808a.entry.js → p-f3b12754.entry.js} +1 -1
- package/dist/twilight/{p-419d6d71.entry.js → p-f6279a13.entry.js} +1 -1
- package/dist/twilight/{p-487b4411.entry.js → p-f83efc62.entry.js} +1 -1
- package/dist/twilight/{p-7b5f97e7.entry.js → p-fa622715.entry.js} +1 -1
- package/dist/twilight/{p-ee479819.entry.js → p-faf00404.entry.js} +1 -1
- package/dist/twilight/twilight.esm.js +1 -1
- package/dist/types/components/salla-bullet-delivery/api-service.d.ts +23 -7
- package/dist/types/components/salla-bullet-delivery/helpers.d.ts +61 -6
- package/dist/types/components/salla-bullet-delivery/icons.d.ts +14 -0
- package/dist/types/components/salla-bullet-delivery/interfaces.d.ts +36 -1
- package/dist/types/components/salla-bullet-delivery/salla-bullet-delivery.d.ts +73 -2
- package/dist/types/components/salla-quick-buy/salla-quick-buy.d.ts +6 -0
- package/package.json +5 -5
- package/dist/cjs/helpers-8bMfm_mm.js +0 -329
- package/dist/esm/helpers-DqzaeXrL.js +0 -309
- package/dist/twilight/p-76584a5b.entry.js +0 -4
- package/dist/twilight/p-DqzaeXrL.js +0 -4
- package/dist/twilight/p-bac25e34.entry.js +0 -4
- package/dist/twilight/p-d346dabd.entry.js +0 -4
- package/dist/twilight/p-e24fbbb4.entry.js +0 -4
|
@@ -343,9 +343,14 @@ export class SallaLoginModal {
|
|
|
343
343
|
});
|
|
344
344
|
}
|
|
345
345
|
async handleGuestCheckout() {
|
|
346
|
+
// buy now resumes its own flow; emitted before closing so the dismissal doesn't race it
|
|
347
|
+
if (this.source === 'buy-now') {
|
|
348
|
+
Salla.event.emit('salla-login::guest-chosen', { source: this.source });
|
|
349
|
+
return this.modal.close();
|
|
350
|
+
}
|
|
346
351
|
this.modal.close();
|
|
347
352
|
if (salla.config.isGuest()) {
|
|
348
|
-
return salla.cart.submit(false, true);
|
|
353
|
+
return salla.cart.submit(false, true, true);
|
|
349
354
|
}
|
|
350
355
|
}
|
|
351
356
|
openModal() {
|
|
@@ -1181,6 +1181,13 @@ export class SallaProductsList {
|
|
|
1181
1181
|
?.find(filter => filter.key === 'category_id')
|
|
1182
1182
|
// loose == : catId may be a number (auto-appended page.id) while API keys are strings
|
|
1183
1183
|
?.values?.find(cat => cat.key == catId)?.value ?? '';
|
|
1184
|
+
// without the filters feature the response has no `filters`, so the lookup above finds no name and the
|
|
1185
|
+
// theme would swap the page heading for a bare count; the page itself still names its own list.
|
|
1186
|
+
// page.title is raw text that themes drop into innerHTML, so it's escaped like the search term
|
|
1187
|
+
const pageId = salla.config.get('page.id');
|
|
1188
|
+
if (!title && catId != null && pageId != null && String(catId) === String(pageId)) {
|
|
1189
|
+
title = Helper.escapeHtml(salla.config.get('page.title'));
|
|
1190
|
+
}
|
|
1184
1191
|
}
|
|
1185
1192
|
title +=
|
|
1186
1193
|
(title ? ' - ' : '') +
|
|
@@ -47,6 +47,9 @@ export class SallaQuickBuy {
|
|
|
47
47
|
/** Session from the initial gesture; begun later when there are no order options. */
|
|
48
48
|
this.pendingApplePaySession = null;
|
|
49
49
|
this.quickBuy = salla.lang.get('pages.products.buy_now');
|
|
50
|
+
/** The guest picked "continue as guest" on the login step, so don't ask again. */
|
|
51
|
+
this.choseGuest = false;
|
|
52
|
+
this.cancelLoginAsk = null;
|
|
50
53
|
/** Apple Pay onStartedSession: first call defers begin() to run the OO check; second (resolved) begins. */
|
|
51
54
|
this.handleApplePaySessionStarted = (session) => {
|
|
52
55
|
// Previously gated on `order_options.mandatory`, which is unrelated to CP-2836: a store with
|
|
@@ -114,6 +117,7 @@ export class SallaQuickBuy {
|
|
|
114
117
|
salla?.event?.off?.('product-options::donation-changed', this.donationChangedListener);
|
|
115
118
|
salla?.product?.event?.offPriceUpdated?.(this.priceUpdatedListener);
|
|
116
119
|
this.langUnsubscribe?.();
|
|
120
|
+
this.cancelLoginAsk?.();
|
|
117
121
|
}
|
|
118
122
|
quickBuyHandler() {
|
|
119
123
|
if (salla.config.isGuest() && !this.isGuestCheckout()) {
|
|
@@ -121,6 +125,10 @@ export class SallaQuickBuy {
|
|
|
121
125
|
salla.api.auth.requireLoginThen('salla-quick-buy::user.logged-in', () => this.quickBuyHandler());
|
|
122
126
|
return;
|
|
123
127
|
}
|
|
128
|
+
if (this.shouldAskLoginFirst()) {
|
|
129
|
+
this.askLoginFirst();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
124
132
|
// Sync validate — AP needs the gesture preserved for new ApplePaySession().
|
|
125
133
|
if (!this.validatePdpOptionsSync())
|
|
126
134
|
return;
|
|
@@ -188,7 +196,10 @@ export class SallaQuickBuy {
|
|
|
188
196
|
injectFreeProductsIntoPayload(data, this.collectedFreeProducts);
|
|
189
197
|
// if the store doesn't have Apple Pay , just create a cart and then redirect to check out page
|
|
190
198
|
if (!this.isApplePayActive) {
|
|
191
|
-
|
|
199
|
+
// tells checkout the guest already chose guest, so it opens on the guest form —
|
|
200
|
+
// re-check isGuest(): a login since the choice must not send guest params authenticated
|
|
201
|
+
const options = this.choseGuest && salla.config.isGuest() ? { params: { guest_checkout: 1, chose_guest: 1 } } : {};
|
|
202
|
+
return salla.api.request('checkout/mini', data, 'post', options)
|
|
192
203
|
.catch(err => this.legacyQuickPurchaseFallback(err, data))
|
|
193
204
|
.then(resp => {
|
|
194
205
|
// mini-checkout answers {to, url}; legacy quick-purchase answered {redirect}
|
|
@@ -313,6 +324,38 @@ export class SallaQuickBuy {
|
|
|
313
324
|
salla.logger.warn('quick-buy: falling back to quick-purchase because of token_not_provided');
|
|
314
325
|
return salla.api.request('checkout/quick-purchase/' + this.productId, data, 'post');
|
|
315
326
|
}
|
|
327
|
+
/** login-before-checkout variant: offer login or guest before buy now. Not for Apple Pay, which must open on the tap. */
|
|
328
|
+
shouldAskLoginFirst() {
|
|
329
|
+
return salla.config.isGuest()
|
|
330
|
+
&& !this.choseGuest
|
|
331
|
+
&& !this.isApplePayActive
|
|
332
|
+
&& salla.cart.loginBeforeCheckoutGroup?.() === 'variant';
|
|
333
|
+
}
|
|
334
|
+
askLoginFirst() {
|
|
335
|
+
// a second click before the popup settles must replace the pending ask, not stack a listener
|
|
336
|
+
this.cancelLoginAsk?.();
|
|
337
|
+
const stop = () => {
|
|
338
|
+
salla.event.off('salla-login::guest-chosen', onGuest);
|
|
339
|
+
salla.event.off('salla-login::dismissed', stop);
|
|
340
|
+
this.cancelLoginAsk = null;
|
|
341
|
+
};
|
|
342
|
+
this.cancelLoginAsk = stop;
|
|
343
|
+
const onGuest = (detail) => {
|
|
344
|
+
if (detail?.source !== 'buy-now')
|
|
345
|
+
return;
|
|
346
|
+
stop();
|
|
347
|
+
this.choseGuest = true;
|
|
348
|
+
this.quickBuyHandler();
|
|
349
|
+
};
|
|
350
|
+
salla.event.on('salla-login::guest-chosen', onGuest);
|
|
351
|
+
salla.event.on('salla-login::dismissed', stop);
|
|
352
|
+
this.requireLogin.emit({ productId: this.productId });
|
|
353
|
+
salla.api.auth.requireLoginThen('salla-quick-buy::user.logged-in', () => {
|
|
354
|
+
stop();
|
|
355
|
+
this.choseGuest = false;
|
|
356
|
+
this.quickBuyHandler();
|
|
357
|
+
}, null, 'buy-now');
|
|
358
|
+
}
|
|
316
359
|
isGuestCheckout() {
|
|
317
360
|
return salla.config.isGuest() && this.isGuestCheckoutAllowed() && this.isPhysicalProduct();
|
|
318
361
|
}
|
|
@@ -21,6 +21,8 @@ async function withApiErrorHandling(fn, fallback, logLabel) {
|
|
|
21
21
|
}
|
|
22
22
|
const apiCache = new Map();
|
|
23
23
|
const CACHE_TTL = 5 * 60 * 1000;
|
|
24
|
+
let branchCacheGeneration = 0;
|
|
25
|
+
const resolvedBranchPages = new WeakMap();
|
|
24
26
|
function getCached(key) {
|
|
25
27
|
const entry = apiCache.get(key);
|
|
26
28
|
if (!entry || Date.now() - entry.timestamp > CACHE_TTL) {
|
|
@@ -32,6 +34,47 @@ function getCached(key) {
|
|
|
32
34
|
function setCache(key, data) {
|
|
33
35
|
apiCache.set(key, { data, timestamp: Date.now() });
|
|
34
36
|
}
|
|
37
|
+
function getCountryBranches(branches, countryId) {
|
|
38
|
+
if (!branches || countryId == null)
|
|
39
|
+
return branches;
|
|
40
|
+
if (branches.some(branch => branch.country?.id == null))
|
|
41
|
+
return null;
|
|
42
|
+
const matches = branches.filter(branch => String(branch.country?.id) === String(countryId));
|
|
43
|
+
return matches.length ? matches : null;
|
|
44
|
+
}
|
|
45
|
+
function getBranchPage({ query, lat, lng, country_id, city_id, page, per_page = 20 }, cachePage = true) {
|
|
46
|
+
const params = { per_page };
|
|
47
|
+
if (query && query.trim().length >= 2)
|
|
48
|
+
params.query = query;
|
|
49
|
+
if (lat)
|
|
50
|
+
params.lat = Number(lat).toFixed(6);
|
|
51
|
+
if (lng)
|
|
52
|
+
params.lng = Number(lng).toFixed(6);
|
|
53
|
+
if (country_id)
|
|
54
|
+
params.country_id = country_id;
|
|
55
|
+
if (city_id)
|
|
56
|
+
params.city_id = city_id;
|
|
57
|
+
if (page && page > 1)
|
|
58
|
+
params.page = page;
|
|
59
|
+
const cacheKey = `branches_${JSON.stringify(params)}`;
|
|
60
|
+
const cached = cachePage ? getCached(cacheKey) : null;
|
|
61
|
+
if (cached)
|
|
62
|
+
return cached;
|
|
63
|
+
const request = (async () => {
|
|
64
|
+
const res = await salla.api.request('branches', { params });
|
|
65
|
+
if (!Array.isArray(res?.data))
|
|
66
|
+
throw new Error('Invalid branches response');
|
|
67
|
+
return { branches: res.data, perPage: per_page, pagination: res.pagination };
|
|
68
|
+
})();
|
|
69
|
+
if (cachePage)
|
|
70
|
+
setCache(cacheKey, request);
|
|
71
|
+
void request.then(page => { resolvedBranchPages.set(request, page); }, () => { });
|
|
72
|
+
void request.catch(() => {
|
|
73
|
+
if (apiCache.get(cacheKey)?.data === request)
|
|
74
|
+
apiCache.delete(cacheKey);
|
|
75
|
+
});
|
|
76
|
+
return request;
|
|
77
|
+
}
|
|
35
78
|
/** Uses country code from API (e.g. 'SA'). */
|
|
36
79
|
const isSaudiArabia = (countryCode) => String(countryCode).toUpperCase() === 'SA';
|
|
37
80
|
/**
|
|
@@ -141,30 +184,105 @@ const bulletDeliveryAPI = {
|
|
|
141
184
|
return Array.isArray(data) ? data : [];
|
|
142
185
|
}, [], 'Error fetching user addresses');
|
|
143
186
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
187
|
+
/** Complete country rows for the city picker; incomplete reads remain retryable. */
|
|
188
|
+
async getAllBranches(country_id, per_page = 20) {
|
|
189
|
+
const collection = await this.getBranchCollection(country_id, per_page, { maxPages: Number.POSITIVE_INFINITY });
|
|
190
|
+
if (!collection.complete)
|
|
191
|
+
throw Object.assign(new Error('Incomplete branch city collection'), { branches: collection.branches });
|
|
192
|
+
return collection.branches;
|
|
193
|
+
},
|
|
194
|
+
/** Keeps loaded branches separate from whether the collection is complete. */
|
|
195
|
+
async getBranchCollection(country_id, per_page = 20, { maxPages = 10, requireComplete = false } = {}) {
|
|
196
|
+
const generation = branchCacheGeneration;
|
|
197
|
+
const cacheKey = `branches_all_${JSON.stringify([country_id ?? null, per_page, maxPages, requireComplete])}`;
|
|
198
|
+
const cached = Number.isFinite(maxPages) ? getCached(cacheKey) : null;
|
|
199
|
+
if (cached)
|
|
200
|
+
return cached;
|
|
201
|
+
const storeBranches = country_id != null ? getCached('branches_all') : null;
|
|
202
|
+
if (country_id == null)
|
|
203
|
+
setCache('branches_first', getBranchPage({ per_page, page: 1 }));
|
|
204
|
+
const partial = { branches: [], complete: false };
|
|
205
|
+
const request = withApiErrorHandling(async () => {
|
|
206
|
+
const sharedFirstPage = storeBranches ? getCached('branches_first') : null;
|
|
207
|
+
const first = sharedFirstPage ? resolvedBranchPages.get(sharedFirstPage) : null;
|
|
208
|
+
if (storeBranches && getCountryBranches(first?.branches ?? null, country_id)) {
|
|
209
|
+
const collection = await storeBranches;
|
|
210
|
+
if (generation !== branchCacheGeneration)
|
|
211
|
+
return { branches: [], complete: false };
|
|
212
|
+
const branches = collection.complete ? getCountryBranches(collection.branches, country_id) : null;
|
|
213
|
+
if (branches)
|
|
214
|
+
return { branches, complete: true };
|
|
215
|
+
}
|
|
216
|
+
for (let page = 1; page <= maxPages; page++) {
|
|
217
|
+
const { branches, pagination } = await getBranchPage({ country_id, per_page, page }, Number.isFinite(maxPages) || page === 1);
|
|
218
|
+
if (generation !== branchCacheGeneration)
|
|
219
|
+
return { branches: [], complete: false };
|
|
220
|
+
const currentPage = Number(pagination?.currentPage ?? page);
|
|
221
|
+
const totalPages = Number(pagination?.totalPages);
|
|
222
|
+
if (page === 1 && totalPages === 0 && !branches.length)
|
|
223
|
+
return { branches: [], complete: true };
|
|
224
|
+
if (pagination && (currentPage !== page || !Number.isInteger(totalPages) || totalPages < page)) {
|
|
225
|
+
throw new Error('Invalid branches pagination');
|
|
226
|
+
}
|
|
227
|
+
partial.branches.push(...branches);
|
|
228
|
+
if (pagination) {
|
|
229
|
+
if (requireComplete && totalPages > maxPages)
|
|
230
|
+
return partial;
|
|
231
|
+
if (page === totalPages)
|
|
232
|
+
return { branches: partial.branches, complete: true };
|
|
233
|
+
if (!branches.length)
|
|
234
|
+
throw new Error('Incomplete branches response');
|
|
235
|
+
}
|
|
236
|
+
else if (branches.length < per_page) {
|
|
237
|
+
return { branches: partial.branches, complete: true };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return partial;
|
|
241
|
+
}, partial, 'Error fetching all branches');
|
|
242
|
+
setCache(cacheKey, request);
|
|
243
|
+
if (country_id == null)
|
|
244
|
+
setCache('branches_all', request);
|
|
245
|
+
void request.then(collection => {
|
|
246
|
+
if (collection.complete) {
|
|
247
|
+
if (generation === branchCacheGeneration && apiCache.get('branches_all')?.data === request) {
|
|
248
|
+
setCache('branches_complete', collection.branches);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (apiCache.get(cacheKey)?.data === request)
|
|
253
|
+
apiCache.delete(cacheKey);
|
|
254
|
+
if (apiCache.get('branches_all')?.data === request)
|
|
255
|
+
apiCache.delete('branches_all');
|
|
256
|
+
});
|
|
257
|
+
return request;
|
|
258
|
+
},
|
|
259
|
+
async getBranches(filters = {}) {
|
|
260
|
+
const generation = branchCacheGeneration;
|
|
261
|
+
const hasSearchParams = (filters.query && filters.query.trim().length >= 2) || filters.lat || filters.lng || filters.city_id;
|
|
262
|
+
const perPage = filters.per_page ?? 20;
|
|
263
|
+
const offset = ((filters.page ?? 1) - 1) * perPage;
|
|
264
|
+
if (!hasSearchParams) {
|
|
265
|
+
const branches = getCountryBranches(getCached('branches_complete'), filters.country_id);
|
|
266
|
+
if (branches) {
|
|
267
|
+
return branches.slice(offset, offset + perPage);
|
|
268
|
+
}
|
|
269
|
+
const firstPage = getCached('branches_first');
|
|
270
|
+
if (firstPage) {
|
|
271
|
+
const result = resolvedBranchPages.get(firstPage);
|
|
272
|
+
if (generation !== branchCacheGeneration)
|
|
273
|
+
return [];
|
|
274
|
+
if (result) {
|
|
275
|
+
const first = getCountryBranches(result.branches, filters.country_id);
|
|
276
|
+
const complete = result.pagination
|
|
277
|
+
? Number(result.pagination.totalPages) === 1
|
|
278
|
+
: result.branches.length < result.perPage;
|
|
279
|
+
if (first && (first.length >= offset + perPage || complete)) {
|
|
280
|
+
return first.slice(offset, offset + perPage);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
151
284
|
}
|
|
152
|
-
|
|
153
|
-
const params = { per_page };
|
|
154
|
-
if (query?.trim().length >= 2)
|
|
155
|
-
params.query = query;
|
|
156
|
-
if (lat)
|
|
157
|
-
params.lat = lat;
|
|
158
|
-
if (lng)
|
|
159
|
-
params.lng = lng;
|
|
160
|
-
if (country_id)
|
|
161
|
-
params.country_id = country_id;
|
|
162
|
-
const res = await salla.api.request("branches", { params });
|
|
163
|
-
return (Array.isArray(res?.data) ? res.data : []);
|
|
164
|
-
}, [], 'Error fetching branches');
|
|
165
|
-
if (cacheKey && result.length > 0)
|
|
166
|
-
setCache(cacheKey, result);
|
|
167
|
-
return result;
|
|
285
|
+
return withApiErrorHandling(async () => (await getBranchPage(filters)).branches, [], 'Error fetching branches');
|
|
168
286
|
},
|
|
169
287
|
async saveAddressLocation(payload) {
|
|
170
288
|
return withApiErrorHandling(async () => {
|
|
@@ -182,7 +300,12 @@ const bulletDeliveryAPI = {
|
|
|
182
300
|
}, false, 'Error setting delivery scope');
|
|
183
301
|
},
|
|
184
302
|
async allocateScope(payload) {
|
|
185
|
-
|
|
303
|
+
// A field reason (e.g. branch_id "not accepting orders") is more useful than the generic "invalid fields" message.
|
|
304
|
+
const errMsg = (d) => {
|
|
305
|
+
const body = d;
|
|
306
|
+
const fieldReason = Object.values(body?.error?.fields ?? {}).flat().find((m) => typeof m === 'string' && m.trim());
|
|
307
|
+
return fieldReason ?? body?.error?.message ?? body?.message ?? 'Failed to allocate scope';
|
|
308
|
+
};
|
|
186
309
|
try {
|
|
187
310
|
const response = await salla.api.withoutNotifier(() => salla.api.request("scopes/allocation", payload, 'post'));
|
|
188
311
|
// SDK resolves with { data } and throws on non-2xx; any resolved value is success
|
|
@@ -203,8 +326,16 @@ const bulletDeliveryAPI = {
|
|
|
203
326
|
},
|
|
204
327
|
};
|
|
205
328
|
function clearApiCache() {
|
|
329
|
+
branchCacheGeneration++;
|
|
206
330
|
apiCache.clear();
|
|
207
331
|
}
|
|
332
|
+
function clearBranchCache() {
|
|
333
|
+
branchCacheGeneration++;
|
|
334
|
+
for (const key of apiCache.keys()) {
|
|
335
|
+
if (key.startsWith('branches_'))
|
|
336
|
+
apiCache.delete(key);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
208
339
|
|
|
209
340
|
/**
|
|
210
341
|
* Helper functions for salla-bullet-delivery component
|
|
@@ -233,13 +364,153 @@ const isBranchSelectable = (branch) => {
|
|
|
233
364
|
const selectable = branch.pickup_readiness?.selectable;
|
|
234
365
|
return typeof selectable === 'boolean' ? selectable : branch.is_open !== false;
|
|
235
366
|
};
|
|
236
|
-
/**
|
|
367
|
+
/** Backend readiness label ("استلمه غدًا", "جاهز {date}") when pickup is not today; null otherwise. */
|
|
368
|
+
const getBranchReadinessLabel = (branch) => {
|
|
369
|
+
const readiness = branch?.pickup_readiness;
|
|
370
|
+
return readiness?.label && ['tomorrow', 'later'].includes(readiness.state) ? readiness.label : null;
|
|
371
|
+
};
|
|
372
|
+
/** Branch-clock "HH:mm" a branch that is closed now becomes ready at later today; null otherwise. */
|
|
373
|
+
const getBranchReadyTodayTime = (branch) => {
|
|
374
|
+
const readiness = branch?.pickup_readiness;
|
|
375
|
+
if (readiness?.state !== 'same_day' || branch?.is_open !== false)
|
|
376
|
+
return null;
|
|
377
|
+
return readiness.ready_at?.match(/T(\d{2}:\d{2})/)?.[1] ?? null;
|
|
378
|
+
};
|
|
379
|
+
/** Busy branches (stock at the merchant's stop-orders threshold) stay listed but dimmed and unselectable. */
|
|
380
|
+
const isBranchBusy = (branch) => branch?.pickup_readiness?.state === 'orders_paused';
|
|
381
|
+
/** Distinct cities of a branch list, in first-seen order (the API lists nearest first). */
|
|
382
|
+
const getBranchCities = (branches) => {
|
|
383
|
+
const seen = new Set();
|
|
384
|
+
const cities = [];
|
|
385
|
+
for (const { city } of branches) {
|
|
386
|
+
if (!city?.id || seen.has(String(city.id)))
|
|
387
|
+
continue;
|
|
388
|
+
seen.add(String(city.id));
|
|
389
|
+
cities.push({ id: city.id, name: city.name });
|
|
390
|
+
}
|
|
391
|
+
return cities;
|
|
392
|
+
};
|
|
393
|
+
/** "3.52" / "12" — at most two decimals, as the checkout picker shows it; '' when the distance is unknown. */
|
|
394
|
+
const formatBranchDistance = (km) => {
|
|
395
|
+
if (km == null || Number.isNaN(Number(km)))
|
|
396
|
+
return '';
|
|
397
|
+
return String(Math.round(Number(km) * 100) / 100);
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Readiness states the picker hides outright instead of showing as a disabled card: closed and not
|
|
401
|
+
* taking pickup orders at all. Busy/out-of-stock stay visible-but-disabled so the shopper sees why
|
|
402
|
+
* (same rule as the checkout picker, INV-1414).
|
|
403
|
+
*/
|
|
404
|
+
const HIDDEN_READINESS_STATES = ['closed', 'unavailable'];
|
|
405
|
+
const isBranchVisible = (branch) => !!branch && !HIDDEN_READINESS_STATES.includes(branch.pickup_readiness?.state);
|
|
406
|
+
/** The branch's pickup offer (INV-1416); null when it has none, or a zero one, so "خصم 0%" never shows. */
|
|
407
|
+
const getBranchDiscount = (branch) => {
|
|
408
|
+
const type = branch?.discount?.type;
|
|
409
|
+
const amount = Number(branch?.discount?.amount);
|
|
410
|
+
if ((type !== 'percentage' && type !== 'fixed_amount') || !(amount > 0))
|
|
411
|
+
return null;
|
|
412
|
+
return { type, amount };
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Min–max pickup offer across the listed branches, for the pickup tab badge; null when none has one.
|
|
416
|
+
* Branches the picker hides are left out so the tab never advertises an offer the shopper can't pick.
|
|
417
|
+
* A percentage and a fixed amount can't share one range, so percentages win when both are set.
|
|
418
|
+
*/
|
|
419
|
+
const getBranchDiscountRange = (branches) => {
|
|
420
|
+
const discounts = branches.filter(isBranchVisible).map(getBranchDiscount).filter((d) => !!d);
|
|
421
|
+
if (!discounts.length)
|
|
422
|
+
return null;
|
|
423
|
+
const type = discounts.some(d => d.type === 'percentage') ? 'percentage' : 'fixed_amount';
|
|
424
|
+
const amounts = discounts.filter(d => d.type === type).map(d => d.amount);
|
|
425
|
+
return { type, min: Math.min(...amounts), max: Math.max(...amounts) };
|
|
426
|
+
};
|
|
427
|
+
/** The storefront's working_hours and ready_at use 24h "HH:mm"; null for anything else. */
|
|
428
|
+
const parseBranchTime = (time) => {
|
|
429
|
+
const match = String(time ?? '').trim().match(/^(\d{1,2}):(\d{2})(?::\d{2})?$/);
|
|
430
|
+
if (!match || Number(match[1]) > 24)
|
|
431
|
+
return null;
|
|
432
|
+
return { hours: Number(match[1]) % 24, minutes: Number(match[2]) };
|
|
433
|
+
};
|
|
434
|
+
/** Date#getDay() keys for the day names the storefront API may send (Arabic or English). */
|
|
435
|
+
const DAY_KEYS = {
|
|
436
|
+
sunday: 0, 'الاحد': 0,
|
|
437
|
+
monday: 1, 'الاثنين': 1,
|
|
438
|
+
tuesday: 2, 'الثلاثاء': 2,
|
|
439
|
+
wednesday: 3, 'الاربعاء': 3,
|
|
440
|
+
thursday: 4, 'الخميس': 4,
|
|
441
|
+
friday: 5, 'الجمعة': 5,
|
|
442
|
+
saturday: 6, 'السبت': 6,
|
|
443
|
+
};
|
|
444
|
+
const dayKeyFromName = (name) => DAY_KEYS[String(name ?? '').trim().toLowerCase().replace(/[أإآ]/g, 'ا')];
|
|
445
|
+
/**
|
|
446
|
+
* The storefront's `working_hours` (`{ name, times: [{ from, to }] }`, 24h times) as weekly
|
|
447
|
+
* schedule rows for the hours dropdown.
|
|
448
|
+
*/
|
|
449
|
+
const getBranchWeekSchedule = (branch) => (branch.working_hours || [])
|
|
450
|
+
.filter(day => day?.name)
|
|
451
|
+
.map(day => ({
|
|
452
|
+
day_key: dayKeyFromName(day.name),
|
|
453
|
+
day: day.name,
|
|
454
|
+
times: (day.times || []).map(t => ({ start: t.from, end: t.to })),
|
|
455
|
+
}));
|
|
456
|
+
const isBranchAlwaysOpen = (branch) => !!branch.always_open;
|
|
457
|
+
/**
|
|
458
|
+
* "11:00 م" / "11:00 PM". ar-SA's ICU pattern puts the day period first, which the bidi algorithm
|
|
459
|
+
* scrambles inside RTL text, so the parts are joined in a fixed "h:mm period" order instead.
|
|
460
|
+
*/
|
|
461
|
+
const formatBranchClockTime = (time, locale) => {
|
|
462
|
+
const parsed = parseBranchTime(time);
|
|
463
|
+
if (!parsed)
|
|
464
|
+
return '';
|
|
465
|
+
const date = new Date(Date.UTC(2000, 0, 1, parsed.hours, parsed.minutes));
|
|
466
|
+
const parts = new Intl.DateTimeFormat(locale === 'ar' ? 'ar-SA-u-nu-latn' : 'en', {
|
|
467
|
+
timeZone: 'UTC',
|
|
468
|
+
hour: '2-digit',
|
|
469
|
+
minute: '2-digit',
|
|
470
|
+
hour12: true,
|
|
471
|
+
}).formatToParts(date);
|
|
472
|
+
const get = (type) => parts.find(p => p.type === type)?.value ?? '';
|
|
473
|
+
return `${get('hour')}:${get('minute')} ${get('dayPeriod')}`;
|
|
474
|
+
};
|
|
475
|
+
const sameTimes = (a, b) => a.length === b.length && a.every((t, i) => t.start === b[i].start && t.end === b[i].end);
|
|
476
|
+
/** Consecutive days with identical hours collapse into one row; the largest groups come first. */
|
|
477
|
+
const groupBranchSchedule = (weekSchedule) => {
|
|
478
|
+
const groups = [];
|
|
479
|
+
const days = [...(weekSchedule || [])].filter(s => s.times?.length).sort((a, b) => (a.day_key ?? 0) - (b.day_key ?? 0));
|
|
480
|
+
for (const day of days) {
|
|
481
|
+
const last = groups[groups.length - 1];
|
|
482
|
+
if (last && day.day_key === last.lastDayKey + 1 && sameTimes(last.times, day.times)) {
|
|
483
|
+
last.days.push(day.day);
|
|
484
|
+
last.lastDayKey = day.day_key;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
groups.push({ days: [day.day], times: day.times, lastDayKey: day.day_key ?? 0 });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return groups.sort((a, b) => b.days.length - a.days.length).map(({ days, times }) => ({ days, times }));
|
|
491
|
+
};
|
|
492
|
+
/** Keeps the digits and a leading "+" the merchant typed; drops spaces, dashes and brackets. */
|
|
493
|
+
const cleanPhone = (value) => String(value ?? '').trim().replace(/[^\d+]/g, '');
|
|
494
|
+
const getBranchPhoneHref = (branch) => {
|
|
495
|
+
const phone = cleanPhone(branch.contacts?.phone);
|
|
496
|
+
return phone ? `tel:${phone}` : null;
|
|
497
|
+
};
|
|
498
|
+
/** wa.me wants the international number as bare digits ("+966…" / "00966…" → "966…"). */
|
|
499
|
+
const getBranchWhatsappHref = (branch) => {
|
|
500
|
+
const digits = cleanPhone(branch.contacts?.whatsapp).replace(/^\+/, '').replace(/^00/, '').replace(/\D/g, '');
|
|
501
|
+
return digits ? `https://wa.me/${digits}` : null;
|
|
502
|
+
};
|
|
503
|
+
const getBranchDirectionsHref = (branch) => branch.location?.lat != null && branch.location?.lng != null
|
|
504
|
+
? `https://www.google.com/maps?q=${branch.location.lat},${branch.location.lng}`
|
|
505
|
+
: null;
|
|
506
|
+
/** Nearest selectable branch by distance; single pass O(n). */
|
|
237
507
|
function findNearestBranch(branches, userLat, userLon) {
|
|
238
508
|
let nearest = null;
|
|
239
509
|
let minD = Number.POSITIVE_INFINITY;
|
|
240
510
|
for (const b of branches) {
|
|
241
511
|
const lat = b.location?.lat != null ? Number(b.location.lat) : Number.NaN;
|
|
242
512
|
const lng = b.location?.lng != null ? Number(b.location.lng) : Number.NaN;
|
|
513
|
+
// Closed branches that still take orders are selectable too, so gate on selectability, not is_open.
|
|
243
514
|
if (Number.isNaN(lat) || Number.isNaN(lng) || !isBranchSelectable(b))
|
|
244
515
|
continue;
|
|
245
516
|
const d = calculateDistance(userLat, userLon, lat, lng);
|
|
@@ -250,11 +521,12 @@ function findNearestBranch(branches, userLat, userLon) {
|
|
|
250
521
|
}
|
|
251
522
|
return nearest;
|
|
252
523
|
}
|
|
524
|
+
/** Matches the fields the branches API searches: branch, city and country names (either language). */
|
|
253
525
|
const filterBranches = (branches, searchQuery) => {
|
|
254
526
|
if (!searchQuery)
|
|
255
527
|
return branches;
|
|
256
528
|
const q = searchQuery.toLowerCase();
|
|
257
|
-
return branches.filter((b) => b.name.
|
|
529
|
+
return branches.filter((b) => [b.name, b.city?.name, b.city?.name_en, b.country?.name, b.country?.name_en].some((name) => name?.toLowerCase().includes(q)));
|
|
258
530
|
};
|
|
259
531
|
const getIntentSubtitle = (intent, toAddress, fromBranch) => {
|
|
260
532
|
if (intent.type === 'branch') {
|
|
@@ -274,9 +546,6 @@ const getIntentRegionId = (i) => idFrom(i?.address_details?.region?.id);
|
|
|
274
546
|
const getIntentCityId = (i) => idFrom(i?.address_details?.city?.id);
|
|
275
547
|
const getIntentDistrictId = (i) => idFrom(i?.address_details?.district?.id);
|
|
276
548
|
const getIntentBranchId = (i) => i?.branch_details?.id;
|
|
277
|
-
/** Intent coordinates (branch tab only; address flow no longer uses coordinates). */
|
|
278
|
-
const getIntentLatitude = (i) => i?.branch_details?.latitude;
|
|
279
|
-
const getIntentLongitude = (i) => i?.branch_details?.longitude;
|
|
280
549
|
const isMapOriginIntent = (i) => !!i && i.type === 'address' && i.address_details?.lat != null && i.address_details?.lng != null;
|
|
281
550
|
const hasSessionAddressIntent = (intent) => {
|
|
282
551
|
if (!intent || intent.type !== 'address' || intent.address_id)
|
|
@@ -295,15 +564,5 @@ const hasSessionAddressIntent = (intent) => {
|
|
|
295
564
|
const requireRegionAndDistrictForSA = (countryCode, regionId, districtIdOrName) => !countryCode || !isSaudiArabia(countryCode)
|
|
296
565
|
? true
|
|
297
566
|
: (regionId != null && Number(regionId) !== 0) && (districtIdOrName != null && (typeof districtIdOrName === 'number' ? districtIdOrName !== 0 : String(districtIdOrName).trim().length > 0));
|
|
298
|
-
// Geolocation error codes - fallback constants for non-browser environments
|
|
299
|
-
const GEO_ERROR_PERMISSION_DENIED = typeof GeolocationPositionError !== 'undefined' ? GeolocationPositionError.PERMISSION_DENIED : 1;
|
|
300
|
-
const GEO_ERROR_POSITION_UNAVAILABLE = typeof GeolocationPositionError !== 'undefined' ? GeolocationPositionError.POSITION_UNAVAILABLE : 2;
|
|
301
|
-
const GEO_ERROR_TIMEOUT = typeof GeolocationPositionError !== 'undefined' ? GeolocationPositionError.TIMEOUT : 3;
|
|
302
|
-
const GEO_ERROR_MESSAGES = {
|
|
303
|
-
[GEO_ERROR_PERMISSION_DENIED]: 'Location permission denied',
|
|
304
|
-
[GEO_ERROR_POSITION_UNAVAILABLE]: 'Location unavailable',
|
|
305
|
-
[GEO_ERROR_TIMEOUT]: 'Location timeout',
|
|
306
|
-
};
|
|
307
|
-
const getGeolocationErrorMessage = (errorCode) => GEO_ERROR_MESSAGES[errorCode] ?? 'An error occurred while detecting location';
|
|
308
567
|
|
|
309
|
-
export {
|
|
568
|
+
export { getBranchDirectionsHref as A, formatBranchClockTime as B, CACHE_TTL as C, getBranchReadyTodayTime as D, isBranchBusy as E, isBranchAlwaysOpen as F, getIntentCountryCode as G, getIntentCountryId as a, bulletDeliveryAPI as b, clearBranchCache as c, getIntentRegionId as d, getIntentDistrictId as e, getBranchCities as f, getIntentCityId as g, hasSessionAddressIntent as h, isSaudiArabia as i, isBranchSelectable as j, getBranchDiscountRange as k, filterBranches as l, getIntentBranchId as m, findNearestBranch as n, clearApiCache as o, getIntentSubtitle as p, isMapOriginIntent as q, requireRegionAndDistrictForSA as r, isBranchVisible as s, getBranchReadinessLabel as t, groupBranchSchedule as u, getBranchWeekSchedule as v, getBranchPhoneHref as w, getBranchWhatsappHref as x, getBranchDiscount as y, formatBranchDistance as z };
|