Fix logic on hidden, loading on edit submission (#75)
This commit is contained in:
parent
6ac68a29e5
commit
1b15597e0e
|
@ -45,7 +45,7 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
|
|
||||||
$this->storeSubmission($formData);
|
$this->storeSubmission($formData);
|
||||||
|
|
||||||
$formData["submission_id"] = $this->submissionData['submission_id'] ?? '';
|
$formData["submission_id"] = $this->submissionData['submission_id'] ?? null;
|
||||||
FormSubmitted::dispatch($this->form, $formData);
|
FormSubmitted::dispatch($this->form, $formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SubmissionConfirmationMail extends OpenFormMail implements ShouldQueue
|
||||||
'fields' => $formatter->getFieldsWithValue(),
|
'fields' => $formatter->getFieldsWithValue(),
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
'noBranding' => $form->no_branding,
|
'noBranding' => $form->no_branding,
|
||||||
'submission_id' => isset($this->event->data['submission_id']) ? $this->event->data['submission_id'] : ''
|
'submission_id' => $this->event->data['submission_id'] ?? null
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -339,7 +339,7 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'does_not_contain' => [
|
'does_not_contain' => [
|
||||||
'expected_type' => 'object',
|
'expected_type' => ['object', 'string'],
|
||||||
'format' => [
|
'format' => [
|
||||||
'type' => 'uuid',
|
'type' => 'uuid',
|
||||||
]
|
]
|
||||||
|
@ -472,6 +472,7 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
|
|
||||||
private $isConditionCorrect = true;
|
private $isConditionCorrect = true;
|
||||||
private $isActionCorrect = true;
|
private $isActionCorrect = true;
|
||||||
|
private $conditionErrors = [];
|
||||||
private $field = [];
|
private $field = [];
|
||||||
private $data = [];
|
private $data = [];
|
||||||
|
|
||||||
|
@ -480,26 +481,31 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
|
|
||||||
if (!isset($condition['value'])) {
|
if (!isset($condition['value'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'missing condition body';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($condition['value']['property_meta'])) {
|
if (!isset($condition['value']['property_meta'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'missing condition property';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($condition['value']['property_meta']['type'])) {
|
if (!isset($condition['value']['property_meta']['type'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'missing condition property type';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($condition['value']['operator'])) {
|
if (!isset($condition['value']['operator'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'missing condition operator';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($condition['value']['value'])) {
|
if (!isset($condition['value']['value'])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'missing condition value';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,14 +515,18 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
|
|
||||||
if (!isset(self::CONDITION_MAPPING[$typeField])) {
|
if (!isset(self::CONDITION_MAPPING[$typeField])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'configuration not found for condition type';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset(self::CONDITION_MAPPING[$typeField]['comparators'][$operator])) {
|
if (!isset(self::CONDITION_MAPPING[$typeField]['comparators'][$operator])) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'configuration not found for condition operator';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: find what's causing the issue when saving this validation rule :(
|
||||||
|
|
||||||
$type = self::CONDITION_MAPPING[$typeField]['comparators'][$operator]['expected_type'];
|
$type = self::CONDITION_MAPPING[$typeField]['comparators'][$operator]['expected_type'];
|
||||||
|
|
||||||
if (is_array($type)) {
|
if (is_array($type)) {
|
||||||
|
@ -532,6 +542,7 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
} else {
|
} else {
|
||||||
if (!$this->valueHasCorrectType($type, $value)) {
|
if (!$this->valueHasCorrectType($type, $value)) {
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
|
$this->conditionErrors[] = 'wrong type of condition value';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -553,16 +564,19 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
{
|
{
|
||||||
if (isset($conditions['operatorIdentifier'])) {
|
if (isset($conditions['operatorIdentifier'])) {
|
||||||
if (($conditions['operatorIdentifier'] !== 'and') && ($conditions['operatorIdentifier'] !== 'or')) {
|
if (($conditions['operatorIdentifier'] !== 'and') && ($conditions['operatorIdentifier'] !== 'or')) {
|
||||||
|
$this->conditionErrors[] = 'missing operator';
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($conditions['operatorIdentifier']['children'])) {
|
if (isset($conditions['operatorIdentifier']['children'])) {
|
||||||
|
$this->conditionErrors[] = 'extra condition';
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($conditions['children'])) {
|
if (!is_array($conditions['children'])) {
|
||||||
|
$this->conditionErrors[] = 'wrong sub-condition type';
|
||||||
$this->isConditionCorrect = false;
|
$this->isConditionCorrect = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -616,14 +630,17 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
*/
|
*/
|
||||||
public function message()
|
public function message()
|
||||||
{
|
{
|
||||||
$errorList = [];
|
$message = null;
|
||||||
if (! $this->isConditionCorrect) {
|
if (! $this->isConditionCorrect) {
|
||||||
$errorList[] = "The logic conditions for ".$this->field['name']." are not complete.";
|
$message = 'The logic conditions for '.$this->field['name'].' are not complete.';
|
||||||
|
} else if (! $this->isActionCorrect) {
|
||||||
|
$message = 'The logic actions for '.$this->field['name'].' are not valid.';
|
||||||
}
|
}
|
||||||
if(!$this->isActionCorrect){
|
if (count($this->conditionErrors) > 0) {
|
||||||
$errorList[] = "The logic actions for ".$this->field['name']." are not valid.";
|
return $message . ' Error detail(s): '.implode(', ', $this->conditionErrors);
|
||||||
}
|
}
|
||||||
return $errorList;
|
|
||||||
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -635,6 +652,9 @@ class FormPropertyLogicRule implements Rule, DataAwareRule
|
||||||
public function setData($data)
|
public function setData($data)
|
||||||
{
|
{
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
|
$this->isConditionCorrect = true;
|
||||||
|
$this->isActionCorrect = true;
|
||||||
|
$this->conditionErrors = [];
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ class FormLogicConditionChecker
|
||||||
return str_starts_with($fieldValue, $condition['value']);
|
return str_starts_with($fieldValue, $condition['value']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkendsWith ($condition, $fieldValue): bool {
|
private function checkEndsWith ($condition, $fieldValue): bool {
|
||||||
return str_ends_with($fieldValue, $condition['value']);
|
return str_ends_with($fieldValue, $condition['value']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ class FormLogicConditionChecker
|
||||||
case 'starts_with':
|
case 'starts_with':
|
||||||
return $this->checkStartsWith($propertyCondition, $value);
|
return $this->checkStartsWith($propertyCondition, $value);
|
||||||
case 'ends_with':
|
case 'ends_with':
|
||||||
return $this->checkendsWith($propertyCondition, $value);
|
return $this->checkEndsWith($propertyCondition, $value);
|
||||||
case 'is_empty':
|
case 'is_empty':
|
||||||
return $this->checkIsEmpty($propertyCondition, $value);
|
return $this->checkIsEmpty($propertyCondition, $value);
|
||||||
case 'is_not_empty':
|
case 'is_not_empty':
|
||||||
|
|
|
@ -23,6 +23,11 @@ class FormLogicPropertyResolver
|
||||||
return (new self($property, $values))->shouldBeRequired();
|
return (new self($property, $values))->shouldBeRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function isHidden(array $property, array $values): bool
|
||||||
|
{
|
||||||
|
return (new self($property, $values))->shouldBeHidden();
|
||||||
|
}
|
||||||
|
|
||||||
public function shouldBeRequired(): bool
|
public function shouldBeRequired(): bool
|
||||||
{
|
{
|
||||||
if(!isset($this->property['required'])){
|
if(!isset($this->property['required'])){
|
||||||
|
@ -42,4 +47,24 @@ class FormLogicPropertyResolver
|
||||||
return $this->property['required'];
|
return $this->property['required'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldBeHidden(): bool
|
||||||
|
{
|
||||||
|
if (! isset($this->property['hidden'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->logic) {
|
||||||
|
return $this->property['hidden'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData);
|
||||||
|
if ($conditionsMet && $this->property['hidden'] && count($this->logic['actions']) > 0 && in_array('show-block', $this->logic['actions'])) {
|
||||||
|
return false;
|
||||||
|
} elseif ($conditionsMet && !$this->property['hidden'] && count($this->logic['actions']) > 0 && in_array('hide-block', $this->logic['actions'])) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return $this->property['hidden'];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,14 @@ class FormSubmissionFormatter
|
||||||
|
|
||||||
private $showRemovedFields = false;
|
private $showRemovedFields = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic resolver needs an array id => value, so we create it here
|
||||||
|
*/
|
||||||
|
private $idFormData = null;
|
||||||
|
|
||||||
public function __construct(private Form $form, private array $formData)
|
public function __construct(private Form $form, private array $formData)
|
||||||
{
|
{
|
||||||
|
$this->initIdFormData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createLinks()
|
public function createLinks()
|
||||||
|
@ -88,9 +94,9 @@ class FormSubmissionFormatter
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If should hide hidden fields
|
// If hide hidden fields
|
||||||
if (!$this->showHiddenFields) {
|
if (!$this->showHiddenFields) {
|
||||||
if (isset($field['hidden']) && $field['hidden']) {
|
if (FormLogicPropertyResolver::isHidden($field, $this->idFormData)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +155,7 @@ class FormSubmissionFormatter
|
||||||
|
|
||||||
// If hide hidden fields
|
// If hide hidden fields
|
||||||
if (!$this->showHiddenFields) {
|
if (!$this->showHiddenFields) {
|
||||||
if (isset($field['hidden']) && $field['hidden']) {
|
if (FormLogicPropertyResolver::isHidden($field, $this->idFormData)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,4 +210,16 @@ class FormSubmissionFormatter
|
||||||
return $transformedFields;
|
return $transformedFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function initIdFormData() {
|
||||||
|
$formProperties = collect($this->form->properties);
|
||||||
|
foreach ($this->formData as $key => $value) {
|
||||||
|
$property = $formProperties->first(function ($item) use ($key) {
|
||||||
|
return $item['id'] == $key;
|
||||||
|
});
|
||||||
|
if ($property) {
|
||||||
|
$this->idFormData[$property['id']] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.form.editable_submissions && this.form.submission_id) {
|
if (this.form.editable_submissions && this.form.submission_id) {
|
||||||
this.dataForm["submission_id"] = this.form.submission_id
|
this.dataForm.submission_id = this.form.submission_id
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('submit', this.dataForm, this.onSubmissionFailure)
|
this.$emit('submit', this.dataForm, this.onSubmissionFailure)
|
||||||
|
@ -290,18 +290,20 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getSubmissionData() {
|
async getSubmissionData() {
|
||||||
if (!this.form || !this.form.editable_submissions || !this.form.submission_id) {
|
if (!this.form || !this.form.editable_submissions || !this.form.submission_id) { return null }
|
||||||
return null
|
await this.$store.dispatch('open/records/loadRecord',
|
||||||
}
|
axios.get('/api/forms/' + this.form.slug + '/submissions/' + this.form.submission_id).then((response) => {
|
||||||
const response = await axios.get('/api/forms/' + this.form.slug + '/submissions/' + this.form.submission_id)
|
return { submission_id: this.form.submission_id, ...response.data.data }
|
||||||
return response.data
|
})
|
||||||
|
)
|
||||||
|
return this.$store.getters['open/records/getById'](this.form.submission_id)
|
||||||
},
|
},
|
||||||
async initForm() {
|
async initForm() {
|
||||||
if (this.isPublicFormPage && this.form.editable_submissions) {
|
if (this.isPublicFormPage && this.form.editable_submissions) {
|
||||||
let urlParam = new URLSearchParams(window.location.search)
|
const urlParam = new URLSearchParams(window.location.search)
|
||||||
if (urlParam && urlParam.get('submission_id')) {
|
if (urlParam && urlParam.get('submission_id')) {
|
||||||
this.form.submission_id = urlParam.get('submission_id')
|
this.form.submission_id = urlParam.get('submission_id')
|
||||||
const {data} = await this.getSubmissionData()
|
const data = await this.getSubmissionData()
|
||||||
if (data !== null && data) {
|
if (data !== null && data) {
|
||||||
this.dataForm = new Form(data)
|
this.dataForm = new Form(data)
|
||||||
return
|
return
|
||||||
|
|
|
@ -34,7 +34,16 @@
|
||||||
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<open-complete-form v-else ref="open-complete-form" :form="form" class="mb-10" @password-entered="passwordEntered" />
|
<template v-else>
|
||||||
|
<div v-if="recordLoading">
|
||||||
|
<p class="text-center mt-6 p-4">
|
||||||
|
<loader class="h-6 w-6 text-nt-blue mx-auto" />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<open-complete-form v-show="!recordLoading" ref="open-complete-form" :form="form" class="mb-10"
|
||||||
|
@password-entered="passwordEntered"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -125,7 +134,6 @@ export default {
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
|
||||||
submitted: false
|
submitted: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -160,7 +168,8 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
forms: state => state['open/forms'].content,
|
forms: state => state['open/forms'].content,
|
||||||
formLoading: state => state['open/forms'].loading
|
formLoading: state => state['open/forms'].loading,
|
||||||
|
recordLoading: state => state['open/records'].loading
|
||||||
}),
|
}),
|
||||||
formSlug () {
|
formSlug () {
|
||||||
return this.$route.params.slug
|
return this.$route.params.slug
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export const namespaced = true
|
||||||
|
export const workspaceEndpoint = '/api/open/records/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads records from database
|
||||||
|
*/
|
||||||
|
|
||||||
|
// state
|
||||||
|
export const state = {
|
||||||
|
content: [],
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getters
|
||||||
|
export const getters = {
|
||||||
|
getById: (state) => (id) => {
|
||||||
|
if (state.content.length === 0) return null
|
||||||
|
return state.content.find(item => item.submission_id === id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mutations
|
||||||
|
export const mutations = {
|
||||||
|
set (state, items) {
|
||||||
|
state.content = items
|
||||||
|
},
|
||||||
|
addOrUpdate (state, item) {
|
||||||
|
state.content = state.content.filter((val) => val.id !== item.id)
|
||||||
|
state.content.push(item)
|
||||||
|
},
|
||||||
|
remove (state, itemId) {
|
||||||
|
state.content = state.content.filter((val) => val.id !== itemId)
|
||||||
|
},
|
||||||
|
startLoading () {
|
||||||
|
state.loading = true
|
||||||
|
},
|
||||||
|
stopLoading () {
|
||||||
|
state.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// actions
|
||||||
|
export const actions = {
|
||||||
|
resetState (context) {
|
||||||
|
context.commit('set', [])
|
||||||
|
context.commit('stopLoading')
|
||||||
|
},
|
||||||
|
loadRecord (context, request) {
|
||||||
|
context.commit('set', [])
|
||||||
|
context.commit('startLoading')
|
||||||
|
return request.then((data) => {
|
||||||
|
context.commit('addOrUpdate', data)
|
||||||
|
context.commit('stopLoading')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -134,5 +134,5 @@ it('can validate form logic rules for conditions', function () {
|
||||||
|
|
||||||
$validatorObj = $this->app['validator']->make($data, $rules);
|
$validatorObj = $this->app['validator']->make($data, $rules);
|
||||||
$this->assertFalse($validatorObj->passes());
|
$this->assertFalse($validatorObj->passes());
|
||||||
expect($validatorObj->errors()->messages()['properties.0.logic'][0])->toBe("The logic conditions for Name are not complete.");
|
expect($validatorObj->errors()->messages()['properties.0.logic'][0])->toBe("The logic conditions for Name are not complete. Error detail(s): missing condition value");
|
||||||
});
|
});
|
Loading…
Reference in New Issue