@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.
- 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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gallery field validation functions
|
|
3
|
+
* Provides comprehensive validation for image gallery fields
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validates minimum number of images
|
|
8
|
+
* @param {Array} images - Array of image URLs or objects
|
|
9
|
+
* @param {number} min - Minimum required images
|
|
10
|
+
* @returns {Object} - Validation result with isValid and message
|
|
11
|
+
*/
|
|
12
|
+
export const minImages = (images, min) => {
|
|
13
|
+
if (!images || !Array.isArray(images)) {
|
|
14
|
+
return {
|
|
15
|
+
isValid: false,
|
|
16
|
+
message: "No images provided",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const validImages = images.filter((img) => img && img !== "");
|
|
21
|
+
const isValid = validImages.length >= min;
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
isValid,
|
|
25
|
+
message: isValid
|
|
26
|
+
? ""
|
|
27
|
+
: `Minimum ${min} image${min > 1 ? "s" : ""} required`,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Validates maximum number of images
|
|
33
|
+
* @param {Array} images - Array of image URLs or objects
|
|
34
|
+
* @param {number} max - Maximum allowed images
|
|
35
|
+
* @returns {Object} - Validation result with isValid and message
|
|
36
|
+
*/
|
|
37
|
+
export const maxImages = (images, max) => {
|
|
38
|
+
if (!images || !Array.isArray(images)) {
|
|
39
|
+
return { isValid: true, message: "" };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const validImages = images.filter((img) => img && img !== "");
|
|
43
|
+
const isValid = validImages.length <= max;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
isValid,
|
|
47
|
+
message: isValid ? "" : `Maximum ${max} image${max > 1 ? "s" : ""} allowed`,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Validates file size for images (if file objects are provided)
|
|
53
|
+
* @param {Array} images - Array of image URLs or file objects
|
|
54
|
+
* @param {number} maxSizeMB - Maximum file size in MB
|
|
55
|
+
* @returns {Object} - Validation result with isValid and message
|
|
56
|
+
*/
|
|
57
|
+
export const fileSize = (images, maxSizeMB) => {
|
|
58
|
+
if (!images || !Array.isArray(images)) {
|
|
59
|
+
return { isValid: true, message: "" };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const maxSizeBytes = maxSizeMB * 1024 * 1024;
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < images.length; i++) {
|
|
65
|
+
const image = images[i];
|
|
66
|
+
|
|
67
|
+
// Check if image is a file object with size property
|
|
68
|
+
if (image && typeof image === "object" && image.size) {
|
|
69
|
+
if (image.size > maxSizeBytes) {
|
|
70
|
+
return {
|
|
71
|
+
isValid: false,
|
|
72
|
+
message: `Image ${i + 1} exceeds ${maxSizeMB}MB limit`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { isValid: true, message: "" };
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validates file types for images
|
|
83
|
+
* @param {Array} images - Array of image URLs or file objects
|
|
84
|
+
* @param {string[]} allowedTypes - Array of allowed MIME types
|
|
85
|
+
* @returns {Object} - Validation result with isValid and message
|
|
86
|
+
*/
|
|
87
|
+
export const fileType = (images, allowedTypes) => {
|
|
88
|
+
if (
|
|
89
|
+
!images ||
|
|
90
|
+
!Array.isArray(images) ||
|
|
91
|
+
!allowedTypes ||
|
|
92
|
+
!Array.isArray(allowedTypes)
|
|
93
|
+
) {
|
|
94
|
+
return { isValid: true, message: "" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const allowedExtensions = allowedTypes.flatMap((type) => {
|
|
98
|
+
// Convert MIME types to extensions for basic validation
|
|
99
|
+
if (type.includes("jpeg")) return ["jpg", "jpeg"];
|
|
100
|
+
if (type.includes("png")) return ["png"];
|
|
101
|
+
if (type.includes("webp")) return ["webp"];
|
|
102
|
+
if (type.includes("gif")) return ["gif"];
|
|
103
|
+
return [];
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < images.length; i++) {
|
|
107
|
+
const image = images[i];
|
|
108
|
+
|
|
109
|
+
if (image && typeof image === "object") {
|
|
110
|
+
// Check file object type
|
|
111
|
+
if (image.type && !allowedTypes.includes(image.type)) {
|
|
112
|
+
return {
|
|
113
|
+
isValid: false,
|
|
114
|
+
message: `Image ${i + 1} has unsupported file type`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
} else if (typeof image === "string") {
|
|
118
|
+
// Check URL extension (basic validation)
|
|
119
|
+
const extension = image.split(".").pop()?.toLowerCase();
|
|
120
|
+
if (extension && !allowedExtensions.includes(extension)) {
|
|
121
|
+
return {
|
|
122
|
+
isValid: false,
|
|
123
|
+
message: `Image ${i + 1} has unsupported file type`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { isValid: true, message: "" };
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validates aspect ratio for images (basic validation for file objects with dimensions)
|
|
134
|
+
* @param {Array} images - Array of image URLs or file objects
|
|
135
|
+
* @param {string} requiredRatio - Required aspect ratio ('16:9', '4:3', '1:1', 'any')
|
|
136
|
+
* @returns {Object} - Validation result with isValid and message
|
|
137
|
+
*/
|
|
138
|
+
export const aspectRatio = (images, requiredRatio) => {
|
|
139
|
+
if (
|
|
140
|
+
!images ||
|
|
141
|
+
!Array.isArray(images) ||
|
|
142
|
+
!requiredRatio ||
|
|
143
|
+
requiredRatio === "any"
|
|
144
|
+
) {
|
|
145
|
+
return { isValid: true, message: "" };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Parse required aspect ratio
|
|
149
|
+
const ratioMap = {
|
|
150
|
+
"16:9": 16 / 9,
|
|
151
|
+
"4:3": 4 / 3,
|
|
152
|
+
"1:1": 1,
|
|
153
|
+
square: 1,
|
|
154
|
+
landscape: 16 / 9,
|
|
155
|
+
portrait: 3 / 4,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const targetRatio = ratioMap[requiredRatio.toLowerCase()];
|
|
159
|
+
if (!targetRatio) {
|
|
160
|
+
return { isValid: true, message: "" }; // Unknown ratio, skip validation
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < images.length; i++) {
|
|
164
|
+
const image = images[i];
|
|
165
|
+
|
|
166
|
+
// Only validate if we have dimension information (file objects with naturalWidth/Height)
|
|
167
|
+
if (image && typeof image === "object") {
|
|
168
|
+
if (image.naturalWidth && image.naturalHeight) {
|
|
169
|
+
const imageRatio = image.naturalWidth / image.naturalHeight;
|
|
170
|
+
const tolerance = 0.1; // 10% tolerance
|
|
171
|
+
|
|
172
|
+
if (Math.abs(imageRatio - targetRatio) > tolerance) {
|
|
173
|
+
return {
|
|
174
|
+
isValid: false,
|
|
175
|
+
message: `Image ${i + 1} does not match required aspect ratio (${requiredRatio})`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { isValid: true, message: "" };
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Validates required field (at least one image)
|
|
187
|
+
* @param {Array} images - Array of image URLs or objects
|
|
188
|
+
* @returns {Object} - Validation result with isValid and message
|
|
189
|
+
*/
|
|
190
|
+
export const required = (images) => {
|
|
191
|
+
if (!images || !Array.isArray(images)) {
|
|
192
|
+
return {
|
|
193
|
+
isValid: false,
|
|
194
|
+
message: "This field is required",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const validImages = images.filter((img) => img && img !== "");
|
|
199
|
+
const isValid = validImages.length > 0;
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
isValid,
|
|
203
|
+
message: isValid ? "" : "At least one image is required",
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Detects duplicate images in array
|
|
209
|
+
* @param {Array} images - Array of image URLs or objects
|
|
210
|
+
* @returns {Object} - Validation result with isValid and message
|
|
211
|
+
*/
|
|
212
|
+
export const noDuplicates = (images) => {
|
|
213
|
+
if (!images || !Array.isArray(images)) {
|
|
214
|
+
return { isValid: true, message: "" };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const validImages = images.filter((img) => img && img !== "");
|
|
218
|
+
const uniqueImages = [...new Set(validImages)];
|
|
219
|
+
|
|
220
|
+
const hasDuplicates = validImages.length !== uniqueImages.length;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
isValid: !hasDuplicates,
|
|
224
|
+
message: hasDuplicates ? "Duplicate images detected" : "",
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Comprehensive gallery field validation
|
|
230
|
+
* @param {Array} images - Array of image URLs or objects
|
|
231
|
+
* @param {Object} rules - Validation rules object
|
|
232
|
+
* @param {boolean} rules.isRequired - Whether field is required
|
|
233
|
+
* @param {number} rules.minImages - Minimum number of images
|
|
234
|
+
* @param {number} rules.maxImages - Maximum number of images
|
|
235
|
+
* @param {string} rules.maxFileSize - Maximum file size (e.g., "5MB")
|
|
236
|
+
* @param {string[]} rules.allowedTypes - Allowed file types
|
|
237
|
+
* @param {string} rules.aspectRatio - Required aspect ratio
|
|
238
|
+
* @param {Function[]} rules.customRules - Custom validation functions
|
|
239
|
+
* @returns {Object} - Validation result with isValid, errors, and warnings
|
|
240
|
+
*/
|
|
241
|
+
export const validateGallery = (images, rules = {}) => {
|
|
242
|
+
const errors = [];
|
|
243
|
+
const warnings = [];
|
|
244
|
+
|
|
245
|
+
// Required validation
|
|
246
|
+
if (rules.isRequired) {
|
|
247
|
+
const requiredResult = required(images);
|
|
248
|
+
if (!requiredResult.isValid) {
|
|
249
|
+
errors.push(requiredResult.message);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Only validate other rules if there are images or field is required
|
|
254
|
+
const hasImages = images && images.length > 0;
|
|
255
|
+
if (!hasImages && !rules.isRequired) {
|
|
256
|
+
return { isValid: true, errors: [], warnings: [] };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Minimum images validation
|
|
260
|
+
if (rules.minImages) {
|
|
261
|
+
const minResult = minImages(images, rules.minImages);
|
|
262
|
+
if (!minResult.isValid) {
|
|
263
|
+
errors.push(minResult.message);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Maximum images validation
|
|
268
|
+
if (rules.maxImages) {
|
|
269
|
+
const maxResult = maxImages(images, rules.maxImages);
|
|
270
|
+
if (!maxResult.isValid) {
|
|
271
|
+
errors.push(maxResult.message);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// File size validation
|
|
276
|
+
if (rules.maxFileSize) {
|
|
277
|
+
const sizeMB = parseFloat(rules.maxFileSize.replace(/[^0-9.]/g, ""));
|
|
278
|
+
const sizeResult = fileSize(images, sizeMB || 5);
|
|
279
|
+
if (!sizeResult.isValid) {
|
|
280
|
+
errors.push(sizeResult.message);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// File type validation
|
|
285
|
+
if (rules.allowedTypes && rules.allowedTypes.length > 0) {
|
|
286
|
+
const typeResult = fileType(images, rules.allowedTypes);
|
|
287
|
+
if (!typeResult.isValid) {
|
|
288
|
+
errors.push(typeResult.message);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Aspect ratio validation
|
|
293
|
+
if (rules.aspectRatio && rules.aspectRatio !== "any") {
|
|
294
|
+
const ratioResult = aspectRatio(images, rules.aspectRatio);
|
|
295
|
+
if (!ratioResult.isValid) {
|
|
296
|
+
warnings.push(ratioResult.message); // Make aspect ratio a warning since it's harder to enforce
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Duplicate validation
|
|
301
|
+
const duplicateResult = noDuplicates(images);
|
|
302
|
+
if (!duplicateResult.isValid) {
|
|
303
|
+
warnings.push(duplicateResult.message);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Custom validation rules
|
|
307
|
+
if (rules.customRules && Array.isArray(rules.customRules)) {
|
|
308
|
+
for (const customRule of rules.customRules) {
|
|
309
|
+
if (typeof customRule === "function") {
|
|
310
|
+
try {
|
|
311
|
+
const customResult = customRule(images);
|
|
312
|
+
if (customResult && typeof customResult === "object") {
|
|
313
|
+
if (!customResult.isValid) {
|
|
314
|
+
if (customResult.severity === "warning") {
|
|
315
|
+
warnings.push(customResult.message);
|
|
316
|
+
} else {
|
|
317
|
+
errors.push(customResult.message);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
// Custom validation rule failed
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
isValid: errors.length === 0,
|
|
330
|
+
errors,
|
|
331
|
+
warnings,
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Export individual validators for specific use cases
|
|
336
|
+
export default {
|
|
337
|
+
minImages,
|
|
338
|
+
maxImages,
|
|
339
|
+
fileSize,
|
|
340
|
+
fileType,
|
|
341
|
+
aspectRatio,
|
|
342
|
+
required,
|
|
343
|
+
noDuplicates,
|
|
344
|
+
validateGallery,
|
|
345
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"values" defines configurable options for this extension.
|
|
3
|
+
It can be swapped prior to publishing via copy:set
|
|
4
|
+
*/
|
|
5
|
+
export const values = {
|
|
6
|
+
featureKey: "feature-builder-a",
|
|
7
|
+
featureKeyRoute: "/feature-builder-a", // Dynamic route prefix
|
|
8
|
+
featureId: "feature-builder-a-default", // Fixed ID for the single feature definition
|
|
9
|
+
reducerKey: "featureBuilderA", // Define reducer 'slice' key.
|
|
10
|
+
singularName: "Build Your Feature A",
|
|
11
|
+
description: "Create custom forms for mobile application (Variant A).",
|
|
12
|
+
emptyText: "No custom forms available",
|
|
13
|
+
textMenuTitle: "Build Your Feature A",
|
|
14
|
+
textPermissionFeatureBuilderDefinition: "Build Your Feature A Definition",
|
|
15
|
+
textPermissionFeatureBuilderContent:
|
|
16
|
+
"Create and Manage Content in Build Your Feature A",
|
|
17
|
+
|
|
18
|
+
// Routes
|
|
19
|
+
routeFormOverviewStep: "/feature-builder-a/definition/overview",
|
|
20
|
+
routeFormFieldsStep: "/feature-builder-a/definition/fields",
|
|
21
|
+
routeFormLayoutStep: "/feature-builder-a/definition/layout",
|
|
22
|
+
routeListingScreen: "/feature-builder-a/listing",
|
|
23
|
+
routeCreateListing: "/feature-builder-a/listing/create",
|
|
24
|
+
|
|
25
|
+
routeEditListing: "/feature-builder-a/listing/edit/:id",
|
|
26
|
+
|
|
27
|
+
// Screen names
|
|
28
|
+
screenFormOverviewStep: "FormOverviewStepA",
|
|
29
|
+
screenFormFieldsStep: "FormFieldsStepA",
|
|
30
|
+
screenFormLayoutStep: "FormLayoutStepA",
|
|
31
|
+
screenListingScreen: "ListingScreenA",
|
|
32
|
+
|
|
33
|
+
// Page names
|
|
34
|
+
pageCreateListing: "CreateListingPageA",
|
|
35
|
+
pageEditListing: "EditListingPageA",
|
|
36
|
+
|
|
37
|
+
// Permissions
|
|
38
|
+
permissionFeatureBuilderDefinition: "featureBuilderDefinitionA",
|
|
39
|
+
permissionFeatureBuilderContent: "featureBuilderContentA",
|
|
40
|
+
|
|
41
|
+
// Menu
|
|
42
|
+
menuIcon: "tool",
|
|
43
|
+
menuKey: "feature-builder-definition-a",
|
|
44
|
+
menuIsFontAwesome: true,
|
|
45
|
+
menuOrder: 20, // Lower number = higher priority
|
|
46
|
+
|
|
47
|
+
// Default Values
|
|
48
|
+
defaultIcon: "star",
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"values" defines configurable options for this extension.
|
|
3
|
+
It can be swapped prior to publishing via copy:set
|
|
4
|
+
*/
|
|
5
|
+
export const values = {
|
|
6
|
+
featureKey: "feature-builder-b",
|
|
7
|
+
featureKeyRoute: "/feature-builder-b", // Dynamic route prefix
|
|
8
|
+
featureId: "feature-builder-b-default", // Fixed ID for the single feature definition
|
|
9
|
+
reducerKey: "featureBuilderB", // Define reducer 'slice' key.
|
|
10
|
+
singularName: "Build Your Feature B",
|
|
11
|
+
description: "Create custom forms for mobile application (Variant B).",
|
|
12
|
+
emptyText: "No custom forms available",
|
|
13
|
+
textMenuTitle: "Build Your Feature B",
|
|
14
|
+
textPermissionFeatureBuilderDefinition: "Build Your Feature B Definition",
|
|
15
|
+
textPermissionFeatureBuilderContent:
|
|
16
|
+
"Create and Manage Content in Build Your Feature B",
|
|
17
|
+
|
|
18
|
+
// Routes
|
|
19
|
+
routeFormOverviewStep: "/feature-builder-b/definition/overview",
|
|
20
|
+
routeFormFieldsStep: "/feature-builder-b/definition/fields",
|
|
21
|
+
routeFormLayoutStep: "/feature-builder-b/definition/layout",
|
|
22
|
+
routeListingScreen: "/feature-builder-b/listing",
|
|
23
|
+
routeCreateListing: "/feature-builder-b/listing/create",
|
|
24
|
+
|
|
25
|
+
routeEditListing: "/feature-builder-b/listing/edit/:id",
|
|
26
|
+
|
|
27
|
+
// Screen names
|
|
28
|
+
screenFormOverviewStep: "FormOverviewStepB",
|
|
29
|
+
screenFormFieldsStep: "FormFieldsStepB",
|
|
30
|
+
screenFormLayoutStep: "FormLayoutStepB",
|
|
31
|
+
screenListingScreen: "ListingScreenB",
|
|
32
|
+
|
|
33
|
+
// Page names
|
|
34
|
+
pageCreateListing: "CreateListingPageB",
|
|
35
|
+
pageEditListing: "EditListingPageB",
|
|
36
|
+
|
|
37
|
+
// Permissions
|
|
38
|
+
permissionFeatureBuilderDefinition: "featureBuilderDefinitionB",
|
|
39
|
+
permissionFeatureBuilderContent: "featureBuilderContentB",
|
|
40
|
+
|
|
41
|
+
// Menu
|
|
42
|
+
menuIcon: "tool",
|
|
43
|
+
menuKey: "feature-builder-definition-b",
|
|
44
|
+
menuIsFontAwesome: true,
|
|
45
|
+
menuOrder: 20, // Lower number = higher priority
|
|
46
|
+
|
|
47
|
+
// Default Values
|
|
48
|
+
defaultIcon: "star",
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"values" defines configurable options for this extension.
|
|
3
|
+
It can be swapped prior to publishing via copy:set
|
|
4
|
+
*/
|
|
5
|
+
export const values = {
|
|
6
|
+
featureKey: "feature-builder-c",
|
|
7
|
+
featureKeyRoute: "/feature-builder-c", // Dynamic route prefix
|
|
8
|
+
featureId: "feature-builder-c-default", // Fixed ID for the single feature definition
|
|
9
|
+
reducerKey: "featureBuilderC", // Define reducer 'slice' key.
|
|
10
|
+
singularName: "Build Your Feature C",
|
|
11
|
+
description: "Create custom forms for mobile application (Variant C).",
|
|
12
|
+
emptyText: "No custom forms available",
|
|
13
|
+
textMenuTitle: "Build Your Feature C",
|
|
14
|
+
textPermissionFeatureBuilderDefinition: "Build Your Feature C Definition",
|
|
15
|
+
textPermissionFeatureBuilderContent:
|
|
16
|
+
"Create and Manage Content in Build Your Feature C",
|
|
17
|
+
|
|
18
|
+
// Routes
|
|
19
|
+
routeFormOverviewStep: "/feature-builder-c/definition/overview",
|
|
20
|
+
routeFormFieldsStep: "/feature-builder-c/definition/fields",
|
|
21
|
+
routeFormLayoutStep: "/feature-builder-c/definition/layout",
|
|
22
|
+
routeListingScreen: "/feature-builder-c/listing",
|
|
23
|
+
routeCreateListing: "/feature-builder-c/listing/create",
|
|
24
|
+
|
|
25
|
+
routeEditListing: "/feature-builder-c/listing/edit/:id",
|
|
26
|
+
|
|
27
|
+
// Screen names
|
|
28
|
+
screenFormOverviewStep: "FormOverviewStepC",
|
|
29
|
+
screenFormFieldsStep: "FormFieldsStepC",
|
|
30
|
+
screenFormLayoutStep: "FormLayoutStepC",
|
|
31
|
+
screenListingScreen: "ListingScreenC",
|
|
32
|
+
|
|
33
|
+
// Page names
|
|
34
|
+
pageCreateListing: "CreateListingPageC",
|
|
35
|
+
pageEditListing: "EditListingPageC",
|
|
36
|
+
|
|
37
|
+
// Permissions
|
|
38
|
+
permissionFeatureBuilderDefinition: "featureBuilderDefinitionC",
|
|
39
|
+
permissionFeatureBuilderContent: "featureBuilderContentC",
|
|
40
|
+
|
|
41
|
+
// Menu
|
|
42
|
+
menuIcon: "tool",
|
|
43
|
+
menuKey: "feature-builder-definition-c",
|
|
44
|
+
menuIsFontAwesome: true,
|
|
45
|
+
menuOrder: 20, // Lower number = higher priority
|
|
46
|
+
|
|
47
|
+
// Default Values
|
|
48
|
+
defaultIcon: "star",
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"values" defines configurable options for this extension.
|
|
3
|
+
It can be swapped prior to publishing via copy:set
|
|
4
|
+
*/
|
|
5
|
+
export const values = {
|
|
6
|
+
featureKey: "feature-builder-d",
|
|
7
|
+
featureKeyRoute: "/feature-builder-d", // Dynamic route prefix
|
|
8
|
+
featureId: "feature-builder-d-default", // Fixed ID for the single feature definition
|
|
9
|
+
reducerKey: "featureBuilderD", // Define reducer 'slice' key.
|
|
10
|
+
singularName: "Build Your Feature D",
|
|
11
|
+
description: "Create custom forms for mobile application (Variant D).",
|
|
12
|
+
emptyText: "No custom forms available",
|
|
13
|
+
textMenuTitle: "Build Your Feature D",
|
|
14
|
+
textPermissionFeatureBuilderDefinition: "Build Your Feature D Definition",
|
|
15
|
+
textPermissionFeatureBuilderContent:
|
|
16
|
+
"Create and Manage Content in Build Your Feature D",
|
|
17
|
+
|
|
18
|
+
// Routes
|
|
19
|
+
routeFormOverviewStep: "/feature-builder-d/definition/overview",
|
|
20
|
+
routeFormFieldsStep: "/feature-builder-d/definition/fields",
|
|
21
|
+
routeFormLayoutStep: "/feature-builder-d/definition/layout",
|
|
22
|
+
routeListingScreen: "/feature-builder-d/listing",
|
|
23
|
+
routeCreateListing: "/feature-builder-d/listing/create",
|
|
24
|
+
|
|
25
|
+
routeEditListing: "/feature-builder-d/listing/edit/:id",
|
|
26
|
+
|
|
27
|
+
// Screen names
|
|
28
|
+
screenFormOverviewStep: "FormOverviewStepD",
|
|
29
|
+
screenFormFieldsStep: "FormFieldsStepD",
|
|
30
|
+
screenFormLayoutStep: "FormLayoutStepD",
|
|
31
|
+
screenListingScreen: "ListingScreenD",
|
|
32
|
+
|
|
33
|
+
// Page names
|
|
34
|
+
pageCreateListing: "CreateListingPageD",
|
|
35
|
+
pageEditListing: "EditListingPageD",
|
|
36
|
+
|
|
37
|
+
// Permissions
|
|
38
|
+
permissionFeatureBuilderDefinition: "featureBuilderDefinitionD",
|
|
39
|
+
permissionFeatureBuilderContent: "featureBuilderContentD",
|
|
40
|
+
|
|
41
|
+
// Menu
|
|
42
|
+
menuIcon: "tool",
|
|
43
|
+
menuKey: "feature-builder-definition-d",
|
|
44
|
+
menuIsFontAwesome: true,
|
|
45
|
+
menuOrder: 20, // Lower number = higher priority
|
|
46
|
+
|
|
47
|
+
// Default Values
|
|
48
|
+
defaultIcon: "star",
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
"values" defines configurable options for this extension.
|
|
3
|
+
It can be swapped prior to publishing via copy:set
|
|
4
|
+
*/
|
|
5
|
+
export const values = {
|
|
6
|
+
featureKey: "feature-builder-a",
|
|
7
|
+
featureKeyRoute: "/feature-builder-a", // Dynamic route prefix
|
|
8
|
+
featureId: "feature-builder-a-default", // Fixed ID for the single feature definition
|
|
9
|
+
reducerKey: "featureBuilderA", // Define reducer 'slice' key.
|
|
10
|
+
singularName: "Build Your Feature A",
|
|
11
|
+
description: "Create custom forms for mobile application (Variant A).",
|
|
12
|
+
emptyText: "No custom forms available",
|
|
13
|
+
textMenuTitle: "Build Your Feature A",
|
|
14
|
+
textPermissionFeatureBuilderDefinition: "Build Your Feature A Definition",
|
|
15
|
+
textPermissionFeatureBuilderContent:
|
|
16
|
+
"Create and Manage Content in Build Your Feature A",
|
|
17
|
+
|
|
18
|
+
// Routes
|
|
19
|
+
routeFormOverviewStep: "/feature-builder-a/definition/overview",
|
|
20
|
+
routeFormFieldsStep: "/feature-builder-a/definition/fields",
|
|
21
|
+
routeFormLayoutStep: "/feature-builder-a/definition/layout",
|
|
22
|
+
routeListingScreen: "/feature-builder-a/listing",
|
|
23
|
+
routeCreateListing: "/feature-builder-a/listing/create",
|
|
24
|
+
|
|
25
|
+
routeEditListing: "/feature-builder-a/listing/edit/:id",
|
|
26
|
+
|
|
27
|
+
// Screen names
|
|
28
|
+
screenFormOverviewStep: "FormOverviewStepA",
|
|
29
|
+
screenFormFieldsStep: "FormFieldsStepA",
|
|
30
|
+
screenFormLayoutStep: "FormLayoutStepA",
|
|
31
|
+
screenListingScreen: "ListingScreenA",
|
|
32
|
+
|
|
33
|
+
// Page names
|
|
34
|
+
pageCreateListing: "CreateListingPageA",
|
|
35
|
+
pageEditListing: "EditListingPageA",
|
|
36
|
+
|
|
37
|
+
// Permissions
|
|
38
|
+
permissionFeatureBuilderDefinition: "featureBuilderDefinitionA",
|
|
39
|
+
permissionFeatureBuilderContent: "featureBuilderContentA",
|
|
40
|
+
|
|
41
|
+
// Menu
|
|
42
|
+
menuIcon: "tool",
|
|
43
|
+
menuKey: "feature-builder-definition-a",
|
|
44
|
+
menuIsFontAwesome: true,
|
|
45
|
+
menuOrder: 20, // Lower number = higher priority
|
|
46
|
+
|
|
47
|
+
// Default Values
|
|
48
|
+
defaultIcon: "star",
|
|
49
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { PlussCore } from "../feature.config";
|
|
2
|
+
const { Helper, Session } = PlussCore;
|
|
3
|
+
|
|
4
|
+
const { getUrl } = Helper;
|
|
5
|
+
const { authedFunction } = Session;
|
|
6
|
+
|
|
7
|
+
export const featureDefinitionActions = {
|
|
8
|
+
/**
|
|
9
|
+
* Get the single feature definition by ID
|
|
10
|
+
* Path: {id}
|
|
11
|
+
*/
|
|
12
|
+
getSingle: async (id, site) => {
|
|
13
|
+
const query = { id, site };
|
|
14
|
+
return authedFunction({
|
|
15
|
+
method: "GET",
|
|
16
|
+
url: getUrl("feature-builder", "definition/get/single", query),
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new feature definition with the provided configuration
|
|
22
|
+
*
|
|
23
|
+
* @param {string} id - The unique identifier for the new feature definition
|
|
24
|
+
* @param {string} site - The site ID where the feature definition will be created
|
|
25
|
+
* @param {FeatureDefinition} featureDefinition - The feature definition data to create
|
|
26
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response with created feature definition
|
|
27
|
+
* @throws {Error} When creation fails due to validation or API errors
|
|
28
|
+
*
|
|
29
|
+
*/
|
|
30
|
+
create: async (id, site, featureDefinition) => {
|
|
31
|
+
return authedFunction({
|
|
32
|
+
method: "POST",
|
|
33
|
+
url: getUrl("feature-builder", "definition/update/create"),
|
|
34
|
+
data: { id, site, featureDefinition },
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Updates an existing feature definition with new configuration
|
|
40
|
+
*
|
|
41
|
+
* @param {FeatureDefinition} featureDefinitionData - The updated feature definition data
|
|
42
|
+
* @param {string} featureDefinitionData.id - The unique identifier of the feature definition to update
|
|
43
|
+
* @param {string} [featureDefinitionData.displayName] - Updated display name
|
|
44
|
+
* @param {Field[]} [featureDefinitionData.fields] - Updated field definitions
|
|
45
|
+
* @param {Object} [featureDefinitionData.layout] - Updated layout configuration
|
|
46
|
+
* @param {string} site - The site ID where the feature definition exists
|
|
47
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response with updated feature definition
|
|
48
|
+
* @throws {Error} When update fails due to validation or API errors
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* try {
|
|
52
|
+
* const response = await featureDefinitionActions.edit(
|
|
53
|
+
* {
|
|
54
|
+
* id: 'feature-123',
|
|
55
|
+
* displayName: 'Updated Form',
|
|
56
|
+
* fields: [...]
|
|
57
|
+
* },
|
|
58
|
+
* 'site-123'
|
|
59
|
+
* );
|
|
60
|
+
*/
|
|
61
|
+
edit: async (featureDefinitionData, site) => {
|
|
62
|
+
// Ensure site is included in the request body
|
|
63
|
+
const dataWithSite = {
|
|
64
|
+
site: site,
|
|
65
|
+
...featureDefinitionData,
|
|
66
|
+
};
|
|
67
|
+
return authedFunction({
|
|
68
|
+
method: "POST",
|
|
69
|
+
url: getUrl("feature-builder", "definition/update/edit"),
|
|
70
|
+
data: dataWithSite,
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Soft deletes a feature definition (marks as deleted but doesn't permanently remove)
|
|
76
|
+
*
|
|
77
|
+
* @param {string} id - The unique identifier of the feature definition to delete
|
|
78
|
+
* @param {string} site - The site ID where the feature definition exists
|
|
79
|
+
* @returns {Promise<ApiResponse>} Promise resolving to API response confirming deletion
|
|
80
|
+
* @throws {Error} When deletion fails or feature definition is not found
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
delete: async (id, site) => {
|
|
84
|
+
return authedFunction({
|
|
85
|
+
method: "POST",
|
|
86
|
+
url: getUrl("feature-builder", "definition/update/delete"),
|
|
87
|
+
data: { id, site },
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|