@plusscommunities/pluss-feature-builder-web-d 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.
- package/.babelrc +4 -0
- package/dist/index.cjs.js +7792 -0
- package/package.json +54 -0
- package/rollup.config.js +68 -0
- package/src/actions/featureBuilderStringsActions.js +88 -0
- package/src/actions/featureDefinitionsIndex.js +258 -0
- package/src/actions/formActions.js +311 -0
- package/src/actions/index.js +12 -0
- package/src/actions/listingActions.js +350 -0
- package/src/actions/wizardActions.js +240 -0
- package/src/components/ActivityCardExample.jsx +86 -0
- package/src/components/ActivityCardExample.module.css +130 -0
- package/src/components/BackgroundLoader.jsx +33 -0
- package/src/components/BackgroundLoader.module.css +46 -0
- package/src/components/BaseFieldConfig.jsx +305 -0
- package/src/components/BaseFieldConfig.module.css +42 -0
- package/src/components/CenteredContainer.jsx +29 -0
- package/src/components/CenteredContainer.module.css +171 -0
- package/src/components/DeleteConfirmationPopup.jsx +95 -0
- package/src/components/DeleteConfirmationPopup.module.css +12 -0
- package/src/components/ErrorBoundary.jsx +134 -0
- package/src/components/ErrorBoundary.module.css +77 -0
- package/src/components/ErrorMessage.jsx +85 -0
- package/src/components/ErrorMessage.module.css +116 -0
- package/src/components/ExampleDisplay.jsx +26 -0
- package/src/components/ExampleDisplay.module.css +3 -0
- package/src/components/FeatureBuilderSidebar.jsx +84 -0
- package/src/components/FeatureBuilderSuccessPopup.jsx +55 -0
- package/src/components/FeatureBuilderSuccessPopup.module.css +43 -0
- package/src/components/FeatureBuilderWelcomePopup.jsx +51 -0
- package/src/components/FeatureBuilderWelcomePopup.module.css +21 -0
- package/src/components/FeatureListingCard.jsx +104 -0
- package/src/components/FeatureListingCard.module.css +62 -0
- package/src/components/Fields.jsx +460 -0
- package/src/components/Fields.module.css +159 -0
- package/src/components/IconLoader.jsx +153 -0
- package/src/components/IconLoader.module.css +92 -0
- package/src/components/IconSelector.jsx +112 -0
- package/src/components/IconSelector.module.css +197 -0
- package/src/components/ListingEditor.jsx +406 -0
- package/src/components/ListingEditor.module.css +14 -0
- package/src/components/ListingSuccessPopup.jsx +52 -0
- package/src/components/LoadingScreen.jsx +54 -0
- package/src/components/LoadingScreen.module.css +103 -0
- package/src/components/LoadingState.jsx +40 -0
- package/src/components/LoadingState.module.css +18 -0
- package/src/components/PreviewFull.js +24 -0
- package/src/components/PreviewFull.module.css +11 -0
- package/src/components/PreviewGrid.js +14 -0
- package/src/components/PreviewWidget.js +27 -0
- package/src/components/PreviewWidget.module.css +15 -0
- package/src/components/SidebarLayout.jsx +292 -0
- package/src/components/SidebarLayout.module.css +145 -0
- package/src/components/SkeletonLoader.jsx +128 -0
- package/src/components/SkeletonLoader.module.css +295 -0
- package/src/components/SortButtonGroup.jsx +34 -0
- package/src/components/SortButtonGroup.module.css +51 -0
- package/src/components/ToastContainer.jsx +98 -0
- package/src/components/ToastContainer.module.css +156 -0
- package/src/components/ToggleSwitch.js +40 -0
- package/src/components/ToggleSwitch.module.css +48 -0
- package/src/components/TwoColumnInput.jsx +29 -0
- package/src/components/TwoColumnInput.module.css +32 -0
- package/src/components/ViewFull.js +139 -0
- package/src/components/ViewFull.module.css +71 -0
- package/src/components/ViewWidget.js +62 -0
- package/src/components/ViewWidget.module.css +28 -0
- package/src/components/iconCategories.js +135 -0
- package/src/components/iconImports.js +409 -0
- package/src/components/index.js +61 -0
- package/src/components/listing/FileListItem.jsx +86 -0
- package/src/components/listing/GalleryDisplay.jsx +331 -0
- package/src/components/listing/GalleryDisplay.module.css +309 -0
- package/src/components/listing/ListingCTAInput.jsx +82 -0
- package/src/components/listing/ListingDescriptionInput.jsx +73 -0
- package/src/components/listing/ListingField.jsx +101 -0
- package/src/components/listing/ListingField.module.css +106 -0
- package/src/components/listing/ListingFileInput.jsx +255 -0
- package/src/components/listing/ListingFileInput.module.css +192 -0
- package/src/components/listing/ListingForm.jsx +90 -0
- package/src/components/listing/ListingForm.module.css +38 -0
- package/src/components/listing/ListingGalleryInput.jsx +236 -0
- package/src/components/listing/ListingGalleryInput.module.css +131 -0
- package/src/components/listing/ListingImageInput.jsx +153 -0
- package/src/components/listing/ListingTextInput.jsx +72 -0
- package/src/feature.config.js +130 -0
- package/src/helper/index.js +135 -0
- package/src/hooks/useFeatureDefinitionLoader.js +62 -0
- package/src/images/full.png +0 -0
- package/src/images/fullNoTitle.png +0 -0
- package/src/images/previewWidget.png +0 -0
- package/src/images/widget.png +0 -0
- package/src/index.js +38 -0
- package/src/pages/CreateListingPage.jsx +49 -0
- package/src/pages/EditListingPage.jsx +58 -0
- package/src/reducers/featureBuilderReducer.js +744 -0
- package/src/screens/CreateListing.module.css +45 -0
- package/src/screens/Form.module.css +734 -0
- package/src/screens/FormFieldsStep.jsx +689 -0
- package/src/screens/FormLayoutStep.jsx +445 -0
- package/src/screens/FormOverviewStep.jsx +396 -0
- package/src/screens/ListingScreen.jsx +478 -0
- package/src/screens/ListingScreen.module.css +333 -0
- package/src/selectors/featureBuilderSelectors.js +529 -0
- package/src/types/index.js +91 -0
- package/src/utils/textUtils.js +89 -0
- package/src/validators/galleryValidators.js +345 -0
- package/src/values.config.a.js +49 -0
- package/src/values.config.b.js +49 -0
- package/src/values.config.c.js +49 -0
- package/src/values.config.d.js +49 -0
- package/src/values.config.js +49 -0
- package/src/webapi/featureDefinitionActions.js +0 -0
- package/src/webapi/featuresActions.js +90 -0
- package/src/webapi/helper.js +4 -0
- package/src/webapi/index.js +12 -0
- 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 };
|