@processmaker/screen-builder 2.83.10 → 2.84.0

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.
@@ -98,231 +98,192 @@
98
98
  <!-- Renderer -->
99
99
  <b-col
100
100
  id="screen-container"
101
- class="overflow-auto mh-100 p-0 px-4 d-flex flex-column position-relative pt-2"
101
+ ref="screen-container"
102
+ class="overflow-auto mh-100 p-0 d-flex flex-column position-relative"
102
103
  >
103
- <b-input-group size="sm" class="bg-white mt-3">
104
- <b-form-select
105
- v-if="showToolbar"
106
- v-model="currentPage"
107
- class="form-control"
108
- data-cy="toolbar-page"
109
- >
110
- <option v-for="(data, page) in config" :key="page" :value="page">
111
- {{ data.name }}
112
- </option>
113
- </b-form-select>
114
-
115
- <div v-if="showToolbar">
116
- <b-button
117
- size="sm"
118
- variant="secondary"
119
- class="ml-1"
120
- :title="$t('Edit Page Title')"
121
- data-cy="toolbar-edit"
122
- @click="openEditPageModal(currentPage)"
123
- >
124
- <i class="far fa-edit" />
125
- </b-button>
126
-
127
- <b-button
128
- size="sm"
129
- variant="danger"
130
- class="ml-1"
131
- :title="$t('Delete Page')"
132
- :disabled="!displayDelete"
133
- data-cy="toolbar-remove"
134
- @click="confirmDelete()"
135
- >
136
- <i class="far fa-trash-alt" />
137
- </b-button>
138
-
139
- <b-button
140
- v-b-modal.addPageModal
141
- size="sm"
142
- variant="secondary"
143
- class="ml-1 mr-1"
144
- :title="$t('Add New Page')"
145
- data-cy="toolbar-add"
146
- @click="originalPageName = null"
147
- >
148
- <i class="fas fa-plus" />
149
- </b-button>
150
- </div>
151
-
152
- <b-button-group size="sm" class="ml-1 ml-auto">
153
- <b-button :disabled="!canUndo" data-cy="toolbar-undo" @click="undo">{{
154
- $t("Undo")
155
- }}</b-button>
156
- <b-button :disabled="!canRedo" data-cy="toolbar-redo" @click="redo">{{
157
- $t("Redo")
158
- }}</b-button>
159
- </b-button-group>
160
-
161
- <hr class="w-100" />
162
- </b-input-group>
163
-
164
- <div
165
- v-if="isCurrentPageEmpty"
166
- data-cy="screen-drop-zone"
167
- class="d-flex justify-content-center align-items-center drag-placeholder text-center position-absolute rounded mt-4 flex-column"
104
+ <tabs-bar
105
+ ref="tabsBar"
106
+ :pages="config"
107
+ @tab-opened="currentPage = $event"
168
108
  >
169
- <svg
170
- width="81"
171
- height="107"
172
- viewBox="0 0 81 107"
173
- fill="none"
174
- xmlns="http://www.w3.org/2000/svg"
175
- >
176
- <path
177
- d="M47.125 28.6562V0.5H5.71875C2.96523 0.5 0.75 2.71523 0.75 5.46875V101.531C0.75 104.285 2.96523 106.5 5.71875 106.5H75.2812C78.0348 106.5 80.25 104.285 80.25 101.531V33.625H52.0938C49.3609 33.625 47.125 31.3891 47.125 28.6562ZM60.375 77.5156C60.375 78.882 59.257 80 57.8906 80H23.1094C21.743 80 20.625 78.882 20.625 77.5156V75.8594C20.625 74.493 21.743 73.375 23.1094 73.375H57.8906C59.257 73.375 60.375 74.493 60.375 75.8594V77.5156ZM60.375 64.2656C60.375 65.632 59.257 66.75 57.8906 66.75H23.1094C21.743 66.75 20.625 65.632 20.625 64.2656V62.6094C20.625 61.243 21.743 60.125 23.1094 60.125H57.8906C59.257 60.125 60.375 61.243 60.375 62.6094V64.2656ZM60.375 49.3594V51.0156C60.375 52.382 59.257 53.5 57.8906 53.5H23.1094C21.743 53.5 20.625 52.382 20.625 51.0156V49.3594C20.625 47.993 21.743 46.875 23.1094 46.875H57.8906C59.257 46.875 60.375 47.993 60.375 49.3594ZM80.25 25.7371V27H53.75V0.5H55.0129C56.3379 0.5 57.6008 1.01758 58.5324 1.94922L78.8008 22.2383C79.7324 23.1699 80.25 24.4328 80.25 25.7371Z"
178
- fill="#699CFF"
109
+ <template #tabs-start>
110
+ <pages-dropdown
111
+ v-if="showToolbar"
112
+ :data="config"
113
+ @addPage="$bvModal.show('addPageModal')"
114
+ @clickPage="onClick"
115
+ @seeAllPages="$bvModal.show('openSortable')"
179
116
  />
180
- </svg>
181
- <h3>{{ $t("Place your controls here.") }}</h3>
182
- <p>
183
- {{
184
- $t(
185
- "To begin creating a screen, drag and drop items from the Controls Menu on the left."
186
- )
187
- }}
188
- </p>
189
- <!-- {{ $t("Drag an element here") }} -->
190
- </div>
191
-
192
- <draggable
193
- v-if="renderControls"
194
- :key="editorContentKey"
195
- data-cy="editor-content"
196
- class="h-100"
197
- ghost-class="form-control-ghost"
198
- :value="config[currentPage].items"
199
- v-bind="{
200
- group: { name: 'controls' },
201
- swapThreshold: 0.5
202
- }"
203
- @input="updateConfig"
204
- >
205
- <div
206
- v-for="(element, index) in config[currentPage].items"
207
- :key="index"
208
- class="control-item mt-4 mb-4"
209
- :class="{
210
- selected: selected === element,
211
- hasError: hasError(element)
212
- }"
213
- :selector="element.config.customCssSelector"
214
- @click="inspect(element)"
215
- >
117
+ </template>
118
+ <template #default>
216
119
  <div
217
- v-if="element.container"
218
- class="card container-lement"
219
- :class="{ 'ai-section-card': isAiSection(element) }"
220
- data-cy="screen-element-container"
221
- @click="inspect(element)"
120
+ v-if="isCurrentPageEmpty(currentPage)"
121
+ data-cy="screen-drop-zone"
122
+ class="d-flex justify-content-center align-items-center drag-placeholder text-center position-absolute rounded mt-4 flex-column"
222
123
  >
223
- <div
224
- v-if="selected === element"
225
- class="card-header form-element-header d-flex align-items-center"
226
- :class="{ pulse: isAiSection(element) && aiPreview(element) }"
124
+ <svg
125
+ width="81"
126
+ height="107"
127
+ viewBox="0 0 81 107"
128
+ fill="none"
129
+ xmlns="http://www.w3.org/2000/svg"
227
130
  >
228
- <i class="fas fa-arrows-alt-v mr-1 text-muted" />
229
- <i
230
- v-if="element.config.icon"
231
- :class="element.config.icon"
232
- class="mr-2 ml-1"
131
+ <path
132
+ d="M47.125 28.6562V0.5H5.71875C2.96523 0.5 0.75 2.71523 0.75 5.46875V101.531C0.75 104.285 2.96523 106.5 5.71875 106.5H75.2812C78.0348 106.5 80.25 104.285 80.25 101.531V33.625H52.0938C49.3609 33.625 47.125 31.3891 47.125 28.6562ZM60.375 77.5156C60.375 78.882 59.257 80 57.8906 80H23.1094C21.743 80 20.625 78.882 20.625 77.5156V75.8594C20.625 74.493 21.743 73.375 23.1094 73.375H57.8906C59.257 73.375 60.375 74.493 60.375 75.8594V77.5156ZM60.375 64.2656C60.375 65.632 59.257 66.75 57.8906 66.75H23.1094C21.743 66.75 20.625 65.632 20.625 64.2656V62.6094C20.625 61.243 21.743 60.125 23.1094 60.125H57.8906C59.257 60.125 60.375 61.243 60.375 62.6094V64.2656ZM60.375 49.3594V51.0156C60.375 52.382 59.257 53.5 57.8906 53.5H23.1094C21.743 53.5 20.625 52.382 20.625 51.0156V49.3594C20.625 47.993 21.743 46.875 23.1094 46.875H57.8906C59.257 46.875 60.375 47.993 60.375 49.3594ZM80.25 25.7371V27H53.75V0.5H55.0129C56.3379 0.5 57.6008 1.01758 58.5324 1.94922L78.8008 22.2383C79.7324 23.1699 80.25 24.4328 80.25 25.7371Z"
133
+ fill="#699CFF"
233
134
  />
234
- {{ element.config.name || element.label || $t("Field Name") }}
235
- <div class="ml-auto">
236
- <button
237
- v-if="isAiSection(element) && aiPreview(element)"
238
- data-test="apply-ai-btn"
239
- class="btn btn-sm btn-primary mr-2"
240
- :title="$t('Apply Changes')"
241
- @click="applyAiChanges(element)"
242
- >
243
- {{ $t("Apply Changes") }}
244
- </button>
245
- <button
246
- v-if="!(isAiSection(element) && aiPreview(element))"
247
- data-test="copy-control-btn"
248
- class="btn btn-sm btn-secondary mr-2"
249
- :title="$t('Copy Control')"
250
- @click="duplicateItem(index)"
251
- >
252
- <i class="fas fa-copy text-light" />
253
- </button>
254
- <button
255
- data-test="delete-control-btn"
256
- class="btn btn-sm btn-danger"
257
- :title="$t('Delete Control')"
258
- @click="deleteItem(index)"
259
- >
260
- <i class="far fa-trash-alt text-light" />
261
- </button>
262
- </div>
263
- </div>
264
- <component
265
- :is="element['editor-component']"
266
- v-model="element.items"
267
- :validation-errors="validationErrors"
268
- class="card-body"
269
- :class="elementCssClass(element)"
270
- :selected="selected"
271
- :config="element.config"
272
- :ai-element="element"
273
- @inspect="inspect"
274
- @update-state="updateState"
275
- />
135
+ </svg>
136
+ <h3>{{ $t("Place your controls here.") }}</h3>
137
+ <p>
138
+ {{
139
+ $t(
140
+ "To begin creating a screen, drag and drop items from the Controls Menu on the left."
141
+ )
142
+ }}
143
+ </p>
144
+ <!-- {{ $t("Drag an element here") }} -->
276
145
  </div>
277
146
 
278
- <div v-else class="card" data-cy="screen-element-container">
147
+ <draggable
148
+ v-if="renderControls"
149
+ :key="editorContentKey"
150
+ data-cy="editor-content"
151
+ class="h-100"
152
+ ghost-class="form-control-ghost"
153
+ :value="config[currentPage].items"
154
+ v-bind="{
155
+ group: { name: 'controls' },
156
+ swapThreshold: 0.5
157
+ }"
158
+ @input="updateConfig"
159
+ >
279
160
  <div
280
- v-if="selected === element"
281
- class="card-header form-element-header d-flex align-items-center"
161
+ v-for="(element, index) in config[currentPage].items"
162
+ :key="index"
163
+ class="control-item mt-4 mb-4"
164
+ :class="{
165
+ selected: selected === element,
166
+ hasError: hasError(element)
167
+ }"
168
+ :selector="element.config.customCssSelector"
169
+ @click="inspect(element)"
282
170
  >
283
- <i class="fas fa-arrows-alt-v mr-1 text-muted" />
284
- <i
285
- v-if="element.config.icon"
286
- :class="element.config.icon"
287
- class="mr-2 ml-1"
288
- />
289
- {{ element.config.name || $t("Variable Name") }}
290
- <div class="ml-auto">
291
- <button
292
- class="btn btn-sm btn-secondary mr-2"
293
- :title="$t('Copy Control')"
294
- @click="duplicateItem(index)"
171
+ <div
172
+ v-if="element.container"
173
+ class="card container-lement"
174
+ :class="{ 'ai-section-card': isAiSection(element) }"
175
+ data-cy="screen-element-container"
176
+ @click="inspect(element)"
177
+ >
178
+ <div
179
+ v-if="selected === element"
180
+ class="card-header form-element-header d-flex align-items-center"
181
+ :class="{ pulse: isAiSection(element) && aiPreview(element) }"
295
182
  >
296
- <i class="fas fa-copy text-light" />
297
- </button>
298
- <button
299
- class="btn btn-sm btn-danger"
300
- :title="$t('Delete Control')"
301
- @click="deleteItem(index)"
183
+ <i class="fas fa-arrows-alt-v mr-1 text-muted" />
184
+ <i
185
+ v-if="element.config.icon"
186
+ :class="element.config.icon"
187
+ class="mr-2 ml-1"
188
+ />
189
+ {{ element.config.name || element.label || $t("Field Name") }}
190
+ <div class="ml-auto">
191
+ <button
192
+ v-if="isAiSection(element) && aiPreview(element)"
193
+ data-test="apply-ai-btn"
194
+ class="btn btn-sm btn-primary mr-2"
195
+ :title="$t('Apply Changes')"
196
+ @click="applyAiChanges(element)"
197
+ >
198
+ {{ $t("Apply Changes") }}
199
+ </button>
200
+ <button
201
+ v-if="!(isAiSection(element) && aiPreview(element))"
202
+ data-test="copy-control-btn"
203
+ class="btn btn-sm btn-secondary mr-2"
204
+ :title="$t('Copy Control')"
205
+ @click="duplicateItem(index)"
206
+ >
207
+ <i class="fas fa-copy text-light" />
208
+ </button>
209
+ <button
210
+ data-test="delete-control-btn"
211
+ class="btn btn-sm btn-danger"
212
+ :title="$t('Delete Control')"
213
+ @click="deleteItem(index)"
214
+ >
215
+ <i class="far fa-trash-alt text-light" />
216
+ </button>
217
+ </div>
218
+ </div>
219
+ <component
220
+ :is="element['editor-component']"
221
+ v-model="element.items"
222
+ :validation-errors="validationErrors"
223
+ class="card-body"
224
+ :class="elementCssClass(element)"
225
+ :selected="selected"
226
+ :config="element.config"
227
+ :ai-element="element"
228
+ @inspect="inspect"
229
+ @update-state="updateState"
230
+ />
231
+ </div>
232
+
233
+ <div v-else class="card" data-cy="screen-element-container">
234
+ <div
235
+ v-if="selected === element"
236
+ class="card-header form-element-header d-flex align-items-center"
302
237
  >
303
- <i class="far fa-trash-alt text-light" />
304
- </button>
238
+ <i class="fas fa-arrows-alt-v mr-1 text-muted" />
239
+ <i
240
+ v-if="element.config.icon"
241
+ :class="element.config.icon"
242
+ class="mr-2 ml-1"
243
+ />
244
+ {{ element.config.name || $t("Variable Name") }}
245
+ <div class="ml-auto">
246
+ <button
247
+ class="btn btn-sm btn-secondary mr-2"
248
+ :title="$t('Copy Control')"
249
+ @click="duplicateItem(index)"
250
+ >
251
+ <i class="fas fa-copy text-light" />
252
+ </button>
253
+ <button
254
+ class="btn btn-sm btn-danger"
255
+ :title="$t('Delete Control')"
256
+ @click="deleteItem(index)"
257
+ >
258
+ <i class="far fa-trash-alt text-light" />
259
+ </button>
260
+ </div>
261
+ </div>
262
+ <component
263
+ v-bind="element.config"
264
+ :is="element['editor-component']"
265
+ :tabindex="element.config.interactive ? 0 : -1"
266
+ class="card-body m-0 pb-4 pt-4"
267
+ :class="[
268
+ elementCssClass(element),
269
+ { 'prevent-interaction': !element.config.interactive }
270
+ ]"
271
+ @input="
272
+ element.config.interactive
273
+ ? (element.config.content = $event)
274
+ : null
275
+ "
276
+ @focusout.native="updateState"
277
+ />
305
278
  </div>
306
279
  </div>
307
- <component
308
- v-bind="element.config"
309
- :is="element['editor-component']"
310
- :tabindex="element.config.interactive ? 0 : -1"
311
- class="card-body m-0 pb-4 pt-4"
312
- :class="[
313
- elementCssClass(element),
314
- { 'prevent-interaction': !element.config.interactive }
315
- ]"
316
- @input="
317
- element.config.interactive
318
- ? (element.config.content = $event)
319
- : null
320
- "
321
- @focusout.native="updateState"
322
- />
280
+ </draggable>
281
+
282
+ <div v-if="!isCurrentPageEmpty" data-cy="screen-drop-zone">
283
+ &nbsp;
323
284
  </div>
324
- </div>
325
- </draggable>
285
+ </template>
286
+ </tabs-bar>
326
287
  </b-col>
327
288
 
328
289
  <!-- Inspector -->
@@ -396,27 +357,61 @@
396
357
  </b-col>
397
358
 
398
359
  <!-- Modals -->
360
+ <b-modal
361
+ id="openSortable"
362
+ ref="openSortable"
363
+ header-close-content="&times;"
364
+ role="dialog"
365
+ size="lg"
366
+ :title="$t('Edit Pages')"
367
+ :ok-title="$t('DONE')"
368
+ ok-only
369
+ ok-variant="secondary"
370
+ header-class = "modal-header-custom"
371
+ >
372
+ <template #modal-title>
373
+ <h5 class="modal-title">{{ $t('Edit Pages') }}</h5>
374
+ <span class="modal-subtitle">{{ $t('Change pages order and name') }}</span>
375
+ </template>
376
+ <template #modal-header-close="{ close }">
377
+ <button type="button" aria-label="Close" class="close" @click="close()">×</button>
378
+ </template>
379
+ <Sortable
380
+ :items="config"
381
+ filter-key="name"
382
+ @item-edit="() => {}"
383
+ @item-delete="confirmDelete"
384
+ @add-page="$bvModal.show('addPageModal')"
385
+ />
386
+ </b-modal>
387
+
399
388
  <b-modal
400
389
  id="addPageModal"
401
- :ok-title="$t('Save')"
402
- :cancel-title="$t('Cancel')"
390
+ ref="addPageModal"
391
+ header-class="pb-2"
392
+ size="lg"
393
+ :ok-title="$t('SAVE')"
394
+ :cancel-title="$t('CANCEL')"
403
395
  cancel-variant="btn btn-outline-secondary"
404
396
  ok-variant="btn btn-secondary ml-2"
405
- :title="$t('Add New Page')"
406
397
  header-close-content="&times;"
407
398
  data-cy="add-page-modal"
399
+ :ok-disabled="!addPageName || !!checkPageName(addPageName)"
408
400
  @ok="addPage"
401
+ @show="addPageName = ''; showAddPageValidations=false;"
409
402
  >
410
- <required />
403
+ <template #modal-title>
404
+ <h5 class="modal-title">{{ $t('Create New Page') }}</h5>
405
+ <small class="modal-subtitle mb-n2">{{ $t('Create a new page in your screen') }}</small>
406
+ </template>
411
407
  <form-input
412
408
  ref="addPageInput"
413
409
  v-model="addPageName"
414
410
  :name="$t('Page Name')"
415
411
  :label="$t('Page Name') + ' *'"
416
412
  :helper="$t('The name of the new page to add')"
417
- validation="unique-page-name|required"
413
+ :error="checkPageName(addPageName)"
418
414
  data-cy="add-page-name"
419
- required
420
415
  aria-required="true"
421
416
  />
422
417
  </b-modal>
@@ -438,7 +433,8 @@
438
433
  :name="$t('Page Name')"
439
434
  :label="$t('Page Name') + ' *'"
440
435
  :helper="$t('The new name of the page')"
441
- validation="unique-page-name|required"
436
+ validation="required"
437
+ :error="checkPageName(editPageName)"
442
438
  required
443
439
  aria-required="true"
444
440
  />
@@ -481,11 +477,14 @@ import "@processmaker/vue-form-elements/dist/vue-form-elements.css";
481
477
  import accordions from "./accordions";
482
478
  import { keyNameProperty } from "../form-control-common-properties";
483
479
  import VariableNameGenerator from "@/components/VariableNameGenerator";
480
+ import PagesDropdown from "@/components/editor/pagesDropdown";
484
481
  import testing from "@/mixins/testing";
485
482
  import defaultValueEditor from "./inspector/default-value-editor";
486
483
  import RequiredCheckbox from "./utils/required-checkbox";
487
484
  import MultipleUploadsCheckbox from "./utils/multiple-uploads-checkbox";
488
485
  import { formTypes } from "@/global-properties";
486
+ import TabsBar from "./TabsBar.vue";
487
+ import Sortable from './sortable/Sortable.vue';
489
488
 
490
489
  // To include another language in the Validator with variable processmaker
491
490
  const globalObject = typeof window === "undefined" ? global : window;
@@ -537,6 +536,7 @@ const DEFAULT_GROUP = "Advanced";
537
536
 
538
537
  export default {
539
538
  components: {
539
+ TabsBar,
540
540
  draggable,
541
541
  FormInput,
542
542
  FormSelectList,
@@ -549,7 +549,9 @@ export default {
549
549
  MultipleUploadsCheckbox,
550
550
  defaultValueEditor,
551
551
  ...inspector,
552
- ...renderer
552
+ ...renderer,
553
+ PagesDropdown,
554
+ Sortable,
553
555
  },
554
556
  mixins: [HasColorProperty, testing],
555
557
  props: {
@@ -588,6 +590,8 @@ export default {
588
590
  }
589
591
 
590
592
  return {
593
+ showAddPageValidations: false,
594
+ openedPages: [0],
591
595
  currentPage: 0,
592
596
  selected: null,
593
597
  display: "editor",
@@ -616,19 +620,13 @@ export default {
616
620
  editorContentKey: 0,
617
621
  cancelledJobs: [],
618
622
  collapse: {},
619
- groupOrder: {},
623
+ groupOrder: {}
620
624
  };
621
625
  },
622
626
  computed: {
623
627
  builder() {
624
628
  return this;
625
629
  },
626
- canUndo() {
627
- return this.$store.getters["undoRedoModule/canUndo"];
628
- },
629
- canRedo() {
630
- return this.$store.getters["undoRedoModule/canRedo"];
631
- },
632
630
  displayDelete() {
633
631
  return this.config.length > 1;
634
632
  },
@@ -685,9 +683,6 @@ export default {
685
683
 
686
684
  return grouped;
687
685
  },
688
- isCurrentPageEmpty() {
689
- return this.config[this.currentPage].items.length === 0;
690
- },
691
686
  showToolbar() {
692
687
  return this.screenType === formTypes.form;
693
688
  }
@@ -732,16 +727,6 @@ export default {
732
727
  }
733
728
  },
734
729
  created() {
735
- Validator.register(
736
- "unique-page-name",
737
- (value) => {
738
- const pageNames = this.config
739
- .map((config) => config.name)
740
- .filter((name) => name !== this.originalPageName);
741
- return !pageNames.includes(value);
742
- },
743
- this.$t("Must be unique")
744
- );
745
730
  this.$store.dispatch("undoRedoModule/pushState", {
746
731
  config: JSON.stringify(this.config),
747
732
  currentPage: this.currentPage
@@ -775,6 +760,24 @@ export default {
775
760
  this.setGroupOrder(defaultGroupOrder);
776
761
  },
777
762
  methods: {
763
+ isCurrentPageEmpty(currentPage) {
764
+ return this.config[currentPage]?.items?.length === 0;
765
+ },
766
+ onClick(page) {
767
+ this.$refs.tabsBar.openPageByIndex(page);
768
+ },
769
+ checkPageName(value, force = false) {
770
+ if (!force && !this.showAddPageValidations) {
771
+ return null;
772
+ }
773
+ if (!value.trim()) {
774
+ return this.$t("The Page Name field is required.");
775
+ }
776
+ const pageNames = this.config
777
+ .map((config) => config.name)
778
+ .filter((name) => name !== this.originalPageName);
779
+ return pageNames.includes(value) ? this.$t("Must be unique.") : "";
780
+ },
778
781
  getGroupOrder(groupName) {
779
782
  let order = _.get(this.groupOrder, groupName, Number.POSITIVE_INFINITY);
780
783
  return order;
@@ -878,6 +881,10 @@ export default {
878
881
  config.forEach((page) =>
879
882
  this.removeDataVariableFromNestedScreens(page.items)
880
883
  );
884
+ // add order attribute
885
+ config.forEach((page, index) => {
886
+ page.order = page.order || index + 1;
887
+ });
881
888
  },
882
889
  updateFieldNameValidation(items) {
883
890
  items.forEach((item) => {
@@ -1043,23 +1050,25 @@ export default {
1043
1050
  currentPage: this.currentPage
1044
1051
  });
1045
1052
  },
1046
- undo() {
1053
+ async undo() {
1047
1054
  this.inspect();
1048
1055
  this.$store.dispatch("undoRedoModule/undo");
1049
1056
  this.config = JSON.parse(
1050
1057
  this.$store.getters["undoRedoModule/currentState"].config
1051
1058
  );
1052
- this.currentPage = JSON.parse(
1059
+ await this.$nextTick();
1060
+ this.$refs.tabsBar.openPageByIndex(
1053
1061
  this.$store.getters["undoRedoModule/currentState"].currentPage
1054
1062
  );
1055
1063
  },
1056
- redo() {
1064
+ async redo() {
1057
1065
  this.inspect();
1058
1066
  this.$store.dispatch("undoRedoModule/redo");
1059
1067
  this.config = JSON.parse(
1060
1068
  this.$store.getters["undoRedoModule/currentState"].config
1061
1069
  );
1062
- this.currentPage = JSON.parse(
1070
+ await this.$nextTick();
1071
+ this.$refs.tabsBar.openPageByIndex(
1063
1072
  this.$store.getters["undoRedoModule/currentState"].currentPage
1064
1073
  );
1065
1074
  },
@@ -1072,7 +1081,7 @@ export default {
1072
1081
  },
1073
1082
  focusInspector(validation) {
1074
1083
  this.showConfiguration = true;
1075
- this.currentPage = this.config.indexOf(validation.page);
1084
+ this.$refs.tabsBar.openPageByIndex(this.config.indexOf(validation.page));
1076
1085
  this.$nextTick(() => {
1077
1086
  this.inspect(validation.item);
1078
1087
  this.$nextTick(() => {
@@ -1091,12 +1100,17 @@ export default {
1091
1100
  });
1092
1101
  });
1093
1102
  },
1094
- confirmDelete() {
1103
+ confirmDelete(item = this.config[this.currentPage]) {
1095
1104
  this.confirmMessage = this.$t(
1096
1105
  "Are you sure you want to delete {{item}}?",
1097
- { item: this.config[this.currentPage].name }
1106
+ { item: item.name }
1098
1107
  );
1099
- this.pageDelete = this.currentPage;
1108
+ const isLastPage = this.config.length === 1;
1109
+ if (isLastPage) {
1110
+ // can not delete the last page
1111
+ return;
1112
+ }
1113
+ this.pageDelete = this.config.indexOf(item);
1100
1114
  this.$refs.confirm.show();
1101
1115
  },
1102
1116
  hideConfirmModal() {
@@ -1131,18 +1145,85 @@ export default {
1131
1145
  this.updateState();
1132
1146
  },
1133
1147
  addPage(e) {
1134
- if (this.$refs.addPageInput.validator.errorCount) {
1148
+ this.showAddPageValidations = true;
1149
+ const error = this.checkPageName(this.addPageName, true);
1150
+ if (error) {
1135
1151
  e.preventDefault();
1136
1152
  return;
1137
1153
  }
1138
- this.config.push({ name: this.addPageName, items: [] });
1139
- this.currentPage = this.config.length - 1;
1154
+
1155
+ const maxOrder = this.config.reduce((max, page) => {
1156
+ return page.order > max ? page.order : max;
1157
+ }, 0);
1158
+
1159
+ this.config.push({
1160
+ name: this.addPageName,
1161
+ order: maxOrder + 1,
1162
+ items: []
1163
+ });
1140
1164
  this.addPageName = "";
1165
+ this.currentPage = this.config.length - 1;
1141
1166
  this.updateState();
1167
+
1168
+ // open new page
1169
+ this.$refs.tabsBar.openPageByIndex(this.config.length - 1);
1170
+ },
1171
+ // This function is used to calculate the new index of the references
1172
+ calcNewIndexFor(index, referencedBy) {
1173
+ if (index === this.pageDelete) {
1174
+ throw new Error(
1175
+ `${this.$t(
1176
+ "Can not delete this page, it is referenced by"
1177
+ )}: ${referencedBy}`
1178
+ );
1179
+ }
1180
+ return index > this.pageDelete ? index - 1 : index;
1181
+ },
1182
+ // Update Record list references
1183
+ updateRecordListReferences() {
1184
+ this.config.forEach((page) => {
1185
+ page.items.forEach((item) => {
1186
+ if (item.component === "FormRecordList") {
1187
+ // eslint-disable-next-line no-param-reassign
1188
+ item.config.form = this.calcNewIndexFor(
1189
+ item.config.form * 1,
1190
+ item.config.label
1191
+ );
1192
+ }
1193
+ });
1194
+ });
1195
+ },
1196
+ // Update navigation buttons references
1197
+ updateNavigationButtonsReferences() {
1198
+ this.config.forEach((page) => {
1199
+ page.items.forEach((item) => {
1200
+ if (
1201
+ item.component === "FormButton" &&
1202
+ item.config.event === "pageNavigate"
1203
+ ) {
1204
+ // eslint-disable-next-line no-param-reassign
1205
+ item.config.eventData = this.calcNewIndexFor(
1206
+ item.config.eventData * 1,
1207
+ item.config.label
1208
+ );
1209
+ }
1210
+ });
1211
+ });
1142
1212
  },
1143
- deletePage() {
1144
- this.config.splice(this.pageDelete, 1);
1145
- this.currentPage = this.pageDelete - 1 >= 0 ? this.pageDelete - 1 : 0;
1213
+ async deletePage() {
1214
+ const back = _.cloneDeep(this.config);
1215
+ try {
1216
+ this.updateRecordListReferences();
1217
+ this.updateNavigationButtonsReferences();
1218
+ this.$refs.tabsBar.closePageByIndex(this.pageDelete);
1219
+ this.$refs.tabsBar.updateTabsReferences(this.pageDelete);
1220
+ await this.$nextTick();
1221
+ this.config.splice(this.pageDelete, 1);
1222
+ } catch (error) {
1223
+ this.config = back;
1224
+ globalObject.ProcessMaker.alert(error.message, "danger");
1225
+ return;
1226
+ }
1146
1227
  this.$store.dispatch("undoRedoModule/pushState", {
1147
1228
  config: JSON.stringify(this.config),
1148
1229
  currentPage: this.currentPage,
@@ -1282,12 +1363,13 @@ export default {
1282
1363
  this.config[this.currentPage].items.push(clone);
1283
1364
  this.updateState();
1284
1365
  this.inspect(clone);
1285
- }
1366
+ },
1367
+
1286
1368
  }
1287
1369
  };
1288
1370
  </script>
1289
1371
 
1290
- <style scoped>
1372
+ <style>
1291
1373
  .custom-popover {
1292
1374
  margin-right: -400px;
1293
1375
  padding: 16px;
@@ -1445,4 +1527,8 @@ $side-bar-font-size: 0.875rem;
1445
1527
  box-shadow: 0 0 0 13px rgba(0, 0, 0, 0);
1446
1528
  }
1447
1529
  }
1530
+ .modal-subtitle {
1531
+ font-size: 15px;
1532
+ font-weight: normal;
1533
+ }
1448
1534
  </style>