Field Logic Error Handling - frontend (#162)
* Field Logic Error Handling - frontend * Fix test case * fix expected_type for multi select * fix variable --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
ec26c211d6
commit
057bfde8b7
|
@ -605,6 +605,8 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$this->isActionCorrect = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,10 +622,9 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
$this->setProperty($attribute);
|
$this->setProperty($attribute);
|
||||||
if(isset($value["conditions"])){
|
if(isset($value["conditions"])){
|
||||||
$this->checkConditions($value["conditions"]);
|
$this->checkConditions($value["conditions"]);
|
||||||
|
$this->checkActions($value['actions'] ?? null);
|
||||||
}
|
}
|
||||||
if(isset($value["actions"])){
|
|
||||||
$this->checkActions($value["actions"]);
|
|
||||||
}
|
|
||||||
return ($this->isConditionCorrect && $this->isActionCorrect);
|
return ($this->isConditionCorrect && $this->isActionCorrect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -342,13 +342,13 @@
|
||||||
"multi_select": {
|
"multi_select": {
|
||||||
"comparators": {
|
"comparators": {
|
||||||
"contains": {
|
"contains": {
|
||||||
"expected_type": "object",
|
"expected_type": ["object", "string"],
|
||||||
"format": {
|
"format": {
|
||||||
"type": "uuid"
|
"type": "uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"does_not_contain": {
|
"does_not_contain": {
|
||||||
"expected_type": "object",
|
"expected_type": ["object", "string"],
|
||||||
"format": {
|
"format": {
|
||||||
"type": "uuid"
|
"type": "uuid"
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ import FormEditorPreview from './form-components/FormEditorPreview.vue'
|
||||||
import FormSecurityPrivacy from './form-components/FormSecurityPrivacy.vue'
|
import FormSecurityPrivacy from './form-components/FormSecurityPrivacy.vue'
|
||||||
import FormCustomSeo from './form-components/FormCustomSeo.vue'
|
import FormCustomSeo from './form-components/FormCustomSeo.vue'
|
||||||
import saveUpdateAlert from '../../../../mixins/forms/saveUpdateAlert.js'
|
import saveUpdateAlert from '../../../../mixins/forms/saveUpdateAlert.js'
|
||||||
|
import fieldsLogic from '../../../../mixins/forms/fieldsLogic.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormEditor',
|
name: 'FormEditor',
|
||||||
|
@ -85,7 +86,7 @@ export default {
|
||||||
FormSecurityPrivacy,
|
FormSecurityPrivacy,
|
||||||
FormCustomSeo
|
FormCustomSeo
|
||||||
},
|
},
|
||||||
mixins: [saveUpdateAlert],
|
mixins: [saveUpdateAlert, fieldsLogic],
|
||||||
props: {
|
props: {
|
||||||
isEdit: {
|
isEdit: {
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -176,6 +177,7 @@ export default {
|
||||||
this.showFormErrorModal = true
|
this.showFormErrorModal = true
|
||||||
},
|
},
|
||||||
saveForm() {
|
saveForm() {
|
||||||
|
this.form.properties = this.validateFieldsLogic(this.form.properties)
|
||||||
if(this.isGuest) {
|
if(this.isGuest) {
|
||||||
this.saveFormGuest()
|
this.saveFormGuest()
|
||||||
} else if (this.isEdit) {
|
} else if (this.isEdit) {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
import OpenFilters from '../../data/open_filters.json'
|
||||||
|
class FormPropertyLogicRule {
|
||||||
|
property = null
|
||||||
|
logic = null
|
||||||
|
isConditionCorrect = true
|
||||||
|
isActionCorrect = true
|
||||||
|
ACTIONS_VALUES = [
|
||||||
|
'show-block',
|
||||||
|
'hide-block',
|
||||||
|
'make-it-optional',
|
||||||
|
'require-answer',
|
||||||
|
'enable-block',
|
||||||
|
'disable-block'
|
||||||
|
]
|
||||||
|
CONDITION_MAPPING = OpenFilters
|
||||||
|
|
||||||
|
constructor (property) {
|
||||||
|
this.property = property
|
||||||
|
this.logic = (property.logic !== undefined && property.logic) ? property.logic : null
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid () {
|
||||||
|
if (this.logic && this.logic['conditions']) {
|
||||||
|
this.checkConditions(this.logic['conditions'])
|
||||||
|
this.checkActions((this.logic && this.logic['actions']) ? this.logic['actions'] : null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isConditionCorrect && this.isActionCorrect
|
||||||
|
}
|
||||||
|
|
||||||
|
checkConditions (conditions) {
|
||||||
|
if (conditions && conditions['operatorIdentifier']) {
|
||||||
|
if ((conditions['operatorIdentifier'] !== 'and') && (conditions['operatorIdentifier'] !== 'or')) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditions['operatorIdentifier']['children'] !== undefined || !Array.isArray(conditions['children'])) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conditions['children'].forEach(childrenCondition => {
|
||||||
|
this.checkConditions(childrenCondition)
|
||||||
|
})
|
||||||
|
} else if (conditions && conditions['identifier']) {
|
||||||
|
this.checkBaseCondition(conditions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkBaseCondition (condition) {
|
||||||
|
if (condition['value'] === undefined ||
|
||||||
|
condition['value']['property_meta'] === undefined ||
|
||||||
|
condition['value']['property_meta']['type'] === undefined ||
|
||||||
|
condition['value']['operator'] === undefined ||
|
||||||
|
condition['value']['value'] === undefined
|
||||||
|
) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeField = condition['value']['property_meta']['type']
|
||||||
|
const operator = condition['value']['operator']
|
||||||
|
const value = condition['value']['value']
|
||||||
|
|
||||||
|
if (this.CONDITION_MAPPING[typeField] === undefined ||
|
||||||
|
this.CONDITION_MAPPING[typeField]['comparators'][operator] === undefined
|
||||||
|
) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = this.CONDITION_MAPPING[typeField]['comparators'][operator]['expected_type']
|
||||||
|
if (Array.isArray(type)) {
|
||||||
|
let foundCorrectType = false
|
||||||
|
type.forEach(subtype => {
|
||||||
|
if (this.valueHasCorrectType(subtype, value)) {
|
||||||
|
foundCorrectType = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!foundCorrectType) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.valueHasCorrectType(type, value)) {
|
||||||
|
this.isConditionCorrect = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valueHasCorrectType (type, value) {
|
||||||
|
if (
|
||||||
|
(type === 'string' && typeof value !== 'string') ||
|
||||||
|
(type === 'boolean' && typeof value !== 'boolean') ||
|
||||||
|
(type === 'number' && typeof value !== 'number') ||
|
||||||
|
(type === 'object' && !Array.isArray(value))
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
checkActions (conditions) {
|
||||||
|
if (Array.isArray(conditions) && conditions.length > 0) {
|
||||||
|
conditions.forEach(val => {
|
||||||
|
if (this.ACTIONS_VALUES.indexOf(val) === -1 ||
|
||||||
|
(['nf-text', 'nf-code', 'nf-page-break', 'nf-divider', 'nf-image'].indexOf(this.property["type"]) > -1 && ['hide-block', 'show-block'].indexOf(val) === -1) ||
|
||||||
|
(this.property["hidden"] !== undefined && this.property["hidden"] && ['show-block', 'require-answer'].indexOf(val) === -1) ||
|
||||||
|
(this.property["required"] !== undefined && this.property["required"] && ['make-it-optional', 'hide-block', 'disable-block'].indexOf(val) === -1) ||
|
||||||
|
(this.property["disabled"] !== undefined && this.property["disabled"] && ['enable-block', 'require-answer', 'make-it-optional'].indexOf(val) === -1)
|
||||||
|
) {
|
||||||
|
this.isActionCorrect = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.isActionCorrect = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FormPropertyLogicRule
|
|
@ -0,0 +1,17 @@
|
||||||
|
import FormPropertyLogicRule from '../../forms/FormPropertyLogicRule.js'
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
validateFieldsLogic (properties) {
|
||||||
|
properties.forEach((field) => {
|
||||||
|
const isValid = (new FormPropertyLogicRule(field)).isValid()
|
||||||
|
if(!isValid){
|
||||||
|
field.logic = {
|
||||||
|
conditions: null,
|
||||||
|
actions: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,22 @@ it('can validate form logic rules for actions', function () {
|
||||||
'hidden' => true,
|
'hidden' => true,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'logic' => [
|
'logic' => [
|
||||||
"conditions" => null,
|
"conditions" => [
|
||||||
|
"operatorIdentifier"=> "and",
|
||||||
|
"children"=> [
|
||||||
|
[
|
||||||
|
"identifier"=> "title",
|
||||||
|
"value"=> [
|
||||||
|
"operator"=> "equals",
|
||||||
|
"property_meta"=> [
|
||||||
|
"id"=> "title",
|
||||||
|
"type"=> "text"
|
||||||
|
],
|
||||||
|
"value"=> "TEST"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
"actions" => ['hide-block']
|
"actions" => ['hide-block']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -51,7 +66,22 @@ it('can validate form logic rules for actions', function () {
|
||||||
'name' => "Custom Test",
|
'name' => "Custom Test",
|
||||||
'type' => 'nf-text',
|
'type' => 'nf-text',
|
||||||
'logic' => [
|
'logic' => [
|
||||||
"conditions" => null,
|
"conditions" => [
|
||||||
|
"operatorIdentifier"=> "and",
|
||||||
|
"children"=> [
|
||||||
|
[
|
||||||
|
"identifier"=> "title",
|
||||||
|
"value"=> [
|
||||||
|
"operator"=> "equals",
|
||||||
|
"property_meta"=> [
|
||||||
|
"id"=> "title",
|
||||||
|
"type"=> "text"
|
||||||
|
],
|
||||||
|
"value"=> "TEST"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
"actions" => ['require-answer']
|
"actions" => ['require-answer']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -93,7 +123,7 @@ it('can validate form logic rules for conditions', function () {
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"actions" => []
|
"actions" => ['hide-block']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -126,7 +156,7 @@ it('can validate form logic rules for conditions', function () {
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"actions" => []
|
"actions" => ['hide-block']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -160,7 +190,7 @@ it('can validate form logic rules for conditions', function () {
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"actions" => []
|
"actions" => ['hide-block']
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue