@plusscommunities/pluss-feature-builder-web-c 1.0.4 → 1.0.6-beta.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.
@@ -4,12 +4,7 @@ const { Components } = PlussCore;
4
4
  const { Popup } = Components;
5
5
  import styles from "./FeatureBuilderSuccessPopup.module.css";
6
6
 
7
- const FeatureBuilderSuccessPopup = ({
8
- isOpen,
9
- onClose,
10
- featureName,
11
- displayName,
12
- }) => {
7
+ const FeatureBuilderSuccessPopup = ({ isOpen, onClose, featureName }) => {
13
8
  if (!isOpen) {
14
9
  return null;
15
10
  }
@@ -23,14 +18,14 @@ const FeatureBuilderSuccessPopup = ({
23
18
  },
24
19
  ];
25
20
 
21
+ const capitalisedName =
22
+ featureName && featureName[0].toUpperCase() + featureName.slice(1);
23
+
26
24
  return (
27
25
  <Popup
28
- title={`${featureName} Created`}
26
+ title={`${capitalisedName} Created`}
29
27
  subtitle={
30
- <>
31
- <span>{featureName}</span> is now saved and available for your
32
- communities!
33
- </>
28
+ <>{capitalisedName} is now saved and available for your communities!</>
34
29
  }
35
30
  onClose={onClose}
36
31
  buttons={buttons}
@@ -40,12 +35,11 @@ const FeatureBuilderSuccessPopup = ({
40
35
  >
41
36
  <div className={styles.successContent}>
42
37
  <div className={styles.successMessage}>
43
- To activate this feature, please add it to your sites using the
44
- Feature Picker.
38
+ {capitalisedName} is saved and ready to go. To use it, add it to your
39
+ site using the <b>Feature Picker.</b>
45
40
  </div>
46
41
  <div className={styles.successMessage}>
47
- Team members with the right permissions can start creating content for
48
- the communities that have installed the feature.
42
+ Once added, your team can start creating content for the feature.
49
43
  </div>
50
44
  </div>
51
45
  </Popup>
@@ -1,7 +1,6 @@
1
1
  .successContent {
2
2
  display: flex;
3
3
  flex-direction: column;
4
- align-items: center;
5
4
  gap: 20px;
6
5
  padding: 20px 0;
7
6
  }
@@ -30,11 +29,10 @@
30
29
  }
31
30
 
32
31
  .successMessage {
33
- text-align: center;
34
- color: #666;
35
32
  font-size: 16px;
36
33
  line-height: 1.5;
37
34
  max-width: 400px;
35
+ color: #181c4a;
38
36
  }
39
37
 
40
38
  .featureName {
@@ -1,6 +1,6 @@
1
- import React, { useState, useEffect } from "react";
1
+ import React from "react";
2
2
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { CheckBox, Text, Button } from "./index.js";
3
+ import { Text, Button } from "./index.js";
4
4
  import { BaseFieldConfig } from "./BaseFieldConfig.jsx";
5
5
  import { iconImports } from "./iconImports.js";
6
6
  import styles from "./Fields.module.css";
@@ -130,7 +130,6 @@ export const Field = (props) => {
130
130
  );
131
131
  };
132
132
 
133
- // Padlock icon component for base fields
134
133
  const PadlockIcon = () => {
135
134
  const icon = iconImports.lock;
136
135
  return (
@@ -140,39 +139,6 @@ const PadlockIcon = () => {
140
139
  );
141
140
  };
142
141
 
143
- // Field type icon component
144
- const FieldTypeIcon = ({ type }) => {
145
- const getIconForType = (type) => {
146
- switch (type) {
147
- case "title":
148
- return iconImports.heading;
149
- case "text":
150
- return iconImports.pen;
151
- case "description":
152
- return iconImports.alignLeft;
153
- case "image":
154
- return iconImports.image;
155
- case "feature-image":
156
- return iconImports.camera;
157
-
158
- case "cta":
159
- return iconImports.link;
160
- case "file":
161
- return iconImports.file;
162
- default:
163
- return iconImports.cog;
164
- }
165
- };
166
-
167
- const icon = getIconForType(type);
168
-
169
- return (
170
- <span className={styles.fieldTypeIcon}>
171
- <FontAwesomeIcon icon={icon} />
172
- </span>
173
- );
174
- };
175
-
176
142
  // Helper function to get display text for field types
177
143
  const getFieldTypeDisplay = (type) => {
178
144
  switch (type) {
@@ -188,7 +154,6 @@ const getFieldTypeDisplay = (type) => {
188
154
  return "Gallery";
189
155
  case "feature-image":
190
156
  return "Feature Image";
191
-
192
157
  case "cta":
193
158
  return "Action Button";
194
159
  case "file":
@@ -207,23 +172,22 @@ const TitleField = (props) => {
207
172
  showWarnings,
208
173
  id,
209
174
  } = props;
210
- const { placeholder, label, isRequired, helpText } = props.values;
175
+ const { placeholder, label, helpText } = props.values;
211
176
 
212
177
  return (
213
178
  <BaseFieldConfig
214
179
  label={label}
215
180
  placeholder={placeholder}
216
181
  helpText={helpText}
217
- isRequired={true} // Title fields are always required
182
+ isRequired={true}
218
183
  setLabel={setLabel}
219
- setPlaceholder={() => {}}
184
+ setPlaceholder={setPlaceholder}
220
185
  setHelpText={setHelpText}
221
- toggleIsRequired={() => {}} // Disable toggling - title fields are always required
186
+ toggleIsRequired={() => {}}
222
187
  stepErrors={stepErrors}
223
188
  showWarnings={showWarnings}
224
189
  id={id}
225
190
  fieldType="title"
226
- id={id} // Fix: pass the field ID
227
191
  />
228
192
  );
229
193
  };
@@ -235,25 +199,25 @@ const TextField = (props) => {
235
199
  setHelpText,
236
200
  stepErrors,
237
201
  showWarnings,
202
+ toggleIsRequired,
238
203
  id,
239
204
  } = props;
240
- const { placeholder, label, isRequired, helpText } = props.values;
205
+ const { placeholder, label, helpText, isRequired } = props.values;
241
206
 
242
207
  return (
243
208
  <BaseFieldConfig
244
209
  label={label}
245
210
  placeholder={placeholder}
246
211
  helpText={helpText}
247
- isRequired={true} // Text fields are always required
212
+ isRequired={isRequired}
248
213
  setLabel={setLabel}
249
- setPlaceholder={() => {}}
214
+ setPlaceholder={setPlaceholder}
250
215
  setHelpText={setHelpText}
251
- toggleIsRequired={() => {}} // Disable toggling - text fields are always required
216
+ toggleIsRequired={toggleIsRequired}
252
217
  stepErrors={stepErrors}
253
218
  showWarnings={showWarnings}
254
219
  id={id}
255
220
  fieldType="text"
256
- id={id} // Fix: pass the field ID
257
221
  />
258
222
  );
259
223
  };
@@ -267,7 +231,7 @@ const DescriptionField = (props) => {
267
231
  stepErrors,
268
232
  showWarnings,
269
233
  setUseAsSummary,
270
- id, // Get ID from root props
234
+ id,
271
235
  } = props;
272
236
  const { placeholder, label, isRequired, helpText, useAsSummary } =
273
237
  props.values;
@@ -279,7 +243,7 @@ const DescriptionField = (props) => {
279
243
  helpText={helpText}
280
244
  isRequired={isRequired}
281
245
  setLabel={setLabel}
282
- setPlaceholder={() => {}}
246
+ setPlaceholder={setPlaceholder}
283
247
  setHelpText={setHelpText}
284
248
  toggleIsRequired={toggleIsRequired}
285
249
  stepErrors={stepErrors}
@@ -301,7 +265,7 @@ const ImageField = (props) => {
301
265
  toggleAllowCaption,
302
266
  stepErrors,
303
267
  showWarnings,
304
- id, // Get ID from root props
268
+ id,
305
269
  } = props;
306
270
  const { label, isRequired, placeholder, allowCaption, helpText } =
307
271
  props.values;
@@ -321,20 +285,14 @@ const ImageField = (props) => {
321
285
  toggleAllowCaption={toggleAllowCaption}
322
286
  stepErrors={stepErrors}
323
287
  showWarnings={showWarnings}
324
- id={id} // Fix: pass the field ID
288
+ id={id}
325
289
  fieldType="image"
326
290
  />
327
291
  );
328
292
  };
329
293
 
330
294
  const FeatureImageField = (props) => {
331
- const {
332
- setLabel,
333
- setHelpText,
334
- stepErrors,
335
- showWarnings,
336
- id, // Get ID from root props
337
- } = props;
295
+ const { setLabel, setHelpText, stepErrors, showWarnings, id } = props;
338
296
  const { label, placeholder, helpText } = props.values;
339
297
 
340
298
  return (
@@ -342,14 +300,14 @@ const FeatureImageField = (props) => {
342
300
  label={label}
343
301
  placeholder={placeholder}
344
302
  helpText={helpText}
345
- isRequired={true} // Feature image fields are always required
303
+ isRequired={true}
346
304
  setLabel={setLabel}
347
305
  setPlaceholder={() => {}}
348
306
  setHelpText={setHelpText}
349
- toggleIsRequired={() => {}} // Disable toggling - feature image fields are always required
307
+ toggleIsRequired={() => {}}
350
308
  stepErrors={stepErrors}
351
309
  showWarnings={showWarnings}
352
- id={id} // Fix: pass the field ID
310
+ id={id}
353
311
  fieldType="feature-image"
354
312
  customLabel="Label"
355
313
  />
@@ -367,7 +325,7 @@ const GalleryField = (props) => {
367
325
  setAllowedTypes,
368
326
  stepErrors,
369
327
  showWarnings,
370
- id, // Get ID from root props
328
+ id,
371
329
  } = props;
372
330
  const {
373
331
  label,
@@ -415,9 +373,9 @@ const CTAField = (props) => {
415
373
  setPlaceholder,
416
374
  stepErrors,
417
375
  showWarnings,
376
+ id,
418
377
  } = props;
419
378
  const { label, isRequired, helpText, placeholder } = props.values;
420
- const { id } = props; // Get ID from root props
421
379
 
422
380
  return (
423
381
  <BaseFieldConfig
@@ -426,21 +384,26 @@ const CTAField = (props) => {
426
384
  helpText={helpText}
427
385
  isRequired={isRequired}
428
386
  setLabel={setLabel}
429
- setPlaceholder={() => {}}
387
+ setPlaceholder={setPlaceholder}
430
388
  setHelpText={setHelpText}
431
389
  toggleIsRequired={toggleIsRequired}
432
390
  stepErrors={stepErrors}
433
391
  showWarnings={showWarnings}
434
- id={id} // Fix: pass the field ID
392
+ id={id}
435
393
  fieldType="cta"
436
394
  />
437
395
  );
438
396
  };
439
397
 
440
398
  const FileField = (props) => {
441
- const { toggleIsRequired, setLabel, setHelpText, stepErrors, showWarnings } =
442
- props;
443
- const { id } = props; // Get ID from root props
399
+ const {
400
+ toggleIsRequired,
401
+ setLabel,
402
+ setHelpText,
403
+ stepErrors,
404
+ showWarnings,
405
+ id,
406
+ } = props;
444
407
  const { label, isRequired, helpText } = props.values;
445
408
 
446
409
  return (
@@ -5,18 +5,14 @@ import { SidebarLayout } from "../components/SidebarLayout.jsx";
5
5
  import { values } from "../values.config.js";
6
6
  import { PlussCore } from "../feature.config";
7
7
  import styles from "./Form.module.css";
8
- import fieldStyles from "../components/Fields.module.css";
9
8
  import { Field } from "../components/Fields.jsx";
10
9
  import { iconImports } from "../components/iconImports";
11
10
  import {
12
11
  Text,
13
12
  LoadingState,
14
- SkeletonLoader,
15
- DropdownInput,
16
13
  Button,
17
14
  ErrorBoundary,
18
15
  Popup,
19
- CenteredContainer,
20
16
  } from "../components";
21
17
 
22
18
  import ToastContainer from "../components/ToastContainer.jsx";
@@ -31,7 +27,6 @@ import {
31
27
  selectIsEditMode,
32
28
  selectIsStepValid,
33
29
  selectStepErrors,
34
- selectCurrentStep,
35
30
  selectFormIsSubmitting,
36
31
  selectFormSubmitError,
37
32
  selectFormSubmitSuccess,
@@ -122,28 +117,20 @@ const FormFieldsStepInner = (props) => {
122
117
  },
123
118
  ];
124
119
 
125
- // Use custom hook to handle definition loading
126
120
  const { definition, definitionIsLoading, reloadDefinition } =
127
121
  useFeatureDefinitionLoader();
128
122
 
129
- // Get form state for display name
130
123
  const formDisplayName = useSelector(selectFormDisplayName);
131
124
 
132
- // Get wizard state
133
125
  const isCreateMode = useSelector(selectIsCreateMode);
134
126
  const isEditMode = useSelector(selectIsEditMode);
135
-
136
- // Get validation state
137
127
  const isStepValid = useSelector(selectIsStepValid("fields"));
138
128
  const stepErrors = useSelector(selectStepErrors("fields"));
139
129
  const showWarnings = !isStepValid && Object.keys(stepErrors).length > 0;
140
-
141
- // Get submission state
142
130
  const isSubmitting = useSelector(selectFormIsSubmitting);
143
131
  const submitError = useSelector(selectFormSubmitError);
144
132
  const submitSuccess = useSelector(selectFormSubmitSuccess);
145
133
 
146
- // Toast management functions
147
134
  const addToast = (type, message) => {
148
135
  const id = Date.now();
149
136
  setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
@@ -153,9 +140,6 @@ const FormFieldsStepInner = (props) => {
153
140
  setToasts((prev) => prev.filter((toast) => toast.id !== id));
154
141
  };
155
142
 
156
- // Note: Removed automatic redirect after successful save in edit mode
157
- // User should stay on the current step until they manually navigate
158
-
159
143
  // Handle successful submission with optimistic update and redirect
160
144
  React.useEffect(() => {
161
145
  if (submitSuccess && !isSubmitting) {
@@ -163,14 +147,11 @@ const FormFieldsStepInner = (props) => {
163
147
  addToast("success", "Changes saved");
164
148
  dispatch(clearFormSubmissionState());
165
149
  } else {
166
- // In create mode, show success toast and redirect immediately
167
150
  addToast("success", "Feature created successfully");
168
-
169
- // Clear submission state and redirect to overview step
170
151
  dispatch(clearFormSubmissionState());
171
152
  setTimeout(() => {
172
153
  history.push(values.routeFormOverviewStep);
173
- }, 1000); // Brief delay to show toast
154
+ }, 1000);
174
155
  }
175
156
  }
176
157
  }, [submitSuccess, isEditMode, isSubmitting, dispatch, history]);
@@ -185,7 +166,6 @@ const FormFieldsStepInner = (props) => {
185
166
  }
186
167
  }, [submitError]);
187
168
 
188
- // Scroll to top when validation errors appear
189
169
  useEffect(() => {
190
170
  if (showWarnings) {
191
171
  // Scroll to top of form to show validation errors
@@ -193,29 +173,18 @@ const FormFieldsStepInner = (props) => {
193
173
  }
194
174
  }, [showWarnings]);
195
175
 
196
- // Error boundary handlers
197
176
  const handleRefresh = () => {
198
- // Refresh current step data
199
177
  dispatch(validateAndUpdateStep("fields"));
200
178
  };
201
179
 
202
- const handleBack = () => {
203
- // Go to overview step
204
- history.push(values.routeFormOverviewStep);
205
- };
206
-
207
180
  useEffect(() => {
208
- // Set current step when component mounts
209
181
  dispatch(setCurrentStepAndSave("fields"));
210
182
  }, [dispatch]);
211
183
 
212
- // Add effect to handle definition loading and validation in edit mode
213
- // ADD THIS EFFECT: Hydrate form data from definition on refresh
214
184
  useEffect(() => {
215
185
  if (definition && !definitionIsLoading && isFormInitial) {
216
186
  dispatch(setInitialValues(definition));
217
187
 
218
- // In edit mode, trigger validation after setting initial values
219
188
  if (isEditMode) {
220
189
  setTimeout(() => {
221
190
  dispatch(validateAndUpdateStep("fields"));
@@ -226,9 +195,7 @@ const FormFieldsStepInner = (props) => {
226
195
 
227
196
  useEffect(() => {
228
197
  // In edit mode, trigger validation when definition is available
229
- // Note: The new effect above handles the data population, this handles re-validation
230
198
  if (isEditMode && definition && !definitionIsLoading && !isFormInitial) {
231
- // Only validate if form is NOT initial (meaning it has data)
232
199
  setTimeout(() => {
233
200
  dispatch(validateAndUpdateStep("fields"));
234
201
  }, 100);
@@ -237,16 +204,16 @@ const FormFieldsStepInner = (props) => {
237
204
 
238
205
  function handleAddField(fieldType) {
239
206
  dispatch(addField(fieldType));
240
- setShowFieldSelector(false); // Close popup after adding field
207
+ setShowFieldSelector(false);
241
208
  }
242
209
 
243
210
  function handleOpenFieldSelector() {
244
- setReplacingFieldIndex(null); // Reset to add mode
211
+ setReplacingFieldIndex(null);
245
212
  setShowFieldSelector(true);
246
213
  }
247
214
 
248
215
  function handleCloseFieldSelector() {
249
- setReplacingFieldIndex(null); // Reset replacement state
216
+ setReplacingFieldIndex(null);
250
217
  setShowFieldSelector(false);
251
218
  }
252
219
 
@@ -255,7 +222,6 @@ const FormFieldsStepInner = (props) => {
255
222
  }
256
223
 
257
224
  function handleReplaceField(fieldIndex) {
258
- // Store the field index to replace and open the field selector
259
225
  setReplacingFieldIndex(fieldIndex);
260
226
  setShowFieldSelector(true);
261
227
  }
@@ -263,82 +229,55 @@ const FormFieldsStepInner = (props) => {
263
229
  function handleAddReplacementField(fieldType) {
264
230
  const fieldIndex = replacingFieldIndex;
265
231
  if (fieldIndex !== null) {
266
- // Delete the current field at this index
267
232
  const currentField = allFields[fieldIndex];
268
233
  if (currentField && !currentField.isMandatory) {
269
234
  dispatch(deleteField(currentField.id));
270
235
  }
271
-
272
- // Add the new field
273
236
  dispatch(addField(fieldType));
274
-
275
- // Reset replacement state
276
237
  setReplacingFieldIndex(null);
277
238
  setShowFieldSelector(false);
278
239
  }
279
240
  }
280
241
 
281
242
  function handleNext() {
282
- // Validate before proceeding
283
243
  const validationResult = dispatch(validateAndUpdateStep("fields"));
284
-
285
- // If validation passes, navigate to next step
286
244
  if (validationResult?.isValid) {
287
- // Clear form submission state when changing steps
288
245
  dispatch(clearFormSubmissionState());
289
-
290
246
  if (isCreateMode) {
291
247
  history.push(values.routeFormLayoutStep);
292
248
  } else {
293
- // In edit mode, navigate directly
294
249
  history.push(values.routeFormLayoutStep);
295
250
  }
296
251
  }
297
252
  // If validation fails, scroll to top to show error summary
298
253
  else {
299
- // Scroll to top of form to show validation errors
300
254
  window.scrollTo({ top: 0, behavior: "smooth" });
301
255
  }
302
256
  }
303
257
 
304
258
  function handlePrevious() {
305
- // Clear form submission state when changing steps
306
259
  dispatch(clearFormSubmissionState());
307
-
308
260
  if (isCreateMode) {
309
261
  history.push(values.routeFormOverviewStep);
310
262
  } else {
311
- // In edit mode, go back to overview screen
312
263
  history.push(values.routeFormOverviewStep);
313
264
  }
314
265
  }
315
266
 
316
267
  function handleSaveStep() {
317
- // Validate before saving in edit mode
318
268
  const validationResult = dispatch(validateAndUpdateStep("fields"));
319
-
320
- // If validation passes, save the entire form
321
269
  if (validationResult?.isValid) {
322
270
  dispatch(submitForm());
323
271
  }
324
272
  // If validation fails, scroll to top to show error summary
325
273
  else {
326
- // Scroll to top of form to show validation errors
327
274
  window.scrollTo({ top: 0, behavior: "smooth" });
328
275
  }
329
276
  }
330
277
 
331
- // Get all fields in unified list (base + custom)
332
278
  const allFields = fields || [];
333
-
334
- // Create sorted copy for rendering
335
279
  const sortedFields = allFields.slice().sort((a, b) => a.order - b.order);
336
280
 
337
- // Filter description fields for summary demo
338
- const descriptionFields = allFields.filter(
339
- (field) => field.type === "description",
340
- );
341
-
342
281
  // Check for definition management permission
343
282
  if (
344
283
  !PlussCore.Session.validateAccess(
@@ -665,11 +604,9 @@ const useField = (id) => {
665
604
  }
666
605
 
667
606
  function setUseAsSummary(value) {
668
- // When setting a field as summary, use the new action to ensure only one field is selected
669
607
  if (value) {
670
608
  dispatch(setSummaryField(id));
671
609
  } else {
672
- // When unsetting, use regular update to unset this specific field
673
610
  const updatedField = { values: { ...values, useAsSummary: false } };
674
611
  dispatch(updateFieldValuesById(id, updatedField));
675
612
  }
@@ -52,11 +52,8 @@ const FormLayoutStepInner = (props) => {
52
52
  const overviewIcon = useSelector(selectFormIcon);
53
53
  const definitionId = useSelector(selectDefinitionId);
54
54
  const layoutType = layout?.type || "round";
55
- // Get wizard state
56
55
  const isCreateMode = useSelector(selectIsCreateMode);
57
56
  const isEditMode = useSelector(selectIsEditMode);
58
-
59
- // Use custom hook to handle definition loading
60
57
  const { definition, definitionIsLoading } = useFeatureDefinitionLoader();
61
58
 
62
59
  // Get form initialization state
@@ -71,13 +68,9 @@ const FormLayoutStepInner = (props) => {
71
68
  const submitError = useSelector(selectFormSubmitError);
72
69
  const submitSuccess = useSelector(selectFormSubmitSuccess);
73
70
 
74
- // Toast state
75
71
  const [toasts, setToasts] = React.useState([]);
76
-
77
- // Success popup state
78
72
  const [showSuccessPopup, setShowSuccessPopup] = React.useState(false);
79
73
 
80
- // Toast management functions
81
74
  const addToast = (type, message) => {
82
75
  const id = Date.now();
83
76
  setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
@@ -87,34 +80,23 @@ const FormLayoutStepInner = (props) => {
87
80
  setToasts((prev) => prev.filter((toast) => toast.id !== id));
88
81
  };
89
82
 
90
- // Handle success popup close
91
83
  const handleSuccessPopupClose = () => {
92
84
  setShowSuccessPopup(false);
93
85
  dispatch(clearFormSubmissionState());
94
- history.push(values.routeFormOverviewStep);
86
+ window.location.replace(values.routeFormOverviewStep);
95
87
  };
96
88
 
97
- // Handle success popup button click
98
- const handleSuccessPopupButtonClick = () => {
99
- setShowSuccessPopup(false);
100
- dispatch(clearFormSubmissionState());
101
- history.push(values.routeFormOverviewStep);
102
- };
103
-
104
- // Handle successful submission with popup and redirect
105
89
  useEffect(() => {
106
90
  if (submitSuccess && !isSubmitting) {
107
91
  if (isEditMode) {
108
92
  addToast("success", "Changes saved");
109
93
  dispatch(clearFormSubmissionState());
110
94
  } else {
111
- // In create mode, show success popup
112
95
  setShowSuccessPopup(true);
113
96
  }
114
97
  }
115
98
  }, [submitSuccess, isEditMode, isSubmitting, dispatch]);
116
99
 
117
- // Handle submit error
118
100
  useEffect(() => {
119
101
  if (submitError) {
120
102
  addToast("error", "It didn't work. Please try again.");
@@ -124,17 +106,10 @@ const FormLayoutStepInner = (props) => {
124
106
  }
125
107
  }, [submitError]);
126
108
 
127
- // Error boundary handlers
128
109
  const handleRefresh = () => {
129
- // Refresh current step data
130
110
  dispatch(validateAndUpdateStep("layout"));
131
111
  };
132
112
 
133
- const handleBack = () => {
134
- // Go to overview step
135
- history.push(values.routeFormOverviewStep);
136
- };
137
-
138
113
  const layoutOptions = [
139
114
  {
140
115
  value: "round",
@@ -185,7 +160,6 @@ const FormLayoutStepInner = (props) => {
185
160
  }
186
161
  }, [definition, definitionIsLoading, isFormInitial, isEditMode, dispatch]);
187
162
 
188
- // Add effect to handle definition loading and validation in edit mode
189
163
  useEffect(() => {
190
164
  // In edit mode, trigger validation when definition is available
191
165
  // Note: The new effect above handles data population, this handles re-validation
@@ -201,35 +175,23 @@ const FormLayoutStepInner = (props) => {
201
175
  dispatch(setLayoutType(layoutType));
202
176
  }
203
177
 
204
- function handleGridIconChange(iconUrl) {
205
- dispatch(setGridLayoutIcon(iconUrl));
206
- }
207
-
208
- function handleGridIconRemove() {
209
- // When custom grid icon is removed, set it to undefined to trigger fallback to overview icon
210
- dispatch(setGridLayoutIcon(undefined));
211
- }
212
-
213
178
  function handlePrevious() {
214
- // Clear form submission state when changing steps
215
179
  dispatch(clearFormSubmissionState());
216
180
 
217
181
  if (isCreateMode) {
218
182
  history.push(values.routeFormFieldsStep);
219
183
  } else {
220
- // In edit mode, go back to fields screen
221
184
  history.push(values.routeFormFieldsStep);
222
185
  }
223
186
  }
224
187
 
225
188
  function handleNext() {
226
- // Validate before proceeding
189
+ if (isSubmitting) {
190
+ return;
191
+ }
227
192
  const validationResult = dispatch(validateAndUpdateStep("layout"));
228
-
229
- // If validation passes, proceed with submission/navigation
230
193
  if (validationResult?.isValid) {
231
194
  if (isCreateMode) {
232
- // In create mode, submit form - success popup will be shown by useEffect
233
195
  dispatch(submitForm());
234
196
  } else {
235
197
  // In edit mode, just save changes