UNPKG

@megaads/variant-builder

Version:

A JavaScript library to fetch and process product variants data

279 lines (251 loc) 10.8 kB
/** * Printerval Variants * @module PrintervalVariants * @exports PrintervalVariants * @type {{init: (function(*): PrintervalVariants)}} */ import { Variant, ProductVariant, Galleries, PrintervalVariantsOptions, VariantStatistic, ProductVariants } from './types'; class PrintervalVariants { variants: Variant[]; productVariants: ProductVariant[]; galleries: Galleries; variantStatistic: VariantStatistic; products: ProductVariants; variantByOption: { [key: number]: number }; groupVariants: Variant[]; currentVariant: ProductVariant | null; constructor(options: PrintervalVariantsOptions) { this.variants = options.variants; this.productVariants = options.productVariants; this.galleries = options.galleries; this.variantStatistic = this.buildVariantStatistic(this.variants, this.productVariants); this.products = this.buildProductVariants(this.productVariants); this.variantByOption = this.buildVariantByOption(this.variants); this.groupVariants = this.buildGroupVariants(this.variants); this.currentVariant = null; } rebuildVariants( variants: Variant[], currentVariant: ProductVariant, variantsStatistic: VariantStatistic, variantByOption: { [key: number]: number }, products: ProductVariants, groupVariants: Variant[] ){ for (let index = 0; index < variants.length; index++) { const variant = variants[index]; let variantKeyMap = new Map(); currentVariant.variants.forEach(id => { variantKeyMap.set(variantByOption[id], id); }) let otherVariants = currentVariant.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 = currentVariant.variants.filter((id, idx) => idx < index).join('-'); if (products.productByUniqIdAbove[aboveKey]) { optionGroup.push(item); } } }); groupVariants.forEach(item => { if (item.id == variant.id) { item.values = optionGroup; } }); } } 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; } 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 }; } 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; } 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; } getProductSkuDetails(productSkuId: number): { product: ProductVariant; variants: Variant[]; galleries: string[]; price: number; highPrice: number; } { 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.currentVariant = this.productVariants.find(v => v.id === productSkuId) || null; if (!this.currentVariant) { throw new Error(`Product SKU ${productSkuId} not found`); } this.rebuildVariants( this.variants, this.currentVariant, this.variantStatistic, this.variantByOption, this.products, this.groupVariants ); let galleries = this.galleries[this.currentVariant.id] ?? []; let price = this.currentVariant.price; let highPrice = this.currentVariant.high_price; return { product: this.currentVariant, variants: this.groupVariants, galleries: galleries, price: price, highPrice: highPrice } } static init(options: PrintervalVariantsOptions) { return new PrintervalVariants(options); } } export default PrintervalVariants;