@processmaker/screen-builder 2.83.11 → 2.84.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.
@@ -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="sortedPages"
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="{ currentPage: tabPage }">
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(tabPage)"
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[tabPage].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[tabPage].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(tabPage)" 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,16 @@ export default {
616
620
  editorContentKey: 0,
617
621
  cancelledJobs: [],
618
622
  collapse: {},
619
- groupOrder: {},
623
+ groupOrder: {}
620
624
  };
621
625
  },
622
626
  computed: {
627
+ sortedPages() {
628
+ return [...this.config].sort((a, b) => a.order - b.order);
629
+ },
623
630
  builder() {
624
631
  return this;
625
632
  },
626
- canUndo() {
627
- return this.$store.getters["undoRedoModule/canUndo"];
628
- },
629
- canRedo() {
630
- return this.$store.getters["undoRedoModule/canRedo"];
631
- },
632
633
  displayDelete() {
633
634
  return this.config.length > 1;
634
635
  },
@@ -685,9 +686,6 @@ export default {
685
686
 
686
687
  return grouped;
687
688
  },
688
- isCurrentPageEmpty() {
689
- return this.config[this.currentPage].items.length === 0;
690
- },
691
689
  showToolbar() {
692
690
  return this.screenType === formTypes.form;
693
691
  }
@@ -732,16 +730,6 @@ export default {
732
730
  }
733
731
  },
734
732
  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
733
  this.$store.dispatch("undoRedoModule/pushState", {
746
734
  config: JSON.stringify(this.config),
747
735
  currentPage: this.currentPage
@@ -775,6 +763,24 @@ export default {
775
763
  this.setGroupOrder(defaultGroupOrder);
776
764
  },
777
765
  methods: {
766
+ isCurrentPageEmpty(currentPage) {
767
+ return this.config[currentPage]?.items?.length === 0;
768
+ },
769
+ onClick(page) {
770
+ this.$refs.tabsBar.openPageByIndex(this.config.indexOf(page));
771
+ },
772
+ checkPageName(value, force = false) {
773
+ if (!force && !this.showAddPageValidations) {
774
+ return null;
775
+ }
776
+ if (!value.trim()) {
777
+ return this.$t("The Page Name field is required.");
778
+ }
779
+ const pageNames = this.config
780
+ .map((config) => config.name)
781
+ .filter((name) => name !== this.originalPageName);
782
+ return pageNames.includes(value) ? this.$t("Must be unique.") : "";
783
+ },
778
784
  getGroupOrder(groupName) {
779
785
  let order = _.get(this.groupOrder, groupName, Number.POSITIVE_INFINITY);
780
786
  return order;
@@ -878,6 +884,10 @@ export default {
878
884
  config.forEach((page) =>
879
885
  this.removeDataVariableFromNestedScreens(page.items)
880
886
  );
887
+ // add order attribute
888
+ config.forEach((page, index) => {
889
+ page.order = page.order || index + 1;
890
+ });
881
891
  },
882
892
  updateFieldNameValidation(items) {
883
893
  items.forEach((item) => {
@@ -1043,23 +1053,25 @@ export default {
1043
1053
  currentPage: this.currentPage
1044
1054
  });
1045
1055
  },
1046
- undo() {
1056
+ async undo() {
1047
1057
  this.inspect();
1048
1058
  this.$store.dispatch("undoRedoModule/undo");
1049
1059
  this.config = JSON.parse(
1050
1060
  this.$store.getters["undoRedoModule/currentState"].config
1051
1061
  );
1052
- this.currentPage = JSON.parse(
1062
+ await this.$nextTick();
1063
+ this.$refs.tabsBar.openPageByIndex(
1053
1064
  this.$store.getters["undoRedoModule/currentState"].currentPage
1054
1065
  );
1055
1066
  },
1056
- redo() {
1067
+ async redo() {
1057
1068
  this.inspect();
1058
1069
  this.$store.dispatch("undoRedoModule/redo");
1059
1070
  this.config = JSON.parse(
1060
1071
  this.$store.getters["undoRedoModule/currentState"].config
1061
1072
  );
1062
- this.currentPage = JSON.parse(
1073
+ await this.$nextTick();
1074
+ this.$refs.tabsBar.openPageByIndex(
1063
1075
  this.$store.getters["undoRedoModule/currentState"].currentPage
1064
1076
  );
1065
1077
  },
@@ -1072,7 +1084,7 @@ export default {
1072
1084
  },
1073
1085
  focusInspector(validation) {
1074
1086
  this.showConfiguration = true;
1075
- this.currentPage = this.config.indexOf(validation.page);
1087
+ this.$refs.tabsBar.openPageByIndex(this.config.indexOf(validation.page));
1076
1088
  this.$nextTick(() => {
1077
1089
  this.inspect(validation.item);
1078
1090
  this.$nextTick(() => {
@@ -1091,12 +1103,17 @@ export default {
1091
1103
  });
1092
1104
  });
1093
1105
  },
1094
- confirmDelete() {
1106
+ confirmDelete(item = this.config[this.currentPage]) {
1095
1107
  this.confirmMessage = this.$t(
1096
1108
  "Are you sure you want to delete {{item}}?",
1097
- { item: this.config[this.currentPage].name }
1109
+ { item: item.name }
1098
1110
  );
1099
- this.pageDelete = this.currentPage;
1111
+ const isLastPage = this.config.length === 1;
1112
+ if (isLastPage) {
1113
+ // can not delete the last page
1114
+ return;
1115
+ }
1116
+ this.pageDelete = this.config.indexOf(item);
1100
1117
  this.$refs.confirm.show();
1101
1118
  },
1102
1119
  hideConfirmModal() {
@@ -1131,18 +1148,85 @@ export default {
1131
1148
  this.updateState();
1132
1149
  },
1133
1150
  addPage(e) {
1134
- if (this.$refs.addPageInput.validator.errorCount) {
1151
+ this.showAddPageValidations = true;
1152
+ const error = this.checkPageName(this.addPageName, true);
1153
+ if (error) {
1135
1154
  e.preventDefault();
1136
1155
  return;
1137
1156
  }
1138
- this.config.push({ name: this.addPageName, items: [] });
1139
- this.currentPage = this.config.length - 1;
1157
+
1158
+ const maxOrder = this.config.reduce((max, page) => {
1159
+ return page.order > max ? page.order : max;
1160
+ }, 0);
1161
+
1162
+ this.config.push({
1163
+ name: this.addPageName,
1164
+ order: maxOrder + 1,
1165
+ items: []
1166
+ });
1140
1167
  this.addPageName = "";
1168
+ this.currentPage = this.config.length - 1;
1141
1169
  this.updateState();
1170
+
1171
+ // open new page
1172
+ this.$refs.tabsBar.openPageByIndex(this.config.length - 1);
1173
+ },
1174
+ // This function is used to calculate the new index of the references
1175
+ calcNewIndexFor(index, referencedBy) {
1176
+ if (index === this.pageDelete) {
1177
+ throw new Error(
1178
+ `${this.$t(
1179
+ "Can not delete this page, it is referenced by"
1180
+ )}: ${referencedBy}`
1181
+ );
1182
+ }
1183
+ return index > this.pageDelete ? index - 1 : index;
1184
+ },
1185
+ // Update Record list references
1186
+ updateRecordListReferences() {
1187
+ this.config.forEach((page) => {
1188
+ page.items.forEach((item) => {
1189
+ if (item.component === "FormRecordList") {
1190
+ // eslint-disable-next-line no-param-reassign
1191
+ item.config.form = this.calcNewIndexFor(
1192
+ item.config.form * 1,
1193
+ item.config.label
1194
+ );
1195
+ }
1196
+ });
1197
+ });
1198
+ },
1199
+ // Update navigation buttons references
1200
+ updateNavigationButtonsReferences() {
1201
+ this.config.forEach((page) => {
1202
+ page.items.forEach((item) => {
1203
+ if (
1204
+ item.component === "FormButton" &&
1205
+ item.config.event === "pageNavigate"
1206
+ ) {
1207
+ // eslint-disable-next-line no-param-reassign
1208
+ item.config.eventData = this.calcNewIndexFor(
1209
+ item.config.eventData * 1,
1210
+ item.config.label
1211
+ );
1212
+ }
1213
+ });
1214
+ });
1142
1215
  },
1143
- deletePage() {
1144
- this.config.splice(this.pageDelete, 1);
1145
- this.currentPage = this.pageDelete - 1 >= 0 ? this.pageDelete - 1 : 0;
1216
+ async deletePage() {
1217
+ const back = _.cloneDeep(this.config);
1218
+ try {
1219
+ this.updateRecordListReferences();
1220
+ this.updateNavigationButtonsReferences();
1221
+ this.$refs.tabsBar.closePageByIndex(this.pageDelete);
1222
+ this.$refs.tabsBar.updateTabsReferences(this.pageDelete);
1223
+ await this.$nextTick();
1224
+ this.config.splice(this.pageDelete, 1);
1225
+ } catch (error) {
1226
+ this.config = back;
1227
+ globalObject.ProcessMaker.alert(error.message, "danger");
1228
+ return;
1229
+ }
1146
1230
  this.$store.dispatch("undoRedoModule/pushState", {
1147
1231
  config: JSON.stringify(this.config),
1148
1232
  currentPage: this.currentPage,
@@ -1282,12 +1366,13 @@ export default {
1282
1366
  this.config[this.currentPage].items.push(clone);
1283
1367
  this.updateState();
1284
1368
  this.inspect(clone);
1285
- }
1369
+ },
1370
+
1286
1371
  }
1287
1372
  };
1288
1373
  </script>
1289
1374
 
1290
- <style scoped>
1375
+ <style>
1291
1376
  .custom-popover {
1292
1377
  margin-right: -400px;
1293
1378
  padding: 16px;
@@ -1445,4 +1530,8 @@ $side-bar-font-size: 0.875rem;
1445
1530
  box-shadow: 0 0 0 13px rgba(0, 0, 0, 0);
1446
1531
  }
1447
1532
  }
1533
+ .modal-subtitle {
1534
+ font-size: 15px;
1535
+ font-weight: normal;
1536
+ }
1448
1537
  </style>