@plusscommunities/pluss-feature-builder-web-a 1.0.2-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.
Files changed (117) hide show
  1. package/.babelrc +4 -0
  2. package/dist/index.cjs.js +7792 -0
  3. package/package.json +54 -0
  4. package/rollup.config.js +68 -0
  5. package/src/actions/featureBuilderStringsActions.js +88 -0
  6. package/src/actions/featureDefinitionsIndex.js +258 -0
  7. package/src/actions/formActions.js +311 -0
  8. package/src/actions/index.js +12 -0
  9. package/src/actions/listingActions.js +350 -0
  10. package/src/actions/wizardActions.js +240 -0
  11. package/src/components/ActivityCardExample.jsx +86 -0
  12. package/src/components/ActivityCardExample.module.css +130 -0
  13. package/src/components/BackgroundLoader.jsx +33 -0
  14. package/src/components/BackgroundLoader.module.css +46 -0
  15. package/src/components/BaseFieldConfig.jsx +305 -0
  16. package/src/components/BaseFieldConfig.module.css +42 -0
  17. package/src/components/CenteredContainer.jsx +29 -0
  18. package/src/components/CenteredContainer.module.css +171 -0
  19. package/src/components/DeleteConfirmationPopup.jsx +95 -0
  20. package/src/components/DeleteConfirmationPopup.module.css +12 -0
  21. package/src/components/ErrorBoundary.jsx +134 -0
  22. package/src/components/ErrorBoundary.module.css +77 -0
  23. package/src/components/ErrorMessage.jsx +85 -0
  24. package/src/components/ErrorMessage.module.css +116 -0
  25. package/src/components/ExampleDisplay.jsx +26 -0
  26. package/src/components/ExampleDisplay.module.css +3 -0
  27. package/src/components/FeatureBuilderSidebar.jsx +84 -0
  28. package/src/components/FeatureBuilderSuccessPopup.jsx +55 -0
  29. package/src/components/FeatureBuilderSuccessPopup.module.css +43 -0
  30. package/src/components/FeatureBuilderWelcomePopup.jsx +51 -0
  31. package/src/components/FeatureBuilderWelcomePopup.module.css +21 -0
  32. package/src/components/FeatureListingCard.jsx +104 -0
  33. package/src/components/FeatureListingCard.module.css +62 -0
  34. package/src/components/Fields.jsx +460 -0
  35. package/src/components/Fields.module.css +159 -0
  36. package/src/components/IconLoader.jsx +153 -0
  37. package/src/components/IconLoader.module.css +92 -0
  38. package/src/components/IconSelector.jsx +112 -0
  39. package/src/components/IconSelector.module.css +197 -0
  40. package/src/components/ListingEditor.jsx +406 -0
  41. package/src/components/ListingEditor.module.css +14 -0
  42. package/src/components/ListingSuccessPopup.jsx +52 -0
  43. package/src/components/LoadingScreen.jsx +54 -0
  44. package/src/components/LoadingScreen.module.css +103 -0
  45. package/src/components/LoadingState.jsx +40 -0
  46. package/src/components/LoadingState.module.css +18 -0
  47. package/src/components/PreviewFull.js +24 -0
  48. package/src/components/PreviewFull.module.css +11 -0
  49. package/src/components/PreviewGrid.js +14 -0
  50. package/src/components/PreviewWidget.js +27 -0
  51. package/src/components/PreviewWidget.module.css +15 -0
  52. package/src/components/SidebarLayout.jsx +292 -0
  53. package/src/components/SidebarLayout.module.css +145 -0
  54. package/src/components/SkeletonLoader.jsx +128 -0
  55. package/src/components/SkeletonLoader.module.css +295 -0
  56. package/src/components/SortButtonGroup.jsx +34 -0
  57. package/src/components/SortButtonGroup.module.css +51 -0
  58. package/src/components/ToastContainer.jsx +98 -0
  59. package/src/components/ToastContainer.module.css +156 -0
  60. package/src/components/ToggleSwitch.js +40 -0
  61. package/src/components/ToggleSwitch.module.css +48 -0
  62. package/src/components/TwoColumnInput.jsx +29 -0
  63. package/src/components/TwoColumnInput.module.css +32 -0
  64. package/src/components/ViewFull.js +139 -0
  65. package/src/components/ViewFull.module.css +71 -0
  66. package/src/components/ViewWidget.js +62 -0
  67. package/src/components/ViewWidget.module.css +28 -0
  68. package/src/components/iconCategories.js +135 -0
  69. package/src/components/iconImports.js +409 -0
  70. package/src/components/index.js +61 -0
  71. package/src/components/listing/FileListItem.jsx +86 -0
  72. package/src/components/listing/GalleryDisplay.jsx +331 -0
  73. package/src/components/listing/GalleryDisplay.module.css +309 -0
  74. package/src/components/listing/ListingCTAInput.jsx +82 -0
  75. package/src/components/listing/ListingDescriptionInput.jsx +73 -0
  76. package/src/components/listing/ListingField.jsx +101 -0
  77. package/src/components/listing/ListingField.module.css +106 -0
  78. package/src/components/listing/ListingFileInput.jsx +255 -0
  79. package/src/components/listing/ListingFileInput.module.css +192 -0
  80. package/src/components/listing/ListingForm.jsx +90 -0
  81. package/src/components/listing/ListingForm.module.css +38 -0
  82. package/src/components/listing/ListingGalleryInput.jsx +236 -0
  83. package/src/components/listing/ListingGalleryInput.module.css +131 -0
  84. package/src/components/listing/ListingImageInput.jsx +153 -0
  85. package/src/components/listing/ListingTextInput.jsx +72 -0
  86. package/src/feature.config.js +130 -0
  87. package/src/helper/index.js +135 -0
  88. package/src/hooks/useFeatureDefinitionLoader.js +62 -0
  89. package/src/images/full.png +0 -0
  90. package/src/images/fullNoTitle.png +0 -0
  91. package/src/images/previewWidget.png +0 -0
  92. package/src/images/widget.png +0 -0
  93. package/src/index.js +38 -0
  94. package/src/pages/CreateListingPage.jsx +49 -0
  95. package/src/pages/EditListingPage.jsx +58 -0
  96. package/src/reducers/featureBuilderReducer.js +744 -0
  97. package/src/screens/CreateListing.module.css +45 -0
  98. package/src/screens/Form.module.css +734 -0
  99. package/src/screens/FormFieldsStep.jsx +689 -0
  100. package/src/screens/FormLayoutStep.jsx +445 -0
  101. package/src/screens/FormOverviewStep.jsx +396 -0
  102. package/src/screens/ListingScreen.jsx +478 -0
  103. package/src/screens/ListingScreen.module.css +333 -0
  104. package/src/selectors/featureBuilderSelectors.js +529 -0
  105. package/src/types/index.js +91 -0
  106. package/src/utils/textUtils.js +89 -0
  107. package/src/validators/galleryValidators.js +345 -0
  108. package/src/values.config.a.js +49 -0
  109. package/src/values.config.b.js +49 -0
  110. package/src/values.config.c.js +49 -0
  111. package/src/values.config.d.js +49 -0
  112. package/src/values.config.js +49 -0
  113. package/src/webapi/featureDefinitionActions.js +0 -0
  114. package/src/webapi/featuresActions.js +90 -0
  115. package/src/webapi/helper.js +4 -0
  116. package/src/webapi/index.js +12 -0
  117. package/src/webapi/listingActions.js +176 -0
@@ -0,0 +1,305 @@
1
+ import React from "react";
2
+ import { PlussCore } from "../feature.config";
3
+ const { Components } = PlussCore;
4
+ const { GenericInput, CheckBox, Text } = Components;
5
+ import { TwoColumnInput } from "./TwoColumnInput.jsx";
6
+ import styles from "./BaseFieldConfig.module.css";
7
+
8
+ /**
9
+ * Base Field Configuration component for form fields
10
+ * Provides common field configuration UI with label, placeholder, help text, and validation settings
11
+ * Used as base for all field types in the feature builder
12
+ *
13
+ * @typedef {Object} BaseFieldConfigProps
14
+ * @property {string} label - Current field label value
15
+ * @property {string} placeholder - Current field placeholder text
16
+ * @property {boolean} allowCaption - Whether field allows caption
17
+ * @property {string} helpText - Current help text for field
18
+ * @property {boolean} isRequired - Whether field is mandatory
19
+ * @property {boolean} useAsSummary - Whether field is used as summary (for description fields)
20
+ * @property {number} minImages - Minimum number of images for gallery fields
21
+ * @property {number} maxImages - Maximum number of images for gallery fields
22
+ * @property {string} maxFileSize - Maximum file size for gallery fields
23
+ * @property {string[]} allowedTypes - Allowed file types for gallery fields
24
+ * @property {Function} setLabel - Callback to update field label
25
+ * @property {Function} setPlaceholder - Callback to update placeholder
26
+ * @property {Function} setAllowCaption - Callback to toggle caption allowance
27
+ * @property {Function} setHelpText - Callback to update help text
28
+ * @property {Function} toggleIsRequired - Callback to toggle required status
29
+ * @property {Function} setUseAsSummary - Callback to toggle summary usage (for description fields)
30
+ * @property {Function} setMinImages - Callback to set minimum images for gallery
31
+ * @property {Function} setMaxImages - Callback to set maximum images for gallery
32
+ * @property {Function} setMaxFileSize - Callback to set maximum file size for gallery
33
+ * @property {Function} setAllowedTypes - Callback to set allowed file types for gallery
34
+ * @property {string} fieldId - Unique identifier of the field
35
+ * @property {string} fieldType - Type of field being configured
36
+ * @property {Object} stepErrors - Validation errors by field ID
37
+ * @property {boolean} showWarnings - Whether to show validation warnings
38
+ * @property {string} customLabel - Custom label for input field
39
+ * @property {React.ReactNode} children - Field-specific configuration options
40
+ *
41
+ * @param {BaseFieldConfigProps} props - Component props
42
+ * @returns {React.ReactElement} Field configuration interface
43
+ *
44
+ * @example
45
+ * <BaseFieldConfig
46
+ * label="Email Address"
47
+ * placeholder="Enter email"
48
+ * isRequired={true}
49
+ * fieldId="field-123"
50
+ * setLabel={(value) => updateField('label', value)}
51
+ * toggleIsRequired={() => updateField('required', !isRequired)}
52
+ * />
53
+ */
54
+ export const BaseFieldConfig = (props) => {
55
+ const {
56
+ label,
57
+ placeholder,
58
+ allowCaption,
59
+ helpText,
60
+ isRequired,
61
+ minImages,
62
+ maxImages,
63
+ maxFileSize,
64
+ allowedTypes,
65
+ setLabel,
66
+ setPlaceholder,
67
+ setAllowCaption,
68
+ setHelpText,
69
+ toggleIsRequired,
70
+ toggleAllowCaption,
71
+ setMinImages,
72
+ setMaxImages,
73
+ setMaxFileSize,
74
+ setAllowedTypes,
75
+ stepErrors,
76
+ showWarnings,
77
+ id,
78
+ fieldType,
79
+ customLabel,
80
+ useAsSummary, // For description fields
81
+ setUseAsSummary, // For description fields
82
+ children, // For field-specific options
83
+ } = props;
84
+
85
+ // Determine if placeholder should be shown
86
+ const showPlaceholder =
87
+ fieldType !== "image" &&
88
+ fieldType !== "gallery" &&
89
+ fieldType !== "feature-image" &&
90
+ fieldType !== "file";
91
+
92
+ // Determine if caption option should be shown
93
+ const showCaptionOption = fieldType === "image";
94
+
95
+ // Determine if use as summary option should be shown
96
+ const showUseAsSummaryOption = fieldType === "description";
97
+
98
+ // Determine if required checkbox should be shown as disabled
99
+ const isRequiredFieldAlwaysRequired =
100
+ fieldType === "text" ||
101
+ fieldType === "feature-image" ||
102
+ fieldType === "title";
103
+
104
+ // Determine if required checkbox should be hidden entirely
105
+ const hideRequiredField = fieldType === "gallery";
106
+
107
+ return (
108
+ <div>
109
+ <div>
110
+ {/* Basic inputs - always visible */}
111
+ <TwoColumnInput
112
+ left={
113
+ <GenericInput
114
+ type="text"
115
+ placeholder="Type your label here (required)"
116
+ value={label}
117
+ label={customLabel || "Label"}
118
+ isRequired
119
+ onChange={(e) => setLabel && setLabel(e.target.value)}
120
+ showError={() => {
121
+ const hasWarning = showWarnings && stepErrors && stepErrors[id];
122
+ return hasWarning;
123
+ }}
124
+ errorMessage={
125
+ stepErrors && stepErrors[id]
126
+ ? stepErrors[id]
127
+ : "Field label is required"
128
+ }
129
+ isValid={() => {
130
+ const isValid = !(stepErrors && stepErrors[id]);
131
+ return isValid;
132
+ }}
133
+ alwaysShowLabel
134
+ />
135
+ }
136
+ right={
137
+ showPlaceholder && (
138
+ <GenericInput
139
+ type="text"
140
+ value={placeholder}
141
+ placeholder={"Add a placeholder (optional)"}
142
+ label={"Placeholder"}
143
+ onChange={(e) =>
144
+ setPlaceholder && setPlaceholder(e.target.value)
145
+ }
146
+ alwaysShowLabel
147
+ />
148
+ )
149
+ }
150
+ />
151
+
152
+ {/* Help Text - Always visible for better accessibility */}
153
+ {helpText !== undefined && (
154
+ <GenericInput
155
+ key={`helpText-${id || "unknown"}`}
156
+ type="text"
157
+ value={helpText || ""}
158
+ placeholder={"Add help text (optional)"}
159
+ label={"Help Text"}
160
+ onChange={(e) => {
161
+ if (setHelpText) {
162
+ setHelpText(e.target.value);
163
+ }
164
+ }}
165
+ className={styles.fieldSpacing}
166
+ alwaysShowLabel
167
+ />
168
+ )}
169
+
170
+ {/* Required Field Setting - Hidden for gallery fields */}
171
+ {toggleIsRequired &&
172
+ !isRequiredFieldAlwaysRequired &&
173
+ !hideRequiredField && (
174
+ <CheckBox
175
+ label="Required field"
176
+ isActive={isRequired}
177
+ onChange={toggleIsRequired}
178
+ className={styles.fieldSpacing}
179
+ />
180
+ )}
181
+
182
+ {/* Show disabled required checkbox for fields that are always required */}
183
+ {isRequiredFieldAlwaysRequired && !hideRequiredField && (
184
+ <CheckBox
185
+ label="Required field"
186
+ isActive={true}
187
+ onChange={() => { }}
188
+ disabled={true}
189
+ className={styles.fieldSpacing}
190
+ />
191
+ )}
192
+
193
+ {/* Advanced options - always visible */}
194
+ {/* Use as Summary Option - Show for description fields */}
195
+ {showUseAsSummaryOption && (
196
+ <div className={styles.checkboxContainer}>
197
+ <CheckBox
198
+ label="Show as preview text"
199
+ isActive={useAsSummary}
200
+ onChange={() => setUseAsSummary(!useAsSummary)}
201
+ />
202
+ <Text type="bodySmall" className={styles.helpText}>
203
+ Display this description as a short preview under the title in
204
+ listings. Only one description can be used as preview.
205
+ </Text>
206
+ </div>
207
+ )}
208
+
209
+ {/* Caption Option - Show for image fields */}
210
+ {showCaptionOption && (
211
+ <div className={styles.checkboxContainer}>
212
+ <CheckBox
213
+ label="Allow users to add caption to image"
214
+ isActive={allowCaption}
215
+ onChange={toggleAllowCaption || setAllowCaption}
216
+ />
217
+ <Text type="bodySmall" className={styles.helpText}>
218
+ When checked, users uploading an image can add a short description
219
+ to explain what the image shows.
220
+ </Text>
221
+ </div>
222
+ )}
223
+
224
+ {/* Field-specific options */}
225
+ {fieldType === "gallery" && (
226
+ <>
227
+ {/* Minimum Images */}
228
+ {setMinImages && (
229
+ <GenericInput
230
+ type="number"
231
+ value={minImages || 1}
232
+ placeholder="1"
233
+ label="Minimum Images"
234
+ onChange={(e) => setMinImages(parseInt(e.target.value) || 1)}
235
+ className={styles.fieldSpacing}
236
+ alwaysShowLabel
237
+ />
238
+ )}
239
+
240
+ {/* Maximum Images */}
241
+ {setMaxImages && (
242
+ <GenericInput
243
+ type="number"
244
+ value={maxImages || 10}
245
+ placeholder="10"
246
+ label="Maximum Images"
247
+ onChange={(e) => setMaxImages(parseInt(e.target.value) || 10)}
248
+ className={styles.fieldSpacing}
249
+ alwaysShowLabel
250
+ />
251
+ )}
252
+
253
+ {/* Maximum File Size */}
254
+ {setMaxFileSize && (
255
+ <GenericInput
256
+ type="text"
257
+ value={maxFileSize || "5MB"}
258
+ placeholder="5MB"
259
+ label="Maximum File Size"
260
+ onChange={(e) => setMaxFileSize(e.target.value)}
261
+ className={styles.fieldSpacing}
262
+ alwaysShowLabel
263
+ />
264
+ )}
265
+
266
+ {/* Allowed File Types */}
267
+ {setAllowedTypes && (
268
+ <div className={styles.fileTypesContainer}>
269
+ <Text type="formLabel" className={styles.formLabel}>
270
+ Allowed File Types
271
+ </Text>
272
+ <div className={styles.fileTypesList}>
273
+ {["image/jpeg", "image/png", "image/webp"].map((type) => (
274
+ <CheckBox
275
+ key={type}
276
+ label={type.replace("image/", "").toUpperCase()}
277
+ isActive={allowedTypes?.includes(type) || false}
278
+ onChange={() => {
279
+ const currentTypes = allowedTypes || [];
280
+ const newTypes = currentTypes.includes(type)
281
+ ? currentTypes.filter((t) => t !== type)
282
+ : [...currentTypes, type];
283
+ setAllowedTypes(newTypes);
284
+ }}
285
+ className={styles.checkboxWithMargin}
286
+ />
287
+ ))}
288
+ </div>
289
+ </div>
290
+ )}
291
+
292
+ {/* Gallery Help Text */}
293
+ <Text
294
+ type="bodySmall"
295
+ className={`${styles.helpText} ${styles.fieldSpacing}`}
296
+ >
297
+ Gallery field for displaying multiple images.
298
+ </Text>
299
+ </>
300
+ )}
301
+ {children}
302
+ </div>
303
+ </div>
304
+ );
305
+ };
@@ -0,0 +1,42 @@
1
+ /* Common spacing */
2
+ .fieldSpacing {
3
+ margin-bottom: 12px;
4
+ }
5
+
6
+ .fieldSpacing2 {
7
+ margin-top: 4px;
8
+ margin-bottom: 12px;
9
+ }
10
+
11
+ /* Help text styling */
12
+ .helpText {
13
+ margin-top: 4px;
14
+ color: #6c7a90;
15
+ }
16
+
17
+ /* Form label styling */
18
+ .formLabel {
19
+ display: block;
20
+ margin-bottom: 8px;
21
+ }
22
+
23
+ /* Container for checkbox with help text */
24
+ .checkboxContainer {
25
+ margin-bottom: 12px;
26
+ }
27
+
28
+ /* File types container */
29
+ .fileTypesContainer {
30
+ margin-bottom: 12px;
31
+ }
32
+
33
+ .fileTypesList {
34
+ display: flex;
35
+ gap: 12px;
36
+ flex-wrap: wrap;
37
+ }
38
+
39
+ /* Checkbox with margin */
40
+ .checkboxWithMargin {
41
+ margin-bottom: 4px;
42
+ }
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import styles from "./CenteredContainer.module.css";
3
+
4
+ const CenteredContainer = ({
5
+ children,
6
+ maxWidth = "3xl",
7
+ padding = "normal",
8
+ className = "",
9
+ background = "white",
10
+ ...props
11
+ }) => {
12
+ const containerClasses = [
13
+ styles.container,
14
+ styles[`maxWidth-${maxWidth}`],
15
+ styles[`padding-${padding}`],
16
+ styles[`background-${background}`],
17
+ className,
18
+ ]
19
+ .filter(Boolean)
20
+ .join(" ");
21
+
22
+ return (
23
+ <div className={containerClasses} {...props}>
24
+ {children}
25
+ </div>
26
+ );
27
+ };
28
+
29
+ export default CenteredContainer;
@@ -0,0 +1,171 @@
1
+ .container {
2
+ width: 100%;
3
+ margin-left: auto;
4
+ margin-right: auto;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ /* Max-width variants - based on Tailwind CSS */
9
+ .maxWidth-sm {
10
+ max-width: 384px;
11
+ }
12
+
13
+ .maxWidth-md {
14
+ max-width: 448px;
15
+ }
16
+
17
+ .maxWidth-lg {
18
+ max-width: 512px;
19
+ }
20
+
21
+ .maxWidth-xl {
22
+ max-width: 576px;
23
+ }
24
+
25
+ .maxWidth-2xl {
26
+ max-width: 672px;
27
+ }
28
+
29
+ .maxWidth-3xl {
30
+ max-width: 768px;
31
+ }
32
+
33
+ .maxWidth-4xl {
34
+ max-width: 896px;
35
+ }
36
+
37
+ .maxWidth-5xl {
38
+ max-width: 1024px;
39
+ }
40
+
41
+ .maxWidth-6xl {
42
+ max-width: 1152px;
43
+ }
44
+
45
+ .maxWidth-7xl {
46
+ max-width: 1280px;
47
+ }
48
+
49
+ .maxWidth-full {
50
+ max-width: 100%;
51
+ }
52
+
53
+ /* Padding variants */
54
+ .padding-compact {
55
+ padding: 16px;
56
+ }
57
+
58
+ @media (min-width: 640px) {
59
+ .padding-compact {
60
+ padding: 24px;
61
+ }
62
+ }
63
+
64
+ .padding-normal {
65
+ padding: 24px;
66
+ }
67
+
68
+ @media (min-width: 640px) {
69
+ .padding-normal {
70
+ padding: 32px;
71
+ }
72
+ }
73
+
74
+ @media (min-width: 1024px) {
75
+ .padding-normal {
76
+ padding: 40px;
77
+ }
78
+ }
79
+
80
+ .padding-spacious {
81
+ padding: 32px;
82
+ }
83
+
84
+ @media (min-width: 640px) {
85
+ .padding-spacious {
86
+ padding: 40px;
87
+ }
88
+ }
89
+
90
+ @media (min-width: 1024px) {
91
+ .padding-spacious {
92
+ padding: 48px;
93
+ }
94
+ }
95
+
96
+ .padding-none {
97
+ padding: 0;
98
+ }
99
+
100
+ /* Background variants */
101
+ .background-white {
102
+ background-color: #ffffff;
103
+ }
104
+
105
+ .background-gray {
106
+ background-color: #f8f9fa;
107
+ }
108
+
109
+ .background-transparent {
110
+ background-color: transparent;
111
+ }
112
+
113
+ /* Responsive adjustments */
114
+ @media (max-width: 768px) {
115
+ .container {
116
+ padding-left: 16px;
117
+ padding-right: 16px;
118
+ }
119
+ }
120
+
121
+ @media (min-width: 769px) and (max-width: 1024px) {
122
+ .container {
123
+ padding-left: 24px;
124
+ padding-right: 24px;
125
+ }
126
+ }
127
+
128
+ @media (min-width: 1025px) {
129
+ .container {
130
+ padding-left: 32px;
131
+ padding-right: 32px;
132
+ }
133
+ }
134
+
135
+ /* Prevent bleeding on very large screens */
136
+ @media (min-width: 1440px) {
137
+ .container {
138
+ max-width: calc(100vw - 40px); /* Ensure 20px margin on each side */
139
+ }
140
+ }
141
+
142
+ /* Form-specific styling */
143
+ .formContainer {
144
+ background-color: #ffffff;
145
+ border-radius: 8px;
146
+ box-shadow:
147
+ 0 1px 3px rgba(0, 0, 0, 0.1),
148
+ 0 1px 2px rgba(0, 0, 0, 0.06);
149
+ }
150
+
151
+ .listingContainer {
152
+ background-color: #ffffff;
153
+ border-radius: 8px;
154
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
155
+ }
156
+
157
+ /* Wrapper for full-screen centering */
158
+ .pageWrapper {
159
+ min-height: 100vh;
160
+ display: flex;
161
+ flex-direction: column;
162
+ justify-content: flex-start;
163
+ padding: 20px 0;
164
+ background-color: #f8f9fa;
165
+ }
166
+
167
+ @media (min-width: 768px) {
168
+ .pageWrapper {
169
+ padding: 40px 0;
170
+ }
171
+ }
@@ -0,0 +1,95 @@
1
+ import React from "react";
2
+ import { PlussCore } from "../feature.config";
3
+ const { Components } = PlussCore;
4
+ const { Popup } = Components;
5
+ import styles from "./DeleteConfirmationPopup.module.css";
6
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7
+ import { faSpinner } from "@fortawesome/free-solid-svg-icons";
8
+
9
+ /**
10
+ * Delete Confirmation Popup component
11
+ * Displays a confirmation dialog for deleting listings or feature definitions
12
+ * Shows loading state during deletion and provides cancel option
13
+ *
14
+ * @param {Object} props - Component props
15
+ * @param {boolean} props.isOpen - Whether the popup is currently visible
16
+ * @param {Object} props.listing - The listing or feature definition to be deleted
17
+ * @param {Function} props.onConfirm - Callback function when user confirms deletion
18
+ * @param {Function} props.onCancel - Callback function when user cancels deletion
19
+ * @param {boolean} props.isDeleting - Whether deletion is currently in progress
20
+ * @param {string} [props.deleteType="listing"] - Type of item being deleted ("listing" or "featureDefinition")
21
+ * @returns {React.ReactElement} Delete confirmation popup interface
22
+ *
23
+ * @example
24
+ * <DeleteConfirmationPopup
25
+ * isOpen={showDeleteDialog}
26
+ * listing={currentListing}
27
+ * onConfirm={handleDelete}
28
+ * onCancel={handleCancel}
29
+ * isDeleting={isDeleting}
30
+ * deleteType="listing"
31
+ * />
32
+ */
33
+ const DeleteConfirmationPopup = ({
34
+ isOpen,
35
+ listing,
36
+ onConfirm,
37
+ onCancel,
38
+ isDeleting,
39
+ deleteType = "listing", // Can be "listing" or "featureDefinition"
40
+ }) => {
41
+ if (!isOpen) {
42
+ return null;
43
+ }
44
+
45
+ const buttons = [
46
+ {
47
+ type: "primary",
48
+ text: isDeleting ? "Deleting..." : "Delete",
49
+ onClick: onConfirm,
50
+ isActive: !isDeleting,
51
+ loading: isDeleting,
52
+ },
53
+ {
54
+ type: "tertiary",
55
+ text: "Cancel",
56
+ onClick: onCancel,
57
+ isActive: !isDeleting,
58
+ },
59
+ ];
60
+
61
+ const isFeatureDefinition = deleteType === "featureDefinition";
62
+ const itemText = isFeatureDefinition ? "feature definition" : "listing";
63
+ const itemDisplayText = isFeatureDefinition
64
+ ? (listing && listing.fields && listing.fields["mandatory-title"]) ||
65
+ "this feature definition"
66
+ : (listing && listing.fields && listing.fields["mandatory-title"]) ||
67
+ "this listing";
68
+
69
+ return (
70
+ <Popup
71
+ title={`Delete ${itemText}?`}
72
+ subtitle={
73
+ isDeleting
74
+ ? `Deleting ${itemText}...`
75
+ : `Are you sure you want to delete "${itemDisplayText}"? This will remove the ${itemText}. You can bring it back later.`
76
+ }
77
+ onClose={isDeleting ? null : onCancel}
78
+ buttons={buttons}
79
+ hasPadding
80
+ minWidth={400}
81
+ maxWidth={500}
82
+ >
83
+ {isDeleting && (
84
+ <div className={styles.deleteConfirmationPopup__spinnerContainer}>
85
+ <FontAwesomeIcon icon={faSpinner} spin />
86
+ <div className={styles.deleteConfirmationPopup__message}>
87
+ Please wait while we delete the {itemText}...
88
+ </div>
89
+ </div>
90
+ )}
91
+ </Popup>
92
+ );
93
+ };
94
+
95
+ export { DeleteConfirmationPopup };
@@ -0,0 +1,12 @@
1
+ /* DeleteConfirmationPopup BEM CSS Module */
2
+
3
+ /* Elements */
4
+ .deleteConfirmationPopup__spinnerContainer {
5
+ text-align: center;
6
+ padding: 20px 0;
7
+ }
8
+
9
+ .deleteConfirmationPopup__message {
10
+ font-size: var(--font-size-md);
11
+ color: #666;
12
+ }