@plusscommunities/pluss-feature-builder-web-d 1.0.2-beta.7 → 1.0.2-beta.9
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/dist/index.cjs.js +165 -238
- package/package.json +1 -1
- package/src/actions/formActions.js +148 -148
- package/src/actions/wizardActions.js +166 -166
- package/src/components/BaseFieldConfig.jsx +3 -3
- package/src/components/FeatureBuilderSuccessPopup.jsx +9 -15
- package/src/components/FeatureBuilderSuccessPopup.module.css +1 -3
- package/src/components/Fields.jsx +31 -68
- package/src/components/ListingEditor.jsx +2 -2
- package/src/components/SidebarLayout.jsx +4 -4
- package/src/components/listing/ListingFileInput.jsx +98 -80
- package/src/components/listing/ListingFileInput.module.css +1 -4
- package/src/components/listing/ListingGalleryInput.module.css +2 -1
- 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/reducers/featureBuilderReducer.js +12 -6
- package/src/screens/FormFieldsStep.jsx +4 -67
- package/src/screens/FormLayoutStep.jsx +383 -420
- package/src/screens/FormOverviewStep.jsx +349 -349
- package/src/selectors/featureBuilderSelectors.js +1 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
3
|
-
import {
|
|
3
|
+
import { Text, Button } from "./index.js";
|
|
4
4
|
import { BaseFieldConfig } from "./BaseFieldConfig.jsx";
|
|
5
5
|
import { iconImports } from "./iconImports.js";
|
|
6
6
|
import styles from "./Fields.module.css";
|
|
@@ -130,7 +130,6 @@ export const Field = (props) => {
|
|
|
130
130
|
);
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
// Padlock icon component for base fields
|
|
134
133
|
const PadlockIcon = () => {
|
|
135
134
|
const icon = iconImports.lock;
|
|
136
135
|
return (
|
|
@@ -140,39 +139,6 @@ const PadlockIcon = () => {
|
|
|
140
139
|
);
|
|
141
140
|
};
|
|
142
141
|
|
|
143
|
-
// Field type icon component
|
|
144
|
-
const FieldTypeIcon = ({ type }) => {
|
|
145
|
-
const getIconForType = (type) => {
|
|
146
|
-
switch (type) {
|
|
147
|
-
case "title":
|
|
148
|
-
return iconImports.heading;
|
|
149
|
-
case "text":
|
|
150
|
-
return iconImports.pen;
|
|
151
|
-
case "description":
|
|
152
|
-
return iconImports.alignLeft;
|
|
153
|
-
case "image":
|
|
154
|
-
return iconImports.image;
|
|
155
|
-
case "feature-image":
|
|
156
|
-
return iconImports.camera;
|
|
157
|
-
|
|
158
|
-
case "cta":
|
|
159
|
-
return iconImports.link;
|
|
160
|
-
case "file":
|
|
161
|
-
return iconImports.file;
|
|
162
|
-
default:
|
|
163
|
-
return iconImports.cog;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const icon = getIconForType(type);
|
|
168
|
-
|
|
169
|
-
return (
|
|
170
|
-
<span className={styles.fieldTypeIcon}>
|
|
171
|
-
<FontAwesomeIcon icon={icon} />
|
|
172
|
-
</span>
|
|
173
|
-
);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
142
|
// Helper function to get display text for field types
|
|
177
143
|
const getFieldTypeDisplay = (type) => {
|
|
178
144
|
switch (type) {
|
|
@@ -188,7 +154,6 @@ const getFieldTypeDisplay = (type) => {
|
|
|
188
154
|
return "Gallery";
|
|
189
155
|
case "feature-image":
|
|
190
156
|
return "Feature Image";
|
|
191
|
-
|
|
192
157
|
case "cta":
|
|
193
158
|
return "Action Button";
|
|
194
159
|
case "file":
|
|
@@ -207,23 +172,22 @@ const TitleField = (props) => {
|
|
|
207
172
|
showWarnings,
|
|
208
173
|
id,
|
|
209
174
|
} = props;
|
|
210
|
-
const { placeholder, label,
|
|
175
|
+
const { placeholder, label, helpText } = props.values;
|
|
211
176
|
|
|
212
177
|
return (
|
|
213
178
|
<BaseFieldConfig
|
|
214
179
|
label={label}
|
|
215
180
|
placeholder={placeholder}
|
|
216
181
|
helpText={helpText}
|
|
217
|
-
isRequired={true}
|
|
182
|
+
isRequired={true}
|
|
218
183
|
setLabel={setLabel}
|
|
219
|
-
setPlaceholder={
|
|
184
|
+
setPlaceholder={setPlaceholder}
|
|
220
185
|
setHelpText={setHelpText}
|
|
221
|
-
toggleIsRequired={() => {}}
|
|
186
|
+
toggleIsRequired={() => {}}
|
|
222
187
|
stepErrors={stepErrors}
|
|
223
188
|
showWarnings={showWarnings}
|
|
224
189
|
id={id}
|
|
225
190
|
fieldType="title"
|
|
226
|
-
id={id} // Fix: pass the field ID
|
|
227
191
|
/>
|
|
228
192
|
);
|
|
229
193
|
};
|
|
@@ -235,25 +199,25 @@ const TextField = (props) => {
|
|
|
235
199
|
setHelpText,
|
|
236
200
|
stepErrors,
|
|
237
201
|
showWarnings,
|
|
202
|
+
toggleIsRequired,
|
|
238
203
|
id,
|
|
239
204
|
} = props;
|
|
240
|
-
const { placeholder, label,
|
|
205
|
+
const { placeholder, label, helpText, isRequired } = props.values;
|
|
241
206
|
|
|
242
207
|
return (
|
|
243
208
|
<BaseFieldConfig
|
|
244
209
|
label={label}
|
|
245
210
|
placeholder={placeholder}
|
|
246
211
|
helpText={helpText}
|
|
247
|
-
isRequired={
|
|
212
|
+
isRequired={isRequired}
|
|
248
213
|
setLabel={setLabel}
|
|
249
|
-
setPlaceholder={
|
|
214
|
+
setPlaceholder={setPlaceholder}
|
|
250
215
|
setHelpText={setHelpText}
|
|
251
|
-
toggleIsRequired={
|
|
216
|
+
toggleIsRequired={toggleIsRequired}
|
|
252
217
|
stepErrors={stepErrors}
|
|
253
218
|
showWarnings={showWarnings}
|
|
254
219
|
id={id}
|
|
255
220
|
fieldType="text"
|
|
256
|
-
id={id} // Fix: pass the field ID
|
|
257
221
|
/>
|
|
258
222
|
);
|
|
259
223
|
};
|
|
@@ -267,7 +231,7 @@ const DescriptionField = (props) => {
|
|
|
267
231
|
stepErrors,
|
|
268
232
|
showWarnings,
|
|
269
233
|
setUseAsSummary,
|
|
270
|
-
id,
|
|
234
|
+
id,
|
|
271
235
|
} = props;
|
|
272
236
|
const { placeholder, label, isRequired, helpText, useAsSummary } =
|
|
273
237
|
props.values;
|
|
@@ -279,7 +243,7 @@ const DescriptionField = (props) => {
|
|
|
279
243
|
helpText={helpText}
|
|
280
244
|
isRequired={isRequired}
|
|
281
245
|
setLabel={setLabel}
|
|
282
|
-
setPlaceholder={
|
|
246
|
+
setPlaceholder={setPlaceholder}
|
|
283
247
|
setHelpText={setHelpText}
|
|
284
248
|
toggleIsRequired={toggleIsRequired}
|
|
285
249
|
stepErrors={stepErrors}
|
|
@@ -301,7 +265,7 @@ const ImageField = (props) => {
|
|
|
301
265
|
toggleAllowCaption,
|
|
302
266
|
stepErrors,
|
|
303
267
|
showWarnings,
|
|
304
|
-
id,
|
|
268
|
+
id,
|
|
305
269
|
} = props;
|
|
306
270
|
const { label, isRequired, placeholder, allowCaption, helpText } =
|
|
307
271
|
props.values;
|
|
@@ -321,20 +285,14 @@ const ImageField = (props) => {
|
|
|
321
285
|
toggleAllowCaption={toggleAllowCaption}
|
|
322
286
|
stepErrors={stepErrors}
|
|
323
287
|
showWarnings={showWarnings}
|
|
324
|
-
id={id}
|
|
288
|
+
id={id}
|
|
325
289
|
fieldType="image"
|
|
326
290
|
/>
|
|
327
291
|
);
|
|
328
292
|
};
|
|
329
293
|
|
|
330
294
|
const FeatureImageField = (props) => {
|
|
331
|
-
const {
|
|
332
|
-
setLabel,
|
|
333
|
-
setHelpText,
|
|
334
|
-
stepErrors,
|
|
335
|
-
showWarnings,
|
|
336
|
-
id, // Get ID from root props
|
|
337
|
-
} = props;
|
|
295
|
+
const { setLabel, setHelpText, stepErrors, showWarnings, id } = props;
|
|
338
296
|
const { label, placeholder, helpText } = props.values;
|
|
339
297
|
|
|
340
298
|
return (
|
|
@@ -342,14 +300,14 @@ const FeatureImageField = (props) => {
|
|
|
342
300
|
label={label}
|
|
343
301
|
placeholder={placeholder}
|
|
344
302
|
helpText={helpText}
|
|
345
|
-
isRequired={true}
|
|
303
|
+
isRequired={true}
|
|
346
304
|
setLabel={setLabel}
|
|
347
305
|
setPlaceholder={() => {}}
|
|
348
306
|
setHelpText={setHelpText}
|
|
349
|
-
toggleIsRequired={() => {}}
|
|
307
|
+
toggleIsRequired={() => {}}
|
|
350
308
|
stepErrors={stepErrors}
|
|
351
309
|
showWarnings={showWarnings}
|
|
352
|
-
id={id}
|
|
310
|
+
id={id}
|
|
353
311
|
fieldType="feature-image"
|
|
354
312
|
customLabel="Label"
|
|
355
313
|
/>
|
|
@@ -367,7 +325,7 @@ const GalleryField = (props) => {
|
|
|
367
325
|
setAllowedTypes,
|
|
368
326
|
stepErrors,
|
|
369
327
|
showWarnings,
|
|
370
|
-
id,
|
|
328
|
+
id,
|
|
371
329
|
} = props;
|
|
372
330
|
const {
|
|
373
331
|
label,
|
|
@@ -415,9 +373,9 @@ const CTAField = (props) => {
|
|
|
415
373
|
setPlaceholder,
|
|
416
374
|
stepErrors,
|
|
417
375
|
showWarnings,
|
|
376
|
+
id,
|
|
418
377
|
} = props;
|
|
419
378
|
const { label, isRequired, helpText, placeholder } = props.values;
|
|
420
|
-
const { id } = props; // Get ID from root props
|
|
421
379
|
|
|
422
380
|
return (
|
|
423
381
|
<BaseFieldConfig
|
|
@@ -426,21 +384,26 @@ const CTAField = (props) => {
|
|
|
426
384
|
helpText={helpText}
|
|
427
385
|
isRequired={isRequired}
|
|
428
386
|
setLabel={setLabel}
|
|
429
|
-
setPlaceholder={
|
|
387
|
+
setPlaceholder={setPlaceholder}
|
|
430
388
|
setHelpText={setHelpText}
|
|
431
389
|
toggleIsRequired={toggleIsRequired}
|
|
432
390
|
stepErrors={stepErrors}
|
|
433
391
|
showWarnings={showWarnings}
|
|
434
|
-
id={id}
|
|
392
|
+
id={id}
|
|
435
393
|
fieldType="cta"
|
|
436
394
|
/>
|
|
437
395
|
);
|
|
438
396
|
};
|
|
439
397
|
|
|
440
398
|
const FileField = (props) => {
|
|
441
|
-
const {
|
|
442
|
-
|
|
443
|
-
|
|
399
|
+
const {
|
|
400
|
+
toggleIsRequired,
|
|
401
|
+
setLabel,
|
|
402
|
+
setHelpText,
|
|
403
|
+
stepErrors,
|
|
404
|
+
showWarnings,
|
|
405
|
+
id,
|
|
406
|
+
} = props;
|
|
444
407
|
const { label, isRequired, helpText } = props.values;
|
|
445
408
|
|
|
446
409
|
return (
|
|
@@ -168,8 +168,8 @@ const ListingEditor = ({ mode = "create", listingId, onSuccess, onCancel }) => {
|
|
|
168
168
|
|
|
169
169
|
// formData always has structure { fields: {...} } after our fixes
|
|
170
170
|
const submissionData = isEditMode
|
|
171
|
-
? { id: listingId, fields: formData.fields }
|
|
172
|
-
: { fields: formData.fields };
|
|
171
|
+
? { id: listingId, fields: formData.fields, site: auth.site }
|
|
172
|
+
: { fields: formData.fields, site: auth.site };
|
|
173
173
|
|
|
174
174
|
const actionPromise = dispatch(
|
|
175
175
|
isEditMode ? editListing(submissionData) : createListing(submissionData),
|
|
@@ -84,7 +84,7 @@ const SideBarInner = (props) => {
|
|
|
84
84
|
|
|
85
85
|
// Build sidebar items based on mode
|
|
86
86
|
const buildSidebarItems = () => {
|
|
87
|
-
const isWizardMode = mode === "create"
|
|
87
|
+
const isWizardMode = mode === "create";
|
|
88
88
|
|
|
89
89
|
return steps.map((step, index) => {
|
|
90
90
|
const isCompleted = selectIsStepComplete(step.key);
|
|
@@ -109,8 +109,8 @@ const SideBarInner = (props) => {
|
|
|
109
109
|
isFontAwesome: true,
|
|
110
110
|
// Enhanced completion indicator
|
|
111
111
|
completed: isCompleted,
|
|
112
|
-
// Disable all navigation in
|
|
113
|
-
disabled:
|
|
112
|
+
// Disable all navigation in create mode, otherwise respect accessibility
|
|
113
|
+
disabled: mode === "create" || !isAccessible,
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
return itemProps;
|
|
@@ -137,7 +137,7 @@ const SideBarInner = (props) => {
|
|
|
137
137
|
|
|
138
138
|
// Add effect to manually attach click handlers since HubSidebar might not use onclick properly
|
|
139
139
|
useEffect(() => {
|
|
140
|
-
const isWizardMode = mode === "create"
|
|
140
|
+
const isWizardMode = mode === "create";
|
|
141
141
|
|
|
142
142
|
const attachClickHandlers = () => {
|
|
143
143
|
const stepsWithUrls = [
|
|
@@ -14,27 +14,26 @@ const ListingFileInput = ({
|
|
|
14
14
|
errorMessage,
|
|
15
15
|
disabled = false,
|
|
16
16
|
}) => {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const [inputs, setInputs] = useState([{ id: 0, fileUrl: null }]);
|
|
18
|
+
const nextIdRef = useRef(1);
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// Initialize inputs from value prop - map each existing file to an input
|
|
21
21
|
useEffect(() => {
|
|
22
22
|
if (value) {
|
|
23
|
+
let fileData = [];
|
|
24
|
+
|
|
23
25
|
// Handle multiple files - could be array of URLs or array of file objects
|
|
24
26
|
if (Array.isArray(value)) {
|
|
25
|
-
|
|
27
|
+
fileData = value
|
|
26
28
|
.map((file) => {
|
|
27
29
|
if (typeof file === "string") {
|
|
28
|
-
// Backward compatibility: array of URLs
|
|
29
30
|
const fileName = file.split("/").pop() || "Unknown File";
|
|
30
31
|
return {
|
|
31
32
|
url: file,
|
|
32
|
-
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
33
|
+
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
33
34
|
originalName: fileName,
|
|
34
|
-
uploading: false,
|
|
35
35
|
};
|
|
36
36
|
} else if (file && typeof file === "object" && file.url) {
|
|
37
|
-
// Enhanced structure
|
|
38
37
|
return {
|
|
39
38
|
url: file.url,
|
|
40
39
|
name: file.name || file.originalName || "Unknown File",
|
|
@@ -42,27 +41,22 @@ const ListingFileInput = ({
|
|
|
42
41
|
file.originalName ||
|
|
43
42
|
file.url.split("/").pop() ||
|
|
44
43
|
"Unknown File",
|
|
45
|
-
uploading: false,
|
|
46
44
|
};
|
|
47
45
|
}
|
|
48
46
|
return null;
|
|
49
47
|
})
|
|
50
48
|
.filter(Boolean);
|
|
51
|
-
setInternalFiles(normalizedFiles);
|
|
52
49
|
} else {
|
|
53
|
-
// Handle single file values
|
|
50
|
+
// Handle single file values
|
|
54
51
|
let singleFile = null;
|
|
55
52
|
if (typeof value === "string") {
|
|
56
|
-
// Backward compatibility: single URL
|
|
57
53
|
const fileName = value.split("/").pop() || "Unknown File";
|
|
58
54
|
singleFile = {
|
|
59
55
|
url: value,
|
|
60
56
|
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
61
57
|
originalName: fileName,
|
|
62
|
-
uploading: false,
|
|
63
58
|
};
|
|
64
59
|
} else if (value && typeof value === "object" && value.url) {
|
|
65
|
-
// Enhanced structure - preserve empty name if explicitly set
|
|
66
60
|
singleFile = {
|
|
67
61
|
url: value.url,
|
|
68
62
|
name:
|
|
@@ -73,60 +67,104 @@ const ListingFileInput = ({
|
|
|
73
67
|
value.originalName ||
|
|
74
68
|
value.url.split("/").pop() ||
|
|
75
69
|
"Unknown File",
|
|
76
|
-
uploading: false,
|
|
77
70
|
};
|
|
78
71
|
}
|
|
79
|
-
|
|
72
|
+
fileData = singleFile ? [singleFile] : [];
|
|
80
73
|
}
|
|
74
|
+
|
|
75
|
+
// Create inputs for each existing file
|
|
76
|
+
const existingInputs = fileData.map((file, index) => ({
|
|
77
|
+
id: index,
|
|
78
|
+
fileUrl: file.url,
|
|
79
|
+
name: file.name,
|
|
80
|
+
originalName: file.originalName,
|
|
81
|
+
}));
|
|
82
|
+
// Add one empty input at the end
|
|
83
|
+
nextIdRef.current = fileData.length;
|
|
84
|
+
setInputs([...existingInputs, { id: nextIdRef.current, fileUrl: null }]);
|
|
85
|
+
nextIdRef.current++;
|
|
81
86
|
} else {
|
|
82
|
-
|
|
87
|
+
setInputs([{ id: 0, fileUrl: null }]);
|
|
83
88
|
}
|
|
84
89
|
}, [value]);
|
|
85
90
|
|
|
86
91
|
// Handle new file upload from FileInput component
|
|
87
|
-
const handleFileUpload = (uploadedUrl) => {
|
|
88
|
-
// Handle case where uploadedUrl might be undefined
|
|
92
|
+
const handleFileUpload = (inputId, uploadedUrl) => {
|
|
89
93
|
if (!uploadedUrl) {
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
const fileName = uploadedUrl.split("/").pop() || "Unknown File";
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
|
|
99
|
+
// Find the input that triggered this upload and update it
|
|
100
|
+
setInputs((prevInputs) => {
|
|
101
|
+
const updatedInputs = prevInputs.map((input) => {
|
|
102
|
+
if (input.id === inputId) {
|
|
103
|
+
return {
|
|
104
|
+
...input,
|
|
105
|
+
fileUrl: uploadedUrl,
|
|
106
|
+
name: "",
|
|
107
|
+
originalName: fileName,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return input;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Add a new empty input for the next upload
|
|
114
|
+
updatedInputs.push({ id: nextIdRef.current, fileUrl: null });
|
|
115
|
+
nextIdRef.current++;
|
|
116
|
+
|
|
117
|
+
return updatedInputs;
|
|
118
|
+
});
|
|
105
119
|
};
|
|
106
120
|
|
|
107
121
|
// Handle file name change
|
|
108
122
|
const handleNameChange = (url, newName) => {
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
setInputs((prevInputs) =>
|
|
124
|
+
prevInputs.map((input) =>
|
|
125
|
+
input.fileUrl === url ? { ...input, name: newName } : input,
|
|
126
|
+
),
|
|
111
127
|
);
|
|
112
|
-
setInternalFiles(updatedFiles);
|
|
113
|
-
notifyParent(updatedFiles);
|
|
114
128
|
};
|
|
115
129
|
|
|
116
130
|
// Handle file removal
|
|
117
131
|
const handleFileRemove = (url) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
setInputs((prevInputs) => {
|
|
133
|
+
return prevInputs.map((input) => {
|
|
134
|
+
if (input.fileUrl === url) {
|
|
135
|
+
return { ...input, fileUrl: null };
|
|
136
|
+
}
|
|
137
|
+
return input;
|
|
138
|
+
});
|
|
139
|
+
});
|
|
121
140
|
};
|
|
122
141
|
|
|
123
|
-
//
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
// Get all uploaded files from inputs
|
|
143
|
+
const getUploadedFiles = () => {
|
|
144
|
+
return inputs
|
|
145
|
+
.filter((input) => input.fileUrl !== null)
|
|
146
|
+
.map((input) => ({
|
|
147
|
+
url: input.fileUrl,
|
|
148
|
+
name: input.name,
|
|
149
|
+
originalName: input.originalName,
|
|
150
|
+
}));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Track previous uploaded files to prevent infinite loops
|
|
154
|
+
const prevFilesRef = useRef([]);
|
|
155
|
+
|
|
156
|
+
// Sync with parent when uploaded files actually change
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
const files = getUploadedFiles();
|
|
159
|
+
const currentUrls = files.map((f) => f.url).join(",");
|
|
160
|
+
const prevUrls = prevFilesRef.current.map((f) => f.url).join(",");
|
|
161
|
+
|
|
162
|
+
// Only call onChange if the file list actually changed
|
|
163
|
+
if (currentUrls !== prevUrls && onChange) {
|
|
164
|
+
prevFilesRef.current = files;
|
|
127
165
|
onChange(field.id, files);
|
|
128
166
|
}
|
|
129
|
-
};
|
|
167
|
+
}, [inputs]);
|
|
130
168
|
|
|
131
169
|
// Define acceptable file types for documents and common formats
|
|
132
170
|
const acceptTypes = {
|
|
@@ -145,7 +183,11 @@ const ListingFileInput = ({
|
|
|
145
183
|
"image/png": [".png"],
|
|
146
184
|
};
|
|
147
185
|
|
|
148
|
-
//
|
|
186
|
+
// Get the last empty input (the one to show)
|
|
187
|
+
const emptyInput = inputs.find((input) => input.fileUrl === null);
|
|
188
|
+
|
|
189
|
+
// Get uploaded files for display
|
|
190
|
+
const uploadedFiles = getUploadedFiles();
|
|
149
191
|
|
|
150
192
|
return (
|
|
151
193
|
<div className={styles.listingFileInput}>
|
|
@@ -169,25 +211,26 @@ const ListingFileInput = ({
|
|
|
169
211
|
)}
|
|
170
212
|
</Text>
|
|
171
213
|
|
|
172
|
-
{/* Drop zone */}
|
|
214
|
+
{/* Drop zone - always show exactly one empty input */}
|
|
173
215
|
<div
|
|
174
216
|
className={`${styles.listingFileInput__dropZone} ${showError ? styles["listingFileInput__dropZone--error"] : ""}`}
|
|
175
217
|
>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
{emptyInput && (
|
|
219
|
+
<FileInput
|
|
220
|
+
key={emptyInput.id}
|
|
221
|
+
refreshCallback={(url) => handleFileUpload(emptyInput.id, url)}
|
|
222
|
+
hasDefault={null}
|
|
223
|
+
accept={acceptTypes}
|
|
224
|
+
multiple={false}
|
|
225
|
+
disabled={disabled}
|
|
226
|
+
/>
|
|
227
|
+
)}
|
|
185
228
|
</div>
|
|
186
229
|
|
|
187
230
|
{/* File list */}
|
|
188
|
-
{
|
|
231
|
+
{uploadedFiles.length > 0 && (
|
|
189
232
|
<div className={styles.listingFileInput__fileList}>
|
|
190
|
-
{
|
|
233
|
+
{uploadedFiles.map((file) => (
|
|
191
234
|
<FileListItem
|
|
192
235
|
key={file.url}
|
|
193
236
|
file={file}
|
|
@@ -199,31 +242,6 @@ const ListingFileInput = ({
|
|
|
199
242
|
</div>
|
|
200
243
|
)}
|
|
201
244
|
|
|
202
|
-
{/* Add more files button - always show when files exist */}
|
|
203
|
-
{internalFiles.length > 0 && (
|
|
204
|
-
<div>
|
|
205
|
-
<FileInput
|
|
206
|
-
ref={fileInputRef}
|
|
207
|
-
className="imageInputOuter-single"
|
|
208
|
-
refreshCallback={handleFileUpload}
|
|
209
|
-
hasDefault={null}
|
|
210
|
-
accept={acceptTypes}
|
|
211
|
-
multiple={false}
|
|
212
|
-
disabled={disabled}
|
|
213
|
-
customButton={
|
|
214
|
-
<Button
|
|
215
|
-
buttonType="secondary"
|
|
216
|
-
className={styles.listingFileInput__addMoreButton}
|
|
217
|
-
disabled={disabled}
|
|
218
|
-
>
|
|
219
|
-
<FontAwesomeIcon icon={iconImports.plus} />
|
|
220
|
-
Add More Files
|
|
221
|
-
</Button>
|
|
222
|
-
}
|
|
223
|
-
/>
|
|
224
|
-
</div>
|
|
225
|
-
)}
|
|
226
|
-
|
|
227
245
|
{showError && errorMessage && (
|
|
228
246
|
<Text
|
|
229
247
|
type="help"
|
|
@@ -38,9 +38,7 @@
|
|
|
38
38
|
display: flex;
|
|
39
39
|
align-items: center;
|
|
40
40
|
gap: 0.75rem;
|
|
41
|
-
padding: 0.75rem;
|
|
42
41
|
background-color: var(--bg-white);
|
|
43
|
-
margin-bottom: 0.5rem;
|
|
44
42
|
transition: all var(--transition-base) ease-in-out;
|
|
45
43
|
}
|
|
46
44
|
|
|
@@ -50,9 +48,8 @@
|
|
|
50
48
|
|
|
51
49
|
.fileListItem__icon {
|
|
52
50
|
color: var(--text-bluegrey);
|
|
53
|
-
font-size:
|
|
51
|
+
font-size: 2rem;
|
|
54
52
|
flex-shrink: 0;
|
|
55
|
-
width: 1.5rem;
|
|
56
53
|
text-align: center;
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* Gallery container styles */
|
|
4
4
|
.galleryContainer {
|
|
5
5
|
margin-top: 0.75rem;
|
|
6
|
+
max-width: 200px;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
/* Error border wrapper */
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
|
|
21
22
|
.imageGrid {
|
|
22
23
|
display: grid;
|
|
23
|
-
grid-template-columns: repeat(
|
|
24
|
+
grid-template-columns: repeat(4, 160px);
|
|
24
25
|
gap: 1rem;
|
|
25
26
|
margin-top: 0.5rem;
|
|
26
27
|
}
|
package/src/images/full.png
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/images/widget.png
CHANGED
|
Binary file
|
|
@@ -86,7 +86,7 @@ const DEFAULT_NEW_FIELD = {
|
|
|
86
86
|
allowCaption: false, // Default to not allowing captions for image fields
|
|
87
87
|
useAsSummary: false, // Default to not using as summary for description fields
|
|
88
88
|
minImages: 1, // Gallery-specific default
|
|
89
|
-
maxImages:
|
|
89
|
+
maxImages: 16, // Gallery-specific default
|
|
90
90
|
maxFileSize: "5MB", // Gallery-specific default
|
|
91
91
|
allowedTypes: [], // Gallery-specific default
|
|
92
92
|
},
|
|
@@ -138,7 +138,7 @@ function createNewField(id, type = "text", existingFields = []) {
|
|
|
138
138
|
baseField.values.label = "Image Gallery";
|
|
139
139
|
baseField.values.helpText = "Upload multiple images to create a gallery";
|
|
140
140
|
baseField.values.minImages = 1;
|
|
141
|
-
baseField.values.maxImages =
|
|
141
|
+
baseField.values.maxImages = 16;
|
|
142
142
|
baseField.values.maxFileSize = "5MB";
|
|
143
143
|
baseField.values.allowedTypes = ["image/jpeg", "image/png", "image/webp"];
|
|
144
144
|
const {
|
|
@@ -277,8 +277,11 @@ const formReducer = (state = INITIAL_FORM_STATE, action) => {
|
|
|
277
277
|
switch (type) {
|
|
278
278
|
case formActionTypes.SET_INITIAL_VALUES: {
|
|
279
279
|
// The actual definition data is in payload.featureDefinition.definition
|
|
280
|
-
const definitionWrapper =
|
|
281
|
-
|
|
280
|
+
const definitionWrapper =
|
|
281
|
+
(payload && payload.featureDefinition) || payload;
|
|
282
|
+
const definition =
|
|
283
|
+
(definitionWrapper && definitionWrapper.definition) ||
|
|
284
|
+
definitionWrapper;
|
|
282
285
|
|
|
283
286
|
// Validate and map definition data to form state structure
|
|
284
287
|
const mappedValues = {
|
|
@@ -513,8 +516,11 @@ const definitionReducer = (state = INITIAL_STATE.definition, action) => {
|
|
|
513
516
|
const featureDefinitionWrapper =
|
|
514
517
|
(data && data.featureDefinition) || data;
|
|
515
518
|
definition =
|
|
516
|
-
(featureDefinitionWrapper && featureDefinitionWrapper.definition) ||
|
|
517
|
-
|
|
519
|
+
(featureDefinitionWrapper && featureDefinitionWrapper.definition) ||
|
|
520
|
+
featureDefinitionWrapper;
|
|
521
|
+
definitionId =
|
|
522
|
+
(featureDefinitionWrapper && featureDefinitionWrapper.id) ||
|
|
523
|
+
values.featureId;
|
|
518
524
|
|
|
519
525
|
// Ensure fields array exists and preserves order property
|
|
520
526
|
if (definition?.fields) {
|