@plusscommunities/pluss-feature-builder-web-d 1.0.7 → 1.0.9-beta.3

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.
Files changed (119) hide show
  1. package/dist/{index.cjs.js → index.js} +3803 -3504
  2. package/dist/index.js.map +1 -0
  3. package/package.json +20 -27
  4. package/.babelrc +0 -4
  5. package/rollup.config.js +0 -69
  6. package/src/actions/featureBuilderStringsActions.js +0 -88
  7. package/src/actions/featureDefinitionsIndex.js +0 -258
  8. package/src/actions/formActions.js +0 -301
  9. package/src/actions/index.js +0 -12
  10. package/src/actions/listingActions.js +0 -352
  11. package/src/actions/wizardActions.js +0 -228
  12. package/src/components/ActivityCardExample.jsx +0 -86
  13. package/src/components/ActivityCardExample.module.css +0 -130
  14. package/src/components/BackgroundLoader.jsx +0 -33
  15. package/src/components/BackgroundLoader.module.css +0 -46
  16. package/src/components/BaseFieldConfig.jsx +0 -305
  17. package/src/components/BaseFieldConfig.module.css +0 -42
  18. package/src/components/CenteredContainer.jsx +0 -29
  19. package/src/components/CenteredContainer.module.css +0 -171
  20. package/src/components/DeleteConfirmationPopup.jsx +0 -95
  21. package/src/components/DeleteConfirmationPopup.module.css +0 -12
  22. package/src/components/ErrorBoundary.jsx +0 -134
  23. package/src/components/ErrorBoundary.module.css +0 -77
  24. package/src/components/ErrorMessage.jsx +0 -85
  25. package/src/components/ErrorMessage.module.css +0 -116
  26. package/src/components/ExampleDisplay.jsx +0 -26
  27. package/src/components/ExampleDisplay.module.css +0 -3
  28. package/src/components/FeatureBuilderSidebar.jsx +0 -84
  29. package/src/components/FeatureBuilderSuccessPopup.jsx +0 -49
  30. package/src/components/FeatureBuilderSuccessPopup.module.css +0 -41
  31. package/src/components/FeatureBuilderWelcomePopup.jsx +0 -51
  32. package/src/components/FeatureBuilderWelcomePopup.module.css +0 -21
  33. package/src/components/FeatureListingCard.jsx +0 -104
  34. package/src/components/FeatureListingCard.module.css +0 -62
  35. package/src/components/Fields.jsx +0 -423
  36. package/src/components/Fields.module.css +0 -159
  37. package/src/components/IconLoader.jsx +0 -153
  38. package/src/components/IconLoader.module.css +0 -92
  39. package/src/components/IconSelector.jsx +0 -111
  40. package/src/components/IconSelector.module.css +0 -197
  41. package/src/components/ListingEditor.jsx +0 -405
  42. package/src/components/ListingEditor.module.css +0 -14
  43. package/src/components/ListingSuccessPopup.jsx +0 -52
  44. package/src/components/LoadingScreen.jsx +0 -54
  45. package/src/components/LoadingScreen.module.css +0 -103
  46. package/src/components/LoadingState.jsx +0 -40
  47. package/src/components/LoadingState.module.css +0 -18
  48. package/src/components/PreviewFull.js +0 -24
  49. package/src/components/PreviewFull.module.css +0 -11
  50. package/src/components/PreviewGrid.js +0 -14
  51. package/src/components/PreviewWidget.js +0 -27
  52. package/src/components/PreviewWidget.module.css +0 -15
  53. package/src/components/SidebarLayout.jsx +0 -252
  54. package/src/components/SidebarLayout.module.css +0 -71
  55. package/src/components/SkeletonLoader.jsx +0 -128
  56. package/src/components/SkeletonLoader.module.css +0 -295
  57. package/src/components/SortButtonGroup.jsx +0 -34
  58. package/src/components/SortButtonGroup.module.css +0 -51
  59. package/src/components/ToastContainer.jsx +0 -98
  60. package/src/components/ToastContainer.module.css +0 -156
  61. package/src/components/ToggleSwitch.js +0 -40
  62. package/src/components/ToggleSwitch.module.css +0 -48
  63. package/src/components/TwoColumnInput.jsx +0 -29
  64. package/src/components/TwoColumnInput.module.css +0 -32
  65. package/src/components/ViewFull.js +0 -139
  66. package/src/components/ViewFull.module.css +0 -71
  67. package/src/components/ViewWidget.js +0 -62
  68. package/src/components/ViewWidget.module.css +0 -28
  69. package/src/components/iconCategories.js +0 -135
  70. package/src/components/iconImports.js +0 -409
  71. package/src/components/index.js +0 -59
  72. package/src/components/listing/FileListItem.jsx +0 -86
  73. package/src/components/listing/GalleryDisplay.jsx +0 -330
  74. package/src/components/listing/GalleryDisplay.module.css +0 -309
  75. package/src/components/listing/ListingCTAInput.jsx +0 -82
  76. package/src/components/listing/ListingDescriptionInput.jsx +0 -73
  77. package/src/components/listing/ListingField.jsx +0 -101
  78. package/src/components/listing/ListingField.module.css +0 -106
  79. package/src/components/listing/ListingFileInput.jsx +0 -273
  80. package/src/components/listing/ListingFileInput.module.css +0 -189
  81. package/src/components/listing/ListingForm.jsx +0 -90
  82. package/src/components/listing/ListingForm.module.css +0 -38
  83. package/src/components/listing/ListingGalleryInput.jsx +0 -239
  84. package/src/components/listing/ListingGalleryInput.module.css +0 -132
  85. package/src/components/listing/ListingImageInput.jsx +0 -153
  86. package/src/components/listing/ListingTextInput.jsx +0 -72
  87. package/src/feature.config.js +0 -130
  88. package/src/helper/index.js +0 -135
  89. package/src/hooks/useFeatureDefinitionLoader.js +0 -66
  90. package/src/images/full.png +0 -0
  91. package/src/images/fullNoTitle.png +0 -0
  92. package/src/images/previewWidget.png +0 -0
  93. package/src/images/widget.png +0 -0
  94. package/src/index.js +0 -38
  95. package/src/pages/CreateListingPage.jsx +0 -49
  96. package/src/pages/EditListingPage.jsx +0 -58
  97. package/src/reducers/featureBuilderReducer.js +0 -739
  98. package/src/screens/CreateListing.module.css +0 -45
  99. package/src/screens/Form.module.css +0 -744
  100. package/src/screens/FormFieldsStep.jsx +0 -626
  101. package/src/screens/FormLayoutStep.jsx +0 -405
  102. package/src/screens/FormOverviewStep.jsx +0 -389
  103. package/src/screens/ListingScreen.jsx +0 -477
  104. package/src/screens/ListingScreen.module.css +0 -333
  105. package/src/selectors/featureBuilderSelectors.js +0 -533
  106. package/src/types/index.js +0 -91
  107. package/src/utils/textUtils.js +0 -89
  108. package/src/validators/galleryValidators.js +0 -345
  109. package/src/values.config.a.js +0 -49
  110. package/src/values.config.b.js +0 -49
  111. package/src/values.config.c.js +0 -49
  112. package/src/values.config.d.js +0 -49
  113. package/src/values.config.default.js +0 -49
  114. package/src/values.config.js +0 -49
  115. package/src/webapi/featureDefinitionActions.js +0 -0
  116. package/src/webapi/featuresActions.js +0 -90
  117. package/src/webapi/helper.js +0 -4
  118. package/src/webapi/index.js +0 -12
  119. package/src/webapi/listingActions.js +0 -176
@@ -1,626 +0,0 @@
1
- import React, { useEffect, useState } from "react";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
4
- import { SidebarLayout } from "../components/SidebarLayout.jsx";
5
- import { values } from "../values.config.js";
6
- import { PlussCore } from "../feature.config";
7
- import styles from "./Form.module.css";
8
- import { Field } from "../components/Fields.jsx";
9
- import { iconImports } from "../components/iconImports";
10
- import {
11
- Text,
12
- LoadingState,
13
- Button,
14
- ErrorBoundary,
15
- Popup,
16
- } from "../components";
17
-
18
- import ToastContainer from "../components/ToastContainer.jsx";
19
-
20
- import { withRouter } from "react-router-dom";
21
- import { useDispatch, useSelector } from "react-redux";
22
- import {
23
- selectFormField,
24
- selectFormFields,
25
- selectFormDisplayName,
26
- selectIsCreateMode,
27
- selectIsEditMode,
28
- selectIsStepValid,
29
- selectStepErrors,
30
- selectFormIsSubmitting,
31
- selectFormSubmitError,
32
- selectFormSubmitSuccess,
33
- selectFormIsInitial,
34
- } from "../selectors/featureBuilderSelectors";
35
- import { useFeatureDefinitionLoader } from "../hooks/useFeatureDefinitionLoader";
36
- import {
37
- addField,
38
- deleteField,
39
- updateFieldById as updateFieldValuesById,
40
- setSummaryField,
41
- submitForm,
42
- clearFormSubmissionState,
43
- setInitialValues,
44
- } from "../actions/formActions";
45
- import {
46
- validateAndUpdateStep,
47
- setCurrentStepAndSave,
48
- } from "../actions/wizardActions";
49
-
50
- /**
51
- * Form Fields Step component for feature builder wizard
52
- * Provides field management interface with add, edit, delete functionality
53
- * Supports multiple field types (text, image, file, CTA, feature-image, description)
54
- * Includes validation, error handling, and step navigation
55
- *
56
- * @param {Object} props - Component props
57
- * @param {Object} props.history - React Router history object for navigation
58
- * @returns {React.ReactElement} Form fields configuration interface
59
- *
60
- * @example
61
- * <FormFieldsStep history={historyObject} />
62
- */
63
- const FormFieldsStepInner = (props) => {
64
- const { history } = props;
65
- const dispatch = useDispatch();
66
- const auth = useSelector((state) => state.auth);
67
- const fields = useSelector(selectFormFields);
68
-
69
- // Get form initialization state
70
- const isFormInitial = useSelector(selectFormIsInitial);
71
-
72
- // Field selection popup state
73
- const [showFieldSelector, setShowFieldSelector] = useState(false);
74
- const [replacingFieldIndex, setReplacingFieldIndex] = useState(null);
75
-
76
- // Toast state
77
- const [toasts, setToasts] = React.useState([]);
78
-
79
- // Available field types for card selection
80
- // Note: We exclude "title" type since it's already included as base field
81
- // UX-optimized order: text-first, media-second pattern for natural content creation workflow
82
- const fieldTypes = [
83
- {
84
- Key: "description",
85
- Title: "Description",
86
- Description: "Add detailed text content",
87
- UseCase: "Provide information, details, or descriptions",
88
- Icon: "paragraph",
89
- },
90
- {
91
- Key: "image",
92
- Title: "Image",
93
- Description: "Add photos or visual content",
94
- UseCase: "Show facility photos, event pictures, product images",
95
- Icon: "image",
96
- },
97
- {
98
- Key: "gallery",
99
- Title: "Gallery",
100
- Description: "Add multiple photos in a gallery layout",
101
- UseCase: "Create photo albums, event galleries, showcase multiple images",
102
- Icon: "th",
103
- },
104
- {
105
- Key: "file",
106
- Title: "Files",
107
- Description: "Share downloadable documents",
108
- UseCase: "Upload menus, brochures, PDFs, or resources",
109
- Icon: "paperclip",
110
- },
111
- {
112
- Key: "cta",
113
- Title: "Action Button",
114
- Description: "Add clickable action button",
115
- UseCase: "Link to reservations, bookings, or external sites",
116
- Icon: "arrow-circle-right",
117
- },
118
- ];
119
-
120
- const { definition, definitionIsLoading, reloadDefinition } =
121
- useFeatureDefinitionLoader();
122
-
123
- const formDisplayName = useSelector(selectFormDisplayName);
124
-
125
- const isCreateMode = useSelector(selectIsCreateMode);
126
- const isEditMode = useSelector(selectIsEditMode);
127
- const isStepValid = useSelector(selectIsStepValid("fields"));
128
- const stepErrors = useSelector(selectStepErrors("fields"));
129
- const showWarnings = !isStepValid && Object.keys(stepErrors).length > 0;
130
- const isSubmitting = useSelector(selectFormIsSubmitting);
131
- const submitError = useSelector(selectFormSubmitError);
132
- const submitSuccess = useSelector(selectFormSubmitSuccess);
133
-
134
- const addToast = (type, message) => {
135
- const id = Date.now();
136
- setToasts((prev) => [...prev, { id, type, message, isVisible: true }]);
137
- };
138
-
139
- const removeToast = (id) => {
140
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
141
- };
142
-
143
- // Handle successful submission with optimistic update and redirect
144
- React.useEffect(() => {
145
- if (submitSuccess && !isSubmitting) {
146
- if (isEditMode) {
147
- addToast("success", "Changes saved");
148
- dispatch(clearFormSubmissionState());
149
- } else {
150
- addToast("success", "Feature created successfully");
151
- dispatch(clearFormSubmissionState());
152
- setTimeout(() => {
153
- history.push(values.routeFormOverviewStep);
154
- }, 1000);
155
- }
156
- }
157
- }, [submitSuccess, isEditMode, isSubmitting, dispatch, history]);
158
-
159
- // Handle submit error
160
- useEffect(() => {
161
- if (submitError) {
162
- addToast("error", "It didn't work. Please try again.");
163
- setTimeout(() => {
164
- window.location.reload();
165
- }, 1000);
166
- }
167
- }, [submitError]);
168
-
169
- useEffect(() => {
170
- if (showWarnings) {
171
- // Scroll to top of form to show validation errors
172
- window.scrollTo({ top: 0, behavior: "smooth" });
173
- }
174
- }, [showWarnings]);
175
-
176
- const handleRefresh = () => {
177
- dispatch(validateAndUpdateStep("fields"));
178
- };
179
-
180
- useEffect(() => {
181
- dispatch(setCurrentStepAndSave("fields"));
182
- }, [dispatch]);
183
-
184
- useEffect(() => {
185
- if (definition && !definitionIsLoading && isFormInitial) {
186
- dispatch(setInitialValues(definition));
187
-
188
- if (isEditMode) {
189
- setTimeout(() => {
190
- dispatch(validateAndUpdateStep("fields"));
191
- }, 100);
192
- }
193
- }
194
- }, [definition, definitionIsLoading, isFormInitial, isEditMode, dispatch]);
195
-
196
- useEffect(() => {
197
- // In edit mode, trigger validation when definition is available
198
- if (isEditMode && definition && !definitionIsLoading && !isFormInitial) {
199
- setTimeout(() => {
200
- dispatch(validateAndUpdateStep("fields"));
201
- }, 100);
202
- }
203
- }, [definition, definitionIsLoading, isEditMode, isFormInitial, dispatch]);
204
-
205
- function handleAddField(fieldType) {
206
- dispatch(addField(fieldType));
207
- setShowFieldSelector(false);
208
- }
209
-
210
- function handleOpenFieldSelector() {
211
- setReplacingFieldIndex(null);
212
- setShowFieldSelector(true);
213
- }
214
-
215
- function handleCloseFieldSelector() {
216
- setReplacingFieldIndex(null);
217
- setShowFieldSelector(false);
218
- }
219
-
220
- function handleDeleteField(fieldId) {
221
- dispatch(deleteField(fieldId));
222
- }
223
-
224
- function handleReplaceField(fieldIndex) {
225
- setReplacingFieldIndex(fieldIndex);
226
- setShowFieldSelector(true);
227
- }
228
-
229
- function handleAddReplacementField(fieldType) {
230
- const fieldIndex = replacingFieldIndex;
231
- if (fieldIndex !== null) {
232
- const currentField = allFields[fieldIndex];
233
- if (currentField && !currentField.isMandatory) {
234
- dispatch(deleteField(currentField.id));
235
- }
236
- dispatch(addField(fieldType));
237
- setReplacingFieldIndex(null);
238
- setShowFieldSelector(false);
239
- }
240
- }
241
-
242
- function handleNext() {
243
- const validationResult = dispatch(validateAndUpdateStep("fields"));
244
- if (validationResult?.isValid) {
245
- dispatch(clearFormSubmissionState());
246
- if (isCreateMode) {
247
- history.push(values.routeFormLayoutStep);
248
- } else {
249
- history.push(values.routeFormLayoutStep);
250
- }
251
- }
252
- // If validation fails, scroll to top to show error summary
253
- else {
254
- window.scrollTo({ top: 0, behavior: "smooth" });
255
- }
256
- }
257
-
258
- function handlePrevious() {
259
- dispatch(clearFormSubmissionState());
260
- if (isCreateMode) {
261
- history.push(values.routeFormOverviewStep);
262
- } else {
263
- history.push(values.routeFormOverviewStep);
264
- }
265
- }
266
-
267
- function handleSaveStep() {
268
- const validationResult = dispatch(validateAndUpdateStep("fields"));
269
- if (validationResult?.isValid) {
270
- dispatch(submitForm());
271
- }
272
- // If validation fails, scroll to top to show error summary
273
- else {
274
- window.scrollTo({ top: 0, behavior: "smooth" });
275
- }
276
- }
277
-
278
- const allFields = fields || [];
279
- const sortedFields = allFields.slice().sort((a, b) => a.order - b.order);
280
-
281
- // Check for definition management permission
282
- if (
283
- !PlussCore.Session.validateAccess(
284
- auth.site,
285
- values.permissionFeatureBuilderDefinition,
286
- auth,
287
- )
288
- ) {
289
- return (
290
- <div className="hub-wrapperContainer">
291
- <div className="hub-contentWrapper">
292
- <div className={styles.welcomeContainer}>
293
- <div className={styles.welcomeHeader}>
294
- <Text type="h1" className={styles.welcomeTitle}>
295
- Access Restricted
296
- </Text>
297
- <Text type="body" className={styles.welcomeSubtitle}>
298
- You don't have permission to manage feature definitions. Please
299
- contact your administrator if you need access.
300
- </Text>
301
- </div>
302
- </div>
303
- </div>
304
- </div>
305
- );
306
- }
307
-
308
- return (
309
- <ErrorBoundary
310
- title="Unable to load fields configuration"
311
- message="If you continue to experience issues with the fields configuration, please try refreshing the page or contact support."
312
- onRetry={handleRefresh}
313
- >
314
- <SidebarLayout>
315
- <div className={styles.formHeader}>
316
- <Text
317
- type="formTitleLarge"
318
- className={` ${isEditMode ? styles.editMode : styles.createMode}`}
319
- >
320
- Configure fields
321
- </Text>
322
- </div>
323
- <Text type="body" className="marginBottom-16">
324
- Add fields to define the shape of your feature's listings.
325
- </Text>
326
-
327
- {/* Unified Fields Section */}
328
- <div className={styles.section}>
329
- {/* Show loading spinner while loading */}
330
- {definitionIsLoading ? (
331
- <div className={styles.fieldsLoadingContainer}>
332
- <LoadingState message="Loading fields..." />
333
- </div>
334
- ) : (
335
- sortedFields.map((field, fieldIndex) => (
336
- <FormField
337
- key={field.id}
338
- id={field.id}
339
- fieldIndex={fieldIndex}
340
- handleDelete={
341
- field.isMandatory ? null : () => handleDeleteField(field.id)
342
- }
343
- onReplaceField={
344
- field.isMandatory
345
- ? null
346
- : () => handleReplaceField(fieldIndex)
347
- }
348
- showWarnings={showWarnings}
349
- />
350
- ))
351
- )}
352
-
353
- {/* Add Field Button at bottom */}
354
- <div className={styles.addFieldContainer}>
355
- <div className={styles.fieldNumberContainer}>
356
- {/* Empty spacer to align with field numbers */}
357
- </div>
358
- <div className={styles.addFieldSection}>
359
- <Button
360
- buttonType="primary"
361
- isActive
362
- onClick={handleOpenFieldSelector}
363
- leftIcon="plus"
364
- aria-label="Add a new content field"
365
- >
366
- Add Content Field
367
- </Button>
368
- </div>
369
- </div>
370
- </div>
371
-
372
- {/* Top-level validation message - positioned above action buttons */}
373
- {!isStepValid && Object.keys(stepErrors).length > 0 && (
374
- <div
375
- className={styles.validationErrorMessage}
376
- role="alert"
377
- aria-live="polite"
378
- >
379
- {stepErrors.summary ? (
380
- <>
381
- {stepErrors.summary}
382
- <br />
383
- <span className={styles.helpNote}>
384
- Only one description field can be marked as "Show as preview
385
- text".
386
- </span>
387
- </>
388
- ) : (
389
- "The form has error, please review the fields above."
390
- )}
391
- </div>
392
- )}
393
-
394
- {/* Mode-aware navigation buttons */}
395
- <div className={styles.navigation}>
396
- {isCreateMode ? (
397
- <>
398
- <Button
399
- buttonType="secondary"
400
- isActive
401
- onClick={handlePrevious}
402
- leftIcon="arrow-left"
403
- >
404
- Previous step: Configure Fields
405
- </Button>
406
- <Button
407
- buttonType="primary"
408
- isActive
409
- onClick={handleNext}
410
- leftIcon="arrow-right"
411
- >
412
- Next step: Choose Layout
413
- </Button>
414
- </>
415
- ) : (
416
- <Button
417
- buttonType="primary"
418
- isActive
419
- onClick={handleSaveStep}
420
- disabled={isSubmitting}
421
- leftIcon={isSubmitting ? "sync" : "save"}
422
- loading={isSubmitting}
423
- >
424
- {isSubmitting ? "Saving..." : "Save"}
425
- </Button>
426
- )}
427
- </div>
428
- </SidebarLayout>
429
-
430
- {/* Field Selector Popup */}
431
- {showFieldSelector && (
432
- <Popup
433
- title={
434
- replacingFieldIndex !== null ? "Replace Field" : "Add Content Field"
435
- }
436
- onClose={handleCloseFieldSelector}
437
- buttons={[
438
- {
439
- text: "Cancel",
440
- type: "secondary",
441
- onClick: handleCloseFieldSelector,
442
- },
443
- ]}
444
- hasPadding
445
- width="1200px"
446
- >
447
- {/* Help Section - Visible for all users */}
448
- <div className={styles.helpSection}>
449
- <Text type="formTitleSmall" className={styles.helpText}>
450
- <FontAwesomeIcon icon={faInfoCircle} /> Add fields to the
451
- template.
452
- <br />
453
- These fields determine layout for every{" "}
454
- {formDisplayName || "Feature"} visible to residents.
455
- </Text>
456
- </div>
457
-
458
- <div className={styles.fieldTypeCards}>
459
- {fieldTypes.map((fieldType) => (
460
- <div
461
- key={fieldType.Key}
462
- className={styles.fieldTypeCard}
463
- onClick={() =>
464
- replacingFieldIndex !== null
465
- ? handleAddReplacementField(fieldType.Key)
466
- : handleAddField(fieldType.Key)
467
- }
468
- role="button"
469
- tabIndex={0}
470
- onKeyDown={(e) => {
471
- if (e.key === "Enter" || e.key === " ") {
472
- e.preventDefault();
473
- if (replacingFieldIndex !== null) {
474
- handleAddReplacementField(fieldType.Key);
475
- } else {
476
- handleAddField(fieldType.Key);
477
- }
478
- }
479
- }}
480
- >
481
- <div className={styles.fieldTypeCardIcon}>
482
- <FieldTypeIcon iconName={fieldType.Icon} />
483
- </div>
484
- <div className={styles.fieldTypeCardContent}>
485
- <Text type="h5" className={styles.fieldTypeCardTitle}>
486
- {fieldType.Title}
487
- </Text>
488
- <Text type="body" className={styles.fieldTypeCardDescription}>
489
- {fieldType.Description}
490
- </Text>
491
- <Text type="help" className={styles.fieldTypeCardUseCase}>
492
- <strong>Use case:</strong> {fieldType.UseCase}
493
- </Text>
494
- </div>
495
- </div>
496
- ))}
497
- </div>
498
- </Popup>
499
- )}
500
-
501
- {/* Toast Container for Notifications */}
502
- <ToastContainer toasts={toasts} onDismiss={removeToast} />
503
- </ErrorBoundary>
504
- );
505
- };
506
-
507
- export const FormFieldsStep = withRouter(FormFieldsStepInner);
508
-
509
- // FieldTypeIcon component for popup cards
510
- const FieldTypeIcon = ({ iconName }) => {
511
- const icon = iconImports[iconName];
512
-
513
- return (
514
- <span className={styles.fieldTypeCardIcon}>
515
- <FontAwesomeIcon icon={icon} />
516
- </span>
517
- );
518
- };
519
-
520
- const FormField = (props) => {
521
- const { id, handleDelete, showWarnings, fieldIndex, onReplaceField } = props;
522
- const field = useField(id);
523
- const stepErrors = useSelector(selectStepErrors("fields"));
524
- const hasError = showWarnings && stepErrors && stepErrors[id];
525
-
526
- // The Field component now handles its own styling, so we just render it directly
527
- // Error states are passed through via props
528
- return (
529
- <Field
530
- {...field}
531
- fieldIndex={fieldIndex}
532
- handleDelete={handleDelete}
533
- onReplaceField={onReplaceField}
534
- stepErrors={stepErrors}
535
- showWarnings={showWarnings}
536
- />
537
- );
538
- };
539
-
540
- /**
541
- * Custom hook for managing individual field state and operations
542
- * Provides convenient methods to update field properties with Redux dispatch
543
- *
544
- * @param {string} id - Unique identifier of field to manage
545
- * @returns {Object} Field object with setter methods for field properties
546
- * @returns {Object} returns.field - Current field state from Redux store
547
- * @returns {Function} returns.setLabel - Update field label
548
- * @returns {Function} returns.toggleIsRequired - Toggle field required status
549
- * @returns {Function} returns.setPlaceholder - Update field placeholder text
550
- * @returns {Function} returns.setUrl - Update field URL
551
- * @returns {Function} returns.setHelpText - Update field help text
552
- * @returns {Function} returns.setAllowCaption - Update caption allowance setting
553
- * @returns {Function} returns.toggleAllowCaption - Toggle caption allowance setting
554
- * @returns {Function} returns.setUseAsSummary - Update use as summary setting for description fields
555
- *
556
- * @example
557
- * const field = useField('field-123');
558
- * field.setLabel('New Label');
559
- * field.toggleIsRequired();
560
- */
561
- const useField = (id) => {
562
- const field = useSelector(selectFormField(id));
563
- const dispatch = useDispatch();
564
- const { values } = field;
565
-
566
- function setPlaceholder(value) {
567
- const updatedField = { values: { ...values, placeholder: value } };
568
- dispatch(updateFieldValuesById(id, updatedField));
569
- }
570
-
571
- function setLabel(value) {
572
- const updatedField = { values: { ...values, label: value } };
573
- dispatch(updateFieldValuesById(id, updatedField));
574
- }
575
-
576
- function toggleIsRequired() {
577
- const updatedField = {
578
- values: { ...values, isRequired: !values.isRequired },
579
- };
580
- dispatch(updateFieldValuesById(id, updatedField));
581
- }
582
-
583
- function setUrl(value) {
584
- const updatedField = { values: { ...values, url: value } };
585
- dispatch(updateFieldValuesById(id, updatedField));
586
- }
587
-
588
- function setHelpText(value) {
589
- const updatedField = { values: { ...values, helpText: value } };
590
- dispatch(updateFieldValuesById(id, updatedField));
591
- }
592
-
593
- function setAllowCaption(value) {
594
- const updatedField = { values: { ...values, allowCaption: value } };
595
-
596
- dispatch(updateFieldValuesById(id, updatedField));
597
- }
598
-
599
- function toggleAllowCaption() {
600
- const updatedField = {
601
- values: { ...values, allowCaption: !values.allowCaption },
602
- };
603
- dispatch(updateFieldValuesById(id, updatedField));
604
- }
605
-
606
- function setUseAsSummary(value) {
607
- if (value) {
608
- dispatch(setSummaryField(id));
609
- } else {
610
- const updatedField = { values: { ...values, useAsSummary: false } };
611
- dispatch(updateFieldValuesById(id, updatedField));
612
- }
613
- }
614
-
615
- return {
616
- ...field,
617
- setLabel,
618
- toggleIsRequired,
619
- setPlaceholder,
620
- setUrl,
621
- setHelpText,
622
- setAllowCaption,
623
- toggleAllowCaption,
624
- setUseAsSummary,
625
- };
626
- };