@processmaker/screen-builder 2.24.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.24.1",
3
+ "version": "2.26.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -1738,9 +1738,9 @@
1738
1738
  }
1739
1739
  },
1740
1740
  "@processmaker/vue-form-elements": {
1741
- "version": "0.28.6",
1742
- "resolved": "https://registry.npmjs.org/@processmaker/vue-form-elements/-/vue-form-elements-0.28.6.tgz",
1743
- "integrity": "sha512-uxHsOkCNLJEF4mJu7dK4+qn2SL/mWDswdoV1kQjNjQkwBk10uGXDdAbtliHRbld3HDMAPUQAu69c+1fS8pDqjA==",
1741
+ "version": "0.28.7",
1742
+ "resolved": "https://registry.npmjs.org/@processmaker/vue-form-elements/-/vue-form-elements-0.28.7.tgz",
1743
+ "integrity": "sha512-CVtaOlyrrcWLoY8bqUyb0KOi9e7kKf0oDdACyrvTpy7UzsaYP05lf6KOqMKyKUXAvpHg8UVJKjxJTbJfflNDKA==",
1744
1744
  "dev": true,
1745
1745
  "requires": {
1746
1746
  "@tinymce/tinymce-vue": "2.0.0",
@@ -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.24.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",
@@ -44,7 +45,7 @@
44
45
  "@cypress/code-coverage": "^3.8.1",
45
46
  "@fortawesome/fontawesome-free": "^5.6.1",
46
47
  "@panter/vue-i18next": "^0.15.2",
47
- "@processmaker/vue-form-elements": "0.28.6",
48
+ "@processmaker/vue-form-elements": "0.28.7",
48
49
  "@processmaker/vue-multiselect": "^2.2.0",
49
50
  "@vue/cli-plugin-babel": "^3.6.0",
50
51
  "@vue/cli-plugin-e2e-cypress": "^4.0.3",
@@ -85,7 +86,7 @@
85
86
  },
86
87
  "peerDependencies": {
87
88
  "@panter/vue-i18next": "^0.15.0",
88
- "@processmaker/vue-form-elements": "0.28.6",
89
+ "@processmaker/vue-form-elements": "0.28.7",
89
90
  "i18next": "^15.0.8",
90
91
  "vue": "^2.6.12",
91
92
  "vuex": "^3.1.1"
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>
@@ -16,7 +16,7 @@
16
16
  >
17
17
  <i class="fas fa-file-download"/> {{ $t('Download') }}
18
18
  </b-btn>
19
- {{ file.name }}
19
+ {{ file.file_name }}
20
20
  </div>
21
21
  </template>
22
22
  <div v-else>
@@ -39,18 +39,12 @@ export default {
39
39
  rowId: null,
40
40
  };
41
41
  },
42
- props: ['name', 'value', 'endpoint', 'requestFiles', 'label'],
42
+ props: ['name', 'value', 'endpoint', 'requestFiles', 'label', 'transient-data'],
43
43
  mounted() {
44
- this.$root.$on('set-upload-data-name',
45
- (recordList, index, id) => this.listenRecordList(recordList, index, id));
46
-
47
44
  if (this.donwloadingNotAvailable) {
48
45
  // Not somewhere we can download anything (like web entry start event)
49
46
  return;
50
47
  }
51
-
52
- this.checkIfInRecordList();
53
- this.setPrefix();
54
48
  this.setFilesInfo();
55
49
  },
56
50
  watch: {
@@ -103,24 +97,11 @@ export default {
103
97
  }
104
98
  return false;
105
99
  },
100
+ requestData() {
101
+ return {_parent: {...this.$parent._parent}, ...this.transientData};
102
+ },
106
103
  },
107
104
  methods: {
108
- parentRecordList(node) {
109
- if (node.$parent && node.$parent.$options) {
110
- if (node.$parent.$options._componentTag === 'form-record-list') {
111
- return node.$parent;
112
- }
113
- return this.parentRecordList(node.$parent);
114
- }
115
- return null;
116
- },
117
- listenRecordList(recordList, index, id) {
118
- const parent = this.parentRecordList(this);
119
- if (parent !== recordList) {
120
- return;
121
- }
122
- this.rowId = (parent !== null) ? id : null;
123
- },
124
105
  downloadFile(file) {
125
106
  if (this.collection) {
126
107
  this.downloadCollectionFile(file);
@@ -133,15 +114,16 @@ export default {
133
114
 
134
115
  if (_.has(window, 'PM4ConfigOverrides.getFileEndpoint')) {
135
116
  endpoint = window.PM4ConfigOverrides.getFileEndpoint;
136
- }
137
-
138
- if (endpoint && file.token) {
139
- return `${endpoint}/${file.id}?&token=${file.token}`;
117
+ return `${endpoint}/${file.id}`;
140
118
  }
141
119
 
142
120
  return `/files/${file.id}/contents`;
143
121
  },
144
122
  setPrefix() {
123
+ if (this.name.startsWith('_parent.')) {
124
+ // do not set the loop prefix
125
+ return;
126
+ }
145
127
  let parent = this.$parent;
146
128
  let i = 0;
147
129
  while (!parent.loopContext) {
@@ -177,93 +159,53 @@ export default {
177
159
  const url = window.URL.createObjectURL(new Blob([response.data]));
178
160
  const link = document.createElement('a');
179
161
  link.href = url;
180
- link.setAttribute('download', file.name);
162
+ link.setAttribute('download', file.file_name);
181
163
  document.body.appendChild(link);
182
164
  link.click();
183
165
  },
184
166
  setFilesInfo() {
185
167
  if (this.collection) {
168
+ this.setPrefix();
186
169
  this.setFilesInfoFromCollectionValue();
187
170
  } else {
188
171
  this.setFilesInfoFromRequest();
189
172
  }
190
173
  },
191
- setFilesInfoFromRequest() {
192
- if (!this.value) {
174
+ setFilesInfoFromRequest() {
175
+ const fileId = this.value ? this.value : _.get(this.requestData, this.fileDataName, null);
176
+ let endpoint = this.endpoint;
177
+
178
+ if (!this.requestId || !fileId) {
193
179
  return;
194
180
  }
195
-
196
- let requestFiles = _.get(
197
- window,
198
- `PM4ConfigOverrides.requestFiles["${this.fileDataName}"]`,
199
- []
200
- );
201
-
202
- requestFiles = requestFiles.filter(file => {
203
- // Filter any requestFiles that don't exist in this component's value. This can happen if
204
- // a file is uploaded but the task is not saved.
205
- if (Array.isArray(this.value)) {
206
- return this.value.some(valueFile => valueFile.file === file.id);
207
- } else {
208
- return file.id === this.value;
181
+
182
+ if (!endpoint) {
183
+ endpoint = 'requests/' + this.requestId + '/files?id=' + fileId;
184
+ if (_.has(window, 'PM4ConfigOverrides.getFileEndpoint')) {
185
+ endpoint = window.PM4ConfigOverrides.getFileEndpoint;
186
+ endpoint += '/' + fileId;
209
187
  }
210
- });
211
-
212
- // Might be accessing individual files from inside a loop
213
- if (requestFiles.length === 0 && this.fileDataName.endsWith('.file')) {
214
- requestFiles = this.requestFileInsideALoop();
215
188
  }
216
189
 
217
- this.filesInfo = requestFiles.map(file => {
218
- const info = { id: file.id, name: file.file_name };
219
- if (file.token) {
220
- // web entry
221
- info.token = file.token;
190
+ this.$dataProvider.get(endpoint).then(response => {
191
+ const fileInfo = response.data.data ? _.get(response, 'data.data.0', null) : _.get(response, 'data', null);
192
+ if (fileInfo) {
193
+ this.filesInfo.push(fileInfo);
194
+ } else {
195
+ window.ProcessMaker.alert(
196
+ this.$t('File Download Missing File'),
197
+ 'danger'
198
+ );
222
199
  }
223
- return info;
224
200
  });
225
201
  },
226
- requestFileInsideALoop() {
227
- const path = this.fileDataName.slice(0, -5);
228
- const loopFile = _.get(
229
- window,
230
- `PM4ConfigOverrides.requestFiles.${path}`,
231
- null
232
- );
233
- if (loopFile) {
234
- return [loopFile]; // Treat as single file download
235
- }
236
- return [];
237
- },
238
202
  setFilesInfoFromCollectionValue() {
239
- if (!this.value) {
203
+ const files = this.value ? this.value : _.get(this.requestData, this.fileDataName);
204
+ if (!this.value && !files) {
240
205
  this.filesInfo = [];
241
206
  return;
242
207
  }
243
- if (Array.isArray(this.value)) {
244
- // multi file upload
245
- this.filesInfo = this.value.map(value => value.file);
246
- } else {
247
- this.filesInfo = [this.value];
248
- }
249
-
250
- },
251
- checkIfInRecordList() {
252
- const parent = this.parentRecordList(this);
253
- if (parent !== null) {
254
- const recordList = parent;
255
- const prefix = recordList.name + '.';
256
- this.setFileUploadNameForChildren(recordList.$children, prefix);
257
- }
258
- },
259
- setFileUploadNameForChildren(children, prefix) {
260
- children.forEach(child => {
261
- if (_.get(child, '$options.name') === 'FileDownload') {
262
- child.prefix = prefix;
263
- } else if (_.get(child, '$children', []).length > 0) {
264
- this.setFileUploadNameForChildren(child.$children, prefix);
265
- }
266
- });
208
+ this.filesInfo = [this.value ? this.value : files];
267
209
  },
268
210
  },
269
211
  };
@@ -103,8 +103,6 @@ export default {
103
103
 
104
104
  this.removeDefaultClasses();
105
105
 
106
- this.checkIfInRecordList();
107
-
108
106
  this.setPrefix();
109
107
  if (this.$refs['uploader']) {
110
108
  this.$refs['uploader'].$forceUpdate();
@@ -438,15 +436,6 @@ export default {
438
436
  this.prefix = parent.loopContext + '.';
439
437
  }
440
438
  },
441
- setFileUploadNameForChildren(children, prefix) {
442
- children.forEach(child => {
443
- if (_.get(child, '$options.name') === 'FileUpload') {
444
- child.prefix = prefix;
445
- } else if (_.get(child, '$children', []).length > 0) {
446
- this.setFileUploadNameForChildren(child.$children, prefix);
447
- }
448
- });
449
- },
450
439
  addFile(file) {
451
440
  if (this.disabled) {
452
441
  file.ignored = true;
@@ -556,14 +545,6 @@ export default {
556
545
  : null;
557
546
  }
558
547
  },
559
- checkIfInRecordList() {
560
- const parent = this.parentRecordList(this);
561
- if (parent !== null) {
562
- const recordList = parent;
563
- const prefix = recordList.name + '.';
564
- this.setFileUploadNameForChildren(recordList.$children, prefix);
565
- }
566
- },
567
548
  },
568
549
  };
569
550
  </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>