@plusscommunities/pluss-feature-builder-web-c 1.0.4 → 1.0.6-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +66 -166
- package/package.json +11 -11
- 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/src/values.config.default.js +0 -49
|
@@ -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
|