@processmaker/screen-builder 2.18.1 → 2.21.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.
@@ -33,28 +33,19 @@
33
33
  <span v-if="validation === 'required' && !value" class="required">{{ $t('Required') }}</span>
34
34
  </uploader-drop>
35
35
  <uploader-list>
36
- <template slot-scope="{ fileList }">
37
- <ul v-if="multipleUpload">
38
- <li v-for="fileId in (value ? value : [])" :key="getFileId(fileId)" :data-cy="fileId">
39
- <div class="container-fluid pl-3 pr-3" v-if="configOverrideFile(fileId) && configOverrideFile(fileId).new && fileList.find(x=>x.name === configOverrideFile(fileId).file_name)">
40
- <div class="row" style="background:rgb(226 238 255)">
41
- <div class="col-11 pr-0 pl-0">
42
- <uploader-file :file="fileList.find(x=>x.name === configOverrideFile(fileId).file_name)" :list="true" />
36
+ <template>
37
+ <ul>
38
+ <li v-for="(file, i) in files " :key="i" :data-cy="file.id">
39
+ <div class="">
40
+ <div class="" style="display:flex; background:rgb(226 238 255)">
41
+ <div v-if="nativeFiles[file.id]" style="flex: 1" :data-cy="file.file_name.replace(/[^0-9a-zA-Z\-]/g, '-')">
42
+ <uploader-file :file="nativeFiles[file.id]" :list="true" />
43
43
  </div>
44
- <div class="col-1 my-auto uploader-file">
45
- <b-btn variant="outline" @click="removeFile(fileId)" v-b-tooltip.hover :title="$t('Delete')">
46
- <i class="fas fa-trash-alt"/>
47
- </b-btn>
44
+ <div v-else style="flex: 1">
45
+ <i class="fas fa-paperclip"/> {{ file.file_name }}
48
46
  </div>
49
- </div>
50
- </div>
51
- <div v-else class="container-fluid border-bottom pl-3 pr-3">
52
- <div class="row">
53
- <div class="col-11 pr-0 pl-0 my-auto">
54
- <i class="fas fa-paperclip"/> {{ displayNameFor(fileId) }}
55
- </div>
56
- <div class="col-1 my-auto">
57
- <b-btn variant="outline" @click="removeFile(fileId)" v-b-tooltip.hover :title="$t('Delete')">
47
+ <div class="pt-1">
48
+ <b-btn variant="outline" @click="removeFile(file)" v-b-tooltip.hover :title="$t('Delete')">
58
49
  <i class="fas fa-trash-alt"/>
59
50
  </b-btn>
60
51
  </div>
@@ -62,16 +53,6 @@
62
53
  </div>
63
54
  </li>
64
55
  </ul>
65
- <ul v-else>
66
- <li v-if="fileList.length === 0 && value">
67
- <div class="border-bottom py-2">
68
- <i class="fas fa-paperclip"/> {{ displayName }}
69
- </div>
70
- </li>
71
- <li v-for="file in fileList" :key="file.id" :data-cy="file.name.replace(/[^0-9a-zA-Z\-]/g, '-')" :nada="JSON.stringify(file)" >
72
- <uploader-file :file="file" :list="true"/>
73
- </li>
74
- </ul>
75
56
  </template>
76
57
  </uploader-list>
77
58
  <div class="invalid-feedback" :class="{'d-block': required && !value}">
@@ -107,9 +88,6 @@ export default {
107
88
  components: uploader,
108
89
  mixins: [uniqIdsMixin],
109
90
  props: ['label', 'error', 'helper', 'name', 'value', 'controlClass', 'endpoint', 'accept', 'validation', 'parent', 'config', 'multipleUpload'],
110
- beforeMount() {
111
- this.getFileType();
112
- },
113
91
  updated() {
114
92
  this.removeDefaultClasses();
115
93
  },
@@ -140,6 +118,43 @@ export default {
140
118
  }
141
119
  },
142
120
  computed: {
121
+ filesFromGlobalRequestFiles() {
122
+ if (!this.value) {
123
+ return [];
124
+ }
125
+ return _.get(window, `PM4ConfigOverrides.requestFiles["${this.fileDataName}"]`, []).filter(file => {
126
+ // Filter any requestFiles that don't exist in this component's value. This can happen if
127
+ // a file is uploaded but the task is not saved.
128
+ if (this.multipleUpload) {
129
+ return this.value.some(valueFile => valueFile.file === file.id);
130
+ } else {
131
+ return file.id === this.value;
132
+ }
133
+ });
134
+ },
135
+ filesFromCollection() {
136
+ if (!this.value) {
137
+ return [];
138
+ }
139
+ return this.filesFromCollectionValue(this.value);
140
+ },
141
+ collection() {
142
+ const collectionIdNode = document.head.querySelector('meta[name="collection-id"]');
143
+ if (collectionIdNode) {
144
+ return collectionIdNode.content;
145
+ }
146
+ return false;
147
+ },
148
+ filesData() {
149
+ if (this.collection) {
150
+ return this.filesFromCollection;
151
+ } else {
152
+ return this.filesFromGlobalRequestFiles;
153
+ }
154
+ },
155
+ fileIds() {
156
+ return this.files.map(f => f.id);
157
+ },
143
158
  nativeButtonAttrs() {
144
159
  const attrs = { 'data-cy':'file-upload-button' };
145
160
  if (this.disabled) {
@@ -157,15 +172,6 @@ export default {
157
172
  inPreviewMode() {
158
173
  return ((this.mode === 'preview' && !window.exampleScreens) || this.mode === 'editor');
159
174
  },
160
- displayName() {
161
- const requestFiles = _.get(window, 'PM4ConfigOverrides.requestFiles', {});
162
- const fileInfo = requestFiles[this.fileDataName];
163
- let id = this.uploaderId;
164
- if (fileInfo && id >= 0) {
165
- return fileInfo.file_name;
166
- }
167
- return this.value.name ? this.value.name : this.value;
168
- },
169
175
  mode() {
170
176
  return this.$root.$children[0].mode;
171
177
  },
@@ -195,6 +201,22 @@ export default {
195
201
  },
196
202
  },
197
203
  watch: {
204
+ filesData: {
205
+ handler() {
206
+ this.setFiles();
207
+ },
208
+ immediate: true,
209
+ deep: true,
210
+ },
211
+ files: {
212
+ handler() {
213
+ if (!this.collection) {
214
+ this.setRequestFiles();
215
+ }
216
+ this.$emit('input', this.valueToSend());
217
+ },
218
+ deep: true,
219
+ },
198
220
  name: {
199
221
  handler() {
200
222
  this.options.query.data_name = this.fileDataName;
@@ -232,7 +254,6 @@ export default {
232
254
  return {
233
255
  uploaderId: 1,
234
256
  content: '',
235
- fileType: null,
236
257
  validator: {
237
258
  errorCount: 0,
238
259
  errors: [],
@@ -261,32 +282,66 @@ export default {
261
282
  accept: this.accept,
262
283
  },
263
284
  disabled: false,
285
+ files: [],
286
+ nativeFiles: {},
264
287
  };
265
288
  },
266
289
  methods: {
267
- configOverrideFile(id) {
268
- // If there is just one file associated to the file_upload PM4ConfigOverrides returns it as an object,
269
- // otherwise an array is returned. So, if the control is configured as multiple upload and
270
- // has just one file, we must return the object, in other case a search by id is done.
271
- const requestFiles = _.get(window, 'PM4ConfigOverrides.requestFiles', {});
272
- const files = requestFiles[this.fileDataName];
273
- if (files) {
274
- return Array.isArray(files)
275
- ? files.find(x => x.id === id)
276
- : files;
290
+ setFiles() {
291
+ if (_.isEqual(this.filesData, this.files)) {
292
+ return;
277
293
  }
278
- return null;
294
+ this.files = this.filesData;
279
295
  },
280
- displayNameFor(fileData) {
281
- if (this.fileType == 'request') {
282
- let file = this.configOverrideFile(fileData);
283
- return file ? file.file_name: fileData;
296
+ filesFromCollectionValue(value) {
297
+ if (!value) {
298
+ return [];
299
+ }
300
+ if (this.multipleUpload) {
301
+ return this.filesFromCollectionMulti(value);
302
+ } else {
303
+ return this.filesFromCollectionSingle(value);
284
304
  }
285
- if (this.fileType == 'collection') {
286
- let file = this.value.find(item => item.id == fileData.id);
287
- return file ? file.name : fileData.id;
305
+ },
306
+ filesFromCollectionSingle(value) {
307
+ return [{ id: value.id, file_name: value.name }];
308
+ },
309
+ filesFromCollectionMulti(value) {
310
+ return value.map(v => {
311
+ return { id: v.file.id, file_name: v.file.name };
312
+ });
313
+ },
314
+ setRequestFiles() {
315
+ _.set(window, `PM4ConfigOverrides.requestFiles["${this.fileDataName}"]`, this.files);
316
+ this.$emit('input', this.valueToSend());
317
+ },
318
+ valueToSend() {
319
+ if (this.multipleUpload) {
320
+ return this.valueForMulti();
321
+ } else {
322
+ return this.valueForSingle();
288
323
  }
289
324
  },
325
+ valueForMulti() {
326
+ return this.files.map(file => {
327
+ return { file: this.formatForType(file) };
328
+ });
329
+ },
330
+ valueForSingle() {
331
+ if (this.files.length > 0) {
332
+ return this.formatForType(this.files[0]);
333
+ }
334
+ return null;
335
+ },
336
+ formatForType(file) {
337
+ if (this.collection) {
338
+ return { id: file.id, name: file.file_name };
339
+ }
340
+ return file.id;
341
+ },
342
+ hasFileId(id) {
343
+ return this.fileIds.includes(id);
344
+ },
290
345
  listenRemovedLoop(loop, removed) {
291
346
  this.deleteAssociatedFiles(removed);
292
347
  },
@@ -297,40 +352,62 @@ export default {
297
352
  }
298
353
  this.deleteAssociatedFiles(record);
299
354
  },
300
- deleteAssociatedFiles(object) {
355
+ async deleteAssociatedFiles(object) {
301
356
  for (const prop in object) {
302
- if (prop === this.name) {
303
- this.deleteFile(object[prop]);
304
- }
305
- if (Array.isArray(object[prop])) {
306
- for (const item of object[prop]) {
307
- this.deleteAssociatedFiles(item);
357
+ if (prop === this.name && object[prop]) {
358
+ const idsInRemoved = this.idsFromValue(object[prop]);
359
+
360
+ for (const id of idsInRemoved) {
361
+ if (this.hasFileId(id)) {
362
+ // In record lists, delete can be called twice on the same file.
363
+ // Catch and igore the error.
364
+ // eslint-disable-next-line no-unused-vars
365
+ await this.$dataProvider.deleteFile(id).catch(e => {});
366
+ this.removeFromFiles(id);
367
+ }
308
368
  }
309
369
  }
310
370
  }
311
371
  },
312
- removeFile(fileData) {
313
- if (this.fileType == 'request') {
314
- this.deleteFile(fileData);
315
- let files = window.PM4ConfigOverrides.requestFiles[this.fileDataName];
316
- let filtered = files.filter(item => item.id !== fileData);
317
- window.PM4ConfigOverrides.requestFiles[this.fileDataName] = filtered;
318
- const ids = window.PM4ConfigOverrides.requestFiles[this.fileDataName].map(item => item.id);
319
- this.$emit('input', ids);
372
+ idsFromValue(value) {
373
+ if (this.collection) {
374
+ return this.filesFromCollectionValue(value).map(f => f.id);
375
+ } else {
376
+ if (this.multipleUpload) {
377
+ return value.map(v => v.file);
378
+ } else {
379
+ return [value];
380
+ }
320
381
  }
321
- if (this.fileType == 'collection') {
322
- this.deleteFile(fileData.id);
323
- let filtered = this.value.filter(item => item.id !== fileData.id);
324
- this.$emit('input', filtered);
382
+ },
383
+ async removeFile(file) {
384
+ const id = file.id;
385
+ const token = file.token ? file.token : null;
386
+
387
+ // If it's not a web entry start event
388
+ if (!isNaN(id)) {
389
+ await this.$dataProvider.deleteFile(id, token);
390
+ }
391
+
392
+ this.removeFromFiles(id);
393
+ },
394
+ removeFromFiles(id) {
395
+ const idx = this.files.findIndex(f => f.id === id);
396
+ this.$delete(this.files, idx);
397
+
398
+ if (this.nativeFiles[id]) {
399
+ if (this.$refs.uploader) {
400
+ this.$refs.uploader.uploader.removeFile(this.nativeFiles[id]);
401
+ }
402
+ this.$delete(this.nativeFiles, id);
325
403
  }
404
+
326
405
  },
327
- deleteFile(fileId) {
328
- if (fileId) {
329
- window.ProcessMaker.apiClient
330
- .delete(`files/${fileId}`)
331
- .catch(() => {
332
- /** ignore exception **/
333
- });
406
+ addToFiles(fileInfo) {
407
+ if (this.multipleUpload) {
408
+ this.files.push(fileInfo);
409
+ } else {
410
+ this.files = [fileInfo];
334
411
  }
335
412
  },
336
413
  listenRecordList(recordList, index, id) {
@@ -339,15 +416,6 @@ export default {
339
416
  return;
340
417
  }
341
418
  this.row_id = (parent !== null) ? id : null;
342
- //update id to refresh computed values
343
- this.uploaderId =new Date().getTime();
344
- if (this.$refs.uploader) {
345
- this.$refs.uploader.files = [];
346
- this.$refs.uploader.fileList = [];
347
- this.$refs.uploader.uploader.files = [];
348
- this.$refs.uploader.uploader.fileList = [];
349
- }
350
- this.$forceUpdate();
351
419
  },
352
420
  setPrefix() {
353
421
  let parent = this.$parent;
@@ -412,70 +480,28 @@ export default {
412
480
  e.target.click();
413
481
  }
414
482
  },
415
- getFileType() {
416
- if (document.head.querySelector('meta[name="collection-id"]')) {
417
- this.fileType = 'collection';
418
- } else {
419
- this.fileType = 'request';
420
- }
421
- },
422
483
  fileUploaded(rootFile, file, message) {
423
- if (this.fileType == 'request') {
424
- let id = file.name;
425
- if (message) {
426
- const msg = JSON.parse(message);
427
- if (!_.has(window, 'PM4ConfigOverrides')) {
428
- window.PM4ConfigOverrides = {};
429
- }
430
- if (!_.has(window, 'PM4ConfigOverrides.requestFiles')) {
431
- window.PM4ConfigOverrides.requestFiles = {};
432
- }
433
- if (typeof (window.PM4ConfigOverrides.requestFiles[this.fileDataName]) == 'undefined') {
434
- window.PM4ConfigOverrides.requestFiles[this.fileDataName] = this.multipleUpload ? [] : {};
435
- }
436
- if (this.multipleUpload) {
437
- const filesData = this.asArray(JSON.parse(JSON.stringify(window.PM4ConfigOverrides.requestFiles[this.fileDataName])));
438
- filesData.push({id: msg.fileUploadId, file_name: file.name, new:true});
439
- window.PM4ConfigOverrides.requestFiles[this.fileDataName] = filesData;
440
- } else {
441
- window.PM4ConfigOverrides.requestFiles[this.fileDataName] = {id: msg.fileUploadId, file_name: file.name, new:true};
442
- }
443
- id = msg.fileUploadId;
444
- }
445
- const valueToSend = this.multipleUpload
446
- ? this.asArray(this.value).concat(id)
447
- : id;
448
- this.$emit('input', valueToSend);
449
- }
450
-
451
- if (this.fileType == 'collection') {
452
- message = JSON.parse(message);
453
- const uploadedObject = {
454
- id: message.id,
455
- name: message.file_name,
456
- };
484
+ let name = file.name;
485
+ if (message) {
486
+ const msg = JSON.parse(message);
457
487
 
458
- if (this.multipleUpload) {
459
- let currentVal = this.value ? this.value : [];
460
- currentVal.push(uploadedObject);
461
- this.$emit('input', currentVal);
462
- }
463
- else {
464
- this.$emit('input', uploadedObject);
488
+ let id = msg.fileUploadId;
489
+ if (this.collection) {
490
+ id = msg.id;
465
491
  }
492
+
493
+ const fileInfo = {
494
+ id,
495
+ file_name: name,
496
+ mime_type: rootFile.fileType,
497
+ };
498
+
499
+ this.$set(this.nativeFiles, id, rootFile);
500
+ this.addToFiles(fileInfo);
501
+ } else {
502
+ this.$emit('input', name);
466
503
  }
467
504
  },
468
- asArray(value) {
469
- if (value === null || value === undefined) {
470
- return [];
471
- }
472
- return Array.isArray(value) ? value : [value];
473
- },
474
- getFileId(value) {
475
- return (typeof value === 'object' && value.id)
476
- ? value.id
477
- : value;
478
- },
479
505
  removed() {
480
506
  if (!this.inProgress) {
481
507
  this.complete();
@@ -515,25 +541,18 @@ export default {
515
541
  return this.endpoint;
516
542
  }
517
543
 
518
- if (this.fileType == 'request') {
519
- const requestIDNode = document.head.querySelector('meta[name="request-id"]');
520
-
521
- return requestIDNode
522
- ? `/api/1.0/requests/${requestIDNode.content}/files`
523
- : null;
524
- }
525
-
526
- if (this.fileType == 'collection') {
527
- const collectionIdNode = document.head.querySelector('meta[name="collection-id"]');
528
-
529
- return collectionIdNode
530
- ? '/api/1.0/files' +
544
+ if (this.collection) {
545
+ return '/api/1.0/files' +
531
546
  '?model=' +
532
547
  'ProcessMaker\\Plugins\\Collections\\Models\\Collection' +
533
548
  '&model_id=' +
534
- collectionIdNode.content +
549
+ this.collection +
535
550
  '&collection=' +
536
- 'collection'
551
+ 'collection';
552
+ } else {
553
+ const requestIDNode = document.head.querySelector('meta[name="request-id"]');
554
+ return requestIDNode
555
+ ? `/api/1.0/requests/${requestIDNode.content}/files`
537
556
  : null;
538
557
  }
539
558
  },
@@ -26,7 +26,7 @@ import Json2Vue from '../mixins/Json2Vue';
26
26
  import CurrentPageProperty from '../mixins/CurrentPageProperty';
27
27
  import WatchersSynchronous from '@/components/watchers-synchronous';
28
28
  import ScreenRendererError from '../components/renderer/screen-renderer-error';
29
- import { cloneDeep, isEqual } from 'lodash';
29
+ import { cloneDeep, isEqual, debounce } from 'lodash';
30
30
 
31
31
  export default {
32
32
  name: 'screen-renderer',
@@ -43,19 +43,23 @@ export default {
43
43
  mounted() {
44
44
  this.currentDefinition = cloneDeep(this.definition);
45
45
  this.component = this.buildComponent(this.currentDefinition);
46
+ this.rebuildScreen = debounce(this.rebuildScreen, 25);
46
47
  },
47
48
  watch: {
48
49
  definition: {
49
50
  deep: true,
50
51
  handler(definition) {
51
- if (!isEqual(definition, this.currentDefinition)) {
52
- this.currentDefinition = cloneDeep(definition);
53
- this.component = this.buildComponent(this.currentDefinition);
54
- }
52
+ this.rebuildScreen(definition);
55
53
  },
56
54
  },
57
55
  },
58
56
  methods: {
57
+ rebuildScreen(definition) {
58
+ if (!isEqual(definition, this.currentDefinition)) {
59
+ this.currentDefinition = cloneDeep(definition);
60
+ this.component = this.buildComponent(this.currentDefinition);
61
+ }
62
+ },
59
63
  onAsyncWatcherOn() {
60
64
  this.displayAsyncLoading = typeof this._parent === 'undefined';
61
65
  },
@@ -91,6 +91,7 @@ export default {
91
91
  requestData: {},
92
92
  hasErrors: false,
93
93
  refreshScreen: 0,
94
+ redirecting: null,
94
95
  };
95
96
  },
96
97
  watch: {
@@ -157,6 +158,7 @@ export default {
157
158
  handler() {
158
159
  this.taskId = this.task.id;
159
160
  this.nodeId = this.task.element_id;
161
+ this.listenForParentChanges();
160
162
  },
161
163
  },
162
164
 
@@ -205,6 +207,9 @@ export default {
205
207
  const screenType = this.screen.type;
206
208
  return screenType.toLowerCase() + '-screen';
207
209
  },
210
+ parentRequest() {
211
+ return _.get(this.task, 'process_request.parent_request_id', null);
212
+ },
208
213
  },
209
214
  methods: {
210
215
  loadScreen(id) {
@@ -227,7 +232,7 @@ export default {
227
232
  loadTask() {
228
233
  const url = `/${this.taskId}?include=data,user,requestor,processRequest,component,screen,requestData,bpmnTagName,interstitial,definition,nested`;
229
234
  // For Vocabularies
230
- if (window.ProcessMaker && window.ProcessMaker.packages && window.ProcessMaker.packages.indexOf('package-vocabularies')) {
235
+ if (window.ProcessMaker && window.ProcessMaker.packages && window.ProcessMaker.packages.includes('package-vocabularies')) {
231
236
  window.ProcessMaker.VocabulariesSchemaUrl = `vocabularies/task_schema/${this.taskId}`;
232
237
  }
233
238
 
@@ -252,6 +257,7 @@ export default {
252
257
 
253
258
  if (this.task.process_request.status === 'ERROR') {
254
259
  this.hasErrors = true;
260
+ this.$emit('error', this.requestId);
255
261
  } else {
256
262
  this.hasErrors = false;
257
263
  }
@@ -291,12 +297,25 @@ export default {
291
297
  this.$emit('closed', this.task.id);
292
298
  }
293
299
  },
294
- loadNextAssignedTask() {
295
- const url = `?user_id=${this.userId}&status=ACTIVE&process_request_id=${this.requestId}&include_sub_tasks=1`;
300
+ loadNextAssignedTask(requestId = null) {
301
+ if (!requestId) {
302
+ requestId = this.requestId;
303
+ }
304
+ const url = `?user_id=${this.userId}&status=ACTIVE&process_request_id=${requestId}&include_sub_tasks=1`;
296
305
  return this.$dataProvider
297
306
  .getTasks(url).then((response) => {
298
307
  if (response.data.data.length > 0) {
299
308
  let task = response.data.data[0];
309
+ if (task.process_request_id !== this.requestId) {
310
+ // Next task is in a subprocess, do a hard redirect
311
+ if (this.redirecting === task.process_request_id) {
312
+ return;
313
+ }
314
+ this.unsubscribeSocketListeners();
315
+ this.redirecting = task.process_request_id;
316
+ this.$emit('redirect', task);
317
+ return;
318
+ }
300
319
  this.taskId = task.id;
301
320
  this.nodeId = task.element_id;
302
321
  }
@@ -336,29 +355,32 @@ export default {
336
355
  },
337
356
  onUpdate(data) {
338
357
  this.$emit('input', data);
339
- window.ProcessMaker.EventBus.$emit('form-data-updated', data);
340
358
  },
341
359
 
342
360
  activityAssigned() {
343
361
  // This may no longer be needed
344
362
  },
345
363
  processCompleted() {
364
+ if (this.parentRequest && this.task.allow_interstitial) {
365
+ // There could be another task in the parent, so don't emit completed
366
+ return;
367
+ }
346
368
  this.$emit('completed', this.requestId);
347
369
  },
348
- processUpdated(data) {
370
+ processUpdated: _.debounce(function(data) {
349
371
  if (
350
372
  data.event === 'ACTIVITY_COMPLETED' ||
351
373
  data.event === 'ACTIVITY_ACTIVATED'
352
374
  ) {
353
375
  this.reload();
354
376
  }
355
- },
356
- initSocketListeners() {
357
- if (this.socketListeners.length > 0) {
358
- return;
377
+ if (data.event === 'ACTIVITY_EXCEPTION') {
378
+ this.$emit('error', this.requestId);
359
379
  }
360
-
380
+ }, 300),
381
+ initSocketListeners() {
361
382
  this.addSocketListener(
383
+ `completed-${this.requestId}`,
362
384
  `ProcessMaker.Models.ProcessRequest.${this.requestId}`,
363
385
  '.ProcessCompleted',
364
386
  (data) => {
@@ -367,6 +389,7 @@ export default {
367
389
  );
368
390
 
369
391
  this.addSocketListener(
392
+ `updated-${this.requestId}`,
370
393
  `ProcessMaker.Models.ProcessRequest.${this.requestId}`,
371
394
  '.ProcessUpdated',
372
395
  (data) => {
@@ -380,18 +403,41 @@ export default {
380
403
  this.reload();
381
404
  }
382
405
  },
383
- addSocketListener(channel, event, callback) {
384
- this.socketListeners.push({
406
+ listenForParentChanges() {
407
+ if (!this.parentRequest) {
408
+ return;
409
+ }
410
+ this.addSocketListener(
411
+ `parent-${this.requestId}`,
412
+ `ProcessMaker.Models.ProcessRequest.${this.parentRequest}`,
413
+ '.ProcessUpdated',
414
+ (data) => {
415
+ if (['ACTIVITY_COMPLETED', 'ACTIVITY_ACTIVATED'].includes(data.event)) {
416
+ this.loadNextAssignedTask(this.parentRequest);
417
+ }
418
+ if (data.event === 'ACTIVITY_EXCEPTION') {
419
+ this.$emit('error', this.requestId);
420
+ }
421
+ }
422
+ );
423
+ },
424
+ addSocketListener(key, channel, event, callback) {
425
+ if (key in this.socketListeners) {
426
+ return;
427
+ }
428
+ this.socketListeners[key] = {
385
429
  channel,
386
430
  event,
431
+ };
432
+ window.Echo.private(channel).listen(event, (data) => {
433
+ callback(data);
387
434
  });
388
- window.Echo.private(channel).listen(event, callback);
389
435
  },
390
436
  unsubscribeSocketListeners() {
391
- this.socketListeners.forEach((element) => {
437
+ Object.values(this.socketListeners).forEach(element => {
392
438
  window.Echo.private(element.channel).stopListening(element.event);
393
439
  });
394
- this.socketListeners = [];
440
+ this.socketListeners = {};
395
441
  },
396
442
  obtainPayload(url) {
397
443
  return new Promise((resolve) => {
@@ -99,7 +99,7 @@
99
99
 
100
100
  <draggable
101
101
  data-cy="editor-content"
102
- class="h-100 custom-css-scope"
102
+ class="h-100"
103
103
  ghost-class="form-control-ghost"
104
104
  :value="config[currentPage].items"
105
105
  @input="updateConfig"