UNPKG

@shopgate/pwa-common-commerce

Version:

Commerce library for the Shopgate Connect PWA.

264 lines • 24.8 kB
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}function _typeof(obj){if(typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"){_typeof=function _typeof(obj){return typeof obj;};}else{_typeof=function _typeof(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj;};}return _typeof(obj);}import{createSelector}from'reselect';import isEqual from'lodash/isEqual';import{getCurrentState}from'@shopgate/pwa-common/selectors/router';import{logger}from'@shopgate/pwa-core/helpers';import{generateResultHash}from'@shopgate/pwa-common/helpers/redux';import{getSortOrder}from'@shopgate/pwa-common/selectors/history';import{SORT_SCOPE_CATEGORY,SORT_SCOPE_SEARCH}from'@shopgate/engage/filter/constants';import{getPreferredLocation}from'@shopgate/engage/locations/selectors';import{getIsLocationBasedShopping}from'@shopgate/engage/core/selectors/merchantSettings';import{getActiveFilters}from"../../filter/selectors";import{filterProperties}from"../helpers";/** * Retrieves the product state from the store. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @return {Object} The product state. */export var getProductState=function getProductState(state){return state.product||{};};/** * Selects all products from the store. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @return {Object} The collection of products. */export var getProducts=createSelector(getProductState,function(state){return state.productsById||{};});/** * Selects the product shipping state. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The product shipping state. */export var getProductShippingState=createSelector(getProductState,function(state){return state.shippingByProductId||{};});/** * Selects the product description state. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The product description state. */export var getProductDescriptionState=createSelector(getProductState,function(state){return state.descriptionsByProductId||{};});/** * Selects the product properties state. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The product properties state. */export var getProductPropertiesState=createSelector(getProductState,function(state){return state.propertiesByProductId||{};});/** * Selects the product images state. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The product images state. */export var getProductImagesState=createSelector(getProductState,function(state){return state.imagesByProductId||{};});/** * Selects the product variants state. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The product variants state. */export var getProductVariantsState=createSelector(getProductState,function(state){return state.variantsByProductId||{};});/** * Retrieves a product by id from state. Different to getProduct() which returns the product * entity data if available, this selector returns the pure state entry for a given productId. * So the expires and the isFetching property is processable. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} The dedicated product. */export var getProductById=createSelector(getProducts,function(state,props){return props;},function(products,props){if(_typeof(props)!=='object'){logger.warn('Invocation of getProductById() with a productId will be deprecated soon. Please provide a props object.');return products[props]||null;}if(!props.productId){return null;}return products[props.productId]||null;});/** * @deprecated Also exists within @shopgate/engage/product/selectors/product */export var getProductDataById=createSelector(getProductById,function(product){return product?product.productData:undefined;});/** * Retrieves the id of the current selected product from the component props. When the props * contain a variant id it will return this one instead of the product id. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @param {Object} [props] The component props. * @return {string|null} The id of the current product. */export var getProductId=function getProductId(state,props){if(!state){return null;}if(typeof props==='undefined'){/** * Before PWA 6.0 some product selectors relied on a "currentProduct" state which doesn't exist * anymore. Their successors require a props object which contains a productId or a variantId. * To support debugging an error will be logged, if the props are missing at invocation. */logger.error('getProductId() needs to be called with a props object that includes a productId.');return null;}// Since a variantId can have falsy values, we need an "undefined" check here. if(typeof props.variantId!=='undefined'&&props.variantId!==null){return props.variantId;}return props.productId||null;};/** * Gets the variant id out of the selector props. * @param {Object} state The current application state. * @param {Object} props The component props. * @returns {string|null} */export var getVariantProductId=function getVariantProductId(state,props){if(typeof props==='undefined'){/** * Before PWA 6.0 the variant selectors relied on a "currentProduct" state which doesn't exist * anymore. Their successors require a props object which contains a variantId. * To support debugging an error will be logged, if the props are missing at invocation. */logger.error('getVariantId() needs to be called with a props object that includes a variantId.');}var _ref=props||{},_ref$variantId=_ref.variantId,variantId=_ref$variantId===void 0?null:_ref$variantId;return variantId;};/** * Checks if currently a variant is selected within the props. * @param {Object} state The current application state. * @param {Object} props The component props. * @returns {boolean} */export var isVariantSelected=function isVariantSelected(state,props){return!!getVariantProductId(state,props);};/** * Retrieves the product data for the passed productId from the store. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object} The current product. */export var getProduct=createSelector(getProducts,getProductId,function(products,productId){var _ref2=products[productId]||{},productData=_ref2.productData;return productData||null;});/** * Retrieves the product name. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getProductName=createSelector(getCurrentState,getProduct,function(routeState,product){if(!product){if(!routeState||!routeState.title){return null;}return routeState.title;}return product.name;});/** * Retrieves the product long name. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getProductLongName=createSelector(getCurrentState,getProduct,function(routeState,product){if(!product){if(!routeState||!routeState.title){return null;}return routeState.title;}if(!product.longName){return product.name;}return product.longName;});/** * Retrieves the product rating. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductRating=createSelector(getProduct,function(product){if(!product){return null;}return product.rating;});/** * Retrieves the product manufacturer. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getProductManufacturer=createSelector(getProduct,function(product){if(!product){return null;}return product.manufacturer;});/** * Retrieves the product stock information. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductStock=createSelector(getProduct,function(product){if(!product){return null;}return product.stock;});/** * Retrieves the product availability. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductAvailability=createSelector(getProduct,function(product){if(!product){return null;}return product.availability;});/** * Retrieves the product flags. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductFlags=createSelector(getProduct,function(product){if(!product){return null;}return product.flags;});/** * Retrieves the product price object. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string} */export var getProductPriceData=createSelector(getProduct,function(product){if(!product){return null;}return product.price;});/** * Retrieves the product currency. * @param {Object} state The current application state. * @param {Object} props The component props. * @todo Move to the price selectors * @return {string|null} */export var getProductCurrency=createSelector(getProductPriceData,function(price){if(!price){return null;}return price.currency;});/** * Retrieves the product discount. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getProductDiscount=createSelector(getProductPriceData,function(price){if(!price){return null;}return price.discount||0;});/** * Retrieves the unit price from a product. * @param {Object} state The current application state. * @param {Object} props The component props. * @todo Move to the price selectors * @return {number|null} */export var getProductUnitPrice=createSelector(getProductPriceData,function(price){if(!price){return null;}return price.unitPrice;});/** * Determines if a product has variants. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {boolean} */export var hasProductVariants=createSelector(getProductFlags,function(flags){if(!flags){return null;}return flags.hasVariants;});/** * Determines if a product has variety (variants, options). * This product can not be added to a cart. Selecting of variety should be done on PDP * @param {Object} state The current application state. * @param {Object} props The component props. * @return {boolean} */export var hasProductVariety=createSelector(getProductFlags,function(flags){if(!flags){return null;}return flags.hasVariants||flags.hasOptions;});/** * Determines if a product is a base product. * @param {Object} state The current application state. * @param {Object} props The component props. * @todo Check if returning null is correct. * @return {boolean|null} */export var isBaseProduct=createSelector(getProduct,hasProductVariants,function(product,hasVariants){if(!product){return false;}/** * Base products are simple products without variants or products with related variant products. * At variant products the baseProductId is used to reference the base product. */return product.baseProductId===null||hasVariants;});/** * Determines a baseProductId for the products which are referenced within the props. * When a variantId is passed, the selector will return the id of the related base product. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getBaseProductId=createSelector(getProduct,function(_){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.productId;},function(_){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.variantId;},function(product,productId,variantId){if(!product){// Return the productId when both ids are present, but no variant product is available yet. if(typeof productId!=='undefined'&&typeof variantId!=='undefined'){return productId;}return null;}// First try to determine a baseProductId for a selected product var _product$baseProductI=product.baseProductId,baseProductId=_product$baseProductI===void 0?null:_product$baseProductI;return baseProductId||product.id;});/** * Retrieves the base product data for the passed productId from the store. * @deprecated Also exists within @shopgate/engage/product/selectors/product * @param {Object} state The current application state. * @returns {Object|null} The current product. */export var getBaseProduct=createSelector(getProducts,getBaseProductId,function(products,baseProductId){if(!baseProductId){return null;}var _ref3=products[baseProductId]||{},_ref3$productData=_ref3.productData,productData=_ref3$productData===void 0?null:_ref3$productData;return productData;});/** * Determines if a base product has variants. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {boolean} */export var hasBaseProductVariants=createSelector(getBaseProduct,function(baseProduct){if(!baseProduct){return false;}var _baseProduct$flags=baseProduct.flags,_baseProduct$flags2=_baseProduct$flags===void 0?{}:_baseProduct$flags,_baseProduct$flags2$h=_baseProduct$flags2.hasVariants,hasVariants=_baseProduct$flags2$h===void 0?false:_baseProduct$flags2$h;return hasVariants;});/** * Retrieves the metadata for the given product. * @param {Object} state The current application state. * @return {Object|null} */export var getProductMetadata=createSelector(getProduct,function(product){if(!product){return null;}return product.metadata;});/** * Retrieves the metadata for the given product. * @param {Object} state The current application state. * @return {Object|null} */export var getBaseProductMetadata=createSelector(getBaseProduct,function(product){if(!product){return null;}return product.metadata;});/** * Retrieves the shipping data for the given product. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductShipping=createSelector(getProductShippingState,getProductId,function(shipping,productId){var entry=shipping[productId];if(!entry||!entry.shipping){return null;}return entry.shipping;});export var getProductPropertiesUnfiltered=createSelector(getProductId,getProductPropertiesState,function(productId,properties){var entry=properties[productId];if(!entry||entry.isFetching||typeof entry.properties==='undefined'){return null;}return entry.properties;});/** * Retrieves the properties for the given product. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductProperties=createSelector(getProductPropertiesState,getProductId,function(properties,productId){var entry=properties[productId];if(!entry||!entry.properties){return null;}return filterProperties(entry.properties);});/** * Retrieves the description for the given product. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getProductDescription=createSelector(getProductDescriptionState,getProductId,function(descriptions,productId){var entry=descriptions[productId];if(!entry||typeof entry.description==='undefined'){return null;}return entry.description;});/** * Retrieves the images for the given product. If the props contain a variantId, and the related * product does not have images, the selector tries to pick images from its base product. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Array|null} */export var getProductImages=createSelector(getProductImagesState,getProductId,getBaseProductId,function(images,productId,baseProductId){var _ref4=images[productId]||{},productImages=_ref4.images,isFetching=_ref4.isFetching;if(isFetching){return null;}// If the product doesn't have images after fetching if(baseProductId&&(!Array.isArray(productImages)||!productImages.length)){// ...check the base product. var _ref6=images[baseProductId]||{},baseProductImages=_ref6.images;if(!Array.isArray(baseProductImages)||!baseProductImages.length){return null;}return baseProductImages;}return productImages||null;});export var getFeaturedImage=createSelector(getProduct,getBaseProduct,function(product,baseProduct){var productImage=null;var baseProductImage=null;if(product===null||product===void 0?void 0:product.featuredMedia){productImage=product.featuredMedia.type==='image'?product.featuredMedia.url:null;}if(baseProduct===null||baseProduct===void 0?void 0:baseProduct.featuredMedia){baseProductImage=baseProduct.featuredMedia.type==='image'?baseProduct.featuredMedia.url:null;}return productImage||baseProductImage||(product===null||product===void 0?void 0:product.featuredImageBaseUrl)||(product===null||product===void 0?void 0:product.featuredImageUrl);});/** * Retrieves the product variant data. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getProductVariants=createSelector(getProductVariantsState,getBaseProductId,function(variants,baseProductId){var entry=variants[baseProductId];if(!entry||!entry.variants){return null;}return entry.variants;});/** * Retrieves a product for the selected variant id from the store. * @param {Object} state The current application state. * @param {Object} props The component props. * @returns {Object|null} The selected variant or null if none is selected */export var getSelectedVariant=createSelector(getProduct,isVariantSelected,function(product,selected){if(!product||!selected){return null;}return product;});/** * Determines if a product is orderable. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {boolean} */export var isProductOrderable=createSelector(getProductStock,function(stockInfo){return!!(stockInfo&&stockInfo.orderable);});/** * Retrieves the product id of a variant product. When no variantId is passed within * the props, the selector will return NULL. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {string|null} */export var getVariantId=createSelector(getProduct,function(product){if(!product){return null;}var id=product.id,baseProductId=product.baseProductId;return baseProductId!==null?id:null;});/** * Retrieves an availability object for a passed set of variant characteristics. * @param {Object} state The current application state. * @param {Object} props The component props. * @return {Object|null} */export var getVariantAvailabilityByCharacteristics=createSelector(getProductVariants,function(state){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.characteristics;},function(variants,characteristics){if(!variants){return null;}var found=variants.products.find(function(product){return isEqual(product.characteristics,characteristics);});if(!found){return null;}return found.availability;});/** * Creates a selector that determines fulfillment params for product requests */export var getFulfillmentParams=createSelector(getIsLocationBasedShopping,getPreferredLocation,function(isLocationBasedShopping,location){if(!isLocationBasedShopping||!location){return{};}return{locationCodes:[location.code]};});/** * Retrieves the generated result hash for a category id or search phrase. * @param {Object} state The current application state. * @param {Object} props The component props. * @returns {string|null} The result hash. */export var getResultHash=createSelector(function(state){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.categoryId;},function(state){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.searchPhrase;},function(state){var props=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};return props.params;},function(state,props){return getSortOrder(state,props);},getActiveFilters,getFulfillmentParams,function(categoryId,searchPhrase,params,sort,filters,fulfillmentParams){if(categoryId){return generateResultHash(_extends({categoryId:categoryId,sort:sort},filters&&{filters:filters},{},params,{},fulfillmentParams));}if(searchPhrase){return generateResultHash(_extends({searchPhrase:searchPhrase,sort:sort},params,{},filters&&{filters:filters},{},fulfillmentParams));}return null;});/** * Retrieves the result by hash. * @param {Object} state The application state. * @param {Object} props The component props. * @returns {Object} The result. */export var getResultByHash=createSelector(getProductState,getResultHash,function(productState,hash){var results=productState.resultsByHash[hash];if(!results){return null;}return results;});/** * Populates the product result object. * @param {Object} state The application state. * @param {Object} props The component props. * @param {string} hash The result hash. * @param {Object} result The result. * @return {Object} The product result. */export var getPopulatedProductsResult=function getPopulatedProductsResult(state,props,hash,result){var searchPhrase=props.searchPhrase;var sort=getSortOrder(state,_extends({},props,{scope:typeof searchPhrase==='undefined'?SORT_SCOPE_CATEGORY:SORT_SCOPE_SEARCH}));var products=[];var totalProductCount=!hash?0:null;var expired=!!(result&&result.expires&&result.expires>0&&result.expires<Date.now());if(result&&result.products){totalProductCount=result.totalResultCount;products=result.products.map(function(productId){return getProductById(state,{productId:productId}).productData;});}return{products:products,totalProductCount:totalProductCount,sort:sort,hash:hash,expired:expired};};/** * Retrieves the populated product result. * @param {Object} state The application state. * @param {Object} props The component props. * @returns {Object} The product result. */export var getProductsResult=createSelector(function(state){return state;},function(state,props){return props;},getResultHash,getResultByHash,getPopulatedProductsResult);/** * Selector factory which creates a selector to retrieve a product results object based on a custom * hash string. * @param {string} hash A resultsByHash hash string * @returns {Function} */export var makeGetProductResultByCustomHash=function makeGetProductResultByCustomHash(hash){var parsedHash=JSON.parse(hash);return createSelector(function(state){return state;},function(state,props){return props;},getProductState,function(state,props,productState){var searchPhrase=props.searchPhrase;var products=[];var totalProductCount=!hash?0:null;var sort=(parsedHash===null||parsedHash===void 0?void 0:parsedHash.sort)||getSortOrder(state,_extends({},props,{scope:typeof searchPhrase==='undefined'?SORT_SCOPE_CATEGORY:SORT_SCOPE_SEARCH}));var result=productState.resultsByHash[hash];if(result&&result.products){totalProductCount=result.totalResultCount;products=result.products.map(function(productId){return getProductById(state,{productId:productId}).productData;});}return{products:products,totalProductCount:totalProductCount,sort:sort,hash:hash};});};/** * Selector mappings for PWA < 6.0 * @deprecated */export var getCurrentProduct=getProduct;export var getCurrentProductId=getProductId;export var getCurrentBaseProductId=getBaseProductId;export var getCurrentBaseProduct=getBaseProduct;export var getCurrentProductStock=getProductStock;export var getProductStockInfo=getProductStock;export var getProductBasePrice=getProductUnitPrice;export var isOrderable=isProductOrderable;