Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented: support for dynamic features for product variants in product summary page (#166) #218

Closed
wants to merge 7 commits into from
12 changes: 12 additions & 0 deletions src/store/modules/product/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,18 @@ const actions: ActionTree<ProductState, RootState> = {
product = resp.data.response.docs.find((product: any) => product.productId === payload.productId)
variants = resp.data.response.docs.filter((product: any) => product.productId !== payload.productId)
}
const features = [['0/COLOR/', '1/COLOR/Black/', '0/SIZE/', '1/SIZE/29/', '0/TYPE/', '1/TYPE/Z/'],
['0/COLOR/', '1/COLOR/Black/', '0/SIZE/', '1/SIZE/28/', '0/TYPE/', '1/TYPE/X/'],
['0/COLOR/', '1/COLOR/Black/', '0/SIZE/', '1/SIZE/28/', '0/TYPE/', '1/TYPE/Y/'],
['0/COLOR/', '1/COLOR/Orange/', '0/SIZE/', '1/SIZE/28/', '0/TYPE/', '1/TYPE/Y/'],
['0/COLOR/', '1/COLOR/Orange/', '0/SIZE/', '1/SIZE/29/', '0/TYPE/', '1/TYPE/Z/'],
['0/COLOR/', '1/COLOR/White/', '0/SIZE/', '1/SIZE/29/', '0/TYPE/', '1/TYPE/Z/']]

let ind = 0
variants.map((variant: any) => {
variant.featureHierarchy = features[ind]
ind++
})
product.variants = variants
commit(types.PRODUCT_CURRENT_CTLGPRDCT_UPDATED, product)
commit(types.PRODUCT_ADD_TO_CACHED_MULTIPLE, { products: [product, ...product.variants] })
Expand Down
159 changes: 102 additions & 57 deletions src/views/catalog-product-details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,13 @@
<p>{{ currentVariant.sku }}</p>
</div>

<div class="product-features">
<ion-list v-if="selectedColor">
<ion-list-header>{{ $t("Colors") }}</ion-list-header>
<ion-item lines="none">
<ion-row>
<ion-chip :outline="selectedColor !== colorFeature" :key="colorFeature" v-for="colorFeature in Object.keys(features)" @click="selectedColor !== colorFeature && applyFeature(colorFeature, 'color')">
<ion-label class="ion-text-wrap">{{ colorFeature }}</ion-label>
</ion-chip>
</ion-row>
</ion-item>
</ion-list>

<ion-list v-if="selectedSize">
<ion-list-header>{{ $t("Sizes") }}</ion-list-header>
<div class="product-features" :key="featureCategory" v-for="[featureCategory, featureOptions] in Object.entries(features)">
<ion-list>
<ion-list-header>{{ featureCategory.charAt(0) + featureCategory.substring(1).toLowerCase() }}</ion-list-header>
<ion-item lines="none">
<ion-row>
<ion-chip :outline="selectedSize !== sizeFeature" :key="sizeFeature" v-for="sizeFeature in features[selectedColor]" @click="selectedSize !== sizeFeature && applyFeature(sizeFeature, 'size')">
<ion-label class="ion-text-wrap">{{ sizeFeature }}</ion-label>
<ion-chip :outline="selectedVariant[featureCategory] !== featureOption" :key="featureOption" v-for="featureOption in featureOptions" @click="selectedVariant[featureCategory] !== featureOption && applyFeature(featureOption, featureCategory)">
<ion-label class="ion-text-wrap">{{ featureOption }}</ion-label>
</ion-chip>
</ion-row>
</ion-item>
Expand Down Expand Up @@ -442,9 +431,7 @@ export default defineComponent({
return {
variantId: '',
productId: '',
selectedColor: '',
selectedSize: '',
features: [] as any,
features: {} as any,
currentVariant: {} as any,
poAndAtpDetails: {} as any,
atpCalcDetails: {} as any,
Expand All @@ -455,6 +442,8 @@ export default defineComponent({
listingJobRunTime: 0,
backorderCategoryId: '',
preOrderCategoryId: '',
selectedVariant: {} as any,
selectedFeature: '',
isCtgryAndBrkrngJobsLoaded: false
}
},
Expand Down Expand Up @@ -491,65 +480,121 @@ export default defineComponent({
async getVariantDetails() {
await this.store.dispatch('product/setCurrentCatalogProduct', { productId: this.productId})
if (this.product.variants) {
this.getFeatures()
await this.updateVariant()
await this.getVariantFeature()
await this.getFeatures()
}
},
applyFeature(feature: string, type: string) {
if (type === 'color') this.selectedColor = feature
else if (type === 'size') this.selectedSize = feature
this.updateVariant();
},
getFeatures() {
const features = {} as any
this.product.variants.map((variant: any) => {
const size = getFeature(variant.featureHierarchy, '1/SIZE/')
const color = getFeature(variant.featureHierarchy, '1/COLOR/')
if (!features[color]) features[color] = [size]
else if (!features[color].includes(size)) features[color].push(size)
})

Object.keys(features).forEach((color) => this.features[color] = sortSizes(features[color]))

let selectedVariant = this.product.variants.find((variant: any) => variant.productId === this.variantId)
applyFeature(featureOption: string, featureCategory: string) {
this.selectedFeature = featureCategory
this.selectedVariant[featureCategory] = featureOption

this.getFeatures()
},
getVariantFeature() {
let selectedVariant = this.product.variants.find((variant: any) => variant.productId === this.variantId)
let selectedVariantFeatures = {} as any

if (!selectedVariant) {
// if the variant does not have color or size as features
selectedVariant = this.product.variants[0]
showToast(translate("Selected variant not available. Reseting to first variant."))
}

// if the selected variant can't be found set it to the first variant of the product.
selectedVariant = this.product.variants[0]
showToast(translate("Selected variant not available. Reseting to first variant."))
}
if (selectedVariant) {
this.selectedColor = getFeature(selectedVariant.featureHierarchy, '1/COLOR/')
this.selectedSize = getFeature(selectedVariant.featureHierarchy, '1/SIZE/')
selectedVariant.featureHierarchy.map((featureItem: any) => {
if(featureItem.startsWith('1/')){
const featureItemSplitted = featureItem.split("/")
selectedVariantFeatures[featureItemSplitted[1]] = featureItemSplitted[2]
}
})

selectedVariantFeatures = Object.keys(selectedVariantFeatures).sort().reduce((result:any, key) => {
result[key] = selectedVariantFeatures[key];
return result;
}, {});
this.selectedVariant = selectedVariantFeatures
}
},
async getFeatures() {
const selectedVariantFeatures = this.selectedVariant
const features = {} as any
Object.entries(selectedVariantFeatures).map((feature, featureIndex) => {
const featureCategory = feature[0]
if(featureIndex === 0){
this.product.variants.map((variant: any) => {
const featureOption = getFeature(variant.featureHierarchy, `1/${featureCategory}`)

if (!features[featureCategory]) features[featureCategory] = [featureOption]
else if (!features[featureCategory].includes(featureOption)) features[featureCategory].push(featureOption)
})
}
const nextFeature = Object.entries(selectedVariantFeatures).find((currentFeature, currentFeatureIndex) => currentFeatureIndex === featureIndex + 1)

if(nextFeature){
const nextFeatureCategory = nextFeature[0]
const availableVariants = this.product.variants.filter((variant: any) => {
let isVariantAvailable = true
Object.entries(this.selectedVariant).map((currentFeature, currentFeatureIndex) => {
if(currentFeatureIndex <= featureIndex && getFeature(variant.featureHierarchy, `1/${currentFeature[0]}`) != currentFeature[1]){
isVariantAvailable = false
}
})
return isVariantAvailable
})

const nextFeatureAvailableValues = [] as any
availableVariants.map((variant: any) => {
if(!nextFeatureAvailableValues.includes(getFeature(variant.featureHierarchy , `1/${nextFeatureCategory}`))){
nextFeatureAvailableValues.push(getFeature(variant.featureHierarchy , `1/${nextFeatureCategory}`))
}
})
features[nextFeatureCategory] = nextFeatureCategory === 'SIZE' ? sortSizes(nextFeatureAvailableValues) : nextFeatureAvailableValues
}
})
this.features = features
await this.updateVariant()
},
async updateVariant() {
let variant
if (this.selectedColor || this.selectedSize) {
variant = this.product.variants.find((variant: any) => {
const hasSize = !this.selectedSize || (this.selectedSize && getFeature(variant.featureHierarchy, '1/SIZE/') === this.selectedSize)
const hasColor = !this.selectedColor || (this.selectedColor && getFeature(variant.featureHierarchy, '1/COLOR/') === this.selectedColor)
return hasSize && hasColor
})

// if the selected size is not available for that color, default it to the first size available
if (this.selectedFeature) {
variant = this.getVariant()

if (!variant) {
this.selectedSize = this.features[this.selectedColor][0];
variant = this.product.variants.find((variant: any) => getFeature(variant.featureHierarchy, '1/SIZE/') === this.selectedSize)
// Updating the selected features value to first available feature.
let isVariantAvailable = true;
Object.entries(this.features).map(([featureType, featureValue] : any) => {
if(!isVariantAvailable){
this.selectedVariant[featureType] = featureValue[0]
}else if(featureType === this.selectedFeature){
isVariantAvailable = false
}
})

variant = this.getVariant()
showToast(translate("Selected variant not available"))
}
}
// if the variant does not have color or size as features

this.currentVariant = variant || this.product.variants[0]
this.variantId = this.currentVariant.variantId
this.variantId = this.currentVariant.productId
this.$route.query.variantId !== this.currentVariant.productId && (this.router.replace({path: this.$route.path, query: { variantId: this.currentVariant.productId } }));
await this.getPoDetails()
await this.getAtpCalcDetails()
await this.prepareInvConfig()
await this.prepareShopListings()
await this.preparePoSummary()
},
getVariant() {
return this.product.variants.find((variant: any) => {
let isFeatureAvailable = true
Object.entries(this.selectedVariant).map(([featureType,featureValue]) => {
if(getFeature(variant.featureHierarchy, `1/${featureType}`) != featureValue){
isFeatureAvailable = false
}
})
return isFeatureAvailable
})
},
async getCtgryAndBrkrngJobs() {
const systemJobEnumIds = JSON.parse(process.env.VUE_APP_CTGRY_AND_BRKRNG_JOB)
this.store.dispatch('job/fetchCtgryAndBrkrngJobs', { systemJobEnumIds }).then(() => {
Expand Down
Loading