@plusscommunities/pluss-feature-builder-web 1.0.4 → 1.0.6

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/dist/index.cjs.js CHANGED
@@ -577,8 +577,8 @@ const DeleteConfirmationPopup = _ref => {
577
577
  }, "Please wait while we delete the ", itemText, "...")));
578
578
  };
579
579
 
580
- var css$v = ".FeatureBuilderSuccessPopup_module_successContent__5b4b36c2 {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tgap: 20px;\n\tpadding: 20px 0;\n}\n\n.FeatureBuilderSuccessPopup_module_gifPlaceholder__5b4b36c2 {\n\twidth: 100%;\n\tmax-width: 400px;\n}\n\n.FeatureBuilderSuccessPopup_module_gifContainer__5b4b36c2 {\n\twidth: 100%;\n\theight: 200px;\n\tbackground-color: #f5f5f5;\n\tborder: 2px dashed #ccc;\n\tborder-radius: 8px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\n\n.FeatureBuilderSuccessPopup_module_gifPlaceholderText__5b4b36c2 {\n\tcolor: #999;\n\tfont-size: 14px;\n\ttext-align: center;\n\tpadding: 20px;\n}\n\n.FeatureBuilderSuccessPopup_module_successMessage__5b4b36c2 {\n\ttext-align: center;\n\tcolor: #666;\n\tfont-size: 16px;\n\tline-height: 1.5;\n\tmax-width: 400px;\n}\n\n.FeatureBuilderSuccessPopup_module_featureName__5b4b36c2 {\n\tfont-weight: 600;\n\tcolor: #333;\n}\n";
581
- var modules_b5a7fccb = {"successContent":"FeatureBuilderSuccessPopup_module_successContent__5b4b36c2","gifPlaceholder":"FeatureBuilderSuccessPopup_module_gifPlaceholder__5b4b36c2","gifContainer":"FeatureBuilderSuccessPopup_module_gifContainer__5b4b36c2","gifPlaceholderText":"FeatureBuilderSuccessPopup_module_gifPlaceholderText__5b4b36c2","successMessage":"FeatureBuilderSuccessPopup_module_successMessage__5b4b36c2","featureName":"FeatureBuilderSuccessPopup_module_featureName__5b4b36c2"};
580
+ var css$v = ".FeatureBuilderSuccessPopup_module_successContent__a6544e11 {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 20px;\n\tpadding: 20px 0;\n}\n\n.FeatureBuilderSuccessPopup_module_gifPlaceholder__a6544e11 {\n\twidth: 100%;\n\tmax-width: 400px;\n}\n\n.FeatureBuilderSuccessPopup_module_gifContainer__a6544e11 {\n\twidth: 100%;\n\theight: 200px;\n\tbackground-color: #f5f5f5;\n\tborder: 2px dashed #ccc;\n\tborder-radius: 8px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\n\n.FeatureBuilderSuccessPopup_module_gifPlaceholderText__a6544e11 {\n\tcolor: #999;\n\tfont-size: 14px;\n\ttext-align: center;\n\tpadding: 20px;\n}\n\n.FeatureBuilderSuccessPopup_module_successMessage__a6544e11 {\n\tfont-size: 16px;\n\tline-height: 1.5;\n\tmax-width: 400px;\n\tcolor: #181c4a;\n}\n\n.FeatureBuilderSuccessPopup_module_featureName__a6544e11 {\n\tfont-weight: 600;\n\tcolor: #333;\n}\n";
581
+ var modules_b5a7fccb = {"successContent":"FeatureBuilderSuccessPopup_module_successContent__a6544e11","gifPlaceholder":"FeatureBuilderSuccessPopup_module_gifPlaceholder__a6544e11","gifContainer":"FeatureBuilderSuccessPopup_module_gifContainer__a6544e11","gifPlaceholderText":"FeatureBuilderSuccessPopup_module_gifPlaceholderText__a6544e11","successMessage":"FeatureBuilderSuccessPopup_module_successMessage__a6544e11","featureName":"FeatureBuilderSuccessPopup_module_featureName__a6544e11"};
582
582
  n(css$v,{});
583
583
 
584
584
  const {
@@ -591,8 +591,7 @@ const FeatureBuilderSuccessPopup = _ref => {
591
591
  let {
592
592
  isOpen,
593
593
  onClose,
594
- featureName,
595
- displayName
594
+ featureName
596
595
  } = _ref;
597
596
  if (!isOpen) {
598
597
  return null;
@@ -603,9 +602,10 @@ const FeatureBuilderSuccessPopup = _ref => {
603
602
  onClick: onClose,
604
603
  isActive: true
605
604
  }];
605
+ const capitalisedName = featureName && featureName[0].toUpperCase() + featureName.slice(1);
606
606
  return /*#__PURE__*/React__default["default"].createElement(Popup$3, {
607
- title: "".concat(featureName, " Created"),
608
- subtitle: /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("span", null, featureName), " is now saved and available for your communities!"),
607
+ title: "".concat(capitalisedName, " Created"),
608
+ subtitle: /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, capitalisedName, " is now saved and available for your communities!"),
609
609
  onClose: onClose,
610
610
  buttons: buttons,
611
611
  hasPadding: true,
@@ -615,9 +615,9 @@ const FeatureBuilderSuccessPopup = _ref => {
615
615
  className: modules_b5a7fccb.successContent
616
616
  }, /*#__PURE__*/React__default["default"].createElement("div", {
617
617
  className: modules_b5a7fccb.successMessage
618
- }, "To activate this feature, please add it to your sites using the Feature Picker."), /*#__PURE__*/React__default["default"].createElement("div", {
618
+ }, capitalisedName, " is saved and ready to go. To use it, add it to your site using the ", /*#__PURE__*/React__default["default"].createElement("b", null, "Feature Picker.")), /*#__PURE__*/React__default["default"].createElement("div", {
619
619
  className: modules_b5a7fccb.successMessage
620
- }, "Team members with the right permissions can start creating content for the communities that have installed the feature.")));
620
+ }, "Once added, your team can start creating content for the feature.")));
621
621
  };
622
622
 
623
623
  var css$u = ".FeatureBuilderWelcomePopup_module_welcomeContent__0e08704b {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 20px;\n\tpadding: 20px 0;\n}\n\n.FeatureBuilderWelcomePopup_module_welcomeMessage__0e08704b {\n\tcolor: #666;\n\tfont-size: 16px;\n\tline-height: 1.6;\n\twidth: 100%;\n}\n\n.FeatureBuilderWelcomePopup_module_welcomeMessage__0e08704b p {\n\tmargin: 0 0 16px 0;\n}\n\n.FeatureBuilderWelcomePopup_module_welcomeMessage__0e08704b p:last-child {\n\tmargin-bottom: 0;\n}\n";
@@ -3578,8 +3578,6 @@ const Field = props => {
3578
3578
  className: "".concat(modules_eca69fd0.field, " ").concat(hasError ? modules_eca69fd0["field--invalid"] : "")
3579
3579
  }, fieldContent));
3580
3580
  };
3581
-
3582
- // Padlock icon component for base fields
3583
3581
  const PadlockIcon = () => {
3584
3582
  const icon = iconImports.lock;
3585
3583
  return /*#__PURE__*/React__default["default"].createElement("span", {
@@ -3624,25 +3622,21 @@ const TitleField = props => {
3624
3622
  const {
3625
3623
  placeholder,
3626
3624
  label,
3627
- isRequired,
3628
3625
  helpText
3629
3626
  } = props.values;
3630
3627
  return /*#__PURE__*/React__default["default"].createElement(BaseFieldConfig, {
3631
3628
  label: label,
3632
3629
  placeholder: placeholder,
3633
3630
  helpText: helpText,
3634
- isRequired: true // Title fields are always required
3635
- ,
3631
+ isRequired: true,
3636
3632
  setLabel: setLabel,
3637
- setPlaceholder: () => {},
3633
+ setPlaceholder: setPlaceholder,
3638
3634
  setHelpText: setHelpText,
3639
- toggleIsRequired: () => {} // Disable toggling - title fields are always required
3640
- ,
3635
+ toggleIsRequired: () => {},
3641
3636
  stepErrors: stepErrors,
3642
3637
  showWarnings: showWarnings,
3643
3638
  id: id,
3644
- fieldType: "title",
3645
- id: id // Fix: pass the field ID
3639
+ fieldType: "title"
3646
3640
  });
3647
3641
  };
3648
3642
  const TextField = props => {
@@ -3652,30 +3646,28 @@ const TextField = props => {
3652
3646
  setHelpText,
3653
3647
  stepErrors,
3654
3648
  showWarnings,
3649
+ toggleIsRequired,
3655
3650
  id
3656
3651
  } = props;
3657
3652
  const {
3658
3653
  placeholder,
3659
3654
  label,
3660
- isRequired,
3661
- helpText
3655
+ helpText,
3656
+ isRequired
3662
3657
  } = props.values;
3663
3658
  return /*#__PURE__*/React__default["default"].createElement(BaseFieldConfig, {
3664
3659
  label: label,
3665
3660
  placeholder: placeholder,
3666
3661
  helpText: helpText,
3667
- isRequired: true // Text fields are always required
3668
- ,
3662
+ isRequired: isRequired,
3669
3663
  setLabel: setLabel,
3670
- setPlaceholder: () => {},
3664
+ setPlaceholder: setPlaceholder,
3671
3665
  setHelpText: setHelpText,
3672
- toggleIsRequired: () => {} // Disable toggling - text fields are always required
3673
- ,
3666
+ toggleIsRequired: toggleIsRequired,
3674
3667
  stepErrors: stepErrors,
3675
3668
  showWarnings: showWarnings,
3676
3669
  id: id,
3677
- fieldType: "text",
3678
- id: id // Fix: pass the field ID
3670
+ fieldType: "text"
3679
3671
  });
3680
3672
  };
3681
3673
  const DescriptionField = props => {
@@ -3687,7 +3679,7 @@ const DescriptionField = props => {
3687
3679
  stepErrors,
3688
3680
  showWarnings,
3689
3681
  setUseAsSummary,
3690
- id // Get ID from root props
3682
+ id
3691
3683
  } = props;
3692
3684
  const {
3693
3685
  placeholder,
@@ -3702,7 +3694,7 @@ const DescriptionField = props => {
3702
3694
  helpText: helpText,
3703
3695
  isRequired: isRequired,
3704
3696
  setLabel: setLabel,
3705
- setPlaceholder: () => {},
3697
+ setPlaceholder: setPlaceholder,
3706
3698
  setHelpText: setHelpText,
3707
3699
  toggleIsRequired: toggleIsRequired,
3708
3700
  stepErrors: stepErrors,
@@ -3722,7 +3714,7 @@ const ImageField = props => {
3722
3714
  toggleAllowCaption,
3723
3715
  stepErrors,
3724
3716
  showWarnings,
3725
- id // Get ID from root props
3717
+ id
3726
3718
  } = props;
3727
3719
  const {
3728
3720
  label,
@@ -3745,8 +3737,7 @@ const ImageField = props => {
3745
3737
  toggleAllowCaption: toggleAllowCaption,
3746
3738
  stepErrors: stepErrors,
3747
3739
  showWarnings: showWarnings,
3748
- id: id // Fix: pass the field ID
3749
- ,
3740
+ id: id,
3750
3741
  fieldType: "image"
3751
3742
  });
3752
3743
  };
@@ -3756,7 +3747,7 @@ const FeatureImageField = props => {
3756
3747
  setHelpText,
3757
3748
  stepErrors,
3758
3749
  showWarnings,
3759
- id // Get ID from root props
3750
+ id
3760
3751
  } = props;
3761
3752
  const {
3762
3753
  label,
@@ -3767,17 +3758,14 @@ const FeatureImageField = props => {
3767
3758
  label: label,
3768
3759
  placeholder: placeholder,
3769
3760
  helpText: helpText,
3770
- isRequired: true // Feature image fields are always required
3771
- ,
3761
+ isRequired: true,
3772
3762
  setLabel: setLabel,
3773
3763
  setPlaceholder: () => {},
3774
3764
  setHelpText: setHelpText,
3775
- toggleIsRequired: () => {} // Disable toggling - feature image fields are always required
3776
- ,
3765
+ toggleIsRequired: () => {},
3777
3766
  stepErrors: stepErrors,
3778
3767
  showWarnings: showWarnings,
3779
- id: id // Fix: pass the field ID
3780
- ,
3768
+ id: id,
3781
3769
  fieldType: "feature-image",
3782
3770
  customLabel: "Label"
3783
3771
  });
@@ -3793,7 +3781,7 @@ const GalleryField = props => {
3793
3781
  setAllowedTypes,
3794
3782
  stepErrors,
3795
3783
  showWarnings,
3796
- id // Get ID from root props
3784
+ id
3797
3785
  } = props;
3798
3786
  const {
3799
3787
  label,
@@ -3836,7 +3824,8 @@ const CTAField = props => {
3836
3824
  setHelpText,
3837
3825
  setPlaceholder,
3838
3826
  stepErrors,
3839
- showWarnings
3827
+ showWarnings,
3828
+ id
3840
3829
  } = props;
3841
3830
  const {
3842
3831
  label,
@@ -3844,23 +3833,18 @@ const CTAField = props => {
3844
3833
  helpText,
3845
3834
  placeholder
3846
3835
  } = props.values;
3847
- const {
3848
- id
3849
- } = props; // Get ID from root props
3850
-
3851
3836
  return /*#__PURE__*/React__default["default"].createElement(BaseFieldConfig, {
3852
3837
  label: label,
3853
3838
  placeholder: placeholder,
3854
3839
  helpText: helpText,
3855
3840
  isRequired: isRequired,
3856
3841
  setLabel: setLabel,
3857
- setPlaceholder: () => {},
3842
+ setPlaceholder: setPlaceholder,
3858
3843
  setHelpText: setHelpText,
3859
3844
  toggleIsRequired: toggleIsRequired,
3860
3845
  stepErrors: stepErrors,
3861
3846
  showWarnings: showWarnings,
3862
- id: id // Fix: pass the field ID
3863
- ,
3847
+ id: id,
3864
3848
  fieldType: "cta"
3865
3849
  });
3866
3850
  };
@@ -3870,11 +3854,9 @@ const FileField = props => {
3870
3854
  setLabel,
3871
3855
  setHelpText,
3872
3856
  stepErrors,
3873
- showWarnings
3874
- } = props;
3875
- const {
3857
+ showWarnings,
3876
3858
  id
3877
- } = props; // Get ID from root props
3859
+ } = props;
3878
3860
  const {
3879
3861
  label,
3880
3862
  isRequired,
@@ -4116,32 +4098,20 @@ const FormFieldsStepInner = props => {
4116
4098
  UseCase: "Link to reservations, bookings, or external sites",
4117
4099
  Icon: "arrow-circle-right"
4118
4100
  }];
4119
-
4120
- // Use custom hook to handle definition loading
4121
4101
  const {
4122
4102
  definition,
4123
4103
  definitionIsLoading,
4124
4104
  reloadDefinition
4125
4105
  } = useFeatureDefinitionLoader();
4126
-
4127
- // Get form state for display name
4128
4106
  const formDisplayName = reactRedux.useSelector(selectFormDisplayName);
4129
-
4130
- // Get wizard state
4131
4107
  const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
4132
4108
  const isEditMode = reactRedux.useSelector(selectIsEditMode);
4133
-
4134
- // Get validation state
4135
4109
  const isStepValid = reactRedux.useSelector(selectIsStepValid("fields"));
4136
4110
  const stepErrors = reactRedux.useSelector(selectStepErrors("fields"));
4137
4111
  const showWarnings = !isStepValid && Object.keys(stepErrors).length > 0;
4138
-
4139
- // Get submission state
4140
4112
  const isSubmitting = reactRedux.useSelector(selectFormIsSubmitting);
4141
4113
  const submitError = reactRedux.useSelector(selectFormSubmitError);
4142
4114
  const submitSuccess = reactRedux.useSelector(selectFormSubmitSuccess);
4143
-
4144
- // Toast management functions
4145
4115
  const addToast = (type, message) => {
4146
4116
  const id = Date.now();
4147
4117
  setToasts(prev => [...prev, {
@@ -4155,9 +4125,6 @@ const FormFieldsStepInner = props => {
4155
4125
  setToasts(prev => prev.filter(toast => toast.id !== id));
4156
4126
  };
4157
4127
 
4158
- // Note: Removed automatic redirect after successful save in edit mode
4159
- // User should stay on the current step until they manually navigate
4160
-
4161
4128
  // Handle successful submission with optimistic update and redirect
4162
4129
  React__default["default"].useEffect(() => {
4163
4130
  if (submitSuccess && !isSubmitting) {
@@ -4165,14 +4132,11 @@ const FormFieldsStepInner = props => {
4165
4132
  addToast("success", "Changes saved");
4166
4133
  dispatch(clearFormSubmissionState());
4167
4134
  } else {
4168
- // In create mode, show success toast and redirect immediately
4169
4135
  addToast("success", "Feature created successfully");
4170
-
4171
- // Clear submission state and redirect to overview step
4172
4136
  dispatch(clearFormSubmissionState());
4173
4137
  setTimeout(() => {
4174
4138
  history.push(values.routeFormOverviewStep);
4175
- }, 1000); // Brief delay to show toast
4139
+ }, 1000);
4176
4140
  }
4177
4141
  }
4178
4142
  }, [submitSuccess, isEditMode, isSubmitting, dispatch, history]);
@@ -4186,8 +4150,6 @@ const FormFieldsStepInner = props => {
4186
4150
  }, 1000);
4187
4151
  }
4188
4152
  }, [submitError]);
4189
-
4190
- // Scroll to top when validation errors appear
4191
4153
  React.useEffect(() => {
4192
4154
  if (showWarnings) {
4193
4155
  // Scroll to top of form to show validation errors
@@ -4197,24 +4159,15 @@ const FormFieldsStepInner = props => {
4197
4159
  });
4198
4160
  }
4199
4161
  }, [showWarnings]);
4200
-
4201
- // Error boundary handlers
4202
4162
  const handleRefresh = () => {
4203
- // Refresh current step data
4204
4163
  dispatch(validateAndUpdateStep("fields"));
4205
4164
  };
4206
4165
  React.useEffect(() => {
4207
- // Set current step when component mounts
4208
4166
  dispatch(setCurrentStepAndSave("fields"));
4209
4167
  }, [dispatch]);
4210
-
4211
- // Add effect to handle definition loading and validation in edit mode
4212
- // ADD THIS EFFECT: Hydrate form data from definition on refresh
4213
4168
  React.useEffect(() => {
4214
4169
  if (definition && !definitionIsLoading && isFormInitial) {
4215
4170
  dispatch(setInitialValues(definition));
4216
-
4217
- // In edit mode, trigger validation after setting initial values
4218
4171
  if (isEditMode) {
4219
4172
  setTimeout(() => {
4220
4173
  dispatch(validateAndUpdateStep("fields"));
@@ -4224,9 +4177,7 @@ const FormFieldsStepInner = props => {
4224
4177
  }, [definition, definitionIsLoading, isFormInitial, isEditMode, dispatch]);
4225
4178
  React.useEffect(() => {
4226
4179
  // In edit mode, trigger validation when definition is available
4227
- // Note: The new effect above handles the data population, this handles re-validation
4228
4180
  if (isEditMode && definition && !definitionIsLoading && !isFormInitial) {
4229
- // Only validate if form is NOT initial (meaning it has data)
4230
4181
  setTimeout(() => {
4231
4182
  dispatch(validateAndUpdateStep("fields"));
4232
4183
  }, 100);
@@ -4234,59 +4185,47 @@ const FormFieldsStepInner = props => {
4234
4185
  }, [definition, definitionIsLoading, isEditMode, isFormInitial, dispatch]);
4235
4186
  function handleAddField(fieldType) {
4236
4187
  dispatch(addField(fieldType));
4237
- setShowFieldSelector(false); // Close popup after adding field
4188
+ setShowFieldSelector(false);
4238
4189
  }
4239
4190
  function handleOpenFieldSelector() {
4240
- setReplacingFieldIndex(null); // Reset to add mode
4191
+ setReplacingFieldIndex(null);
4241
4192
  setShowFieldSelector(true);
4242
4193
  }
4243
4194
  function handleCloseFieldSelector() {
4244
- setReplacingFieldIndex(null); // Reset replacement state
4195
+ setReplacingFieldIndex(null);
4245
4196
  setShowFieldSelector(false);
4246
4197
  }
4247
4198
  function handleDeleteField(fieldId) {
4248
4199
  dispatch(deleteField(fieldId));
4249
4200
  }
4250
4201
  function handleReplaceField(fieldIndex) {
4251
- // Store the field index to replace and open the field selector
4252
4202
  setReplacingFieldIndex(fieldIndex);
4253
4203
  setShowFieldSelector(true);
4254
4204
  }
4255
4205
  function handleAddReplacementField(fieldType) {
4256
4206
  const fieldIndex = replacingFieldIndex;
4257
4207
  if (fieldIndex !== null) {
4258
- // Delete the current field at this index
4259
4208
  const currentField = allFields[fieldIndex];
4260
4209
  if (currentField && !currentField.isMandatory) {
4261
4210
  dispatch(deleteField(currentField.id));
4262
4211
  }
4263
-
4264
- // Add the new field
4265
4212
  dispatch(addField(fieldType));
4266
-
4267
- // Reset replacement state
4268
4213
  setReplacingFieldIndex(null);
4269
4214
  setShowFieldSelector(false);
4270
4215
  }
4271
4216
  }
4272
4217
  function handleNext() {
4273
- // Validate before proceeding
4274
4218
  const validationResult = dispatch(validateAndUpdateStep("fields"));
4275
-
4276
- // If validation passes, navigate to next step
4277
4219
  if (validationResult !== null && validationResult !== void 0 && validationResult.isValid) {
4278
- // Clear form submission state when changing steps
4279
4220
  dispatch(clearFormSubmissionState());
4280
4221
  if (isCreateMode) {
4281
4222
  history.push(values.routeFormLayoutStep);
4282
4223
  } else {
4283
- // In edit mode, navigate directly
4284
4224
  history.push(values.routeFormLayoutStep);
4285
4225
  }
4286
4226
  }
4287
4227
  // If validation fails, scroll to top to show error summary
4288
4228
  else {
4289
- // Scroll to top of form to show validation errors
4290
4229
  window.scrollTo({
4291
4230
  top: 0,
4292
4231
  behavior: "smooth"
@@ -4294,42 +4233,29 @@ const FormFieldsStepInner = props => {
4294
4233
  }
4295
4234
  }
4296
4235
  function handlePrevious() {
4297
- // Clear form submission state when changing steps
4298
4236
  dispatch(clearFormSubmissionState());
4299
4237
  if (isCreateMode) {
4300
4238
  history.push(values.routeFormOverviewStep);
4301
4239
  } else {
4302
- // In edit mode, go back to overview screen
4303
4240
  history.push(values.routeFormOverviewStep);
4304
4241
  }
4305
4242
  }
4306
4243
  function handleSaveStep() {
4307
- // Validate before saving in edit mode
4308
4244
  const validationResult = dispatch(validateAndUpdateStep("fields"));
4309
-
4310
- // If validation passes, save the entire form
4311
4245
  if (validationResult !== null && validationResult !== void 0 && validationResult.isValid) {
4312
4246
  dispatch(submitForm());
4313
4247
  }
4314
4248
  // If validation fails, scroll to top to show error summary
4315
4249
  else {
4316
- // Scroll to top of form to show validation errors
4317
4250
  window.scrollTo({
4318
4251
  top: 0,
4319
4252
  behavior: "smooth"
4320
4253
  });
4321
4254
  }
4322
4255
  }
4323
-
4324
- // Get all fields in unified list (base + custom)
4325
4256
  const allFields = fields || [];
4326
-
4327
- // Create sorted copy for rendering
4328
4257
  const sortedFields = allFields.slice().sort((a, b) => a.order - b.order);
4329
4258
 
4330
- // Filter description fields for summary demo
4331
- allFields.filter(field => field.type === "description");
4332
-
4333
4259
  // Check for definition management permission
4334
4260
  if (!PlussCore__namespace.Session.validateAccess(auth.site, values.permissionFeatureBuilderDefinition, auth)) {
4335
4261
  return /*#__PURE__*/React__default["default"].createElement("div", {
@@ -4586,11 +4512,9 @@ const useField = id => {
4586
4512
  dispatch(updateFieldById(id, updatedField));
4587
4513
  }
4588
4514
  function setUseAsSummary(value) {
4589
- // When setting a field as summary, use the new action to ensure only one field is selected
4590
4515
  if (value) {
4591
4516
  dispatch(setSummaryField(id));
4592
4517
  } else {
4593
- // When unsetting, use regular update to unset this specific field
4594
4518
  const updatedField = {
4595
4519
  values: _objectSpread$5(_objectSpread$5({}, values), {}, {
4596
4520
  useAsSummary: false
@@ -4623,11 +4547,8 @@ const FormLayoutStepInner = props => {
4623
4547
  const overviewIcon = reactRedux.useSelector(selectFormIcon);
4624
4548
  const definitionId = reactRedux.useSelector(selectDefinitionId);
4625
4549
  const layoutType = (layout === null || layout === void 0 ? void 0 : layout.type) || "round";
4626
- // Get wizard state
4627
4550
  const isCreateMode = reactRedux.useSelector(selectIsCreateMode);
4628
4551
  const isEditMode = reactRedux.useSelector(selectIsEditMode);
4629
-
4630
- // Use custom hook to handle definition loading
4631
4552
  const {
4632
4553
  definition,
4633
4554
  definitionIsLoading
@@ -4644,14 +4565,8 @@ const FormLayoutStepInner = props => {
4644
4565
  const isSubmitting = reactRedux.useSelector(selectFormIsSubmitting);
4645
4566
  const submitError = reactRedux.useSelector(selectFormSubmitError);
4646
4567
  const submitSuccess = reactRedux.useSelector(selectFormSubmitSuccess);
4647
-
4648
- // Toast state
4649
4568
  const [toasts, setToasts] = React__default["default"].useState([]);
4650
-
4651
- // Success popup state
4652
4569
  const [showSuccessPopup, setShowSuccessPopup] = React__default["default"].useState(false);
4653
-
4654
- // Toast management functions
4655
4570
  const addToast = (type, message) => {
4656
4571
  const id = Date.now();
4657
4572
  setToasts(prev => [...prev, {
@@ -4664,28 +4579,21 @@ const FormLayoutStepInner = props => {
4664
4579
  const removeToast = id => {
4665
4580
  setToasts(prev => prev.filter(toast => toast.id !== id));
4666
4581
  };
4667
-
4668
- // Handle success popup close
4669
4582
  const handleSuccessPopupClose = () => {
4670
4583
  setShowSuccessPopup(false);
4671
4584
  dispatch(clearFormSubmissionState());
4672
- history.push(values.routeFormOverviewStep);
4585
+ window.location.replace(values.routeFormOverviewStep);
4673
4586
  };
4674
-
4675
- // Handle successful submission with popup and redirect
4676
4587
  React.useEffect(() => {
4677
4588
  if (submitSuccess && !isSubmitting) {
4678
4589
  if (isEditMode) {
4679
4590
  addToast("success", "Changes saved");
4680
4591
  dispatch(clearFormSubmissionState());
4681
4592
  } else {
4682
- // In create mode, show success popup
4683
4593
  setShowSuccessPopup(true);
4684
4594
  }
4685
4595
  }
4686
4596
  }, [submitSuccess, isEditMode, isSubmitting, dispatch]);
4687
-
4688
- // Handle submit error
4689
4597
  React.useEffect(() => {
4690
4598
  if (submitError) {
4691
4599
  addToast("error", "It didn't work. Please try again.");
@@ -4694,10 +4602,7 @@ const FormLayoutStepInner = props => {
4694
4602
  }, 1000);
4695
4603
  }
4696
4604
  }, [submitError]);
4697
-
4698
- // Error boundary handlers
4699
4605
  const handleRefresh = () => {
4700
- // Refresh current step data
4701
4606
  dispatch(validateAndUpdateStep("layout"));
4702
4607
  };
4703
4608
  const layoutOptions = [{
@@ -4739,8 +4644,6 @@ const FormLayoutStepInner = props => {
4739
4644
  }
4740
4645
  }
4741
4646
  }, [definition, definitionIsLoading, isFormInitial, isEditMode, dispatch]);
4742
-
4743
- // Add effect to handle definition loading and validation in edit mode
4744
4647
  React.useEffect(() => {
4745
4648
  // In edit mode, trigger validation when definition is available
4746
4649
  // Note: The new effect above handles data population, this handles re-validation
@@ -4755,23 +4658,20 @@ const FormLayoutStepInner = props => {
4755
4658
  dispatch(setLayoutType(layoutType));
4756
4659
  }
4757
4660
  function handlePrevious() {
4758
- // Clear form submission state when changing steps
4759
4661
  dispatch(clearFormSubmissionState());
4760
4662
  if (isCreateMode) {
4761
4663
  history.push(values.routeFormFieldsStep);
4762
4664
  } else {
4763
- // In edit mode, go back to fields screen
4764
4665
  history.push(values.routeFormFieldsStep);
4765
4666
  }
4766
4667
  }
4767
4668
  function handleNext() {
4768
- // Validate before proceeding
4669
+ if (isSubmitting) {
4670
+ return;
4671
+ }
4769
4672
  const validationResult = dispatch(validateAndUpdateStep("layout"));
4770
-
4771
- // If validation passes, proceed with submission/navigation
4772
4673
  if (validationResult !== null && validationResult !== void 0 && validationResult.isValid) {
4773
4674
  if (isCreateMode) {
4774
- // In create mode, submit form - success popup will be shown by useEffect
4775
4675
  dispatch(submitForm());
4776
4676
  } else {
4777
4677
  // In edit mode, just save changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-feature-builder-web",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Contains the feature builder extension for the pluss communities ",
5
5
  "main": "dist/index.cjs.js",
6
6
  "scripts": {
@@ -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