UNPKG

@ecomplus/storefront-components

Version:
352 lines (326 loc) 9.37 kB
import { i19add$1ToEarn, i19calculateShipping, i19freeShipping, i19zipCode } from '@ecomplus/i18n' import { $ecomConfig, i18n, price as getPrice, formatMoney } from '@ecomplus/utils' import { modules } from '@ecomplus/client' import sortApps from './helpers/sort-apps' import CleaveInput from 'vue-cleave-component' import ShippingLine from '../ShippingLine.vue' const localStorage = typeof window === 'object' && window.localStorage const zipStorageKey = 'shipping-to-zip' const globalOpts = (typeof window === 'object' && window.propsShippingCalculator) || {} const reduceItemBody = itemOrProduct => { const shippedItem = {} ;[ 'product_id', 'variation_id', 'sku', 'name', 'quantity', 'inventory', 'currency_id', 'currency_symbol', 'price', 'final_price', 'dimensions', 'weight' ].forEach(field => { if (itemOrProduct[field] !== undefined) { shippedItem[field] = itemOrProduct[field] } }) return shippedItem } export default { name: 'ShippingCalculator', components: { CleaveInput, ShippingLine }, props: { zipCode: String, canSelectServices: Boolean, canAutoSelectService: { type: Boolean, default: typeof globalOpts.canAutoSelectService === 'boolean' ? globalOpts.canAutoSelectService : true }, canInputZip: { type: Boolean, default: typeof globalOpts.canInputZip === 'boolean' ? globalOpts.canInputZip : true }, countryCode: { type: String, default: $ecomConfig.get('country_code') }, shippedItems: { type: Array, default () { return [] } }, shippingResult: { type: Array, default () { return [] } }, shippingData: { type: Object, default () { return {} } }, skipAppIds: Array, shippingAppsSort: { type: Array, default () { return window.ecomShippingApps || [] } } }, data () { return { localZipCode: null, localShippedItems: [], amountSubtotal: null, shippingServices: [], selectedService: null, hasPaidOption: false, freeFromValue: null, isScheduled: false, retryTimer: null, isWaiting: false, hasCalculated: false } }, computed: { i19add$1ToEarn: () => i18n(i19add$1ToEarn), i19calculateShipping: () => i18n(i19calculateShipping), i19zipCode: () => i18n(i19zipCode), i19freeShipping: () => i18n(i19freeShipping).toLowerCase(), i19selectShippingMsg: () => i18n({ pt_br: 'Selecione uma forma de envio abaixo', en_us: 'Select a shipping method below' }), cleaveOptions () { return this.countryCode === 'BR' ? { blocks: [5, 3], delimiter: '-' } : { blocks: [30] } }, freeFromPercentage () { return this.hasPaidOption && this.amountSubtotal < this.freeFromValue ? Math.round(this.amountSubtotal * 100 / this.freeFromValue) : null }, productionDeadline () { let maxDeadline = 0 this.shippedItems.forEach(item => { if (item.quantity && item.production_time) { const { days, cumulative } = item.production_time const itemDeadline = cumulative ? days * item.quantity : days if (itemDeadline > maxDeadline) { maxDeadline = itemDeadline } } }) return maxDeadline } }, methods: { formatMoney, updateZipCode () { this.$emit('update:zip-code', this.localZipCode) }, parseShippingOptions (shippingResult = [], isRetry = false) { this.freeFromValue = null this.shippingServices = [] if (shippingResult.length) { shippingResult.forEach(appResult => { const { validated, error, response } = appResult if (!validated || error) { return } if ( this.skipAppIds && this.skipAppIds.includes(appResult.app_id) && shippingResult.filter(({ app_id: appId }) => !this.skipAppIds.includes(appId)).length ) { return } response.shipping_services.forEach(service => { this.shippingServices.push({ app_id: appResult.app_id, ...service }) }) const freeShippingFromValue = response.free_shipping_from_value if ( freeShippingFromValue && (!this.freeFromValue || this.freeFromValue > freeShippingFromValue) ) { this.freeFromValue = freeShippingFromValue } }) if (!this.shippingServices.length) { if (!isRetry) { this.fetchShippingServices(true) } else { this.scheduleRetry() } } else { this.shippingServices = this.shippingServices.sort((a, b) => { const priceDiff = a.shipping_line.total_price - b.shipping_line.total_price return priceDiff < 0 ? -1 : priceDiff > 0 ? 1 : a.shipping_line.delivery_time && b.shipping_line.delivery_time && a.shipping_line.delivery_time.days < b.shipping_line.delivery_time.days ? -1 : 1 }) if (this.canAutoSelectService) { this.setSelectedService(0) } else { this.selectedService = null } this.hasPaidOption = Boolean(this.shippingServices.find(service => { return service.shipping_line.total_price || service.shipping_line.price })) if (Array.isArray(this.shippingAppsSort) && this.shippingAppsSort.length) { this.shippingServices = sortApps(this.shippingServices, this.shippingAppsSort) } } } }, scheduleRetry (timeout = 10000) { clearTimeout(this.retryTimer) this.retryTimer = setTimeout(() => { if (this.localZipCode && !this.shippingServices.length && this.shippedItems.length) { this.fetchShippingServices(true) } }, timeout) }, fetchShippingServices (isRetry) { if (!this.isScheduled) { this.isScheduled = true setTimeout(() => { this.isScheduled = false const { storeId } = this let url = '/calculate_shipping.json' if (this.skipAppIds && this.skipAppIds.length) { url += '?skip_ids=' this.skipAppIds.forEach((appId, i) => { if (i > 0) url += ',' url += `${appId}` }) } const method = 'POST' const data = { ...this.shippingData, to: { zip: this.localZipCode, ...this.shippingData.to } } if (this.localShippedItems.length) { data.items = this.localShippedItems data.subtotal = this.amountSubtotal } this.isWaiting = true modules({ url, method, storeId, data }) .then(({ data }) => this.parseShippingOptions(data.result, isRetry)) .catch(err => { if (!isRetry) { this.scheduleRetry(4000) } console.error(err) }) .finally(() => { this.hasCalculated = true this.isWaiting = false }) }, this.hasCalculated ? 150 : 50) } }, submitZipCode () { this.updateZipCode() if (localStorage) { localStorage.setItem(zipStorageKey, this.localZipCode) } this.fetchShippingServices() }, setSelectedService (i) { if (this.canSelectServices) { this.$emit('select-service', this.shippingServices[i]) this.selectedService = i } } }, watch: { shippedItems: { handler () { setTimeout(() => { this.localShippedItems = this.shippedItems.map(reduceItemBody) const { amountSubtotal } = this this.amountSubtotal = this.shippedItems.reduce((subtotal, item) => { return subtotal + getPrice(item) * item.quantity }, 0) if ( this.hasCalculated && (this.canSelectServices || amountSubtotal !== this.amountSubtotal || (!this.shippingServices.length && !this.isWaiting)) ) { this.fetchShippingServices() } }, 50) }, deep: true, immediate: true }, localZipCode (zipCode) { if (this.countryCode === 'BR' && zipCode.replace(/\D/g, '').length === 8) { this.submitZipCode() } }, zipCode: { handler (zipCode) { if (zipCode) { this.localZipCode = zipCode } }, immediate: true }, skipAppIds () { this.fetchShippingServices() }, shippingResult: { handler (result) { if (result.length) { this.parseShippingOptions(result) } }, immediate: true } }, created () { if (!this.zipCode && localStorage) { const storedZip = localStorage.getItem(zipStorageKey) if (storedZip) { this.localZipCode = storedZip } } } }