@processmaker/screen-builder 2.25.1 → 2.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/screen-builder",
3
- "version": "2.25.1",
3
+ "version": "2.26.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -10192,6 +10192,11 @@
10192
10192
  "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
10193
10193
  "dev": true
10194
10194
  },
10195
+ "is-proxy": {
10196
+ "version": "1.0.6",
10197
+ "resolved": "https://registry.npmjs.org/is-proxy/-/is-proxy-1.0.6.tgz",
10198
+ "integrity": "sha512-RzUROKx085vc7tpo9zCinKAmsD7ihAnXJ7HvKdcsc3m0XW/vpJDFSvZgutqMam0aBxpYBmDVYv48+T/+AMYl1g=="
10199
+ },
10195
10200
  "is-regex": {
10196
10201
  "version": "1.1.3",
10197
10202
  "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/screen-builder",
3
- "version": "2.25.1",
3
+ "version": "2.26.0",
4
4
  "scripts": {
5
5
  "serve": "vue-cli-service serve",
6
6
  "build": "vue-cli-service build",
@@ -30,6 +30,7 @@
30
30
  "*.js"
31
31
  ],
32
32
  "dependencies": {
33
+ "is-proxy": "^1.0.6",
33
34
  "lodash": "^4.17.20",
34
35
  "moment": "^2.29.1",
35
36
  "moment-timezone": "^0.5.27",
package/src/App.vue CHANGED
@@ -74,6 +74,13 @@
74
74
  <!-- Preview -->
75
75
  <b-row class="h-100 m-0" id="preview" v-show="displayPreview" data-cy="preview">
76
76
  <b-col class="overflow-auto h-100" data-cy="preview-content">
77
+ <div v-if="$store.getters['globalErrorsModule/isValidScreen'] === false" class="alert alert-danger mt-3">
78
+ <i class="fas fa-exclamation-circle"/>
79
+ {{ $store.getters['globalErrorsModule/getErrorMessage'] }}
80
+ <button type="button" class="close" aria-label="Close" @click="$store.dispatch('globalErrorsModule/close')">
81
+ <span aria-hidden="true">&times;</span>
82
+ </button>
83
+ </div>
77
84
  <vue-form-renderer ref="renderer"
78
85
  :key="rendererKey"
79
86
  v-model="previewData"
@@ -422,10 +429,13 @@ export default {
422
429
  this.previewData = this.previewInputValid ? JSON.parse(this.previewInput) : {};
423
430
  this.rendererKey++;
424
431
  if (mode == 'preview') {
432
+ this.$dataProvider.flushScreenCache();
425
433
  this.preview.config = cloneDeep(this.config);
426
434
  this.preview.computed = cloneDeep(this.computed);
427
435
  this.preview.customCSS = cloneDeep(this.customCSS);
428
436
  this.preview.watchers = cloneDeep(this.watchers);
437
+ } else {
438
+ this.$refs.builder.refreshContent();
429
439
  }
430
440
  },
431
441
  loadFromLocalStorage() {
@@ -109,6 +109,10 @@ export default {
109
109
  }
110
110
  },
111
111
 
112
+ flushScreenCache() {
113
+ this.cachedScreenPromises.splice(0, this.cachedScreenPromises.length);
114
+ },
115
+
112
116
  postScript(id, params, options = {}) {
113
117
  let endpoint = _.get(
114
118
  window,
@@ -10,6 +10,7 @@ class Validations {
10
10
  screen = null;
11
11
  firstPage = 0;
12
12
  data = {};
13
+ insideLoop = false;
13
14
  constructor(element, options) {
14
15
  this.element = element;
15
16
  Object.assign(this, options);
@@ -51,7 +52,7 @@ class Validations {
51
52
  class ArrayOfFieldsValidations extends Validations {
52
53
  async addValidations(validations) {
53
54
  for (const item of this.element) {
54
- await ValidationsFactory(item, { screen: this.screen, data: this.data, parentVisibilityRule: this.parentVisibilityRule }).addValidations(validations);
55
+ await ValidationsFactory(item, { screen: this.screen, data: this.data, parentVisibilityRule: this.parentVisibilityRule, insideLoop: this.insideLoop }).addValidations(validations);
55
56
  }
56
57
  }
57
58
  }
@@ -114,13 +115,16 @@ class FormLoopValidations extends Validations {
114
115
  const loopField = get(validations, this.element.config.name);
115
116
  loopField['$each'] = {};
116
117
  this.checkForSiblings(validations);
117
- await ValidationsFactory(this.element.items, { screen: this.screen, data: {_parent: this.data, noData:true }, parentVisibilityRule: this.element.config.conditionalHide }).addValidations(loopField['$each']);
118
+ await ValidationsFactory(this.element.items, { screen: this.screen, data: {_parent: this.data, noData:true }, parentVisibilityRule: this.element.config.conditionalHide, insideLoop: true }).addValidations(loopField['$each']);
118
119
  }
119
120
  checkForSiblings(validations) {
120
121
  const siblings = [];
121
122
  const siblingValidations = [];
122
123
  // Find loops that reference the same variable
123
124
  this.screen.config.forEach(page => {
125
+ if (!page || !page.items) {
126
+ return;
127
+ }
124
128
  page.items.filter(item => {
125
129
  if (item.component === 'FormLoop' && item.config.name === this.element.config.name) {
126
130
  siblings.push(item);
@@ -215,6 +219,7 @@ class FormElementValidations extends Validations {
215
219
  const validationConfig = this.element.config.validation;
216
220
  const conditionalHide = this.element.config.conditionalHide;
217
221
  const parentVisibilityRule = this.parentVisibilityRule;
222
+ const insideLoop = this.insideLoop || false;
218
223
 
219
224
  set(validations, fieldName, get(validations, fieldName, {}));
220
225
  const fieldValidation = get(validations, fieldName);
@@ -240,16 +245,14 @@ class FormElementValidations extends Validations {
240
245
  }
241
246
  fieldValidation[rule] = function(...props) {
242
247
  const data = props[1];
243
- let dataWithParent = this.addReferenceToParents(data);
244
- const nestedDataWithParent = this.addReferenceToParents(this.findParent(data));
245
- if (nestedDataWithParent) {
246
- dataWithParent = Object.assign(nestedDataWithParent, dataWithParent);
247
- }
248
- // Check Parent Visibility
248
+ const level = fieldName.split('.').length - 1;
249
+ const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level);
249
250
  if (parentVisibilityRule) {
251
+ const nextParentLevel = insideLoop ? 1 : 0;
252
+ const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel);
250
253
  let isParentVisible = true;
251
254
  try {
252
- isParentVisible = !!Parser.evaluate(parentVisibilityRule, dataWithParent);
255
+ isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent);
253
256
  } catch (error) {
254
257
  isParentVisible = false;
255
258
  }
@@ -282,16 +285,15 @@ class FormElementValidations extends Validations {
282
285
  }
283
286
  fieldValidation[validationConfig] = function(...props) {
284
287
  const data = props[1];
285
- let dataWithParent = this.addReferenceToParents(data);
286
- const nestedDataWithParent = this.addReferenceToParents(this.findParent(data));
287
- if (nestedDataWithParent) {
288
- dataWithParent = Object.assign(nestedDataWithParent, dataWithParent);
289
- }
288
+ const level = fieldName.split('.').length - 1;
289
+ const dataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level);
290
290
  // Check Parent Visibility
291
291
  if (parentVisibilityRule) {
292
+ const nextParentLevel = insideLoop ? 1 : 0;
293
+ const parentDataWithParent = this.getDataAccordingToFieldLevel(this.getRootScreen().addReferenceToParents(data), level + nextParentLevel);
292
294
  let isParentVisible = true;
293
295
  try {
294
- isParentVisible = !!Parser.evaluate(parentVisibilityRule, dataWithParent);
296
+ isParentVisible = !!Parser.evaluate(parentVisibilityRule, parentDataWithParent);
295
297
  } catch (error) {
296
298
  isParentVisible = false;
297
299
  }
@@ -95,7 +95,10 @@ export default {
95
95
  return false;
96
96
  }
97
97
  }
98
- this.$emit('input', this.storeId ? get(value, this.trackBy) : value);
98
+ const id = this.storeId ? get(value, this.trackBy) : value;
99
+ // Make sure to load latest config from screen and nested screens
100
+ this.$dataProvider.flushScreenCache();
101
+ this.$emit('input', id);
99
102
  },
100
103
  },
101
104
  mounted() {
@@ -107,6 +110,7 @@ export default {
107
110
  pmql += ' and id != ' + this.builder.screen.id;
108
111
  }
109
112
  this.pmql = pmql;
113
+ this.fields = 'screens.id,title';
110
114
  },
111
115
  };
112
116
  </script>
@@ -1,9 +1,5 @@
1
1
  <template>
2
2
  <div class="form-group" style="overflow-x: hidden">
3
- <div class="alert alert-danger" v-if="!valid">
4
- <i class="fas fa-exclamation-circle"/>
5
- {{ message }}
6
- </div>
7
3
  <button v-b-tooltip="options" @click="click" :class="classList" :name="name" :aria-label="$attrs['aria-label']" :tabindex="$attrs['tabindex']">
8
4
  {{ label }}
9
5
  </button>
@@ -13,17 +9,39 @@
13
9
  <script>
14
10
  import Mustache from 'mustache';
15
11
  import { getValidPath } from '@/mixins';
12
+ import { isProxy } from 'is-proxy';
16
13
 
17
14
  export default {
18
15
  mixins: [getValidPath],
19
16
  props: ['variant', 'label', 'event', 'eventData', 'name', 'fieldValue', 'value', 'tooltip', 'transientData'],
17
+ watch: {
18
+ '$attrs.validate': {
19
+ deep: true,
20
+ handler(validate) {
21
+ if (validate && !isProxy(validate.vdata.$model)) {
22
+ this.errors = 0;
23
+ let message = '';
24
+ if (validate.$invalid) {
25
+ this.countErrors(validate.vdata);
26
+ this.countErrors(validate.schema);
27
+ message = this.errors === 1
28
+ ? 'There is a validation error in your form.'
29
+ : 'There are {{items}} validation errors in your form.';
30
+ message = this.$t(message, {items: this.errors});
31
+ }
32
+ this.$store.commit('globalErrorsModule/basic', {key: 'valid', value: !validate.$invalid});
33
+ this.$store.commit('globalErrorsModule/basic', {key: 'message', value: message});
34
+ }
35
+ },
36
+ },
37
+ },
20
38
  computed: {
21
39
  classList() {
22
40
  let variant = this.variant || 'primary';
23
41
  return {
24
42
  btn: true,
25
43
  ['btn-' + variant]: true,
26
- disabled: !this.valid,
44
+ disabled: this.errors,
27
45
  };
28
46
  },
29
47
  options() {
@@ -45,26 +63,6 @@ export default {
45
63
  boundary: 'window',
46
64
  };
47
65
  },
48
- valid() {
49
- if (this.$attrs.validate) {
50
- return !this.$attrs.validate.$invalid;
51
- }
52
- return true;
53
- },
54
- message() {
55
- // eslint-disable-next-line vue/no-side-effects-in-computed-properties
56
- this.errors = 0;
57
- if (!this.valid) {
58
- this.countErrors(this.$attrs.validate.vdata);
59
- this.countErrors(this.$attrs.validate.schema);
60
- let message = 'There are {{items}} validation errors in your form.';
61
- if (this.errors === 1) {
62
- message = 'There is a validation error in your form.';
63
- }
64
- return this.$t(message, {items: this.errors});
65
- }
66
- return '';
67
- },
68
66
  },
69
67
  data() {
70
68
  return {
@@ -124,6 +122,3 @@ export default {
124
122
  },
125
123
  };
126
124
  </script>
127
-
128
- <style lang="scss">
129
- </style>
@@ -21,12 +21,11 @@
21
21
  :fields="tableFields"
22
22
  :items="tableData.data"
23
23
  :sort-compare-options="{ numeric: false }"
24
+ :sort-null-last="true"
24
25
  sort-icon-left
25
26
  :css="css"
26
27
  :empty-text="$t('No Data Available')"
27
28
  :current-page="currentPage"
28
- @sort-changed="sortChanged"
29
- @input="onInput"
30
29
  data-cy="table"
31
30
  >
32
31
  <template #cell()="{index,field,item}">
@@ -40,13 +39,13 @@
40
39
  {{ formatIfDate(mustache(field.key, item)) }}
41
40
  </template>
42
41
  </template>
43
- <template #cell(__actions)="{index}">
42
+ <template #cell(__actions)="{index,item}">
44
43
  <div class="actions">
45
44
  <div class="btn-group btn-group-sm" role="group" aria-label="Actions">
46
- <button @click="showEditForm(index)" class="btn btn-primary" :title="$t('Edit')" data-cy="edit-row">
45
+ <button @click="showEditForm(index, item.row_id)" class="btn btn-primary" :title="$t('Edit')" data-cy="edit-row">
47
46
  <i class="fas fa-edit"/>
48
47
  </button>
49
- <button @click="showDeleteConfirmation(index)" class="btn btn-danger" :title="$t('Delete')" data-cy="remove-row">
48
+ <button @click="showDeleteConfirmation(index, item.row_id)" class="btn btn-danger" :title="$t('Delete')" data-cy="remove-row">
50
49
  <i class="fas fa-trash-alt"/>
51
50
  </button>
52
51
  </div>
@@ -287,21 +286,6 @@ export default {
287
286
  updateRowDataNamePrefix() {
288
287
  this.setUploadDataNamePrefix(this.currentRowIndex);
289
288
  },
290
- sortChanged(payload) {
291
- this.lastSortConfig = payload;
292
- this.tableData.data = this.sort(this.tableData.data, payload);
293
- },
294
- onInput() {
295
- if (this.lastSortConfig) {
296
- this.tableData.data = this.sort(this.tableData.data, this.lastSortConfig);
297
- }
298
- },
299
- sort(data, options) {
300
- if (options.sortDesc) {
301
- return data.sort((b,a) => a[options.sortBy].localeCompare(b[options.sortBy], 0, {numeric: false}));
302
- }
303
- return data.sort((a,b) => a[options.sortBy].localeCompare(b[options.sortBy], 0, {numeric: false}));
304
- },
305
289
  emitShownEvent() {
306
290
  window.ProcessMaker.EventBus.$emit('modal-shown');
307
291
  },
@@ -383,10 +367,10 @@ export default {
383
367
  this.paginatorPage = this.lastPage;
384
368
  }
385
369
  },
386
- showEditForm(index) {
370
+ showEditForm(index, rowId) {
387
371
  let pageIndex = ((this.currentPage-1) * this.perPage) + index;
388
372
  // Reset edit to be a copy of our data model item
389
- this.editItem = JSON.parse(JSON.stringify(this.tableData.data[pageIndex]));
373
+ this.editItem = _.find(this.tableData.data, {'row_id': rowId});
390
374
  this.editIndex = pageIndex;
391
375
  // rebuild the edit screen to avoid
392
376
  this.editFormVersion++;
@@ -403,10 +387,11 @@ export default {
403
387
 
404
388
  // Edit the item in our model and emit change
405
389
  let data = this.tableData.data ? JSON.parse(JSON.stringify(this.tableData.data)) : [];
406
- data[this.editIndex] = JSON.parse(JSON.stringify(this.editItem));
390
+ var index = _.findIndex(data, {'row_id': this.editItem.rowId});
391
+ data[index] = JSON.parse(JSON.stringify(this.editItem));
407
392
 
408
393
  // Remove the parent object
409
- delete data[this.editIndex]._parent;
394
+ delete data[index]._parent;
410
395
 
411
396
  // Emit the newly updated data model
412
397
  this.$emit('input', data);
@@ -450,9 +435,8 @@ export default {
450
435
  this.$refs.addModal.hide();
451
436
  });
452
437
  },
453
- showDeleteConfirmation(index) {
454
- let pageIndex = ((this.currentPage-1) * this.perPage) + index;
455
- this.deleteIndex = pageIndex;
438
+ showDeleteConfirmation(index, rowId) {
439
+ this.deleteIndex = _.find(this.tableData.data, {'row_id': rowId});
456
440
  this.$refs.deleteModal.show();
457
441
  },
458
442
  downloadFile(rowData, rowField, rowIndex) {
@@ -489,9 +473,11 @@ export default {
489
473
  // Add the item to our model and emit change
490
474
  // @todo Also check that value is an array type, if not, reset it to an array
491
475
  let data = this.tableData.data ? JSON.parse(JSON.stringify(this.tableData.data)) : [];
492
- let recordData = data[this.deleteIndex];
476
+ let recordData = this.deleteIndex;
493
477
  // Remove item from data array
494
- data.splice(this.deleteIndex, 1);
478
+ _.remove(data, {
479
+ 'row_id': this.deleteIndex.row_id,
480
+ });
495
481
  // Emit the newly updated data model
496
482
  this.$emit('input', data);
497
483
  this.$root.$emit('removed-record', this, recordData);
@@ -8,6 +8,13 @@
8
8
  <template v-if="screen">
9
9
  <div class="card card-body border-top-0 h-100" :class="screenTypeClass">
10
10
  <div v-if="renderComponent === 'task-screen'">
11
+ <div v-if="$store.getters['globalErrorsModule/isValidScreen'] === false" class="alert alert-danger mt-3">
12
+ <i class="fas fa-exclamation-circle"/>
13
+ {{ $store.getters['globalErrorsModule/getErrorMessage'] }}
14
+ <button type="button" class="close" aria-label="Close" @click="$store.dispatch('globalErrorsModule/close')">
15
+ <span aria-hidden="true">&times;</span>
16
+ </button>
17
+ </div>
11
18
  <vue-form-renderer
12
19
  ref="renderer"
13
20
  v-model="requestData"
@@ -102,6 +102,7 @@
102
102
  class="h-100"
103
103
  ghost-class="form-control-ghost"
104
104
  :value="config[currentPage].items"
105
+ :key="editorContentKey"
105
106
  @input="updateConfig"
106
107
  v-bind="{
107
108
  group: {name: 'controls'},
@@ -311,6 +312,7 @@ import * as renderer from './renderer';
311
312
  import * as inspector from './inspector';
312
313
  import '@processmaker/vue-form-elements/dist/vue-form-elements.css';
313
314
  import undoRedoModule from '../undoRedoModule';
315
+ import globalErrorsModule from '@/store/modules/global-errors';
314
316
  import accordions from './accordions';
315
317
  import { keyNameProperty } from '../form-control-common-properties';
316
318
  import VariableNameGenerator from '@/components/VariableNameGenerator';
@@ -437,6 +439,7 @@ export default {
437
439
  variablesTree: [],
438
440
  language: 'en',
439
441
  collator: null,
442
+ editorContentKey: 0,
440
443
  };
441
444
  },
442
445
  computed: {
@@ -511,6 +514,9 @@ export default {
511
514
  },
512
515
  },
513
516
  methods: {
517
+ refreshContent() {
518
+ this.editorContentKey++;
519
+ },
514
520
  loadVariablesTree() {
515
521
  const definition = {
516
522
  config : this.$parent.config,
@@ -810,6 +816,13 @@ export default {
810
816
  }
811
817
  this.collator = Intl.Collator(this.language);
812
818
  },
819
+ registerStoreModule(moduleName, storeModule) {
820
+ const store = this.$store;
821
+
822
+ if (!(store && store.state && store.state[moduleName])) {
823
+ store.registerModule(moduleName, storeModule);
824
+ }
825
+ },
813
826
  },
814
827
  created() {
815
828
  this.loadVariablesTree = _.debounce(this.loadVariablesTree, 2000);
@@ -823,7 +836,8 @@ export default {
823
836
  },
824
837
  this.$t('Must be unique')
825
838
  );
826
- this.$store.registerModule('undoRedoModule', undoRedoModule);
839
+ this.registerStoreModule('globalErrorsModule', globalErrorsModule);
840
+ this.registerStoreModule('undoRedoModule', undoRedoModule);
827
841
  this.$store.dispatch('undoRedoModule/pushState', {'config': JSON.stringify(this.config), 'currentPage': this.currentPage});
828
842
  this.initiateLanguageSupport();
829
843
  },
@@ -13,6 +13,7 @@ import Inputmask from 'inputmask';
13
13
  import { getItemsFromConfig } from '../itemProcessingUtils';
14
14
  import { ValidatorFactory } from '../factories/ValidatorFactory';
15
15
  import CurrentPageProperty from '../mixins/CurrentPageProperty';
16
+ import globalErrorsModule from '@/store/modules/global-errors';
16
17
 
17
18
  const csstree = require('css-tree');
18
19
  const Scrollparent = require('scrollparent');
@@ -107,6 +108,7 @@ export default {
107
108
  },
108
109
  },
109
110
  created() {
111
+ this.registerStoreModule('globalErrorsModule', globalErrorsModule);
110
112
  this.parseCss = _.debounce(this.parseCss, 500, {leading: true});
111
113
  },
112
114
  mounted() {
@@ -118,6 +120,13 @@ export default {
118
120
  this.scrollable = Scrollparent(this.$el);
119
121
  },
120
122
  methods: {
123
+ registerStoreModule(moduleName, storeModule) {
124
+ const store = this.$store;
125
+
126
+ if (!(store && store.state && store.state[moduleName])) {
127
+ store.registerModule(moduleName, storeModule);
128
+ }
129
+ },
121
130
  countElements(config) {
122
131
  const definition = { config };
123
132
  return this.$refs.renderer.countElements(definition);
@@ -31,6 +31,7 @@ export default {
31
31
  pmql: null,
32
32
  options: [],
33
33
  selectedOption: null,
34
+ fields: null,
34
35
  };
35
36
  },
36
37
  watch: {
@@ -50,8 +51,9 @@ export default {
50
51
  },
51
52
  loadOptions(filter) {
52
53
  const pmql = this.pmql;
54
+ const fields = this.fields || undefined;
53
55
  window.ProcessMaker.apiClient
54
- .get(this.api, { params: { filter, pmql } })
56
+ .get(this.api, { params: { filter, pmql, fields } })
55
57
  .then(response => {
56
58
  this.options = response.data.data || [];
57
59
  });
@@ -0,0 +1,30 @@
1
+ const namespaced = true;
2
+ const globalErrorsModule = {
3
+ namespaced,
4
+ state: () => {
5
+ return {
6
+ valid: true,
7
+ message: '',
8
+ };
9
+ },
10
+ getters: {
11
+ isValidScreen(state) {
12
+ return state.valid;
13
+ },
14
+ getErrorMessage(state) {
15
+ return state.message;
16
+ },
17
+ },
18
+ mutations: {
19
+ basic(state, payload) {
20
+ state[payload.key] = payload.value;
21
+ },
22
+ },
23
+ actions: {
24
+ close({ commit }) {
25
+ commit('basic', { key: 'valid', value: true });
26
+ },
27
+ },
28
+ };
29
+
30
+ export default globalErrorsModule;