Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

skava / @skava-features/header   js

Repository URL to install this package:

Version: 0.1.7 

/ dist / src / state / cart / container.js

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
            t[p[i]] = s[p[i]];
    return t;
};
/**
 * @file @todo need to convert to actions @@training
 */
import { oneRouter } from '@skava/router';
import { getTyped } from 'composition';
import { observable, action } from 'xmobx/mobx';
import { isNumber, isObj, isFunction, isEmpty, toNumber, isArray, isSafe } from 'exotic';
import { isErrorLikeResponse } from '@skava/is-error-like-response';
import ObservableContainer from 'src/bootstrapper/connectData/ObservableContainer';
import { locationContainer } from 'src/bootstrapper/google';
// domain
import { geocodeApi } from 'src/state/storeLocator/container.geocode';
import { OneProduct } from 'src/state/__product';
// !!!!!!!!!!!! FIXME !!!!!!!!!!!!!!!!!!! STATE NEVER IMPORTS FROM VIEWS !!!!!!!!!!!!!!!!!
import { estimateShippingFormContainer } from 'src/views/widgets/ShoppingCart/MiniCart/EstimateShipping/FormState';
import { oneStorage } from '@skava/persistence';
// @note !!! CHANGED TO AVOID CIRCULAR
import { sessionApis } from 'state/session/container.apis';
import { listContainer } from 'state/list/container';
import { listContainerApi } from 'state/list/container.list';
import { application } from 'src/state/application';
import { OneSkuCartPlugin } from 'state/__product/OneSku.CartPlugin';
// local
import { toastMessage, responseMessage } from 'src/state/errorView/_fixture';
import { errorContainer } from 'src/state/errorView/container';
import { transformGeoCodeData } from './transform';
import { shippingAndTaxContainer } from './container.shippingAndTax';
import AddToBag from './queries/AddToBag.graphql';
import MultipleAddToBag from './queries/MultipleAddToBag.graphql';
import UpdateBag from './queries/UpdateBag.graphql';
import DeleteFromBag from './queries/DeleteFromBag.graphql';
import EstimateShipping from './queries/EstimateShipping.graphql';
/**
 * @todo reuse @@perf @@haircut
 * @todo some responses don't map to this...
 */
function toStatus(response) {
    return getTyped(response).string('properties.state.status');
}
function toErrorMessage(response) {
    return getTyped(response).string('properties.state.errormessage');
}
// we can easily separate our stores for atomically sized stores
// takes no more time than 1 big store, but then we can hot swap pieces
class CartApi extends ObservableContainer {
    constructor() {
        super(...arguments);
        /**
         * @protected we only access this in CartContainer
         *
         * @todo any params can use oneRouter
         * @description debug, fetch cart, update view
         * @example
         *    this.fetchCartApi = this.fetchCartApi.bind(this) === fetchCart = () => {}
         *
         * @todo we may just want to return fixture here
         *
         * @return {Promise<Object>}
         */
        this.fetchViewBag = () => __awaiter(this, void 0, void 0, function* () {
            const response = yield sessionApis.viewBag();
            cartContainer.updateFrom(response);
            return response;
        });
    }
}
CartApi.debugName = 'CartApi';
const cartApi = new CartApi();
/**
 * @todo move to deps
 */
const _toQuantity = (item, sku) => {
    const isOneProduct = isObj(item.dynamicState) === true && isNumber(item.dynamicState.quantity) === true;
    if (isOneProduct === true) {
        return item.dynamicState.quantity;
    }
    else if (isNumber(item.quantity) === true) {
        return item.quantity;
    }
    else if (isNumber(sku.quantity) === true) {
        return sku.quantity;
    }
    else {
        return 1;
    }
};
const toQuantity = (item, sku) => {
    // calling this inside to always get 1, should not be needed
    return _toQuantity(item, sku) || 1;
};
const getPayLoad = (props) => {
    const { item, sku, isMaster } = props;
    const itemQuantity = toQuantity(item, sku);
    const quantity = itemQuantity ? toNumber(itemQuantity) : 1;
    return {
        skuId: item.isBundle ? item.identifier : sku.identifier,
        itemid: item.identifier,
        title: encodeURIComponent(sku.name || item.name),
        quantity,
        itemType: isMaster ? 'bundle' : 'sku',
        customParams: {
            categoryId: isMaster ? 'bundles' : isObj(item.firstCategory) ? item.firstCategory.value : '',
        },
    };
};
const fromProductToAddToBagParams = (item) => {
    const sku = item.currentlySelectedSku || item.skus[0];
    const products = [];
    const isMaster = item.isMasterProduct === true ? true : false;
    if (isMaster) {
        const bundleItems = item.bundleList.map(subproduct => products.push(constructSubPayload(subproduct)));
    }
    const subproducts = products.filter(product => !!product);
    const payload = getPayLoad({ item, sku, isMaster });
    if (isMaster) {
        payload.subProducts = subproducts;
    }
    return payload;
};
function bundleParams(label) {
    switch (label) {
        case 'bundleMainProduct':
            return 'mainProduct';
        case 'bundleOptionalProducts':
            return 'optionalProduct';
        case 'bundleMandatoryProducts':
            return 'mandatoryProduct';
        default:
            return 'otherProduct';
    }
}
const constructSubPayload = (item) => {
    const sku = item.currentlySelectedSku || item.skus[0];
    // @todo customParams to be taken by looping flags - need clarity
    const customParams = (item.iteminfo.flags && item.iteminfo.flags[0]) || {};
    const customParamLabel = bundleParams(customParams.label);
    const customParamValue = customParams.value;
    if ((customParamLabel === 'optionalProduct' && !item.addOnEnable) ||
        customParamLabel === 'otherProduct') {
        return '';
    }
    const subProductsPayload = {
        skuId: sku.identifier,
        itemid: item.identifier,
        customParams: {},
    };
    subProductsPayload.customParams[customParamLabel] = customParamValue;
    subProductsPayload.customParams.quantity = item.quantity || 1;
    return subProductsPayload;
};
/**
 * @description there are a few ways we can do this relational data
 *
 * @example 1. since we have stores for Product already and can store the api data there
 *             because those state-tree stores already have simplified transforms
 *
 * @example 2. even just importing Catalog/Product or /ProductDisplay/Product, can extend it easily
 *             then just do .create(apiData).toJSON() and it would be transformed
 *
 * @example 3. or you can do the same transforms in this store
 *
 */
class CartContainer extends ObservableContainer {
    /**
     * @example this.fetchCartApi = this.fetchCartApi.bind(this) === fetchCart = () => {}
     * @description debug, fetch cart, update view
     */
    constructor() {
        super();
        this.cartDetails = {};
        /**
         * @todo need to take save for later out of cart
         */
        this.saveForLaterProduct = [];
        this.cartCount = 0;
        this.saveListItemsId = [];
        this.saveListId = '';
        this._shouldShowPlaceHolderOnRender = false;
        this.fetched = {
            hasFetchedSaveForLater: false,
            hasFetchedCart: false,
        };
        // ========= @lifecycle =========
        /**
         * @deprecated
         */
        this.handleMount = () => {
            if (typeof window !== 'object') {
                // this seems to mount way too many times on the server
                console.warn('@note was viewing cart on server');
                return;
            }
            /**
             * @todo - only use this when we mount the zip
             */
            // if (window.google === undefined) {
            //   loadGoogle()
            // }
            // this.viewCart()
            if (this.fetched.hasFetchedSaveForLater === false) {
                this.fetched.hasFetchedSaveForLater = true;
                this.fetchSaveForLater();
            }
            else {
                console.warn('[Cart] MOUNTED MULTIPLE TIMES!!!');
            }
        };
        /**
         * @deprecated
         */
        this.handleUnMount = () => {
            // @todo
            // componentWillUnmount
        };
        /**
         * !!!!!!! theey do not eveen belong iin a class
         * @todo needs to be in state/list
         */
        /**
         * @todo move to deps
         */
        this.transformSaveForLaterItems = (item) => {
            const saveForLaterProduct = item;
            const { array } = getTyped(item);
            const { skuId } = array('sku.0');
            saveForLaterProduct.skuId = skuId;
            array('additionalProps').forEach((product) => {
                if (product.label === 'productId') {
                    saveForLaterProduct.identifier = product.value;
                }
                if (product.label === 'quantity') {
                    saveForLaterProduct.quantity = product.value;
                }
            });
            return saveForLaterProduct;
        };
        /**
         * @todo move to deps
         */
        this.transformListId = (listId) => {
            const list = {
                listId: listId.listItemId,
                productId: listId.identifier,
            };
            return list;
        };
        // default until we replace it
        // do not push to this array, only replace saveForLaterProduct =
    }
    // ========= @actions =========
    /**
     * @public
     * @type {Action}
     * @see CartApi
     *
     * @param {Object} params object (in this case I think it is ProductDetails???)
     * return value is not used unless the view needs it
     *
     * @description this is the interface provided to the views/components
     *              we can use this to call CartApi
     *              and CartApi class can use
     */
    handleAddToCartResponse(addToCartResponse, viewCartResponse) {
        if (isErrorLikeResponse(addToCartResponse) || isErrorLikeResponse(viewCartResponse)) {
            const isCartLimitExceeded = addToCartResponse.data.addToBag.properties.state.status ===
                responseMessage.cartLimitExceeded;
            const toastText = isCartLimitExceeded
                ? toastMessage.cartLimitFailure
                : toastMessage.addToCartFailure;
            errorContainer.setError({
                errorMessage: toastText,
            });
            return false;
        }
        else if (!application.isDesktop) {
            errorContainer.setError({
                errorMessage: toastMessage.addToCartSuccess,
            });
        }
        // done loading
        application.setIsLoadingBig(false).setIsLoading(false);
        return true;
    }
    addToBag(item, transformOneProduct = true) {
        return __awaiter(this, void 0, void 0, function* () {
            // Enable the placeholder in minicart if no products till viewbag gets success
            this._shouldShowPlaceHolderOnRender = true;
            // start loading
            application.setIsLoadingBig(true).setIsLoading(true);
            const getAddToBag = {
                response: {},
            };
            // @note the tslint error that shows up for `prefer-conditional-expression`
            let payload = item;
            if (transformOneProduct) {
                if (isArray(item)) {
                    payload = item.map(fromProductToAddToBagParams);
                }
                else {
                    payload = fromProductToAddToBagParams(item);
                }
            }
            if (isArray(item) && item.length > 1) {
                const params = {
                    addToBagParams: payload,
                };
                getAddToBag.response = (yield this.client.mutate({
                    mutation: MultipleAddToBag,
                    variables: {
                        input: params,
                    },
                }));
            }
            else {
                getAddToBag.response = (yield this.client.mutate({
                    mutation: AddToBag,
                    variables: {
                        input: payload,
                    },
                }));
            }
            const cartResponse = yield this.viewCart();
Loading ...