@plusscommunities/pluss-feature-builder-web-b 1.0.2-beta.8 → 1.0.4-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.
@@ -1,12 +1,12 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { SidebarLayout } from "../components/SidebarLayout.jsx";
3
3
  import {
4
- GenericInput,
5
- Text,
6
- Button,
7
- LoadingState,
8
- ErrorBoundary,
9
- FeatureBuilderWelcomePopup,
4
+ GenericInput,
5
+ Text,
6
+ Button,
7
+ LoadingState,
8
+ ErrorBoundary,
9
+ FeatureBuilderWelcomePopup,
10
10
  } from "../components";
11
11
  import ToastContainer from "../components/ToastContainer.jsx";
12
12
  import { values } from "../values.config.js";
@@ -17,26 +17,26 @@ import { withRouter } from "react-router-dom";
17
17
 
18
18
  import { useDispatch, useSelector } from "react-redux";
19
19
  import {
20
- setDisplayName,
21
- clearFormSubmissionState,
22
- setIcon,
23
- setInitialValues,
24
- setTitle,
25
- submitForm,
20
+ setDisplayName,
21
+ clearFormSubmissionState,
22
+ setIcon,
23
+ setInitialValues,
24
+ setTitle,
25
+ submitForm,
26
26
  } from "../actions/formActions";
27
27
  import {
28
- selectFormIcon,
29
- selectFormDisplayName,
30
- selectFormTitle,
31
- selectDefinition,
32
- selectDefinitionIsLoading,
33
- selectIsCreateMode,
34
- selectIsEditMode,
35
- selectIsStepValid,
36
- selectStepErrors,
37
- selectFormIsSubmitting,
38
- selectFormSubmitError,
39
- selectFormSubmitSuccess,
28
+ selectFormIcon,
29
+ selectFormDisplayName,
30
+ selectFormTitle,
31
+ selectDefinition,
32
+ selectDefinitionIsLoading,
33
+ selectIsCreateMode,
34
+ selectIsEditMode,
35
+ selectIsStepValid,
36
+ selectStepErrors,
37
+ selectFormIsSubmitting,
38
+ selectFormSubmitError,
39
+ selectFormSubmitSuccess,
40
40
  } from "../selectors/featureBuilderSelectors";
41
41
  import { validateAndUpdateStep } from "../actions/wizardActions";
42
42
  import IconSelector from "../components/IconSelector.jsx";
@@ -59,331 +59,331 @@ import ExampleDisplay from "../components/ExampleDisplay.jsx";
59
59
  * <FormOverviewStep history={historyObject} location={locationObject} />
60
60
  */
61
61
  const FormOverviewStepInner = (props) => {
62
- const { history, location } = props;
63
- const dispatch = useDispatch();
64
- const auth = useSelector((state) => state.auth);
65
-
66
- // Get wizard state
67
- const isCreateMode = useSelector(selectIsCreateMode);
68
- const isEditMode = useSelector(selectIsEditMode);
69
- const definition = useSelector(selectDefinition);
70
- const definitionIsLoading = useSelector(selectDefinitionIsLoading);
71
-
72
- // Get form state
73
- const selectedIcon = useSelector(selectFormIcon);
74
- const title = useSelector(selectFormTitle);
75
- const displayName = useSelector(selectFormDisplayName);
76
-
77
- // Get validation state
78
- const isStepValid = useSelector(selectIsStepValid("overview"));
79
- const stepErrors = useSelector(selectStepErrors("overview"));
80
-
81
- // Get submission state
82
- const isSubmitting = useSelector(selectFormIsSubmitting);
83
- const submitError = useSelector(selectFormSubmitError);
84
- const submitSuccess = useSelector(selectFormSubmitSuccess);
85
-
86
- // State for welcome popup (only show in create mode)
87
- const [showWelcomePopup, setShowWelcomePopup] = useState(false);
88
-
89
- // Toast state
90
- const [toasts, setToasts] = React.useState([]);
91
-
92
- // Toast management functions
93
- const addToast = (type, message) => {
94
- const id = Date.now();
95
- setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
96
- };
97
-
98
- const removeToast = (id) => {
99
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
100
- };
101
-
102
- // Handle redirect after successful save in edit mode
103
- useEffect(() => {
104
- if (submitSuccess && isEditMode && !isSubmitting) {
105
- // Show toast notification
106
- addToast("success", "Changes saved");
107
-
108
- // Clear the success state after a short delay and redirect to overview
109
- const timer = setTimeout(() => {
110
- dispatch(clearFormSubmissionState());
111
- history.push(values.routeFormOverviewStep);
112
- }, 1500); // Show success message for 1.5 seconds before redirect
113
-
114
- return () => clearTimeout(timer);
115
- }
116
- }, [submitSuccess, isEditMode, isSubmitting, dispatch, history]);
117
-
118
- // Show toast notification for general success cases
119
- React.useEffect(() => {
120
- if (submitSuccess) {
121
- if (!isEditMode) {
122
- addToast("success", "Step saved");
123
- }
124
-
125
- // Clear submission state after showing toast (only for non-edit cases)
126
- if (!isEditMode) {
127
- dispatch(clearFormSubmissionState());
128
- }
129
- }
130
- }, [submitSuccess, isEditMode, dispatch]);
131
-
132
- // Handle submit error
133
- useEffect(() => {
134
- if (submitError) {
135
- addToast("error", "It didn't work. Please try again.");
136
- setTimeout(() => {
137
- window.location.reload();
138
- }, 1000);
139
- }
140
- }, [submitError]);
141
-
142
- // Error boundary handlers
143
- const handleRefresh = () => {
144
- // Refresh current step data
145
- dispatch(validateAndUpdateStep("overview"));
146
- };
147
-
148
- const handleBack = () => {
149
- // Always go to listing screen when going back
150
- history.push(values.routeListingScreen);
151
- };
152
-
153
- useEffect(() => {
154
- // Check if we should populate the form from definition
155
- // Primary condition: edit mode with definition available
156
- // Fallback condition: definition exists regardless of mode (handles timing issues)
157
- const shouldPopulateForm =
158
- definition &&
159
- !definitionIsLoading &&
160
- (isEditMode || (definition && !isCreateMode)); // Fallback: if definition exists and we're not in create mode
161
-
162
- if (shouldPopulateForm) {
163
- dispatch(setInitialValues(definition));
164
-
165
- // In edit mode, trigger validation after setting initial values to enable save button
166
- if (isEditMode) {
167
- setTimeout(() => {
168
- dispatch(validateAndUpdateStep("overview"));
169
- }, 100); // Small delay to ensure state is updated
170
- }
171
- }
172
- }, [definition, definitionIsLoading, isEditMode, isCreateMode, dispatch]);
173
-
174
- // Show welcome popup on component mount for create mode
175
- useEffect(() => {
176
- if (isCreateMode && !definitionIsLoading && !definition) {
177
- setShowWelcomePopup(true);
178
- }
179
- }, [isCreateMode, definitionIsLoading, definition]);
180
-
181
- // Handle welcome popup close
182
- const handleWelcomePopupClose = () => {
183
- setShowWelcomePopup(false);
184
- };
185
-
186
- function handleNext() {
187
- // Validate before proceeding - validation will update Redux state
188
- const validationResult = dispatch(validateAndUpdateStep("overview"));
189
-
190
- // If validation passes, navigate to next step
191
- if (validationResult && validationResult.isValid) {
192
- // Clear form submission state when changing steps
193
- dispatch(clearFormSubmissionState());
194
-
195
- if (isCreateMode) {
196
- history.push(values.routeFormFieldsStep);
197
- } else {
198
- // In edit mode, navigate directly
199
- history.push(values.routeFormFieldsStep);
200
- }
201
- }
202
- // If validation fails, the validation errors will be displayed and user can fix them
203
- }
204
-
205
- function handlePrevious() {
206
- // Clear form submission state when changing steps
207
- dispatch(clearFormSubmissionState());
208
-
209
- // Always go to listing screen after completion
210
- history.push(values.routeListingScreen);
211
- }
212
-
213
- function handleSaveStep() {
214
- // Validate before saving in edit mode
215
- const validationResult = dispatch(validateAndUpdateStep("overview"));
216
-
217
- // If validation passes, save the entire form
218
- if (validationResult && validationResult.isValid) {
219
- dispatch(submitForm());
220
- }
221
- }
222
-
223
- function handleTitleChange(ev) {
224
- dispatch(setTitle(ev.target.value));
225
- }
226
-
227
- function handleSelectIcon(icon) {
228
- dispatch(setIcon(icon));
229
- }
230
-
231
- function handleDisplayNameChange(ev) {
232
- dispatch(setDisplayName(ev.target.value));
233
- }
234
-
235
- // Check for definition management permission
236
- if (
237
- !PlussCore.Session.validateAccess(
238
- auth.site,
239
- values.permissionFeatureBuilderDefinition,
240
- auth,
241
- )
242
- ) {
243
- return (
244
- <div className="hub-wrapperContainer">
245
- <div className="hub-contentWrapper">
246
- <div className={styles.welcomeContainer}>
247
- <div className={styles.welcomeHeader}>
248
- <Text type="h1" className={styles.welcomeTitle}>
249
- Access Restricted
250
- </Text>
251
- <Text type="body" className={styles.welcomeSubtitle}>
252
- You don't have permission to manage feature definitions. Please
253
- contact your administrator if you need access.
254
- </Text>
255
- </div>
256
- </div>
257
- </div>
258
- </div>
259
- );
260
- }
261
-
262
- return (
263
- <ErrorBoundary
264
- title="Unable to load overview"
265
- message="If you continue to experience issues with the overview step, please try refreshing the page or contact support."
266
- onRetry={handleRefresh}
267
- >
268
- <SidebarLayout>
269
- <Text
270
- type="formTitleLarge"
271
- className={`${isEditMode ? styles.editMode : styles.createMode}`}
272
- >
273
- {definitionIsLoading
274
- ? "Loading..."
275
- : isEditMode
276
- ? `Edit Feature: ${capitalizeTextWithFallback(title, "Unnamed")}`
277
- : "Create New Feature"}
278
- </Text>
279
- {definitionIsLoading ? (
280
- <div className={styles.overviewLoadingContainer}>
281
- <LoadingState message="Loading feature..." size={48} />
282
- </div>
283
- ) : (
284
- <>
285
- <GenericInput
286
- label="Feature Name"
287
- help="Name your feature as it should appear in the Community Manager and the app"
288
- isRequired
289
- type="text"
290
- onChange={handleTitleChange}
291
- value={title}
292
- className="mt-8"
293
- showError={() => {
294
- return !isStepValid && stepErrors && stepErrors.title;
295
- }}
296
- errorMessage="Title is required"
297
- isValid={() => {
298
- return title && title.trim().length > 0;
299
- }}
300
- />
301
-
302
- <IconSelector
303
- selectedIcon={selectedIcon}
304
- onIconSelect={handleSelectIcon}
305
- className=""
306
- stepErrors={stepErrors}
307
- showError={() => {
308
- return !isStepValid && stepErrors && stepErrors.icon;
309
- }}
310
- />
311
-
312
- <GenericInput
313
- label="Display Name"
314
- help="Display name for this feature (used in buttons, notifications, and activity feeds)"
315
- isRequired
316
- type="text"
317
- onChange={handleDisplayNameChange}
318
- value={displayName}
319
- className="mt-8"
320
- showError={() => {
321
- return !isStepValid && stepErrors && stepErrors.displayName;
322
- }}
323
- errorMessage="Display name is required"
324
- isValid={() => {
325
- return displayName && displayName.trim().length > 0;
326
- }}
327
- />
328
-
329
- {/* Examples Display */}
330
- <ExampleDisplay displayName={displayName} />
331
- </>
332
- )}
333
-
334
- {/* Top-level validation message - positioned above action buttons */}
335
- {!isStepValid && Object.keys(stepErrors).length > 0 && (
336
- <div
337
- className={styles.validationErrorMessage}
338
- role="alert"
339
- aria-live="polite"
340
- >
341
- {Object.keys(stepErrors).length}{" "}
342
- {Object.keys(stepErrors).length === 1
343
- ? "field needs"
344
- : "fields need"}{" "}
345
- attention before proceeding
346
- </div>
347
- )}
348
-
349
- {/* Mode-aware navigation buttons */}
350
- <div className={styles.navigation}>
351
- {isCreateMode ? (
352
- <Button
353
- buttonType="primary"
354
- isActive
355
- onClick={handleNext}
356
- leftIcon="arrow-right"
357
- >
358
- Next step: Configure Fields
359
- </Button>
360
- ) : (
361
- <Button
362
- buttonType="primary"
363
- isActive
364
- onClick={handleSaveStep}
365
- disabled={!isStepValid || isSubmitting}
366
- leftIcon={isSubmitting ? "sync" : "save"}
367
- loading={isSubmitting}
368
- >
369
- {isSubmitting ? "Saving..." : "Save"}
370
- </Button>
371
- )}
372
- </div>
373
-
374
- {/* Submission feedback */}
375
- </SidebarLayout>
376
-
377
- {/* Welcome popup for create mode */}
378
- <FeatureBuilderWelcomePopup
379
- isOpen={showWelcomePopup}
380
- onClose={handleWelcomePopupClose}
381
- />
382
-
383
- {/* Toast Container for Notifications */}
384
- <ToastContainer toasts={toasts} onDismiss={removeToast} />
385
- </ErrorBoundary>
386
- );
62
+ const { history, location } = props;
63
+ const dispatch = useDispatch();
64
+ const auth = useSelector((state) => state.auth);
65
+
66
+ // Get wizard state
67
+ const isCreateMode = useSelector(selectIsCreateMode);
68
+ const isEditMode = useSelector(selectIsEditMode);
69
+ const definition = useSelector(selectDefinition);
70
+ const definitionIsLoading = useSelector(selectDefinitionIsLoading);
71
+
72
+ // Get form state
73
+ const selectedIcon = useSelector(selectFormIcon);
74
+ const title = useSelector(selectFormTitle);
75
+ const displayName = useSelector(selectFormDisplayName);
76
+
77
+ // Get validation state
78
+ const isStepValid = useSelector(selectIsStepValid("overview"));
79
+ const stepErrors = useSelector(selectStepErrors("overview"));
80
+
81
+ // Get submission state
82
+ const isSubmitting = useSelector(selectFormIsSubmitting);
83
+ const submitError = useSelector(selectFormSubmitError);
84
+ const submitSuccess = useSelector(selectFormSubmitSuccess);
85
+
86
+ // State for welcome popup (only show in create mode)
87
+ const [showWelcomePopup, setShowWelcomePopup] = useState(false);
88
+
89
+ // Toast state
90
+ const [toasts, setToasts] = React.useState([]);
91
+
92
+ // Toast management functions
93
+ const addToast = (type, message) => {
94
+ const id = Date.now();
95
+ setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
96
+ };
97
+
98
+ const removeToast = (id) => {
99
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
100
+ };
101
+
102
+ // Handle redirect after successful save in edit mode
103
+ useEffect(() => {
104
+ if (submitSuccess && isEditMode && !isSubmitting) {
105
+ // Show toast notification
106
+ addToast("success", "Changes saved");
107
+
108
+ // Clear the success state after a short delay and redirect to overview
109
+ const timer = setTimeout(() => {
110
+ dispatch(clearFormSubmissionState());
111
+ history.push(values.routeFormOverviewStep);
112
+ }, 1500); // Show success message for 1.5 seconds before redirect
113
+
114
+ return () => clearTimeout(timer);
115
+ }
116
+ }, [submitSuccess, isEditMode, isSubmitting, dispatch, history]);
117
+
118
+ // Show toast notification for general success cases
119
+ React.useEffect(() => {
120
+ if (submitSuccess) {
121
+ if (!isEditMode) {
122
+ addToast("success", "Step saved");
123
+ }
124
+
125
+ // Clear submission state after showing toast (only for non-edit cases)
126
+ if (!isEditMode) {
127
+ dispatch(clearFormSubmissionState());
128
+ }
129
+ }
130
+ }, [submitSuccess, isEditMode, dispatch]);
131
+
132
+ // Handle submit error
133
+ useEffect(() => {
134
+ if (submitError) {
135
+ addToast("error", "It didn't work. Please try again.");
136
+ setTimeout(() => {
137
+ window.location.reload();
138
+ }, 1000);
139
+ }
140
+ }, [submitError]);
141
+
142
+ // Error boundary handlers
143
+ const handleRefresh = () => {
144
+ // Refresh current step data
145
+ dispatch(validateAndUpdateStep("overview"));
146
+ };
147
+
148
+ const handleBack = () => {
149
+ // Always go to listing screen when going back
150
+ history.push(values.routeListingScreen);
151
+ };
152
+
153
+ useEffect(() => {
154
+ // Check if we should populate the form from definition
155
+ // Primary condition: edit mode with definition available
156
+ // Fallback condition: definition exists regardless of mode (handles timing issues)
157
+ const shouldPopulateForm =
158
+ definition &&
159
+ !definitionIsLoading &&
160
+ (isEditMode || (definition && !isCreateMode)); // Fallback: if definition exists and we're not in create mode
161
+
162
+ if (shouldPopulateForm) {
163
+ dispatch(setInitialValues(definition));
164
+
165
+ // In edit mode, trigger validation after setting initial values to enable save button
166
+ if (isEditMode) {
167
+ setTimeout(() => {
168
+ dispatch(validateAndUpdateStep("overview"));
169
+ }, 100); // Small delay to ensure state is updated
170
+ }
171
+ }
172
+ }, [definition, definitionIsLoading, isEditMode, isCreateMode, dispatch]);
173
+
174
+ // Show welcome popup on component mount for create mode
175
+ useEffect(() => {
176
+ if (isCreateMode && !definitionIsLoading && !definition) {
177
+ setShowWelcomePopup(true);
178
+ }
179
+ }, [isCreateMode, definitionIsLoading, definition]);
180
+
181
+ // Handle welcome popup close
182
+ const handleWelcomePopupClose = () => {
183
+ setShowWelcomePopup(false);
184
+ };
185
+
186
+ function handleNext() {
187
+ // Validate before proceeding - validation will update Redux state
188
+ const validationResult = dispatch(validateAndUpdateStep("overview"));
189
+
190
+ // If validation passes, navigate to next step
191
+ if (validationResult && validationResult.isValid) {
192
+ // Clear form submission state when changing steps
193
+ dispatch(clearFormSubmissionState());
194
+
195
+ if (isCreateMode) {
196
+ history.push(values.routeFormFieldsStep);
197
+ } else {
198
+ // In edit mode, navigate directly
199
+ history.push(values.routeFormFieldsStep);
200
+ }
201
+ }
202
+ // If validation fails, the validation errors will be displayed and user can fix them
203
+ }
204
+
205
+ function handlePrevious() {
206
+ // Clear form submission state when changing steps
207
+ dispatch(clearFormSubmissionState());
208
+
209
+ // Always go to listing screen after completion
210
+ history.push(values.routeListingScreen);
211
+ }
212
+
213
+ function handleSaveStep() {
214
+ // Validate before saving in edit mode
215
+ const validationResult = dispatch(validateAndUpdateStep("overview"));
216
+
217
+ // If validation passes, save the entire form
218
+ if (validationResult && validationResult.isValid) {
219
+ dispatch(submitForm());
220
+ }
221
+ }
222
+
223
+ function handleTitleChange(ev) {
224
+ dispatch(setTitle(ev.target.value));
225
+ }
226
+
227
+ function handleSelectIcon(icon) {
228
+ dispatch(setIcon(icon));
229
+ }
230
+
231
+ function handleDisplayNameChange(ev) {
232
+ dispatch(setDisplayName(ev.target.value));
233
+ }
234
+
235
+ // Check for definition management permission
236
+ if (
237
+ !PlussCore.Session.validateAccess(
238
+ auth.site,
239
+ values.permissionFeatureBuilderDefinition,
240
+ auth,
241
+ )
242
+ ) {
243
+ return (
244
+ <div className="hub-wrapperContainer">
245
+ <div className="hub-contentWrapper">
246
+ <div className={styles.welcomeContainer}>
247
+ <div className={styles.welcomeHeader}>
248
+ <Text type="h1" className={styles.welcomeTitle}>
249
+ Access Restricted
250
+ </Text>
251
+ <Text type="body" className={styles.welcomeSubtitle}>
252
+ You don't have permission to manage feature definitions. Please
253
+ contact your administrator if you need access.
254
+ </Text>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ );
260
+ }
261
+
262
+ return (
263
+ <ErrorBoundary
264
+ title="Unable to load overview"
265
+ message="If you continue to experience issues with the overview step, please try refreshing the page or contact support."
266
+ onRetry={handleRefresh}
267
+ >
268
+ <SidebarLayout>
269
+ <Text
270
+ type="formTitleLarge"
271
+ className={`${isEditMode ? styles.editMode : styles.createMode}`}
272
+ >
273
+ {definitionIsLoading
274
+ ? "Loading..."
275
+ : isEditMode
276
+ ? `Edit Feature: ${capitalizeTextWithFallback(title, "Unnamed")}`
277
+ : "Create New Feature"}
278
+ </Text>
279
+ {definitionIsLoading ? (
280
+ <div className={styles.overviewLoadingContainer}>
281
+ <LoadingState message="Loading feature..." size={48} />
282
+ </div>
283
+ ) : (
284
+ <>
285
+ <GenericInput
286
+ label="Feature Name"
287
+ help="Name your feature as it should appear in the Community Manager and the app"
288
+ isRequired
289
+ type="text"
290
+ onChange={handleTitleChange}
291
+ value={title}
292
+ className="mt-8"
293
+ showError={() => {
294
+ return !isStepValid && stepErrors && stepErrors.title;
295
+ }}
296
+ errorMessage="Title is required"
297
+ isValid={() => {
298
+ return title && title.trim().length > 0;
299
+ }}
300
+ />
301
+
302
+ <IconSelector
303
+ selectedIcon={selectedIcon}
304
+ onIconSelect={handleSelectIcon}
305
+ className=""
306
+ stepErrors={stepErrors}
307
+ showError={() => {
308
+ return !isStepValid && stepErrors && stepErrors.icon;
309
+ }}
310
+ />
311
+
312
+ <GenericInput
313
+ label="Display Name"
314
+ help="Display name for this feature (used in buttons, notifications, and activity feeds)"
315
+ isRequired
316
+ type="text"
317
+ onChange={handleDisplayNameChange}
318
+ value={displayName}
319
+ className="mt-8"
320
+ showError={() => {
321
+ return !isStepValid && stepErrors && stepErrors.displayName;
322
+ }}
323
+ errorMessage="Display name is required"
324
+ isValid={() => {
325
+ return displayName && displayName.trim().length > 0;
326
+ }}
327
+ />
328
+
329
+ {/* Examples Display */}
330
+ <ExampleDisplay displayName={displayName} />
331
+ </>
332
+ )}
333
+
334
+ {/* Top-level validation message - positioned above action buttons */}
335
+ {!isStepValid && Object.keys(stepErrors).length > 0 && (
336
+ <div
337
+ className={styles.validationErrorMessage}
338
+ role="alert"
339
+ aria-live="polite"
340
+ >
341
+ {Object.keys(stepErrors).length}{" "}
342
+ {Object.keys(stepErrors).length === 1
343
+ ? "field needs"
344
+ : "fields need"}{" "}
345
+ attention before proceeding
346
+ </div>
347
+ )}
348
+
349
+ {/* Mode-aware navigation buttons */}
350
+ <div className={styles.navigation}>
351
+ {isCreateMode ? (
352
+ <Button
353
+ buttonType="primary"
354
+ isActive
355
+ onClick={handleNext}
356
+ leftIcon="arrow-right"
357
+ >
358
+ Next step: Configure Fields
359
+ </Button>
360
+ ) : (
361
+ <Button
362
+ buttonType="primary"
363
+ isActive
364
+ onClick={handleSaveStep}
365
+ disabled={!isStepValid || isSubmitting}
366
+ leftIcon={isSubmitting ? "sync" : "save"}
367
+ loading={isSubmitting}
368
+ >
369
+ {isSubmitting ? "Saving..." : "Save"}
370
+ </Button>
371
+ )}
372
+ </div>
373
+
374
+ {/* Submission feedback */}
375
+ </SidebarLayout>
376
+
377
+ {/* Welcome popup for create mode */}
378
+ <FeatureBuilderWelcomePopup
379
+ isOpen={showWelcomePopup}
380
+ onClose={handleWelcomePopupClose}
381
+ />
382
+
383
+ {/* Toast Container for Notifications */}
384
+ <ToastContainer toasts={toasts} onDismiss={removeToast} />
385
+ </ErrorBoundary>
386
+ );
387
387
  };
388
388
 
389
389
  export const FormOverviewStep = withRouter(FormOverviewStepInner);