@processmaker/screen-builder 3.0.1 → 3.0.3

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.
Files changed (31) hide show
  1. package/dist/vue-form-builder.css +1 -1
  2. package/dist/vue-form-builder.es.js +6134 -5704
  3. package/dist/vue-form-builder.es.js.map +1 -1
  4. package/dist/vue-form-builder.umd.js +51 -51
  5. package/dist/vue-form-builder.umd.js.map +1 -1
  6. package/package.json +3 -3
  7. package/src/assets/Shape.svg +3 -0
  8. package/src/assets/pencil-square.svg +3 -0
  9. package/src/components/ScreenTemplateCard.vue +128 -74
  10. package/src/components/ScreenTemplates.vue +138 -110
  11. package/src/components/accordions.js +1 -1
  12. package/src/components/editor/loop.vue +26 -0
  13. package/src/components/editor/multi-column.vue +26 -0
  14. package/src/components/inspector/collection-data-source.vue +3 -1
  15. package/src/components/inspector/collection-designer-mode.vue +73 -0
  16. package/src/components/inspector/color-select-modern.vue +101 -0
  17. package/src/components/inspector/encrypted-config.vue +1 -1
  18. package/src/components/inspector/index.js +3 -0
  19. package/src/components/renderer/file-upload.vue +60 -13
  20. package/src/components/renderer/form-collection-record-control.vue +5 -4
  21. package/src/components/renderer/form-collection-view-control.vue +2 -2
  22. package/src/components/renderer/form-masked-input.vue +7 -5
  23. package/src/components/renderer/form-record-list.vue +200 -23
  24. package/src/components/sortable/tableStyles.scss +42 -0
  25. package/src/components/task.vue +82 -38
  26. package/src/components/vue-form-builder.vue +84 -7
  27. package/src/form-builder-controls.js +14 -2
  28. package/src/form-control-common-properties.js +115 -2
  29. package/src/main.js +8 -0
  30. package/src/mixins/Clipboard.js +15 -5
  31. package/src/mixins/HasColorProperty.js +5 -0
@@ -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 the process completion and redirects the user based on the task assignment.
682
- * @param {object} data - The data object containing endEventDestination.
683
- * @param {string} userId - The ID of the current user.
684
- * @param {string} requestId - The ID of the current request.
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
- // Verify if is not anotherProcess type
689
- if (data.endEventDestination.type !== "anotherProcess") {
690
- if (data?.endEventDestination.value) {
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
- // Parse endEventDestination from the provided data
698
- const endEventDestination = this.parseJsonSafely(
699
- data.endEventDestination.value
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)
@@ -866,6 +909,7 @@ export default {
866
909
 
867
910
  if (
868
911
  elementDestinationValue &&
912
+ elementDestinationValue !== 'taskSource' &&
869
913
  data?.params[0]?.tokenId === this.taskId &&
870
914
  data?.params[0]?.requestStatus === 'ACTIVE'
871
915
  ) {
@@ -127,6 +127,7 @@
127
127
  v-if="isClipboardPage(tabPage)"
128
128
  variant="link"
129
129
  @click="clearClipboard"
130
+ class="no-text-transform"
130
131
  >
131
132
  {{ $t('Clear All') }}
132
133
  </b-button>
@@ -201,6 +202,15 @@
201
202
  class="mr-2 ml-1"
202
203
  />
203
204
  {{ element.config.name || element.label || $t("Field Name") }}
205
+ <b-badge
206
+ v-if="!isClipboardPage(tabPage) && isInClipboard(extendedPages[tabPage].items[index])"
207
+ data-cy="copied-badge"
208
+ class="m-2 custom-badge"
209
+ pill
210
+ >
211
+ <i class="far fa-check-circle"></i>
212
+ <span class="pl-2">{{ $t('Copied')}}</span>
213
+ </b-badge>
204
214
  <div class="ml-auto">
205
215
  <clipboard-button
206
216
  v-if="!isClipboardPage(tabPage)"
@@ -245,7 +255,9 @@
245
255
  v-model="element.items"
246
256
  :validation-errors="validationErrors"
247
257
  class="card-body"
248
- :class="elementCssClass(element)"
258
+ :class="styleMode === 'Modern' && element.component === 'FormRecordList'
259
+ ? elementCssClassModern(element)
260
+ : elementCssClass(element)"
249
261
  :selected="selected"
250
262
  :config="element.config"
251
263
  :ai-element="element"
@@ -266,6 +278,15 @@
266
278
  class="mr-2 ml-1"
267
279
  />
268
280
  {{ element.config.name || $t("Variable Name") }}
281
+ <b-badge
282
+ v-if="!isClipboardPage(tabPage) && isInClipboard(extendedPages[tabPage].items[index])"
283
+ data-cy="copied-badge"
284
+ class="m-2 custom-badge"
285
+ pill
286
+ >
287
+ <i class="far fa-check-circle"></i>
288
+ <span class="pl-2">{{ $t('Copied')}}</span>
289
+ </b-badge>
269
290
  <div class="ml-auto">
270
291
  <clipboard-button
271
292
  v-if="!isClipboardPage(tabPage)"
@@ -299,7 +320,9 @@
299
320
  :tabindex="element.config.interactive ? 0 : -1"
300
321
  class="card-body m-0 pb-4 pt-4"
301
322
  :class="[
302
- elementCssClass(element),
323
+ styleMode === 'Modern' && element.component === 'FormRecordList'
324
+ ? elementCssClassModern(element)
325
+ : elementCssClass(element),
303
326
  { 'prevent-interaction': !element.config.interactive }
304
327
  ]"
305
328
  @input="
@@ -376,7 +399,7 @@
376
399
  v-model="accordion.open"
377
400
  >
378
401
  <component
379
- v-if="shouldShow(item)"
402
+ v-if="shouldShow(item, accordion)"
380
403
  :is="item.type"
381
404
  v-for="(item, index) in getInspectorFields(accordion)"
382
405
  :key="index"
@@ -694,8 +717,10 @@ export default {
694
717
  collapse: {},
695
718
  groupOrder: {},
696
719
  searchProperties: ['name'],
697
- showTemplatesPanel: false,
698
- enableOption: true
720
+ enableOption: true,
721
+ enableDesignOption: true,
722
+ styleMode: null,
723
+ showTemplatesPanel: false
699
724
  };
700
725
  },
701
726
  computed: {
@@ -711,7 +736,7 @@ export default {
711
736
  clipboardItems() {
712
737
  return this.$store.getters["clipboardModule/clipboardItems"];
713
738
  },
714
-
739
+
715
740
  sortedPages() {
716
741
  return [...this.config].sort((a, b) => a.order - b.order);
717
742
  },
@@ -855,6 +880,9 @@ export default {
855
880
  this.$root.$on("ai-form-progress-updated", (progress, nonce) => {
856
881
  this.updateProgress(progress, nonce);
857
882
  });
883
+ this.$root.$on("style-mode", (mode) => {
884
+ this.styleMode = mode;
885
+ });
858
886
  this.setGroupOrder(defaultGroupOrder);
859
887
  },
860
888
  methods: {
@@ -876,7 +904,7 @@ export default {
876
904
  this.$refs.tabsBar.openClipboard();
877
905
  });
878
906
  },
879
- shouldShow(item) {
907
+ shouldShow(item, accordion) {
880
908
  const sourceOptions = this.inspection.config[item.field]?.sourceOptions;
881
909
 
882
910
  if (sourceOptions === 'Variable') {
@@ -888,6 +916,21 @@ export default {
888
916
  this.enableOption = false;
889
917
  }
890
918
 
919
+ if(this.styleMode === "Modern") {
920
+ this.enableDesignOption = false;
921
+ } else {
922
+ this.enableDesignOption = true;
923
+ }
924
+
925
+ if(accordion.name === "Design") {
926
+ if(item.type === "ColorSelectRecord" && !this.enableDesignOption) {
927
+ return false;
928
+ }
929
+ if(item.type === "ColorSelectModern" && this.enableDesignOption ) {
930
+ return false;
931
+ }
932
+ }
933
+
891
934
  return !(item.if === "hideControl" && this.enableOption === false);
892
935
  },
893
936
  isCurrentPageEmpty(currentPage) {
@@ -1189,6 +1232,9 @@ export default {
1189
1232
  });
1190
1233
  },
1191
1234
  updateState() {
1235
+ if (this.selected) {
1236
+ this.checkAndRefreshUuids(this.selected, this.clipboardPage.items);
1237
+ }
1192
1238
  // paste the clipboard items into the current page
1193
1239
  this.replaceClipboardContent(this.config);
1194
1240
  this.$store.dispatch("undoRedoModule/pushState", {
@@ -1523,6 +1569,26 @@ export default {
1523
1569
  this.updateState();
1524
1570
  this.inspect(clone);
1525
1571
  },
1572
+ /**
1573
+ * Compares a config with an array and updates UUIDs if different.
1574
+ *
1575
+ * @param {Object} selected - The config to compare.
1576
+ * @param {Array<Object>} clipboardItems - Array of configs to check.
1577
+ * @returns {Array<Object>} - Updated array with new UUIDs.
1578
+ */
1579
+ checkAndRefreshUuids(selected, clipboardItems) {
1580
+ return clipboardItems.map(config => {
1581
+ // Use lodash to compare the config objects
1582
+ const isEqual = _.isEqual(selected.config, config.config);
1583
+
1584
+ // If they are not equal, update the UUID
1585
+ if (!isEqual) {
1586
+ config.uuid = this.generateUUID(); // Update UUID
1587
+ }
1588
+
1589
+ return config;
1590
+ });
1591
+ },
1526
1592
  }
1527
1593
  };
1528
1594
  </script>
@@ -1702,4 +1768,15 @@ $side-bar-font-size: 0.875rem;
1702
1768
  cursor: not-allowed; /* Cambia el cursor cuando se pasa por encima */
1703
1769
  pointer-events: all; /* Permite que el pseudo-elemento reciba eventos del ratón */
1704
1770
  }
1771
+ .custom-badge {
1772
+ background-color: #D1F4D7 !important;
1773
+ color: #06723A !important;
1774
+ padding: 0.5rem 0.75rem;
1775
+ border-radius: 8px;
1776
+ font-weight: 500;
1777
+ font-size: 14px;
1778
+ }
1779
+ .no-text-transform {
1780
+ text-transform: none;
1781
+ }
1705
1782
  </style>
@@ -46,6 +46,9 @@ import {
46
46
  tooltipProperty,
47
47
  LoadingSubmitButtonProperty,
48
48
  LabelSubmitButtonProperty,
49
+ bgcolorModern,
50
+ bgcolorPropertyRecord,
51
+ colorPropertyRecord,
49
52
  } from './form-control-common-properties';
50
53
 
51
54
  export default [
@@ -557,8 +560,17 @@ export default [
557
560
  },
558
561
  if: 'hideControl'
559
562
  },
560
- colorProperty,
561
- bgcolorProperty,
563
+ {
564
+ type: 'collectionDesignerMode',
565
+ field: 'designerMode',
566
+ config: {
567
+ label: 'Table Style',
568
+ helper: ''
569
+ }
570
+ },
571
+ colorPropertyRecord,
572
+ bgcolorPropertyRecord,
573
+ bgcolorModern
562
574
  ],
563
575
  },
564
576
  },
@@ -1,6 +1,33 @@
1
1
  import Tooltip from './components/inspector/tooltip';
2
2
  import DeviceVisibility from './components/inspector/device-visibility';
3
3
 
4
+ export const bgcolorModern = {
5
+ type: 'ColorSelectModern',
6
+ field: 'bgcolormodern',
7
+ config: {
8
+ label: '',
9
+ helper: '',
10
+ options: [
11
+ {
12
+ value: 'alert alert-primary',
13
+ content: 'primary',
14
+ },
15
+ {
16
+ value: 'alert alert-success',
17
+ content: 'success',
18
+ },
19
+ {
20
+ value: 'alert alert-warning',
21
+ content: 'warning',
22
+ },
23
+ {
24
+ value: 'alert alert-secondary',
25
+ content: 'secondary',
26
+ },
27
+ ],
28
+ },
29
+ };
30
+
4
31
  export const bgcolorProperty = {
5
32
  type: 'ColorSelect',
6
33
  field: 'bgcolor',
@@ -44,6 +71,49 @@ export const bgcolorProperty = {
44
71
  },
45
72
  };
46
73
 
74
+ export const bgcolorPropertyRecord = {
75
+ type: 'ColorSelectRecord',
76
+ field: 'bgcolor',
77
+ config: {
78
+ label: 'Background Color',
79
+ helper: 'Set the element\'s background color',
80
+ options: [
81
+ {
82
+ value: 'alert alert-primary',
83
+ content: 'primary',
84
+ },
85
+ {
86
+ value: 'alert alert-secondary',
87
+ content: 'secondary',
88
+ },
89
+ {
90
+ value: 'alert alert-success',
91
+ content: 'success',
92
+ },
93
+ {
94
+ value: 'alert alert-danger',
95
+ content: 'danger',
96
+ },
97
+ {
98
+ value: 'alert alert-warning',
99
+ content: 'warning',
100
+ },
101
+ {
102
+ value: 'alert alert-info',
103
+ content: 'info',
104
+ },
105
+ {
106
+ value: 'alert alert-light',
107
+ content: 'light',
108
+ },
109
+ {
110
+ value: 'alert alert-dark',
111
+ content: 'dark',
112
+ },
113
+ ],
114
+ },
115
+ };
116
+
47
117
  export const colorProperty = {
48
118
  type: 'ColorSelect',
49
119
  field: 'color',
@@ -87,6 +157,49 @@ export const colorProperty = {
87
157
  },
88
158
  };
89
159
 
160
+ export const colorPropertyRecord = {
161
+ type: 'ColorSelectRecord',
162
+ field: 'color',
163
+ config: {
164
+ label: 'Text Color',
165
+ helper: 'Set the element\'s text color',
166
+ options: [
167
+ {
168
+ value: 'text-primary',
169
+ content: 'primary',
170
+ },
171
+ {
172
+ value: 'text-secondary',
173
+ content: 'secondary',
174
+ },
175
+ {
176
+ value: 'text-success',
177
+ content: 'success',
178
+ },
179
+ {
180
+ value: 'text-danger',
181
+ content: 'danger',
182
+ },
183
+ {
184
+ value: 'text-warning',
185
+ content: 'warning',
186
+ },
187
+ {
188
+ value: 'text-info',
189
+ content: 'info',
190
+ },
191
+ {
192
+ value: 'text-light',
193
+ content: 'light',
194
+ },
195
+ {
196
+ value: 'text-dark',
197
+ content: 'dark',
198
+ },
199
+ ],
200
+ },
201
+ };
202
+
90
203
  // Ref https://mathiasbynens.be/notes/javascript-identifiers
91
204
  export const javascriptReservedKeywords = 'null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false';
92
205
 
@@ -96,8 +209,8 @@ export const keyNameProperty = {
96
209
  config: {
97
210
  label: 'Variable Name',
98
211
  name: 'Variable Name',
99
- // Update tests/e2e/specs/Builder.spec.js when changing this
100
- validation: 'regex:/^([a-zA-Z]([a-zA-Z0-9_]?)+\\.?)+(?<!\\.)$/|required|not_in:' + javascriptReservedKeywords,
212
+ // Update tests/e2e/specs/Builder.spec.js when changing dot_notation
213
+ validation: 'dot_notation|required|not_in:' + javascriptReservedKeywords,
101
214
  helper: 'A variable name is a symbolic name to reference information.',
102
215
  },
103
216
  };
package/src/main.js CHANGED
@@ -325,6 +325,14 @@ window.Echo = {
325
325
  }, 1000);
326
326
  });
327
327
  },
328
+
329
+ eventMockNext(event, response) {
330
+ this.listeners.forEach((listener) => {
331
+ setTimeout(() => {
332
+ listener.callback(response);
333
+ }, 1000);
334
+ });
335
+ },
328
336
  private() {
329
337
  return {
330
338
  notification(callback) {
@@ -58,11 +58,17 @@ export default {
58
58
  addUuidToElements(screenConfig) {
59
59
  const replaceInPage = (page) => {
60
60
  page.items.forEach((item, index) => {
61
- if (!item.uuid) {
62
- item.uuid = this.generateUUID();
63
- }
64
- if (item.items) {
65
- replaceInPage(item);
61
+ if (item instanceof Array) {
62
+ // multi-column each item in the column
63
+ replaceInPage({ items: item });
64
+ } else {
65
+ // loop through children
66
+ if (!item.uuid) {
67
+ item.uuid = this.generateUUID();
68
+ }
69
+ if (item.items) {
70
+ replaceInPage(item);
71
+ }
66
72
  }
67
73
  });
68
74
  }
@@ -109,6 +115,9 @@ export default {
109
115
  // replace uuids in clipboard content
110
116
  clipboardContent.forEach(this.updateUuids);
111
117
  page.items.splice(index, 1, ...clipboardContent);
118
+ if (clipboardContent.length) {
119
+ window.ProcessMaker.alert(this.$t("Clipboard Pasted Succesfully"), "success");
120
+ }
112
121
  }
113
122
  if (item.items) {
114
123
  replaceInPage(item);
@@ -138,6 +147,7 @@ export default {
138
147
  }
139
148
  );
140
149
  if (confirm) {
150
+ this.clipboardPage.items = [];
141
151
  this.$store.dispatch("clipboardModule/clearClipboard");
142
152
  this.$root.$emit('update-clipboard');
143
153
  }
@@ -7,6 +7,11 @@ export default {
7
7
  element.config.color ? css.push(element.config.color) : null;
8
8
  return css.join(' ');
9
9
  },
10
+ elementCssClassModern(element) {
11
+ const css = [];
12
+ element.config.bgcolormodern ? css.push(element.config.bgcolormodern) : null;
13
+ return css.join(' ');
14
+ }
10
15
  },
11
16
  };
12
17