@processmaker/screen-builder 3.8.15-patch.a.1 → 3.8.15-patch.c

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.
@@ -1,782 +0,0 @@
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>