Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.11.0-rc.2] - unreleased

### Fixed
- Fixed deprecated getter in cmsBlock store - @resubaka (#3683)
- Fixed problem around dynamic urls when default storeView is set with appendStoreCode false and url set to / . @resubaka (#3685)
- Fixed three problems you can run into when you have bundle products - @resubaka (#3692)
- Reset nested menu after logout - @gibkigonzo (#3680)

- Fixed deprecated getter in cmsBlock store - @resubaka (#3683)
- Fixed problem around dynamic urls when default storeView is set with appendStoreCode false and url set to / . @resubaka (#3685)
- Fixed three problems you can run into when you have bundle products - @resubaka (#3692)
- Fixed handling checkbox custom option (#2781)

## [1.11.0-rc.1] - 2019.10.03

Expand Down
56 changes: 28 additions & 28 deletions core/modules/catalog/components/ProductCustomOptions.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
import { customOptionFieldName, selectedCustomOptionValue, defaultCustomOptionValue } from '@vue-storefront/core/modules/catalog/helpers/customOption';
import { mapMutations } from 'vuex'
import * as types from '../store/product/mutation-types'
import rootStore from '@vue-storefront/core/store'
import i18n from '@vue-storefront/i18n'
import { Logger } from '@vue-storefront/core/lib/logger'

function _defaultOptionValue (co) {
switch (co.type) {
case 'radio': return co.values && co.values.length ? co.values[0].option_type_id : 0
case 'checkbox': return false
default: return ''
}
}

function _fieldName (co) {
return 'customOption_' + co.option_id
}

export const ProductCustomOptions = {
name: 'ProductCustomOptions',
props: {
Expand All @@ -26,21 +15,34 @@ export const ProductCustomOptions = {
},
data () {
return {
inputValues: {
},
selectedOptions: {
},
inputValues: {},
validation: {
rules: {},
results: {}
}
}
},
computed: {
selectedOptions () {
const customOptions = this.product.custom_options
if (!customOptions) {
return {}
}

return customOptions.reduce((selectedOptions, option) => {
const fieldName = customOptionFieldName(option)
selectedOptions[fieldName] = selectedCustomOptionValue(option.type, option.values, this.inputValues[fieldName])
return selectedOptions
}, {})
}
},
created () {
rootStore.dispatch('product/addCustomOptionValidator', {
validationRule: 'required', // You may add your own custom fields validators elsewhere in the theme
validatorFunction: (value) => {
return { error: (value === null || value === '') || (value === false) || (value === 0), message: i18n.t('Field is required.') }
const error = Array.isArray(value) ? !value.length : !value
const message = i18n.t('Field is required.')
return { error, message }
}
})
this.setupInputFields()
Expand All @@ -50,26 +52,24 @@ export const ProductCustomOptions = {
setCustomOptionValue: types.PRODUCT_SET_CUSTOM_OPTION // map `this.add()` to `this.$store.commit('increment')`
}),
setupInputFields () {
for (let co of this.product.custom_options) {
const fieldName = _fieldName(co)
this['inputValues'][fieldName] = _defaultOptionValue(co)
if (co.is_require) { // validation rules are very basic
for (const customOption of this.product.custom_options) {
const fieldName = customOptionFieldName(customOption)
this['inputValues'][fieldName] = defaultCustomOptionValue(customOption)
if (customOption.is_require) { // validation rules are very basic
this.validation.rules[fieldName] = 'required' // TODO: add custom validators for the custom options
}
this.optionChanged(co, co.values && co.values.length > 0 ? co.values[0] : null)
this.optionChanged(customOption)
}
},
optionChanged (option, opval = null) {
const fieldName = _fieldName(option)
const value = opval === null ? this.inputValues[fieldName] : opval.option_type_id
optionChanged (option) {
const fieldName = customOptionFieldName(option)
this.validateField(option)
this.setCustomOptionValue({ optionId: option.option_id, optionValue: value })
this.setCustomOptionValue({ optionId: option.option_id, optionValue: this.selectedOptions[fieldName] })
this.$store.dispatch('product/setCustomOptions', { product: this.product, customOptions: this.$store.state.product.current_custom_options }) // TODO: move it to "AddToCart"
this.selectedOptions[fieldName] = (opval === null ? value : opval)
this.$bus.$emit('product-after-customoptions', { product: this.product, option: option, optionValues: this.selectedOptions })
},
validateField (option) {
const fieldName = _fieldName(option)
const fieldName = customOptionFieldName(option)
const validationRule = this.validation.rules[fieldName]
this.product.errors.custom_options = null
if (validationRule) {
Expand Down
44 changes: 44 additions & 0 deletions core/modules/catalog/helpers/customOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { CustomOption, OptionValue, InputValue } from './../types/CustomOption';

export const defaultCustomOptionValue = (customOption: CustomOption): InputValue => {
switch (customOption.type) {
case 'radio': {
return customOption.values && customOption.values.length ? customOption.values[0].option_type_id : 0
}
case 'checkbox': {
return []
}
default: {
return ''
}
}
}

export const customOptionFieldName = (customOption: CustomOption): string => {
return 'customOption_' + customOption.option_id
}

export const selectedCustomOptionValue = (optionType: string, optionValues: OptionValue[] = [], inputValue: InputValue): string => {
switch (optionType) {
case 'field': {
return inputValue as string
}
case 'radio':
case 'select':
case 'drop_down': {
const selectedValue = optionValues.find((value) => value.option_type_id === inputValue as number)

return String(selectedValue && selectedValue.option_type_id) || ''
}
case 'checkbox': {
const checkboxOptionValues = inputValue as number[] || []

return optionValues.filter((value) => checkboxOptionValues.includes(value.option_type_id))
.map((value) => value.option_type_id)
.join(',')
}
default: {
return ''
}
}
}
24 changes: 24 additions & 0 deletions core/modules/catalog/types/CustomOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export interface CustomOption {
image_size_x: number,
image_size_y: number,
is_require: boolean,
max_characters: number,
option_id: number,
product_sku: string,
sort_order: number,
title: string,
type: string,
price?: number,
price_type?: string,
values?: OptionValue[]
}

export interface OptionValue {
option_type_id: number,
price: number,
price_type: string,
sort_order: number,
title: string
}

export type InputValue = string | number | number[]
4 changes: 2 additions & 2 deletions src/themes/default/components/core/ProductCustomOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
>
<div class="m5 relative" v-for="opval in option.values" :key="opval.option_type_id" v-if="option.type === 'radio' || option.type === 'select' || option.type === 'drop_down'">
<input
@change="optionChanged(option, opval)"
@change="optionChanged(option)"
type="radio"
class="m0 no-outline"
:name="('customOption_' + option.option_id)"
Expand All @@ -30,7 +30,7 @@
</div>
<div class="m5 relative" v-for="opval in option.values" :key="opval.option_type_id" v-if="option.type === 'checkbox'">
<input
@change="optionChanged(option, opval)"
@change="optionChanged(option)"
type="checkbox"
class="m0 no-outline"
:name="('customOption_' + option.option_id)"
Expand Down