@processmaker/screen-builder 3.8.14 → 3.8.15-patch.a.1
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 +6931 -6491
- package/dist/vue-form-builder.es.js.map +1 -1
- package/dist/vue-form-builder.umd.js +51 -51
- package/dist/vue-form-builder.umd.js.map +1 -1
- package/package.json +9 -1
- package/src/components/accordions.js +1 -0
- package/src/components/inspector/index.js +1 -0
- package/src/components/inspector/variables-to-submit.vue +782 -0
- package/src/components/renderer/form-button.vue +4 -2
- package/src/components/task.vue +19 -4
- package/src/components/vue-form-renderer.vue +4 -2
- package/src/form-builder-controls.js +8 -0
- package/src/mixins/Json2Vue.js +2 -1
- package/src/mixins/ScreenBase.js +28 -3
- package/src/mixins/VariablesToSubmitFilter.js +76 -0
- package/src/mixins/index.js +1 -0
- package/src/stories/VariablesToSubmit.stories.js +686 -0
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="event === 'submit'" class="variables-to-submit-wrapper">
|
|
3
|
+
<!-- Warning for missing required fields (outside card) -->
|
|
4
|
+
<b-alert
|
|
5
|
+
v-if="isEnabled && missingRequiredVariables.length > 0"
|
|
6
|
+
show
|
|
7
|
+
variant="warning"
|
|
8
|
+
class="warning-alert"
|
|
9
|
+
data-cy="missing-required-warning"
|
|
10
|
+
>
|
|
11
|
+
<i class="fas fa-bolt warning-icon"></i>
|
|
12
|
+
<span class="warning-text">
|
|
13
|
+
{{ $t('The following required fields are not included') }} "<strong>{{ missingRequiredVariables.join('", "') }}</strong>".
|
|
14
|
+
{{ $t('This may cause validation errors during submission.') }}
|
|
15
|
+
</span>
|
|
16
|
+
</b-alert>
|
|
17
|
+
|
|
18
|
+
<!-- Card Container -->
|
|
19
|
+
<div class="variables-to-submit-card">
|
|
20
|
+
<!-- Header Section -->
|
|
21
|
+
<div class="header-section">
|
|
22
|
+
<h6 class="header-title">{{ $t('Submit Information') }}</h6>
|
|
23
|
+
<b-form-checkbox
|
|
24
|
+
v-model="isEnabled"
|
|
25
|
+
switch
|
|
26
|
+
size="lg"
|
|
27
|
+
class="toggle-switch"
|
|
28
|
+
data-cy="variables-to-submit-toggle"
|
|
29
|
+
>
|
|
30
|
+
</b-form-checkbox>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="description-text">
|
|
34
|
+
<p>{{ $t('Select variables to submit, otherwise all variables will be submitted by default.') }}</p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div v-if="isEnabled && availableVariables.length === 0" class="alert alert-info">
|
|
38
|
+
<small>{{ $t('No variables available. Variables will be available after you add form fields to your screen.') }}</small>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div v-else-if="isEnabled">
|
|
42
|
+
<div class="divider"></div>
|
|
43
|
+
|
|
44
|
+
<!-- Select All and Search Section -->
|
|
45
|
+
<div class="controls-section">
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
class="select-all-button"
|
|
49
|
+
@click="selectAll"
|
|
50
|
+
:disabled="filteredVariables.length === 0 || selectedVariables.length === filteredVariables.length"
|
|
51
|
+
data-cy="variables-to-submit-select-all"
|
|
52
|
+
>
|
|
53
|
+
{{ $t('Select All') }}
|
|
54
|
+
</button>
|
|
55
|
+
<button
|
|
56
|
+
type="button"
|
|
57
|
+
class="search-button"
|
|
58
|
+
@click="toggleSearch"
|
|
59
|
+
data-cy="variables-to-submit-search-toggle"
|
|
60
|
+
>
|
|
61
|
+
<i class="fas fa-search"></i>
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Search Input (shown when search is active) -->
|
|
66
|
+
<div v-if="showSearch" class="search-container">
|
|
67
|
+
<b-input-group>
|
|
68
|
+
<b-form-input
|
|
69
|
+
v-model="searchQuery"
|
|
70
|
+
:placeholder="$t('Search variables...')"
|
|
71
|
+
data-cy="variables-to-submit-search"
|
|
72
|
+
class="search-input"
|
|
73
|
+
/>
|
|
74
|
+
<b-input-group-append>
|
|
75
|
+
<b-button
|
|
76
|
+
@click="searchQuery = ''"
|
|
77
|
+
:disabled="!searchQuery"
|
|
78
|
+
data-cy="variables-to-submit-clear-search"
|
|
79
|
+
variant="outline-secondary"
|
|
80
|
+
class="clear-search-button"
|
|
81
|
+
>
|
|
82
|
+
<i class="fas fa-times"></i>
|
|
83
|
+
</b-button>
|
|
84
|
+
</b-input-group-append>
|
|
85
|
+
</b-input-group>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="divider"></div>
|
|
88
|
+
<!-- Variables List -->
|
|
89
|
+
<div class="variables-list">
|
|
90
|
+
<div
|
|
91
|
+
v-for="variable in filteredVariables"
|
|
92
|
+
:key="variable"
|
|
93
|
+
class="variable-item"
|
|
94
|
+
:data-cy="`variable-item-${variable}`"
|
|
95
|
+
>
|
|
96
|
+
<b-form-checkbox
|
|
97
|
+
v-model="selectedVariables"
|
|
98
|
+
:value="variable"
|
|
99
|
+
class="variable-checkbox"
|
|
100
|
+
:data-cy="`variable-checkbox-${variable}`"
|
|
101
|
+
>
|
|
102
|
+
<span class="variable-name">{{ variable }}</span>
|
|
103
|
+
</b-form-checkbox>
|
|
104
|
+
</div>
|
|
105
|
+
<div v-if="filteredVariables.length === 0" class="no-results">
|
|
106
|
+
<small>{{ $t('No variables match your search.') }}</small>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<script>
|
|
115
|
+
export default {
|
|
116
|
+
name: 'VariablesToSubmit',
|
|
117
|
+
props: {
|
|
118
|
+
value: {
|
|
119
|
+
type: Array,
|
|
120
|
+
default: () => []
|
|
121
|
+
},
|
|
122
|
+
builder: {
|
|
123
|
+
type: Object,
|
|
124
|
+
required: true
|
|
125
|
+
},
|
|
126
|
+
formConfig: {
|
|
127
|
+
type: Array,
|
|
128
|
+
required: true
|
|
129
|
+
},
|
|
130
|
+
selectedControl: {
|
|
131
|
+
type: Object,
|
|
132
|
+
default: null
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
data() {
|
|
136
|
+
return {
|
|
137
|
+
searchQuery: '',
|
|
138
|
+
selectedVariables: this.value || [],
|
|
139
|
+
event: '',
|
|
140
|
+
isEnabled: (this.value && this.value.length > 0) || false,
|
|
141
|
+
showSearch: false
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
computed: {
|
|
145
|
+
/**
|
|
146
|
+
* Get all available variables from form config, variables tree, and computed properties
|
|
147
|
+
* Excludes only _parent variables, includes all others (root level and nested)
|
|
148
|
+
*/
|
|
149
|
+
availableVariables() {
|
|
150
|
+
const variables = {};
|
|
151
|
+
|
|
152
|
+
// Extract from form config
|
|
153
|
+
const config = this.formConfig || this.builder?.config || this.$root?.$children[0]?.config || [];
|
|
154
|
+
if (Array.isArray(config) && config.length > 0) {
|
|
155
|
+
Object.assign(variables, this.extractVariablesFromConfig(config));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Extract from variables tree
|
|
159
|
+
const tree = this.builder?.variablesTree || this.$root?.$children[0]?.variablesTree || [];
|
|
160
|
+
if (Array.isArray(tree) && tree.length > 0) {
|
|
161
|
+
const result = this.loadVariables(tree);
|
|
162
|
+
Object.assign(variables, result.variables || {});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Extract calculated variables (computed properties)
|
|
166
|
+
Object.assign(variables, this.extractCalculatedVariables());
|
|
167
|
+
|
|
168
|
+
// Filter: exclude _parent variables, include all others
|
|
169
|
+
return Object.keys(variables)
|
|
170
|
+
.filter(variable => !variable.startsWith('_parent.'))
|
|
171
|
+
.sort();
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
filteredVariables() {
|
|
175
|
+
if (!this.searchQuery) {
|
|
176
|
+
return this.availableVariables;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const query = this.searchQuery.toLowerCase();
|
|
180
|
+
return this.availableVariables.filter(variable =>
|
|
181
|
+
variable.toLowerCase().includes(query)
|
|
182
|
+
);
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get list of required variables from form config
|
|
187
|
+
*/
|
|
188
|
+
requiredVariables() {
|
|
189
|
+
const required = [];
|
|
190
|
+
const config = this.formConfig || this.builder?.config || this.$root?.$children[0]?.config || [];
|
|
191
|
+
|
|
192
|
+
if (Array.isArray(config) && config.length > 0) {
|
|
193
|
+
config.forEach(page => {
|
|
194
|
+
if (Array.isArray(page.items)) {
|
|
195
|
+
this.findRequiredFields(page.items, required);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return required;
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get list of required variables that are not in selectedVariables
|
|
205
|
+
*/
|
|
206
|
+
missingRequiredVariables() {
|
|
207
|
+
if (!this.isEnabled) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return this.requiredVariables.filter(
|
|
212
|
+
variable => !this.selectedVariables.includes(variable)
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
watch: {
|
|
217
|
+
value(newValue) {
|
|
218
|
+
if (JSON.stringify(newValue) !== JSON.stringify(this.selectedVariables)) {
|
|
219
|
+
this.selectedVariables = newValue || [];
|
|
220
|
+
this.isEnabled = (newValue && newValue.length > 0) || false;
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
selectedVariables(newValue) {
|
|
224
|
+
// Emit the selected variables array
|
|
225
|
+
if (this.isEnabled) {
|
|
226
|
+
this.$emit('input', newValue);
|
|
227
|
+
this.$emit('change', newValue);
|
|
228
|
+
} else {
|
|
229
|
+
// If disabled, emit empty array to submit all variables
|
|
230
|
+
this.$emit('input', []);
|
|
231
|
+
this.$emit('change', []);
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
isEnabled(newValue) {
|
|
235
|
+
if (!newValue) {
|
|
236
|
+
// When disabled, clear selection to submit all variables
|
|
237
|
+
this.selectedVariables = [];
|
|
238
|
+
this.$emit('input', []);
|
|
239
|
+
this.$emit('change', []);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
'selectedControl.config.event'(newVal) {
|
|
243
|
+
this.event = newVal;
|
|
244
|
+
},
|
|
245
|
+
'builder.variablesTree'() {
|
|
246
|
+
// Force recomputation when variables tree changes
|
|
247
|
+
this.$forceUpdate();
|
|
248
|
+
},
|
|
249
|
+
// Watch for computed properties changes in App.vue
|
|
250
|
+
'$root.computed'() {
|
|
251
|
+
// Force recomputation when computed properties change
|
|
252
|
+
this.$forceUpdate();
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
methods: {
|
|
256
|
+
/**
|
|
257
|
+
* Load variables from the variables tree
|
|
258
|
+
* Only includes root-level variables (no prefix, no dots in name)
|
|
259
|
+
*/
|
|
260
|
+
loadVariables(def, prefix = '', variables = {}) {
|
|
261
|
+
if (!Array.isArray(def)) {
|
|
262
|
+
return { variables, prefix };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
def.forEach(item => {
|
|
266
|
+
// Include root-level variables only
|
|
267
|
+
if (item.name && !item.prefix && !prefix) {
|
|
268
|
+
const variableName = item.name;
|
|
269
|
+
if (!variableName.includes('.') && !variableName.startsWith('_parent.')) {
|
|
270
|
+
variables[variableName] = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Skip nested container items
|
|
275
|
+
if (item.items && Array.isArray(item.items) && item.prefix) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return { variables, prefix };
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
selectAll() {
|
|
284
|
+
this.selectedVariables = [...new Set([...this.selectedVariables, ...this.filteredVariables])];
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
deselectAll() {
|
|
288
|
+
const filteredSet = new Set(this.filteredVariables);
|
|
289
|
+
this.selectedVariables = this.selectedVariables.filter(v => !filteredSet.has(v));
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
toggleSearch() {
|
|
293
|
+
this.showSearch = !this.showSearch;
|
|
294
|
+
if (!this.showSearch) {
|
|
295
|
+
this.searchQuery = '';
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Extract calculated variables (computed properties) from the screen
|
|
301
|
+
* Searches in multiple locations: App.vue, builder, or parent components
|
|
302
|
+
*/
|
|
303
|
+
extractCalculatedVariables() {
|
|
304
|
+
const calculatedVars = {};
|
|
305
|
+
const computed = this.getComputedProperties();
|
|
306
|
+
|
|
307
|
+
if (Array.isArray(computed) && computed.length > 0) {
|
|
308
|
+
computed.forEach(calc => {
|
|
309
|
+
if (calc.property && !calc.byPass && !calc.property.startsWith('_parent.')) {
|
|
310
|
+
calculatedVars[calc.property] = null;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return calculatedVars;
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get computed properties from various sources
|
|
320
|
+
*/
|
|
321
|
+
getComputedProperties() {
|
|
322
|
+
// Try App.vue (root component)
|
|
323
|
+
if (this.$root?.$data?.computed) {
|
|
324
|
+
return this.$root.$data.computed;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Try builder sources
|
|
328
|
+
if (this.builder?.screen?.computed) {
|
|
329
|
+
return this.builder.screen.computed;
|
|
330
|
+
}
|
|
331
|
+
if (this.builder?.computed) {
|
|
332
|
+
return this.builder.computed;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Try parent components
|
|
336
|
+
if (this.$root?.$parent?.computed) {
|
|
337
|
+
return this.$root.$parent.computed;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Search in parent chain
|
|
341
|
+
let parent = this.$parent;
|
|
342
|
+
for (let depth = 0; depth < 10 && parent; depth++) {
|
|
343
|
+
if (parent.$data?.computed) {
|
|
344
|
+
return parent.$data.computed;
|
|
345
|
+
}
|
|
346
|
+
parent = parent.$parent;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return [];
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Extract variables from form config
|
|
354
|
+
* Recursively searches through all pages and items
|
|
355
|
+
*/
|
|
356
|
+
extractVariablesFromConfig(config, prefix = '', variables = {}) {
|
|
357
|
+
if (!Array.isArray(config)) {
|
|
358
|
+
return variables;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
config.forEach(page => {
|
|
362
|
+
if (Array.isArray(page.items)) {
|
|
363
|
+
this.extractVariablesFromConfigItems(page.items, prefix, variables);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
return variables;
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Recursively extract variables from config items
|
|
372
|
+
*/
|
|
373
|
+
extractVariablesFromConfigItems(items, prefix = '', variables = {}, depth = 0) {
|
|
374
|
+
if (!Array.isArray(items)) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
items.forEach(item => {
|
|
379
|
+
// Extract variable from current item
|
|
380
|
+
this.extractVariableFromItem(item, variables);
|
|
381
|
+
|
|
382
|
+
// Handle special component types (Open/Closed Principle)
|
|
383
|
+
this.processSpecialComponents(item, prefix, variables, depth);
|
|
384
|
+
|
|
385
|
+
// Process nested items in containers
|
|
386
|
+
this.processNestedItems(item, prefix, variables, depth);
|
|
387
|
+
});
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Extract variable name from a single item
|
|
392
|
+
* Single Responsibility: Only handles variable name extraction
|
|
393
|
+
*/
|
|
394
|
+
extractVariableFromItem(item, variables) {
|
|
395
|
+
const variableName = item.config?.name;
|
|
396
|
+
if (variableName && !variableName.startsWith('_parent.')) {
|
|
397
|
+
variables[variableName] = null;
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Process special component types
|
|
403
|
+
*/
|
|
404
|
+
processSpecialComponents(item, prefix, variables, depth) {
|
|
405
|
+
const componentHandlers = {
|
|
406
|
+
'FormNestedScreen': () => this.extractFromNestedScreen(item, prefix, variables, depth),
|
|
407
|
+
// Add more special component handlers here in the future
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const handler = componentHandlers[item.component];
|
|
411
|
+
if (handler) {
|
|
412
|
+
handler();
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Extract variables from FormNestedScreen
|
|
418
|
+
*/
|
|
419
|
+
extractFromNestedScreen(item, prefix, variables, depth) {
|
|
420
|
+
if (!item.config?.screen) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const nestedScreenPages = this.getNestedScreenPages(item.config.screen);
|
|
425
|
+
if (!nestedScreenPages) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
nestedScreenPages.forEach(page => {
|
|
430
|
+
if (Array.isArray(page.items)) {
|
|
431
|
+
this.extractVariablesFromConfigItems(page.items, prefix, variables, depth + 1);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Get nested screen pages from global store
|
|
438
|
+
*/
|
|
439
|
+
getNestedScreenPages(screenId) {
|
|
440
|
+
const globalObject = typeof window === 'undefined' ? global : window;
|
|
441
|
+
|
|
442
|
+
if (!globalObject.nestedScreens) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const nestedScreenData = globalObject.nestedScreens[`id_${screenId}`];
|
|
447
|
+
return Array.isArray(nestedScreenData) ? nestedScreenData : null;
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Process nested items in containers
|
|
452
|
+
*/
|
|
453
|
+
processNestedItems(item, prefix, variables, depth) {
|
|
454
|
+
if (!Array.isArray(item.items) || item.items.length === 0) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (this.isMultiColumn(item)) {
|
|
459
|
+
this.processMultiColumnItems(item.items, prefix, variables, depth);
|
|
460
|
+
} else {
|
|
461
|
+
this.processRegularContainerItems(item.items, prefix, variables, depth);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Process FormMultiColumn items
|
|
467
|
+
*/
|
|
468
|
+
processMultiColumnItems(items, prefix, variables, depth) {
|
|
469
|
+
items.forEach(columnItems => {
|
|
470
|
+
if (Array.isArray(columnItems) && columnItems.length > 0) {
|
|
471
|
+
this.extractVariablesFromConfigItems(columnItems, prefix, variables, depth + 1);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
},
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Process regular container items
|
|
478
|
+
*/
|
|
479
|
+
processRegularContainerItems(items, prefix, variables, depth) {
|
|
480
|
+
this.extractVariablesFromConfigItems(items, prefix, variables, depth + 1);
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Check if an item is a FormMultiColumn
|
|
485
|
+
*/
|
|
486
|
+
isMultiColumn(item) {
|
|
487
|
+
return item.component === 'FormMultiColumn';
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Check if a validation item indicates required field
|
|
492
|
+
*/
|
|
493
|
+
isRequiredValidation(validation) {
|
|
494
|
+
if (typeof validation === 'string') {
|
|
495
|
+
return validation.includes('required');
|
|
496
|
+
}
|
|
497
|
+
if (Array.isArray(validation)) {
|
|
498
|
+
return validation.some(v => {
|
|
499
|
+
if (typeof v === 'string') return v.includes('required');
|
|
500
|
+
if (v?.value && typeof v.value === 'string') return v.value.includes('required');
|
|
501
|
+
if (v?.rule && typeof v.rule === 'string') return v.rule.includes('required');
|
|
502
|
+
return false;
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return false;
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Recursively find required fields in form config
|
|
510
|
+
*/
|
|
511
|
+
findRequiredFields(items, required) {
|
|
512
|
+
if (!Array.isArray(items)) return;
|
|
513
|
+
|
|
514
|
+
items.forEach(item => {
|
|
515
|
+
const { validation, name } = item.config || {};
|
|
516
|
+
|
|
517
|
+
// Add to required list if has required validation
|
|
518
|
+
if (name && !name.startsWith('_parent.') && this.isRequiredValidation(validation)) {
|
|
519
|
+
required.push(name);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Handle FormNestedScreen components
|
|
523
|
+
if (item.component === 'FormNestedScreen' && item.config?.screen) {
|
|
524
|
+
const nestedScreenPages = this.getNestedScreenPages(item.config.screen);
|
|
525
|
+
if (nestedScreenPages) {
|
|
526
|
+
nestedScreenPages.forEach(page => {
|
|
527
|
+
if (Array.isArray(page.items)) {
|
|
528
|
+
this.findRequiredFields(page.items, required);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Recurse into nested items
|
|
535
|
+
if (Array.isArray(item.items)) {
|
|
536
|
+
if (this.isMultiColumn(item)) {
|
|
537
|
+
item.items.forEach(columnItems => {
|
|
538
|
+
if (Array.isArray(columnItems)) {
|
|
539
|
+
this.findRequiredFields(columnItems, required);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
} else {
|
|
543
|
+
this.findRequiredFields(item.items, required);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
mounted() {
|
|
550
|
+
this.event = this.selectedControl?.config?.event || '';
|
|
551
|
+
this.selectedVariables = this.value || [];
|
|
552
|
+
|
|
553
|
+
// Force update to ensure variables tree is loaded
|
|
554
|
+
this.$nextTick(() => {
|
|
555
|
+
this.$forceUpdate();
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
</script>
|
|
560
|
+
|
|
561
|
+
<style scoped>
|
|
562
|
+
/* Wrapper Container */
|
|
563
|
+
.variables-to-submit-wrapper {
|
|
564
|
+
padding: 0;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* Card Container with Border */
|
|
568
|
+
.variables-to-submit-card {
|
|
569
|
+
background-color: #ffffff;
|
|
570
|
+
border: 1px solid #e3e8ef;
|
|
571
|
+
border-radius: 8px;
|
|
572
|
+
padding: 16px;
|
|
573
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/* Header Section */
|
|
577
|
+
.header-section {
|
|
578
|
+
display: flex;
|
|
579
|
+
justify-content: space-between;
|
|
580
|
+
align-items: center;
|
|
581
|
+
margin-bottom: 0;
|
|
582
|
+
padding: 0 0 12px 0;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.header-title {
|
|
586
|
+
margin: 0;
|
|
587
|
+
font-size: 18px;
|
|
588
|
+
font-weight: 600;
|
|
589
|
+
color: #1a1a1a;
|
|
590
|
+
line-height: 1.4;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.toggle-switch {
|
|
594
|
+
margin: 0;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/* Description Text */
|
|
598
|
+
.description-text {
|
|
599
|
+
margin-top: 8px;
|
|
600
|
+
margin-bottom: 0;
|
|
601
|
+
padding: 0;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.description-text p {
|
|
605
|
+
margin: 0;
|
|
606
|
+
font-size: 13px;
|
|
607
|
+
color: #6c757d;
|
|
608
|
+
line-height: 1.5;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* Controls Section (Select All + Search) */
|
|
612
|
+
.controls-section {
|
|
613
|
+
display: flex;
|
|
614
|
+
justify-content: space-between;
|
|
615
|
+
align-items: center;
|
|
616
|
+
margin-bottom: 12px;
|
|
617
|
+
margin-top: 0;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.select-all-button {
|
|
621
|
+
background: none;
|
|
622
|
+
border: none;
|
|
623
|
+
padding: 0;
|
|
624
|
+
color: #0d6efd;
|
|
625
|
+
font-size: 15px;
|
|
626
|
+
font-weight: 600;
|
|
627
|
+
cursor: pointer;
|
|
628
|
+
text-decoration: none;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.select-all-button:hover:not(:disabled) {
|
|
632
|
+
color: #0a58ca;
|
|
633
|
+
text-decoration: none;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.select-all-button:disabled {
|
|
637
|
+
color: #adb5bd;
|
|
638
|
+
cursor: not-allowed;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.search-button {
|
|
642
|
+
background: #fff;
|
|
643
|
+
border: 1px solid #ced4da;
|
|
644
|
+
border-radius: 6px;
|
|
645
|
+
padding: 6px 12px;
|
|
646
|
+
color: #495057;
|
|
647
|
+
cursor: pointer;
|
|
648
|
+
transition: all 0.2s ease;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.search-button:hover {
|
|
652
|
+
background: #f8f9fa;
|
|
653
|
+
border-color: #adb5bd;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.search-button i {
|
|
657
|
+
font-size: 14px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/* Search Container */
|
|
661
|
+
.search-container {
|
|
662
|
+
margin-bottom: 12px;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
.search-input {
|
|
666
|
+
border-radius: 6px;
|
|
667
|
+
font-size: 14px;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
.clear-search-button {
|
|
671
|
+
border-radius: 0 6px 6px 0;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* Divider */
|
|
675
|
+
.divider {
|
|
676
|
+
height: 1px;
|
|
677
|
+
background-color: #e3e8ef;
|
|
678
|
+
margin: 16px 0 12px 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/* Variables List */
|
|
682
|
+
.variables-list {
|
|
683
|
+
max-height: 320px;
|
|
684
|
+
overflow-y: auto;
|
|
685
|
+
padding: 0;
|
|
686
|
+
margin-top: 12px;
|
|
687
|
+
background-color: transparent;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.variables-list::-webkit-scrollbar {
|
|
691
|
+
width: 8px;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
.variables-list::-webkit-scrollbar-track {
|
|
695
|
+
background: #f8f9fa;
|
|
696
|
+
border-radius: 4px;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.variables-list::-webkit-scrollbar-thumb {
|
|
700
|
+
background: #ced4da;
|
|
701
|
+
border-radius: 4px;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.variables-list::-webkit-scrollbar-thumb:hover {
|
|
705
|
+
background: #adb5bd;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/* Variable Item */
|
|
709
|
+
.variable-item {
|
|
710
|
+
padding: 12px 0;
|
|
711
|
+
border-bottom: 1px solid #e3e8ef;
|
|
712
|
+
transition: background-color 0.15s ease;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.variable-item:hover {
|
|
716
|
+
background-color: transparent;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.variable-item:last-child {
|
|
720
|
+
border-bottom: none;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.variable-checkbox {
|
|
724
|
+
margin: 0;
|
|
725
|
+
width: 100%;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
.variable-checkbox >>> .custom-control-label {
|
|
729
|
+
cursor: pointer;
|
|
730
|
+
user-select: none;
|
|
731
|
+
width: 100%;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.variable-name {
|
|
735
|
+
color: #212529;
|
|
736
|
+
font-size: 14px;
|
|
737
|
+
font-weight: 400;
|
|
738
|
+
line-height: 1.5;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/* No Results */
|
|
742
|
+
.no-results {
|
|
743
|
+
padding: 32px 16px;
|
|
744
|
+
text-align: center;
|
|
745
|
+
color: #6c757d;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.no-results small {
|
|
749
|
+
font-size: 14px;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
/* Warning Alert (outside card) */
|
|
753
|
+
.warning-alert {
|
|
754
|
+
display: flex;
|
|
755
|
+
align-items: flex-start;
|
|
756
|
+
margin: 0 0 12px 0;
|
|
757
|
+
padding: 12px 16px;
|
|
758
|
+
background-color: #fff3cd;
|
|
759
|
+
border: 1px solid #ffc107;
|
|
760
|
+
border-radius: 8px;
|
|
761
|
+
font-size: 13px;
|
|
762
|
+
line-height: 1.5;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.warning-icon {
|
|
766
|
+
color: #ffc107;
|
|
767
|
+
margin-right: 10px;
|
|
768
|
+
flex-shrink: 0;
|
|
769
|
+
margin-top: 2px;
|
|
770
|
+
font-size: 16px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.warning-text {
|
|
774
|
+
color: #856404;
|
|
775
|
+
flex: 1;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
.warning-text strong {
|
|
779
|
+
color: #664d03;
|
|
780
|
+
font-weight: 600;
|
|
781
|
+
}
|
|
782
|
+
</style>
|