@megaads/variant-builder
Version:
A JavaScript library to fetch and process product variants data
279 lines (251 loc) • 10.8 kB
text/typescript
/**
* 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;