@processmaker/screen-builder 3.7.0 → 3.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/screen-builder",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
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.65.0",
60
+ "@processmaker/vue-form-elements": "^0.61.1",
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.65.0",
119
+ "@processmaker/vue-form-elements": "^0.61.1",
120
120
  "i18next": "^15.0.8",
121
121
  "vue": "^2.6.12",
122
122
  "vuex": "^3.1.1"
@@ -3,6 +3,9 @@
3
3
  <p>
4
4
  <img src="../assets/icons/ErrorMessage.svg" class="simple-error-message-icon"/>
5
5
  </p>
6
+ <p>
7
+ <b-button variant="primary" @click="redirectToError">{{ $t(label) }}</b-button>
8
+ </p>
6
9
  <p>
7
10
  <span class="simple-error-message-title"> {{ $t(title) }} </span>
8
11
  </p>
@@ -14,12 +17,22 @@
14
17
 
15
18
  <script>
16
19
  export default {
20
+ props: ['instanceId'],
17
21
  data() {
18
22
  return {
19
23
  title: "We're Sorry",
20
24
  message:
21
- "An error has occurred. Please try again. If the problem persists, please contact your administrator."
25
+ "An error has occurred. Please try again. If the problem persists, please contact your administrator.",
26
+ label: "View error details"
22
27
  };
28
+ },
29
+ methods: {
30
+ redirectToError() {
31
+ if (!this.instanceId) {
32
+ return;
33
+ }
34
+ window.location.href = `/requests/${this.instanceId}`;
35
+ }
23
36
  }
24
37
  };
25
38
  </script>
@@ -254,6 +254,8 @@ import _ from "lodash";
254
254
  import { dateUtils } from "@processmaker/vue-form-elements";
255
255
  import VueFormRenderer from "@/components/vue-form-renderer.vue";
256
256
  import mustacheEvaluation from "../../mixins/mustacheEvaluation";
257
+ import MustacheHelper from "../inspector/mustache-helper.vue";
258
+ import Mustache from "mustache";
257
259
 
258
260
  const jsonOptionsActionsColumn = {
259
261
  key: "__actions",
@@ -264,7 +266,8 @@ const jsonOptionsActionsColumn = {
264
266
 
265
267
  export default {
266
268
  components: {
267
- VueFormRenderer
269
+ VueFormRenderer,
270
+ MustacheHelper
268
271
  },
269
272
  mixins: [mustacheEvaluation],
270
273
  props: [
@@ -466,6 +469,18 @@ export default {
466
469
  this.currentPage = this.currentPage == 0 ? 1 : this.currentPage;
467
470
  }
468
471
  },
472
+ // Watch for changes in the option input field
473
+ "validationData.option": {
474
+ handler(newValue) {
475
+ if (this.source?.sourceOptions === "Collection" && this.source?.collectionFields?.pmql) {
476
+ this.onCollectionChange(
477
+ this.source?.collectionFields?.collectionId,
478
+ this.source?.collectionFields?.pmql
479
+ );
480
+ }
481
+ },
482
+ immediate: false
483
+ }
469
484
  },
470
485
  mounted() {
471
486
  if (this._perPage) {
@@ -566,18 +581,123 @@ export default {
566
581
 
567
582
  return keys1.every(key => obj1[key] === obj2[key]);
568
583
  },
569
- onCollectionChange(collectionId,pmql) {
570
- let param = {params:{pmql:pmql}};
571
- let rowsCollection = [];
584
+ onCollectionChange(collectionId, pmql) {
585
+ // If there is no PMQL, get all records
586
+ if (!pmql || pmql.trim() === "") {
587
+ this.fetchAllRecords(collectionId);
588
+ return;
589
+ }
590
+
591
+ // Process Mustache variables in PMQL
592
+ const processedPmql = this.processMustacheInPmql(pmql);
593
+
594
+ // If processing failed or resulted in invalid PMQL, return
595
+ if (!processedPmql) {
596
+ return;
597
+ }
598
+
599
+ // Fetch records with processed PMQL
600
+ this.fetchRecordsWithPmql(collectionId, processedPmql);
601
+ },
602
+
603
+ /**
604
+ * Process Mustache variables in PMQL string
605
+ * @param {string} pmql - The PMQL string to process
606
+ * @returns {string|null} - Processed PMQL or null if invalid
607
+ */
608
+ processMustacheInPmql(pmql) {
609
+ if (!pmql || !pmql.includes("{{")) {
610
+ return pmql;
611
+ }
612
+
613
+ try {
614
+ // Get data from validationData
615
+ const data = this.validationData || {};
616
+
617
+ // Clean up the PMQL by removing unnecessary quotes around Mustache variables
618
+ let processedPmql = pmql.replace(/"{{([^}]+)}}"/g, "{{$1}}");
619
+
620
+ // Process Mustache variables
621
+ processedPmql = Mustache.render(processedPmql, data);
622
+
623
+ // Check if the processed PMQL has empty values
624
+ if (this.hasEmptyValues(processedPmql)) {
625
+ this.collectionData = [];
626
+ return null;
627
+ }
628
+
629
+ // Add quotes around string values in PMQL if they don't have them
630
+ processedPmql = processedPmql.replace(/= ([^"'\s]+)/g, '= "$1"');
631
+
632
+ return processedPmql;
633
+ } catch (error) {
634
+ this.collectionData = [];
635
+ return null;
636
+ }
637
+ },
638
+
639
+ /**
640
+ * Check if processed PMQL contains empty values
641
+ * @param {string} processedPmql - The processed PMQL string
642
+ * @returns {boolean} - True if contains empty values
643
+ */
644
+ hasEmptyValues(processedPmql) {
645
+ const emptyValues = ['= ""', '= " "', "= null", "= undefined"];
646
+ return emptyValues.some(value => processedPmql.includes(value));
647
+ },
648
+
649
+ /**
650
+ * Validate if processed PMQL is valid for API call
651
+ * @param {string} processedPmql - The processed PMQL string
652
+ * @returns {boolean} - True if valid
653
+ */
654
+ isValidPmql(processedPmql) {
655
+ return processedPmql &&
656
+ processedPmql.trim() !== "" &&
657
+ !processedPmql.includes("{{");
658
+ },
659
+
660
+ /**
661
+ * Fetch all records from collection without PMQL filter
662
+ * @param {number} collectionId - The collection ID
663
+ */
664
+ fetchAllRecords(collectionId) {
572
665
  this.$dataProvider
573
- .getCollectionRecordsList(collectionId, param)
666
+ .getCollectionRecordsList(collectionId, {})
574
667
  .then((response) => {
575
- rowsCollection = response.data;
668
+ const rowsCollection = response.data;
669
+ this.changeCollectionColumns(rowsCollection, this.fields);
670
+ })
671
+ .catch(() => {
672
+ this.collectionData = [];
673
+ });
674
+ },
576
675
 
577
- this.changeCollectionColumns(rowsCollection,this.fields);
676
+ /**
677
+ * Fetch records from collection with PMQL filter
678
+ * @param {number} collectionId - The collection ID
679
+ * @param {string} processedPmql - The processed PMQL string
680
+ */
681
+ fetchRecordsWithPmql(collectionId, processedPmql) {
682
+ // Final validation before making API call
683
+ if (!this.isValidPmql(processedPmql)) {
684
+ this.collectionData = [];
685
+ return;
686
+ }
687
+
688
+ const param = { params: { pmql: processedPmql } };
689
+
690
+ this.$dataProvider
691
+ .getCollectionRecordsList(collectionId, param)
692
+ .then((response) => {
693
+ const rowsCollection = response.data;
694
+ this.changeCollectionColumns(rowsCollection, this.fields);
695
+ })
696
+ .catch(() => {
697
+ this.collectionData = [];
578
698
  });
579
699
 
580
- this.$emit('change', this.field);
700
+ this.$emit("change", this.field);
581
701
  },
582
702
  changeCollectionColumns(collectionFieldsColumns,columnsSelected) {
583
703
 
@@ -1382,14 +1382,7 @@ export default {
1382
1382
  return index > this.pageDelete ? index - 1 : index;
1383
1383
  },
1384
1384
  // This function is used to calculate the new index of the references FormRecordList
1385
- calcNewIndexForFormRecordList(index, referencedBy, config) {
1386
- if (config[this.pageDelete].items.length > 0) {
1387
- throw new Error(
1388
- `${this.$t(
1389
- "Can not delete this page, it is referenced by"
1390
- )}: ${referencedBy}`
1391
- );
1392
- }
1385
+ calcNewIndexForFormRecordList(index, referencedBy) {
1393
1386
  return index > this.pageDelete ? index - 1 : index;
1394
1387
  },
1395
1388
  // Update Record list references
@@ -1398,11 +1391,12 @@ export default {
1398
1391
  page.items.forEach((item) => {
1399
1392
  if (item.component === "FormRecordList") {
1400
1393
  // eslint-disable-next-line no-param-reassign
1401
- item.config.form = this.calcNewIndexForFormRecordList(
1402
- item.config.form * 1,
1403
- item.config.label,
1404
- this.config,
1405
- );
1394
+ if (this.isValidInteger(item.config.form)) {
1395
+ item.config.form = this.calcNewIndexForFormRecordList(
1396
+ item.config.form * 1,
1397
+ item.config.label,
1398
+ );
1399
+ }
1406
1400
  }
1407
1401
  });
1408
1402
  });
@@ -1426,6 +1420,9 @@ export default {
1426
1420
  },
1427
1421
  async deletePage() {
1428
1422
  const back = _.cloneDeep(this.config);
1423
+ if(!this.isNotReferenceToRecordForm()) {
1424
+ return;
1425
+ }
1429
1426
  try {
1430
1427
  this.updateRecordListReferences();
1431
1428
  this.updateNavigationButtonsReferences();
@@ -1446,6 +1443,32 @@ export default {
1446
1443
  });
1447
1444
  this.$store.dispatch("clipboardModule/pushState", this.clipboardPage.items);
1448
1445
  },
1446
+ isNotReferenceToRecordForm() {
1447
+ for (let page of this.config) {
1448
+ for (let item of page.items) {
1449
+ if (item.component === "FormRecordList") {
1450
+ if (this.isValidInteger(item.config.form) && Number(item.config.form) === this.pageDelete) {
1451
+ const referencedBy = item.config.label;
1452
+ const message = `${this.$t("Can not delete this page, it is referenced by")}: ${referencedBy}`;
1453
+ globalObject.ProcessMaker.alert(message, "danger");
1454
+ return false;
1455
+ }
1456
+ }
1457
+ }
1458
+ }
1459
+ return true;
1460
+ },
1461
+ isValidInteger(value) {
1462
+ if (typeof value === 'boolean' || value === null || value === undefined) {
1463
+ return false;
1464
+ }
1465
+ const str = String(value).trim();
1466
+ if (str === '') {
1467
+ return false;
1468
+ }
1469
+ const num = Number(str);
1470
+ return Number.isInteger(num);
1471
+ },
1449
1472
  inspect(element = {}) {
1450
1473
  this.closeTemplatesPanel();
1451
1474
  this.inspection = element;