UNPKG

@megaads/wm

Version:

To install the library, use npm:

418 lines (371 loc) 16.4 kB
/** * Variants * @module Variants * @exports Variants * @type {{init: (function(*): Variants)}} */ import { Variant, ProductVariant, Galleries, VariantsOptions, VariantStatistic, ProductVariants, ProductSkuDetail } from './types'; class Variants { variants: Variant[]; productVariants: ProductVariant[]; galleries: Galleries; variantStatistic: VariantStatistic; products: ProductVariants; variantByOption: { [key: number]: number }; groupVariants: Variant[]; currentProductVariant: ProductVariant | null; constructor(options: VariantsOptions) { this.variants = options.variants; this.productVariants = options.productVariants; this.galleries = options.galleries; this.variantStatistic = this.buildVariantStatistic(options.variants, options.productVariants); this.products = this.buildProductVariants(options.productVariants); this.variantByOption = this.buildVariantByOption(options.variants); this.groupVariants = this.buildGroupVariants(options.variants); this.currentProductVariant = null; } private rebuildVariants( variants: Variant[], currentProductVariant: ProductVariant, variantsStatistic: VariantStatistic, variantByOption: { [key: number]: number }, products: ProductVariants, groupVariants: Variant[] ) { const groupVariantsCopy = groupVariants.map(variant => ({ ...variant, values: [...variant.values] })); for (let index = 0; index < variants.length; index++) { const variant = variants[index]; let variantKeyMap = new Map(); currentProductVariant.variants.forEach(id => { variantKeyMap.set(variantByOption[id], id); }) let otherVariants = currentProductVariant.variants.filter(id => variantByOption[id] != variant.id); otherVariants.forEach(id => { variantKeyMap.set(variantByOption[id], id); }); let optionGroup: { id: number; variant_id?: number; variant_slug?: string; }[] = []; variant.values.forEach(item => { let tmpKeyMap = new Map(variantKeyMap); tmpKeyMap.set(variant.id, item.id); let itemVariantKey = [...tmpKeyMap.values()].join('-'); let prefixVariant = ''; let variantIsOk = true; if (variant.slug == 'style' && index >= 0) { if (otherVariants && otherVariants.length >= 2) { prefixVariant = otherVariants[0] + '-' + item.id; variantIsOk = false; if (prefixVariant && variantsStatistic) { if (variantsStatistic[prefixVariant]) { variantIsOk = true; } else if (variantsStatistic[item.id + '-' + otherVariants[0]]) { variantIsOk = true; } } if (index == 0 && !variantIsOk && variantsStatistic[item.id]) { variantIsOk = true; } } } if (variantIsOk && ((products.productByUniqId[itemVariantKey] && products.productByUniqId[itemVariantKey].id) || variant.show_invalid)) { optionGroup.push(item); } else if (variantIsOk && variant.show_invalid_above) { let aboveKey = currentProductVariant.variants.filter((id, idx) => idx < index).join('-'); if (products.productByUniqIdAbove[aboveKey]) { optionGroup.push(item); } } }); for (const groupVariant of groupVariantsCopy) { if (groupVariant.id == variant.id) { groupVariant.values = optionGroup; } } } this.groupVariants = groupVariantsCopy; } private buildVariantStatistic (variants: Variant[], productVariants: ProductVariant[]): VariantStatistic { let variantsStatistic: { [key: string]: {id: number, sku_key: string, count: number, price: number, high_price: number} } = {}; if (variants.length < 2) { return variantsStatistic; } let firstOptionIndex = 0; for (let key in variants) { if (variants[key].type == 'OPTION') { firstOptionIndex = parseInt(key); break; } } for (let item of productVariants) { let vKeyArr = item.variants.filter((value, index) => index <= firstOptionIndex).map(item => item); for (let i = firstOptionIndex + 1; i < item.variants.length; i++) { let vKey = vKeyArr.join('-') + '-' + item.variants[i]; if (!variantsStatistic[vKey]) { variantsStatistic[vKey] = { id: item.id, sku_key: item.variants.join('-'), count: 1, price: item.price, high_price: item.high_price } } else { variantsStatistic[vKey].count++; if (item.price < variantsStatistic[vKey].price) { variantsStatistic[vKey].id = item.id; variantsStatistic[vKey].sku_key = item.variants.join('-'); variantsStatistic[vKey].price = item.price; variantsStatistic[vKey].high_price = item.high_price; } } } let vKey = vKeyArr.join('-'); if (!variantsStatistic[vKey]) { variantsStatistic[vKey] = { id: item.id, sku_key: item.variants.join('-'), count: 1, price: item.price, high_price: item.high_price } } else { variantsStatistic[vKey].count++; if (item.price < variantsStatistic[vKey].price) { variantsStatistic[vKey].id = item.id; variantsStatistic[vKey].sku_key = item.variants.join('-'); variantsStatistic[vKey].price = item.price; variantsStatistic[vKey].high_price = item.high_price; } } } return variantsStatistic; } private buildProductVariants(productVariants: ProductVariant[]): ProductVariants { const productById: { [key: number]: {id: number, sku: string, price: number, high_price: number, variants: number[]} } = {}; const productByUniqId: { [key: string]: any } = {}; const productByUniqIdAbove: { [key: string]: any } = {}; if (productVariants) { productVariants.forEach(function(item) { productById[item.id] = { id: item.id, sku: item.sku, price: item.price, high_price: item.high_price, variants: item.variants }; const variantOptionIds = item.variants; if (variantOptionIds.length > 0) { const key = variantOptionIds.join("-"); productByUniqId[key] = item; if (item.variants.length > 2) { for (let j = 1; j < variantOptionIds.length - 2; j++) { let aboveKey = variantOptionIds.slice(0, j).join('-'); productByUniqIdAbove[aboveKey] = item; } } } }); } return { productById: productById, productByUniqId: productByUniqId, productByUniqIdAbove: productByUniqIdAbove }; } private buildVariantByOption(variants: Variant[]): { [key: number]: number } { let variantByOption: { [key: number]: number } = {}; let optionById: { [key: number]: any } = {}; variants.forEach(variant => { variant.values.forEach(item => { variantByOption[item.id] = variant.id item.variant_id = variant.id; item.variant_slug = variant.slug; optionById[item.id] = item; }) }); return variantByOption; } private buildGroupVariants(variants: Variant[]): Variant[] { variants.forEach((element, index) => { element.show_invalid = index == 0; if (variants[0].type != 'OPTION') { if (index <= variants.length - 3) { element.show_invalid = true; } if (index <= variants.length - 2 && element.type != "IMAGE") { element.show_invalid_above = true; } } else if (index && index == variants.length - 2 && element.type == "OPTION") { element.show_invalid_above = true; } }); return variants; } private isSelectedVariantValue(variantValueId: number): boolean { if (!this.currentProductVariant || !this.currentProductVariant.variants) { return false; } let isExists = this.currentProductVariant.variants.find(id => id == variantValueId); return !!isExists; } private getPriceVariant(option: { id: number; variant_id?: number; variant_slug?: string; is_selected?: boolean, name?: string, slug?: string, price?: number, high_price?: number, }, groupId: number): number { let result = 0; if (this.variants.length > 0) { if (!this.currentProductVariant) { return result; } let isStyleFirstVariant = this.variants[0].slug == 'style'; let key: string = option.id.toString(); let listIndex: (number | string)[] = []; this.currentProductVariant?.variants.forEach(element => { if (this.variantByOption[element] != groupId) { key += "-" + element; listIndex.push(element); } }); let currentProductBySpid = this.products.productById[this.currentProductVariant.id]; if (key in this.products.productByUniqId) { result = this.products.productByUniqId[key].price; } else { let prefixVariant = ''; let variantIsOk = true; if (listIndex && listIndex.length >= 2) { prefixVariant = listIndex[0] + '-' + option.id; variantIsOk = false; if (this.currentProductVariant && this.currentProductVariant.id) { let keyWithColor = currentProductBySpid.variants .filter(id => this.variantByOption[id] != 2) .map(id => this.variantByOption[id] == this.variantByOption[option.id] ? option.id : id).join('-'); if (this.variantStatistic[keyWithColor]) { variantIsOk = true; prefixVariant = keyWithColor; } } if (prefixVariant && this.variantStatistic) { if (this.variantStatistic[prefixVariant]) { variantIsOk = true; } else if (this.variantStatistic[option.id + '-' + listIndex[0]]) { variantIsOk = true; prefixVariant = option.id + '-' + listIndex[0]; } if (!variantIsOk && isStyleFirstVariant && this.variantStatistic[option.id]) { prefixVariant = option.id.toString(); variantIsOk = true; } } } if (variantIsOk && key) { if (prefixVariant && this.variantStatistic && this.variantStatistic[prefixVariant]) { result = this.variantStatistic[prefixVariant].price; } } } } return result; } getProductSkuDetail(productSkuId: number): ProductSkuDetail { if (!this.variants) { throw new Error('Variants Data not set'); } if (!this.productVariants) { throw new Error('Product Variants Data not set'); } if (!this.galleries) { throw new Error('Galleries not set'); } this.currentProductVariant = this.productVariants.find(v => v.id === productSkuId) || null; if (!this.currentProductVariant) { throw new Error(`Product SKU ${productSkuId} not found`); } this.rebuildVariants( this.variants, this.currentProductVariant, this.variantStatistic, this.variantByOption, this.products, this.groupVariants ); let galleries = this.galleries[this.currentProductVariant.id] ?? []; let price = this.currentProductVariant.price; let highPrice = this.currentProductVariant.high_price; for (const variant of this.groupVariants) { for (const value of variant.values) { value.is_selected = this.isSelectedVariantValue(value.id); if (value.is_selected) { variant.current_value_id = value.id; variant.current_value_name = value.name; } } } //build price for size variant value const getMatchCount = (source: any, target: any) => { let matchCount = 0; for (let i = 0; i < source.length; i++) { if (source[i] === target[i]) { matchCount++; } } return matchCount; } for (const variant of this.groupVariants) { if (['size', 'sizes', 'style'].includes(variant.slug)) { for (const value of variant.values) { value.price = this.getPriceVariant(value, variant.id); } } } return { product: this.currentProductVariant, variants: this.groupVariants, galleries: galleries, price: price, high_price: highPrice } } getSkuIdByValueIds(valueIds: number[], selectedValueId: number): number { const getMatchCount = (source: any, target: any) => { let matchCount = 0; for (let i = 0; i < source.length; i++) { if (source[i] === target[i]) { matchCount++; } } return matchCount; } let productVariantMatch = null let highestMatch = -1; for (const productVariant of this.productVariants) { if (!productVariant.variants.includes(selectedValueId)) { continue; } const currentMatches = getMatchCount(valueIds, productVariant.variants); if (currentMatches > highestMatch) { highestMatch = currentMatches; productVariantMatch = productVariant; } } if (!productVariantMatch) { productVariantMatch = this.productVariants[0]; } return productVariantMatch ? productVariantMatch.id : 0; } } export default Variants;