import { action, decorate, computed, observable } from 'mobx'; import { ProductOrderService, DeliveryValidator } from 'lib'; import { BasketItemType, OrderItemType } from 'types/stores'; import { ProductVariation, MerchantProduct } from 'types/product'; import store from 'stores'; export default class BasketStore { basket: BasketItemType[] = []; selectedDeliveryConfig: any = null; deliveryConfig: any = null; deliveryConfigError: string | null = null; get totalPrice(): number { return this.getTotalPrice(); } get totalPriceWithDelivery(): number { return this.getTotalPrice() + this.calculateDeliveryCost(); } get totalItems(): number { return this.basket.reduce((acc: number, item: BasketItemType) => { return acc + item.quantity; }, 0); } get totalItemTypes(): number { return this.basket.length; } setInitialValues = (): void => { this.basket = []; this.selectedDeliveryConfig = null; this.deliveryConfig = null; this.deliveryConfigError = null; } getTotalPrice = (): number => { return this.basket.reduce((acc: number, basketItem: BasketItemType) => { return acc + this.getBasketItemTotal(basketItem); }, 0); } buildDeliveryConfig = (): void => { if (this.basket.length) { ProductOrderService.fetchProductDeliveryConfig( this.basket.map(item => item.product.fxId), store.deliveryStore!.livePostalCode.valueToSend ) .then(result => { const validator = new DeliveryValidator( result.data.data.products, store.deliveryStore!.livePostalCode.valueToSend ) .validateDeliveryZones(); if (!validator.hasValidDeliveryCoverage()) { return false; } const availableConfig = validator .validateSameDay() .buildDeliveryOptions() .buildConfig(); this.deliveryConfig = availableConfig; }) .catch(error => { console.log(error); }); } else { this.deliveryConfig = null; } } setDeliveryConfigError = (error = 'Please Select a delivery date'): void => { this.deliveryConfigError = error; } updateItemQuantity(basketIndex: number): void { this.basket[basketIndex].quantity = this.getItemQuantity(basketIndex); } updateMessage(basketIndex: number, orderIndex: number, message: string): void { this.basket[basketIndex].orderItems[orderIndex].message = message; } setDeliveryDate = (index: any): void => { this.selectedDeliveryConfig = { ...this.deliveryConfig.deliveryOptions[index], index } this.deliveryConfigError = null; } increaseQuantity(productId: string, variation: ProductVariation): void { const basketIndex = this.getBasketIndex(productId, variation); store.checkoutStore.resetPaymentIntent(); if (this.isInBasket(basketIndex)) { this.addOrderItem(basketIndex); this.updateItemQuantity(basketIndex); } } removeOrderItem(basketIndex: number): void { store.checkoutStore.resetPaymentIntent(); this.basket[basketIndex].orderItems.pop(); this.basket[basketIndex].total = this.basket[basketIndex].total - this.basket[basketIndex].orderItems[0].price this.updateItemQuantity(basketIndex); this.buildDeliveryConfig(); } decreaseQuantity(productId: string, variation: ProductVariation): void { store.checkoutStore.resetPaymentIntent(); const basketIndex = this.getBasketIndex(productId, variation); if (this.isInBasket(basketIndex)) { this.removeOrderItem(basketIndex); } } addToBasket(product: MerchantProduct, variationType: ProductVariation): void { store.checkoutStore.resetPaymentIntent(); const basketIndex = this.getBasketIndex(product.id, variationType); if (this.isInBasket(basketIndex)) { this.addOrderItem(basketIndex); } else { this.basket.push({ quantity: 1, variationType, product: { ...product }, total: variationType.price, orderItems: [this.buildOrderItem(product, variationType)] }); this.buildDeliveryConfig(); } } removeItem(productId: string, variation: ProductVariation): void { store.checkoutStore.resetPaymentIntent(); const basketIndex = this.getBasketIndex(productId, variation); const newBasket = this.basket; newBasket.splice(basketIndex, 1); this.basket = newBasket; this.buildDeliveryConfig(); } public getBasketItemTotal(item: BasketItemType): number { return item.orderItems.reduce((total: number, orderItem: OrderItemType) => { return total + orderItem.price; }, 0); } private calculateDeliveryCost = (): number => { return this.selectedDeliveryConfig ? this.selectedDeliveryConfig.price : 0; } private buildOrderItem(product: MerchantProduct, variationType: ProductVariation): OrderItemType { return { productId: product.id, variationId: variationType.id, price: variationType.price, message: '' }; } private addOrderItem(basketIndex: number): void { const orderItem = this.buildOrderItem( this.basket[basketIndex].product, this.basket[basketIndex].variationType ); this.basket[basketIndex].orderItems.push(orderItem); this.basket[basketIndex].quantity = this.getItemQuantity(basketIndex); this.basket[basketIndex].total = this.basket[basketIndex].total + this.basket[basketIndex].variationType.price; } private isInBasket(basketIndex: number,): boolean { return basketIndex >= 0; } private getItemQuantity(basketIndex: number): number { return this.basket[basketIndex] ? (this.basket[basketIndex].orderItems.length) : 0; } private getBasketIndex(productId: string, variation: ProductVariation): number { return this.basket.findIndex((basketItem: BasketItemType) => { const { product, variationType } = basketItem; return (product.id === productId) && (variationType.type === variation.type); }); } } decorate(BasketStore, { basket: observable, deliveryConfig: observable, selectedDeliveryConfig: observable, deliveryConfigError: observable, increaseQuantity: action, decreaseQuantity: action, setDeliveryDate: action, addToBasket: action, removeItem: action, totalPriceWithDelivery: computed, totalItems: computed, totalPrice: computed, totalItemTypes: computed });