@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 +42 -142
- package/package.json +1 -1
- package/src/components/FeatureBuilderSuccessPopup.jsx +9 -15
- package/src/components/FeatureBuilderSuccessPopup.module.css +1 -3
- package/src/components/Fields.jsx +31 -68
- package/src/screens/FormFieldsStep.jsx +4 -67
- package/src/screens/FormLayoutStep.jsx +4 -42
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 = ".
|
|
581
|
-
var modules_b5a7fccb = {"successContent":"
|
|
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(
|
|
608
|
-
subtitle: /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
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
|
|
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
|
-
}, "
|
|
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
|
|
3635
|
-
,
|
|
3631
|
+
isRequired: true,
|
|
3636
3632
|
setLabel: setLabel,
|
|
3637
|
-
setPlaceholder:
|
|
3633
|
+
setPlaceholder: setPlaceholder,
|
|
3638
3634
|
setHelpText: setHelpText,
|
|
3639
|
-
toggleIsRequired: () => {}
|
|
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
|
-
|
|
3661
|
-
|
|
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:
|
|
3668
|
-
,
|
|
3662
|
+
isRequired: isRequired,
|
|
3669
3663
|
setLabel: setLabel,
|
|
3670
|
-
setPlaceholder:
|
|
3664
|
+
setPlaceholder: setPlaceholder,
|
|
3671
3665
|
setHelpText: setHelpText,
|
|
3672
|
-
toggleIsRequired:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3771
|
-
,
|
|
3761
|
+
isRequired: true,
|
|
3772
3762
|
setLabel: setLabel,
|
|
3773
3763
|
setPlaceholder: () => {},
|
|
3774
3764
|
setHelpText: setHelpText,
|
|
3775
|
-
toggleIsRequired: () => {}
|
|
3776
|
-
,
|
|
3765
|
+
toggleIsRequired: () => {},
|
|
3777
3766
|
stepErrors: stepErrors,
|
|
3778
3767
|
showWarnings: showWarnings,
|
|
3779
|
-
id: 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
|
|
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
|
|
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;
|
|
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);
|
|
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);
|
|
4188
|
+
setShowFieldSelector(false);
|
|
4238
4189
|
}
|
|
4239
4190
|
function handleOpenFieldSelector() {
|
|
4240
|
-
setReplacingFieldIndex(null);
|
|
4191
|
+
setReplacingFieldIndex(null);
|
|
4241
4192
|
setShowFieldSelector(true);
|
|
4242
4193
|
}
|
|
4243
4194
|
function handleCloseFieldSelector() {
|
|
4244
|
-
setReplacingFieldIndex(null);
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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={`${
|
|
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
|
|
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
|
-
|
|
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
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
3
|
-
import {
|
|
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,
|
|
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}
|
|
182
|
+
isRequired={true}
|
|
218
183
|
setLabel={setLabel}
|
|
219
|
-
setPlaceholder={
|
|
184
|
+
setPlaceholder={setPlaceholder}
|
|
220
185
|
setHelpText={setHelpText}
|
|
221
|
-
toggleIsRequired={() => {}}
|
|
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,
|
|
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={
|
|
212
|
+
isRequired={isRequired}
|
|
248
213
|
setLabel={setLabel}
|
|
249
|
-
setPlaceholder={
|
|
214
|
+
setPlaceholder={setPlaceholder}
|
|
250
215
|
setHelpText={setHelpText}
|
|
251
|
-
toggleIsRequired={
|
|
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,
|
|
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,
|
|
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}
|
|
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}
|
|
303
|
+
isRequired={true}
|
|
346
304
|
setLabel={setLabel}
|
|
347
305
|
setPlaceholder={() => {}}
|
|
348
306
|
setHelpText={setHelpText}
|
|
349
|
-
toggleIsRequired={() => {}}
|
|
307
|
+
toggleIsRequired={() => {}}
|
|
350
308
|
stepErrors={stepErrors}
|
|
351
309
|
showWarnings={showWarnings}
|
|
352
|
-
id={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,
|
|
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}
|
|
392
|
+
id={id}
|
|
435
393
|
fieldType="cta"
|
|
436
394
|
/>
|
|
437
395
|
);
|
|
438
396
|
};
|
|
439
397
|
|
|
440
398
|
const FileField = (props) => {
|
|
441
|
-
const {
|
|
442
|
-
|
|
443
|
-
|
|
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);
|
|
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);
|
|
207
|
+
setShowFieldSelector(false);
|
|
241
208
|
}
|
|
242
209
|
|
|
243
210
|
function handleOpenFieldSelector() {
|
|
244
|
-
setReplacingFieldIndex(null);
|
|
211
|
+
setReplacingFieldIndex(null);
|
|
245
212
|
setShowFieldSelector(true);
|
|
246
213
|
}
|
|
247
214
|
|
|
248
215
|
function handleCloseFieldSelector() {
|
|
249
|
-
setReplacingFieldIndex(null);
|
|
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
|
-
|
|
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
|
-
|
|
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
|