@processmaker/screen-builder 3.0.2 → 3.0.4
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/dist/vue-form-builder.css +1 -1
- package/dist/vue-form-builder.es.js +1164 -1083
- package/dist/vue-form-builder.es.js.map +1 -1
- package/dist/vue-form-builder.umd.js +38 -38
- package/dist/vue-form-builder.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/App.vue +2 -0
- package/src/components/ScreenTemplateCard.vue +4 -2
- package/src/components/editor/loop.vue +44 -3
- package/src/components/editor/multi-column.vue +31 -1
- package/src/components/inspector/collection-data-source.vue +3 -0
- package/src/components/inspector/collection-designer-mode.vue +9 -1
- package/src/components/inspector/collection-records-list.vue +12 -7
- package/src/components/inspector/encrypted-config.vue +1 -1
- package/src/components/renderer/file-upload.vue +31 -1
- package/src/components/renderer/form-collection-record-control.vue +5 -4
- package/src/components/renderer/form-collection-view-control.vue +2 -2
- package/src/components/renderer/form-masked-input.vue +7 -5
- package/src/components/task.vue +81 -38
- package/src/components/vue-form-builder.vue +41 -4
- package/src/main.js +8 -0
- package/src/mixins/Clipboard.js +40 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@processmaker/screen-builder",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "VITE_COVERAGE=true vite",
|
|
6
6
|
"build": "vite build",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@fortawesome/fontawesome-free": "^5.6.1",
|
|
58
58
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
59
59
|
"@panter/vue-i18next": "^0.15.2",
|
|
60
|
-
"@processmaker/vue-form-elements": "0.61.
|
|
60
|
+
"@processmaker/vue-form-elements": "0.61.2",
|
|
61
61
|
"@processmaker/vue-multiselect": "2.3.0",
|
|
62
62
|
"@storybook/addon-essentials": "^7.6.13",
|
|
63
63
|
"@storybook/addon-interactions": "^7.6.13",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
118
118
|
"@panter/vue-i18next": "^0.15.0",
|
|
119
|
-
"@processmaker/vue-form-elements": "0.61.
|
|
119
|
+
"@processmaker/vue-form-elements": "0.61.2",
|
|
120
120
|
"i18next": "^15.0.8",
|
|
121
121
|
"vue": "^2.6.12",
|
|
122
122
|
"vuex": "^3.1.1"
|
package/src/App.vue
CHANGED
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
title="Default"
|
|
115
115
|
:render-controls="displayBuilder"
|
|
116
116
|
@change="updateConfig"
|
|
117
|
+
:screen-type="displayType"
|
|
117
118
|
>
|
|
118
119
|
<default-loading-spinner />
|
|
119
120
|
</vue-form-builder>
|
|
@@ -448,6 +449,7 @@ export default {
|
|
|
448
449
|
},
|
|
449
450
|
showTemplatesPanel: false,
|
|
450
451
|
sharedTemplatesData: null,
|
|
452
|
+
displayType: 'form'
|
|
451
453
|
};
|
|
452
454
|
},
|
|
453
455
|
computed: {
|
|
@@ -24,9 +24,11 @@
|
|
|
24
24
|
<hr class="card-divider" />
|
|
25
25
|
<b-card-body class="p-1">
|
|
26
26
|
<div class="template-details">
|
|
27
|
-
<span class="template-name d-block pt-1">{{
|
|
27
|
+
<span class="template-name d-block pt-1">{{
|
|
28
|
+
truncateText(template.name, 45)
|
|
29
|
+
}}</span>
|
|
28
30
|
<span class="template-description d-block">{{
|
|
29
|
-
truncateText(template.description,
|
|
31
|
+
truncateText(template.description, 60)
|
|
30
32
|
}}</span>
|
|
31
33
|
</div>
|
|
32
34
|
<b-collapse v-model="isApplyOptionsActive">
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="column-draggable" :selector="config.customCssSelector">
|
|
3
|
-
<draggable
|
|
3
|
+
<draggable
|
|
4
|
+
style="min-height: 80px"
|
|
5
|
+
:list="items"
|
|
6
|
+
group="controls"
|
|
7
|
+
@change="onChange"
|
|
8
|
+
>
|
|
4
9
|
<div
|
|
5
10
|
v-for="(element, index) in items"
|
|
6
11
|
:key="index"
|
|
@@ -27,8 +32,18 @@
|
|
|
27
32
|
class="mr-2 ml-1"
|
|
28
33
|
/>
|
|
29
34
|
{{ element.config.name || $t("Variable Name") }}
|
|
35
|
+
<b-badge
|
|
36
|
+
v-if="isInClipboard(items[index]) && screenType === 'form'"
|
|
37
|
+
data-cy="copied-badge"
|
|
38
|
+
class="m-2 custom-badge"
|
|
39
|
+
pill
|
|
40
|
+
>
|
|
41
|
+
<i class="far fa-check-circle"></i>
|
|
42
|
+
<span class="pl-2">{{ $t('Copied')}}</span>
|
|
43
|
+
</b-badge>
|
|
30
44
|
<div class="ml-auto">
|
|
31
45
|
<clipboard-button
|
|
46
|
+
v-if="screenType === 'form'"
|
|
32
47
|
:index="index"
|
|
33
48
|
:config="element.config"
|
|
34
49
|
:isInClipboard="isInClipboard(items[index])"
|
|
@@ -74,6 +89,7 @@
|
|
|
74
89
|
:config="element.config"
|
|
75
90
|
@inspect="inspect"
|
|
76
91
|
@update-state="$emit('update-state')"
|
|
92
|
+
:screen-type="screenType"
|
|
77
93
|
/>
|
|
78
94
|
</div>
|
|
79
95
|
</div>
|
|
@@ -91,8 +107,18 @@
|
|
|
91
107
|
class="mr-2 ml-1"
|
|
92
108
|
/>
|
|
93
109
|
{{ element.config.name || $t("Variable Name") }}
|
|
110
|
+
<b-badge
|
|
111
|
+
v-if="isInClipboard(items[index]) && screenType === 'form'"
|
|
112
|
+
data-cy="copied-badge"
|
|
113
|
+
class="m-2 custom-badge"
|
|
114
|
+
pill
|
|
115
|
+
>
|
|
116
|
+
<i class="far fa-check-circle"></i>
|
|
117
|
+
<span class="pl-2">{{ $t('Copied')}}</span>
|
|
118
|
+
</b-badge>
|
|
94
119
|
<div class="ml-auto">
|
|
95
120
|
<clipboard-button
|
|
121
|
+
v-if="screenType === 'form'"
|
|
96
122
|
:index="index"
|
|
97
123
|
:config="element.config"
|
|
98
124
|
:isInClipboard="isInClipboard(items[index])"
|
|
@@ -128,6 +154,7 @@
|
|
|
128
154
|
]"
|
|
129
155
|
:tabindex="element.config.interactive ? 0 : -1"
|
|
130
156
|
:config="element.config"
|
|
157
|
+
:screen-type="screenType"
|
|
131
158
|
@input="
|
|
132
159
|
element.config.interactive
|
|
133
160
|
? (element.config.content = $event)
|
|
@@ -173,7 +200,7 @@ export default {
|
|
|
173
200
|
...renderer
|
|
174
201
|
},
|
|
175
202
|
mixins: [HasColorProperty, Clipboard],
|
|
176
|
-
props: ["value", "name", "config", "selected", "validationErrors"],
|
|
203
|
+
props: ["value", "name", "config", "selected", "validationErrors", "screenType"],
|
|
177
204
|
data() {
|
|
178
205
|
return {
|
|
179
206
|
items: [],
|
|
@@ -258,7 +285,13 @@ export default {
|
|
|
258
285
|
this.$set(item.config.aiConfig, "progress", progress);
|
|
259
286
|
}
|
|
260
287
|
});
|
|
261
|
-
}
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* Triggered when the draggable container is changed.
|
|
291
|
+
*/
|
|
292
|
+
onChange(e) {
|
|
293
|
+
this.$emit("update-state");
|
|
294
|
+
},
|
|
262
295
|
}
|
|
263
296
|
};
|
|
264
297
|
</script>
|
|
@@ -334,4 +367,12 @@ export default {
|
|
|
334
367
|
box-shadow: 0 0 0 13px rgba(0, 0, 0, 0);
|
|
335
368
|
}
|
|
336
369
|
}
|
|
370
|
+
.custom-badge {
|
|
371
|
+
background-color: #D1F4D7 !important;
|
|
372
|
+
color: #06723A !important;
|
|
373
|
+
padding: 0.5rem 0.75rem;
|
|
374
|
+
border-radius: 8px;
|
|
375
|
+
font-weight: 500;
|
|
376
|
+
font-size: 14px;
|
|
377
|
+
}
|
|
337
378
|
</style>
|
|
@@ -42,8 +42,18 @@
|
|
|
42
42
|
class="mr-2 ml-1"
|
|
43
43
|
/>
|
|
44
44
|
{{ element.config.name || $t("Variable Name") }}
|
|
45
|
+
<b-badge
|
|
46
|
+
v-if="isInClipboard(element) && screenType === 'form'"
|
|
47
|
+
data-cy="copied-badge"
|
|
48
|
+
class="m-2 custom-badge"
|
|
49
|
+
pill
|
|
50
|
+
>
|
|
51
|
+
<i class="far fa-check-circle"></i>
|
|
52
|
+
<span class="pl-2">{{ $t('Copied')}}</span>
|
|
53
|
+
</b-badge>
|
|
45
54
|
<div class="ml-auto">
|
|
46
55
|
<clipboard-button
|
|
56
|
+
v-if="screenType === 'form'"
|
|
47
57
|
:index="index"
|
|
48
58
|
:config="element.config"
|
|
49
59
|
:isInClipboard="isInClipboard(element)"
|
|
@@ -86,6 +96,7 @@
|
|
|
86
96
|
:selected="selected"
|
|
87
97
|
:ai-element="element"
|
|
88
98
|
:config="element.config"
|
|
99
|
+
:screen-type="screenType"
|
|
89
100
|
@inspect="inspect"
|
|
90
101
|
@update-state="$emit('update-state')"
|
|
91
102
|
/>
|
|
@@ -108,8 +119,18 @@
|
|
|
108
119
|
class="mr-2 ml-1"
|
|
109
120
|
/>
|
|
110
121
|
{{ element.config.name || $t("Variable Name") }}
|
|
122
|
+
<b-badge
|
|
123
|
+
v-if="isInClipboard(element) && screenType === 'form'"
|
|
124
|
+
data-cy="copied-badge"
|
|
125
|
+
class="m-2 custom-badge"
|
|
126
|
+
pill
|
|
127
|
+
>
|
|
128
|
+
<i class="far fa-check-circle"></i>
|
|
129
|
+
<span class="pl-2">{{ $t('Copied')}}</span>
|
|
130
|
+
</b-badge>
|
|
111
131
|
<div class="ml-auto">
|
|
112
132
|
<clipboard-button
|
|
133
|
+
v-if="screenType === 'form'"
|
|
113
134
|
:index="index"
|
|
114
135
|
:config="element.config"
|
|
115
136
|
:isInClipboard="isInClipboard(element)"
|
|
@@ -145,6 +166,7 @@
|
|
|
145
166
|
]"
|
|
146
167
|
:tabindex="element.config.interactive ? 0 : -1"
|
|
147
168
|
:config="element.config"
|
|
169
|
+
:screen-type="screenType"
|
|
148
170
|
@input="
|
|
149
171
|
element.config.interactive
|
|
150
172
|
? (element.config.content = $event)
|
|
@@ -194,7 +216,7 @@ export default {
|
|
|
194
216
|
...renderer
|
|
195
217
|
},
|
|
196
218
|
mixins: [HasColorProperty, Clipboard],
|
|
197
|
-
props: ["value", "name", "config", "selected", "validationErrors"],
|
|
219
|
+
props: ["value", "name", "config", "selected", "validationErrors", "screenType"],
|
|
198
220
|
data() {
|
|
199
221
|
return {
|
|
200
222
|
items: [],
|
|
@@ -375,4 +397,12 @@ export default {
|
|
|
375
397
|
box-shadow: 0 0 0 13px rgba(0, 0, 0, 0);
|
|
376
398
|
}
|
|
377
399
|
}
|
|
400
|
+
.custom-badge {
|
|
401
|
+
background-color: #D1F4D7 !important;
|
|
402
|
+
color: #06723A !important;
|
|
403
|
+
padding: 0.5rem 0.75rem;
|
|
404
|
+
border-radius: 8px;
|
|
405
|
+
font-weight: 500;
|
|
406
|
+
font-size: 14px;
|
|
407
|
+
}
|
|
378
408
|
</style>
|
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
],
|
|
37
37
|
};
|
|
38
38
|
},
|
|
39
|
+
mounted () {
|
|
40
|
+
this.callBuilder(this.designerOptions);
|
|
41
|
+
},
|
|
39
42
|
computed: {
|
|
40
43
|
options() {
|
|
41
44
|
return Object.fromEntries(
|
|
@@ -56,10 +59,15 @@
|
|
|
56
59
|
options: {
|
|
57
60
|
handler() {
|
|
58
61
|
this.$emit("input", this.options);
|
|
59
|
-
this
|
|
62
|
+
this.callBuilder(this.options.designerOptions);
|
|
60
63
|
},
|
|
61
64
|
deep: true
|
|
62
65
|
},
|
|
63
66
|
},
|
|
67
|
+
methods: {
|
|
68
|
+
callBuilder(option) {
|
|
69
|
+
this.$root.$emit("style-mode", option);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
64
72
|
};
|
|
65
73
|
</script>
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
3
|
<div>
|
|
4
|
-
<label for="collection">{{ $t("Collection") }}</label>
|
|
5
|
-
<b-form-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
<label for="collection">{{ $t("Collection Name") }}</label>
|
|
5
|
+
<b-form-group>
|
|
6
|
+
<b-form-select
|
|
7
|
+
id="collection"
|
|
8
|
+
v-model="collectionId"
|
|
9
|
+
:options="collections"
|
|
10
|
+
data-cy="inspector-collection"
|
|
11
|
+
/>
|
|
12
|
+
<b-form-text class="mt-2">
|
|
13
|
+
{{ $t("Collection Record Control is not available for Anonymous Web Entry") }}
|
|
14
|
+
</b-form-text>
|
|
15
|
+
</b-form-group>
|
|
11
16
|
</div>
|
|
12
17
|
<div v-if="collectionId > 0" class="screen-link mt-2">
|
|
13
18
|
<a
|
|
@@ -158,6 +158,10 @@ export default {
|
|
|
158
158
|
this.removeDefaultClasses();
|
|
159
159
|
},
|
|
160
160
|
mounted() {
|
|
161
|
+
if (this.value) {
|
|
162
|
+
this.fetchFiles();
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
this.$root.$on('set-upload-data-name',
|
|
162
166
|
(recordList, index, id) => this.listenRecordList(recordList, index, id));
|
|
163
167
|
|
|
@@ -597,6 +601,10 @@ export default {
|
|
|
597
601
|
}
|
|
598
602
|
|
|
599
603
|
if (displayMessage.length > 0) {
|
|
604
|
+
const data = JSON.parse(displayMessage);
|
|
605
|
+
if (data.message) {
|
|
606
|
+
displayMessage = data.message;
|
|
607
|
+
}
|
|
600
608
|
window.ProcessMaker.alert(`${this.$t('File Upload Error:')} ${displayMessage}`, 'danger');
|
|
601
609
|
}
|
|
602
610
|
|
|
@@ -699,7 +707,29 @@ export default {
|
|
|
699
707
|
},
|
|
700
708
|
cfSkipFileUpload() {
|
|
701
709
|
this.$emit('cf-skip-file-upload');
|
|
702
|
-
}
|
|
710
|
+
},
|
|
711
|
+
async fetchFiles() {
|
|
712
|
+
const fileIds = Array.isArray(this.value) ? this.value : [this.value];
|
|
713
|
+
|
|
714
|
+
const fetchPromises = fileIds.map(async (file) => {
|
|
715
|
+
const id = file?.file ?? file;
|
|
716
|
+
const endpoint = `files/${id}`;
|
|
717
|
+
try {
|
|
718
|
+
const response = await ProcessMaker.apiClient.get(endpoint);
|
|
719
|
+
if (response?.data) {
|
|
720
|
+
const fileExists = this.files.some(existingFile => existingFile.id === response.data.id);
|
|
721
|
+
// Check if the file already exists in the list before adding it.
|
|
722
|
+
if (!fileExists) {
|
|
723
|
+
this.files.push(response.data);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
} catch (error) {
|
|
727
|
+
console.error(`Failed to fetch file ${id}`, error);
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
return await Promise.all(fetchPromises);
|
|
732
|
+
},
|
|
703
733
|
},
|
|
704
734
|
};
|
|
705
735
|
</script>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
<script>
|
|
17
17
|
import VueFormRenderer from "../vue-form-renderer.vue";
|
|
18
18
|
import CollectionRecordsList from "../inspector/collection-records-list.vue";
|
|
19
|
+
import _ from 'lodash';
|
|
19
20
|
|
|
20
21
|
const globalObject = typeof window === "undefined" ? global : window;
|
|
21
22
|
|
|
@@ -179,12 +180,12 @@ export default {
|
|
|
179
180
|
this.selDisplayMode === "View" ? viewScreen : editScreen;
|
|
180
181
|
|
|
181
182
|
this.loadScreen(this.screenCollectionId);
|
|
182
|
-
|
|
183
|
+
|
|
183
184
|
//This section validates if Collection has draft data
|
|
184
185
|
if(this.taskDraft?.draft?.data == null || this.taskDraft.draft.data === '') {
|
|
185
186
|
this.localData = respData;
|
|
186
187
|
}else{
|
|
187
|
-
this.localData = this.taskDraft.draft.data;
|
|
188
|
+
this.localData = _.merge({}, respData, this.taskDraft.draft.data);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
})
|
|
@@ -212,9 +213,9 @@ export default {
|
|
|
212
213
|
},
|
|
213
214
|
record(record) {
|
|
214
215
|
this.hasMustache = false;
|
|
215
|
-
if (record && !isNaN(record) && record > 0 && this.collection) {
|
|
216
|
+
if (record && !isNaN(record) && record > 0 && this.collection.collectionId) {
|
|
216
217
|
this.selRecordId = record;
|
|
217
|
-
this.loadRecordCollection(this.
|
|
218
|
+
this.loadRecordCollection(this.collection.collectionId, record, this.selDisplayMode);
|
|
218
219
|
} else {
|
|
219
220
|
if (this.isMustache(record)) {
|
|
220
221
|
this.callbackRecord();
|
|
@@ -206,9 +206,9 @@ export default {
|
|
|
206
206
|
},
|
|
207
207
|
record(record) {
|
|
208
208
|
this.hasMustache = false;
|
|
209
|
-
if (record && !isNaN(record) && record > 0 && this.collection) {
|
|
209
|
+
if (record && !isNaN(record) && record > 0 && this.collection.collectionId) {
|
|
210
210
|
this.selRecordId = record;
|
|
211
|
-
this.loadRecordCollection(this.
|
|
211
|
+
this.loadRecordCollection(this.collection.collectionId, record, this.collectionmode);
|
|
212
212
|
} else {
|
|
213
213
|
if (this.isMustache(record)) {
|
|
214
214
|
this.callbackRecord();
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
<input
|
|
42
42
|
v-else
|
|
43
43
|
v-model="localValue"
|
|
44
|
-
v-bind="
|
|
44
|
+
v-bind="componentConfigEncField"
|
|
45
45
|
v-uni-id="name"
|
|
46
46
|
:name="name"
|
|
47
47
|
class="form-control"
|
|
@@ -231,6 +231,7 @@ export default {
|
|
|
231
231
|
labelBtn: '',
|
|
232
232
|
errorEncryptedField: '',
|
|
233
233
|
concealExecuted: false,
|
|
234
|
+
componentConfigEncField: null,
|
|
234
235
|
};
|
|
235
236
|
},
|
|
236
237
|
computed: {
|
|
@@ -289,17 +290,18 @@ export default {
|
|
|
289
290
|
* "encrypted" attribute is enabled
|
|
290
291
|
*/
|
|
291
292
|
if (this.encryptedConfig?.encrypted) {
|
|
293
|
+
this.componentConfigEncField = JSON.parse(JSON.stringify(this.componentConfig));
|
|
292
294
|
if (uuidValidate(this.localValue)) {
|
|
293
295
|
this.inputType = "password";
|
|
294
296
|
this.iconBtn = "fas fa-eye";
|
|
295
297
|
this.labelBtn = this.$t("Reveal");
|
|
296
298
|
this.concealExecuted = true;
|
|
297
|
-
this.
|
|
299
|
+
this.componentConfigEncField.readonly = true;
|
|
298
300
|
} else {
|
|
299
301
|
this.inputType = "text";
|
|
300
302
|
this.iconBtn = "fas fa-eye-slash";
|
|
301
303
|
this.labelBtn = this.$t("Conceal");
|
|
302
|
-
this.
|
|
304
|
+
this.componentConfigEncField.readonly = this.componentConfig.readonly;
|
|
303
305
|
}
|
|
304
306
|
} else {
|
|
305
307
|
this.inputType = this.dataType;
|
|
@@ -381,7 +383,7 @@ export default {
|
|
|
381
383
|
this.iconBtn = "fas fa-eye";
|
|
382
384
|
this.labelBtn = this.$t("Reveal");
|
|
383
385
|
this.concealExecuted = true;
|
|
384
|
-
this.
|
|
386
|
+
this.componentConfigEncField.readonly = true;
|
|
385
387
|
this.errorEncryptedField = "";
|
|
386
388
|
|
|
387
389
|
// Assign uuid from encrypted data
|
|
@@ -392,7 +394,7 @@ export default {
|
|
|
392
394
|
this.inputType = this.dataType;
|
|
393
395
|
this.iconBtn = "fas fa-eye-slash";
|
|
394
396
|
this.labelBtn = this.$t("Conceal");
|
|
395
|
-
this.
|
|
397
|
+
this.componentConfigEncField.readonly = this.componentConfig.readonly;
|
|
396
398
|
|
|
397
399
|
// Assign value decrypted
|
|
398
400
|
this.localValue = decryptedValue;
|
package/src/components/task.vue
CHANGED
|
@@ -107,6 +107,7 @@ export default {
|
|
|
107
107
|
alwaysAllowEditing: { type: Boolean, default: false },
|
|
108
108
|
disableInterstitial: { type: Boolean, default: false },
|
|
109
109
|
waitLoadingListeners: { type: Boolean, default: false },
|
|
110
|
+
isWebEntry: { type: Boolean, default: false },
|
|
110
111
|
},
|
|
111
112
|
data() {
|
|
112
113
|
return {
|
|
@@ -677,55 +678,97 @@ export default {
|
|
|
677
678
|
return null;
|
|
678
679
|
}
|
|
679
680
|
},
|
|
681
|
+
|
|
680
682
|
/**
|
|
681
|
-
* Handles
|
|
682
|
-
* @
|
|
683
|
-
* @param {
|
|
684
|
-
* @param {
|
|
683
|
+
* Handles redirection upon process completion, considering destination type and user task validation.
|
|
684
|
+
* @async
|
|
685
|
+
* @param {Object} data - Contains information about the end event destination.
|
|
686
|
+
* @param {number} userId - ID of the current user.
|
|
687
|
+
* @param {number} requestId - ID of the request to complete.
|
|
688
|
+
* @returns {Promise<void>}
|
|
685
689
|
*/
|
|
686
690
|
async processCompletedRedirect(data, userId, requestId) {
|
|
691
|
+
// Emit completion event if accessed through web entry.
|
|
692
|
+
if (this.isWebEntry) {
|
|
693
|
+
this.$emit("completed", requestId);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
|
|
687
697
|
try {
|
|
688
|
-
|
|
689
|
-
if (
|
|
690
|
-
|
|
691
|
-
window.location.href = data?.endEventDestination.value;
|
|
692
|
-
} else {
|
|
693
|
-
window.location.href = `/requests/${this.requestId}`;
|
|
694
|
-
}
|
|
698
|
+
const destinationUrl = this.resolveDestinationUrl(data);
|
|
699
|
+
if (destinationUrl) {
|
|
700
|
+
window.location.href = destinationUrl;
|
|
695
701
|
return;
|
|
696
702
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
);
|
|
701
|
-
// Get the next request using retry logic
|
|
702
|
-
const nextRequest = await this.retryApiCall(() =>
|
|
703
|
-
this.getNextRequest(
|
|
704
|
-
endEventDestination.processId,
|
|
705
|
-
endEventDestination.startEvent
|
|
706
|
-
)
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
const params = {
|
|
710
|
-
processRequestId: nextRequest.data.id,
|
|
711
|
-
status: "ACTIVE",
|
|
712
|
-
page: 1,
|
|
713
|
-
perPage: 1
|
|
714
|
-
};
|
|
715
|
-
// Get the tasks for the next request using retry logic
|
|
716
|
-
const response = await this.retryApiCall(() => this.getTasks(params));
|
|
717
|
-
// Handle the first task from the response
|
|
718
|
-
const firstTask = response.data.data[0];
|
|
719
|
-
if (firstTask && firstTask.user_id === userId) {
|
|
720
|
-
this.redirectToTask(firstTask.id);
|
|
721
|
-
} else {
|
|
722
|
-
this.redirectToRequest(requestId);
|
|
723
|
-
}
|
|
703
|
+
|
|
704
|
+
// Proceed to handle redirection to the next request if applicable.
|
|
705
|
+
await this.handleNextRequestRedirection(data, userId, requestId);
|
|
724
706
|
} catch (error) {
|
|
725
707
|
console.error("Error processing completed redirect:", error);
|
|
726
708
|
this.$emit("completed", requestId);
|
|
727
709
|
}
|
|
728
710
|
},
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Resolves the URL to redirect to if the end event is not another process.
|
|
714
|
+
* @param {Object} data - Contains the end event destination data.
|
|
715
|
+
* @returns {string|null} - The URL for redirection, or null if proceeding to another process.
|
|
716
|
+
*/
|
|
717
|
+
resolveDestinationUrl(data) {
|
|
718
|
+
if (data.endEventDestination.type !== "anotherProcess") {
|
|
719
|
+
return data.endEventDestination.value || `/requests/${this.requestId}`;
|
|
720
|
+
}
|
|
721
|
+
return null;
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Handles redirection logic to the next request's task or fallback to the request itself.
|
|
726
|
+
* @async
|
|
727
|
+
* @param {Object} data - Contains the end event destination.
|
|
728
|
+
* @param {number} userId - ID of the current user.
|
|
729
|
+
* @param {number} requestId - ID of the request to complete.
|
|
730
|
+
* @returns {Promise<void>}
|
|
731
|
+
*/
|
|
732
|
+
async handleNextRequestRedirection(data, userId, requestId) {
|
|
733
|
+
const nextRequest = await this.fetchNextRequest(data.endEventDestination);
|
|
734
|
+
const firstTask = await this.fetchFirstTask(nextRequest);
|
|
735
|
+
|
|
736
|
+
if (firstTask?.user_id === userId) {
|
|
737
|
+
this.redirectToTask(firstTask.id);
|
|
738
|
+
} else {
|
|
739
|
+
this.redirectToRequest(requestId);
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Fetch the next request using retry logic.
|
|
745
|
+
* @async
|
|
746
|
+
* @param {Object} endEventDestination - The parsed end event destination object.
|
|
747
|
+
* @returns {Promise<Object>} - The next request data.
|
|
748
|
+
*/
|
|
749
|
+
async fetchNextRequest(endEventDestination) {
|
|
750
|
+
const destinationData = this.parseJsonSafely(endEventDestination.value);
|
|
751
|
+
return await this.retryApiCall(() =>
|
|
752
|
+
this.getNextRequest(destinationData.processId, destinationData.startEvent)
|
|
753
|
+
);
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Fetch the first task from the next request using retry logic.
|
|
758
|
+
* @async
|
|
759
|
+
* @param {Object} nextRequest - The next request object.
|
|
760
|
+
* @returns {Promise<Object|null>} - The first task data, or null if no tasks found.
|
|
761
|
+
*/
|
|
762
|
+
async fetchFirstTask(nextRequest) {
|
|
763
|
+
const params = {
|
|
764
|
+
processRequestId: nextRequest.data.id,
|
|
765
|
+
status: "ACTIVE",
|
|
766
|
+
page: 1,
|
|
767
|
+
perPage: 1
|
|
768
|
+
};
|
|
769
|
+
const response = await this.retryApiCall(() => this.getTasks(params));
|
|
770
|
+
return response.data.data[0] || null;
|
|
771
|
+
},
|
|
729
772
|
getAllowedRequestId() {
|
|
730
773
|
const permissions = this.task.user_request_permission || [];
|
|
731
774
|
const permission = permissions.find(item => item.process_request_id === this.parentRequest)
|