@plusscommunities/pluss-feature-builder-web-a 1.0.2-beta.6 → 1.0.2-beta.8
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 +133 -109
- package/package.json +1 -1
- package/src/components/BaseFieldConfig.jsx +3 -3
- 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/index.js +6 -6
- package/src/reducers/featureBuilderReducer.js +7 -7
- package/src/selectors/featureBuilderSelectors.js +3 -8
package/dist/index.cjs.js
CHANGED
|
@@ -206,14 +206,9 @@ const selectDefinitionMode = state => {
|
|
|
206
206
|
|
|
207
207
|
// Check if we have a definition loaded or if we've determined the mode
|
|
208
208
|
// This is used by hiddenFromFeaturePicker to determine if feature should be shown
|
|
209
|
-
// When mode is "create", definition is null but we've determined the feature doesn't exist
|
|
210
|
-
// When mode is "edit", definition exists and we've determined the feature exists
|
|
211
209
|
const selectHasDefinition = state => {
|
|
212
210
|
const definition = selectDefinition(state);
|
|
213
|
-
|
|
214
|
-
// Consider feature "loaded" if definition exists OR mode is set
|
|
215
|
-
// Mode being set means we've successfully fetched and determined the state
|
|
216
|
-
return !!definition || !!mode;
|
|
211
|
+
return !!definition;
|
|
217
212
|
};
|
|
218
213
|
|
|
219
214
|
// ============ LISTINGS SELECTORS ============
|
|
@@ -337,16 +332,14 @@ const selectIsStepAccessible = step => state => {
|
|
|
337
332
|
|
|
338
333
|
// Get current sort method
|
|
339
334
|
const selectSortBy = state => {
|
|
340
|
-
var _listingsState$sortBy;
|
|
341
335
|
const listingsState = selectListingsState(state);
|
|
342
|
-
return (
|
|
336
|
+
return (listingsState === null || listingsState === void 0 ? void 0 : listingsState.sortBy) || "newest";
|
|
343
337
|
};
|
|
344
338
|
|
|
345
339
|
// Get show deleted toggle state
|
|
346
340
|
const selectShowDeleted = state => {
|
|
347
|
-
var _listingsState$showDe;
|
|
348
341
|
const listingsState = selectListingsState(state);
|
|
349
|
-
return (
|
|
342
|
+
return (listingsState === null || listingsState === void 0 ? void 0 : listingsState.showDeleted) || false;
|
|
350
343
|
};
|
|
351
344
|
|
|
352
345
|
// Get sorted and filtered listings
|
|
@@ -1781,10 +1774,10 @@ const BaseFieldConfig = props => {
|
|
|
1781
1774
|
alwaysShowLabel: true
|
|
1782
1775
|
}), setMaxImages && /*#__PURE__*/React__default["default"].createElement(GenericInput$1, {
|
|
1783
1776
|
type: "number",
|
|
1784
|
-
value: maxImages ||
|
|
1785
|
-
placeholder: "
|
|
1777
|
+
value: maxImages || 16,
|
|
1778
|
+
placeholder: "16",
|
|
1786
1779
|
label: "Maximum Images",
|
|
1787
|
-
onChange: e => setMaxImages(parseInt(e.target.value) ||
|
|
1780
|
+
onChange: e => setMaxImages(parseInt(e.target.value) || 16),
|
|
1788
1781
|
className: modules_18fc9e14.fieldSpacing,
|
|
1789
1782
|
alwaysShowLabel: true
|
|
1790
1783
|
}), setMaxFileSize && /*#__PURE__*/React__default["default"].createElement(GenericInput$1, {
|
|
@@ -1910,8 +1903,8 @@ var css$g = "/* Simple listing field styling - clean vertical layout */\n\n.List
|
|
|
1910
1903
|
var modules_2b7292f7 = {"listingField":"ListingField_module_listingField__19044565","listingField__header":"ListingField_module_listingField__header__19044565","listingField__icon":"ListingField_module_listingField__icon__19044565","listingField__content":"ListingField_module_listingField__content__19044565","listingField__title":"ListingField_module_listingField__title__19044565","listingField__description":"ListingField_module_listingField__description__19044565","listingField--error":"ListingField_module_listingField_Error__19044565","requiredAsterisk":"ListingField_module_requiredAsterisk__19044565","listingField__error":"ListingField_module_listingField__error__19044565","hasError":"ListingField_module_hasError__19044565","genericInput":"ListingField_module_genericInput__19044565","imageInputOuter-single":"ListingField_module_imageInputOuterSingle__19044565","imageInputOuter-grid":"ListingField_module_imageInputOuterGrid__19044565","FileInput":"ListingField_module_FileInput__19044565","imageInput":"ListingField_module_imageInput__19044565","file-input":"ListingField_module_fileInput__19044565","image-container":"ListingField_module_imageContainer__19044565","image-grid":"ListingField_module_imageGrid__19044565"};
|
|
1911
1904
|
n(css$g,{});
|
|
1912
1905
|
|
|
1913
|
-
var css$f = "/* ListingGalleryInput BEM CSS Module */\n\n/* Gallery container styles */\n.
|
|
1914
|
-
var modules_2a7847a1 = {"galleryContainer":"
|
|
1906
|
+
var css$f = "/* ListingGalleryInput BEM CSS Module */\n\n/* Gallery container styles */\n.ListingGalleryInput_module_galleryContainer__b1316031 {\n\tmargin-top: 0.75rem;\n\t\tmax-width:200px;\n}\n\n/* Error border wrapper */\n.ListingGalleryInput_module_galleryErrorWrapper__b1316031 {\n\tborder: 1px solid var(--colour-error);\n\tborder-radius: var(--border-radius-base);\n\tpadding: 0.25rem;\n\tbackground-color: var(--colour-error-light, rgba(220, 53, 69, 0.05));\n}\n\n/* Image preview grid container */\n.ListingGalleryInput_module_imagePreviewContainer__b1316031 {\n\tmargin-top: 1rem;\n}\n\n.ListingGalleryInput_module_imageGrid__b1316031 {\n\tdisplay: grid;\n grid-template-columns: repeat(4, 160px);\n\tgap: 1rem;\n\tmargin-top: 0.5rem;\n}\n\n/* Responsive grid for different screen sizes */\n@media (max-width: 768px) {\n\t.ListingGalleryInput_module_imageGrid__b1316031 {\n\t\tgrid-template-columns: repeat(3, 1fr);\n\t\tgap: 0.75rem;\n\t}\n}\n\n@media (max-width: 480px) {\n\t.ListingGalleryInput_module_imageGrid__b1316031 {\n\t\tgrid-template-columns: repeat(2, 1fr);\n\t\tgap: 0.5rem;\n\t}\n}\n\n/* Individual image item */\n.ListingGalleryInput_module_imageItem__b1316031 {\n\tposition: relative;\n\tborder-radius: var(--border-radius-lg);\n\toverflow: hidden;\n\tbackground-color: var(--bg-bluegrey);\n\ttransition:\n\t\ttransform var(--transition-base) ease,\n\t\tbox-shadow var(--transition-base) ease;\n}\n\n.ListingGalleryInput_module_imageItem__b1316031:hover {\n\ttransform: translateY(-2px);\n\tbox-shadow: var(--shadow-lg);\n}\n\n.ListingGalleryInput_module_imageThumbnail__b1316031 {\n\twidth: 100%;\n\theight: 100px;\n\tobject-fit: cover;\n\tdisplay: block;\n\taspect-ratio: 1;\n}\n\n/* Remove image button */\n.ListingGalleryInput_module_removeImageButton__b1316031 {\n\tposition: absolute;\n\ttop: 0.5rem;\n\tright: 0.5rem;\n\tbackground: transparent;\n\tcolor: var(--colour-branding-primary);\n\tborder: none;\n\tborder-radius: var(--border-radius-full);\n\twidth: 1.75rem;\n\theight: 1.75rem;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tcursor: pointer;\n\topacity: 0;\n\ttransition: opacity var(--transition-base) ease;\n}\n\n.ListingGalleryInput_module_imageItem__b1316031:hover .ListingGalleryInput_module_removeImageButton__b1316031 {\n\topacity: 1;\n}\n\n.ListingGalleryInput_module_removeImageButton__b1316031:hover:not(:disabled) {\n\tcolor: var(--colour-branding-light);\n}\n\n.ListingGalleryInput_module_removeImageButton__b1316031:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n/* Error message styling */\n.ListingGalleryInput_module_errorMessage__b1316031 {\n\tcolor: var(--colour-red);\n\tmargin-top: 0.5rem;\n\tdisplay: block;\n}\n\n/* Help text styling */\n.ListingGalleryInput_module_helpText__b1316031 {\n\tcolor: var(--text-bluegrey);\n\tmargin-top: 0.5rem;\n\tdisplay: block;\n}\n\n/* Validation hints */\n.ListingGalleryInput_module_validationHint__b1316031 {\n\tcolor: var(--text-bluegrey);\n\tmargin-top: 0.25rem;\n\tdisplay: block;\n\tfont-size: var(--font-size-xxs);\n}\n\n/* Form label styling */\n.ListingGalleryInput_module_formLabel__b1316031 {\n\tdisplay: block;\n\tmargin-bottom: 8px;\n}\n\n/* Help text for image count */\n.ListingGalleryInput_module_imageCountText__b1316031 {\n\tmargin-bottom: 0.5rem;\n\tdisplay: block;\n}\n";
|
|
1907
|
+
var modules_2a7847a1 = {"galleryContainer":"ListingGalleryInput_module_galleryContainer__b1316031","galleryErrorWrapper":"ListingGalleryInput_module_galleryErrorWrapper__b1316031","imagePreviewContainer":"ListingGalleryInput_module_imagePreviewContainer__b1316031","imageGrid":"ListingGalleryInput_module_imageGrid__b1316031","imageItem":"ListingGalleryInput_module_imageItem__b1316031","imageThumbnail":"ListingGalleryInput_module_imageThumbnail__b1316031","removeImageButton":"ListingGalleryInput_module_removeImageButton__b1316031","errorMessage":"ListingGalleryInput_module_errorMessage__b1316031","helpText":"ListingGalleryInput_module_helpText__b1316031","validationHint":"ListingGalleryInput_module_validationHint__b1316031","formLabel":"ListingGalleryInput_module_formLabel__b1316031","imageCountText":"ListingGalleryInput_module_imageCountText__b1316031"};
|
|
1915
1908
|
n(css$f,{});
|
|
1916
1909
|
|
|
1917
1910
|
const ListingGalleryInput = _ref => {
|
|
@@ -2816,7 +2809,7 @@ const SideBarInner = props => {
|
|
|
2816
2809
|
|
|
2817
2810
|
// Build sidebar items based on mode
|
|
2818
2811
|
const buildSidebarItems = () => {
|
|
2819
|
-
const isWizardMode = mode === "create"
|
|
2812
|
+
const isWizardMode = mode === "create";
|
|
2820
2813
|
return steps.map((step, index) => {
|
|
2821
2814
|
const isCompleted = selectIsStepComplete(step.key);
|
|
2822
2815
|
const isAccessible = selectIsStepAccessible(step.key);
|
|
@@ -2835,8 +2828,8 @@ const SideBarInner = props => {
|
|
|
2835
2828
|
isFontAwesome: true,
|
|
2836
2829
|
// Enhanced completion indicator
|
|
2837
2830
|
completed: isCompleted,
|
|
2838
|
-
// Disable all navigation in
|
|
2839
|
-
disabled:
|
|
2831
|
+
// Disable all navigation in create mode, otherwise respect accessibility
|
|
2832
|
+
disabled: mode === "create" || !isAccessible
|
|
2840
2833
|
};
|
|
2841
2834
|
return itemProps;
|
|
2842
2835
|
});
|
|
@@ -2860,7 +2853,7 @@ const SideBarInner = props => {
|
|
|
2860
2853
|
|
|
2861
2854
|
// Add effect to manually attach click handlers since HubSidebar might not use onclick properly
|
|
2862
2855
|
React.useEffect(() => {
|
|
2863
|
-
const isWizardMode = mode === "create"
|
|
2856
|
+
const isWizardMode = mode === "create";
|
|
2864
2857
|
const attachClickHandlers = () => {
|
|
2865
2858
|
const stepsWithUrls = [{
|
|
2866
2859
|
key: "overview",
|
|
@@ -5369,7 +5362,7 @@ const DEFAULT_NEW_FIELD = {
|
|
|
5369
5362
|
// Default to not using as summary for description fields
|
|
5370
5363
|
minImages: 1,
|
|
5371
5364
|
// Gallery-specific default
|
|
5372
|
-
maxImages:
|
|
5365
|
+
maxImages: 16,
|
|
5373
5366
|
// Gallery-specific default
|
|
5374
5367
|
maxFileSize: "5MB",
|
|
5375
5368
|
// Gallery-specific default
|
|
@@ -5422,7 +5415,7 @@ function createNewField(id) {
|
|
|
5422
5415
|
baseField.values.label = "Image Gallery";
|
|
5423
5416
|
baseField.values.helpText = "Upload multiple images to create a gallery";
|
|
5424
5417
|
baseField.values.minImages = 1;
|
|
5425
|
-
baseField.values.maxImages =
|
|
5418
|
+
baseField.values.maxImages = 16;
|
|
5426
5419
|
baseField.values.maxFileSize = "5MB";
|
|
5427
5420
|
baseField.values.allowedTypes = ["image/jpeg", "image/png", "image/webp"];
|
|
5428
5421
|
const _baseField$values2 = baseField.values,
|
|
@@ -5565,10 +5558,9 @@ const formReducer = function () {
|
|
|
5565
5558
|
switch (type) {
|
|
5566
5559
|
case actionsTypes.SET_INITIAL_VALUES:
|
|
5567
5560
|
{
|
|
5568
|
-
var _payload$featureDefin, _definitionWrapper$de;
|
|
5569
5561
|
// The actual definition data is in payload.featureDefinition.definition
|
|
5570
|
-
const definitionWrapper =
|
|
5571
|
-
const definition =
|
|
5562
|
+
const definitionWrapper = payload && payload.featureDefinition || payload;
|
|
5563
|
+
const definition = definitionWrapper && definitionWrapper.definition || definitionWrapper;
|
|
5572
5564
|
|
|
5573
5565
|
// Validate and map definition data to form state structure
|
|
5574
5566
|
const mappedValues = {
|
|
@@ -5784,12 +5776,12 @@ const definitionReducer = function () {
|
|
|
5784
5776
|
let definitionId = values.featureId; // Always use hardcoded ID
|
|
5785
5777
|
|
|
5786
5778
|
if (mode === "edit" && data) {
|
|
5787
|
-
var
|
|
5779
|
+
var _definition;
|
|
5788
5780
|
// Extract from API response for edit mode
|
|
5789
5781
|
// Handle nested structure: data.featureDefinition.definition
|
|
5790
|
-
const featureDefinitionWrapper =
|
|
5791
|
-
definition =
|
|
5792
|
-
definitionId =
|
|
5782
|
+
const featureDefinitionWrapper = data && data.featureDefinition || data;
|
|
5783
|
+
definition = featureDefinitionWrapper && featureDefinitionWrapper.definition || featureDefinitionWrapper;
|
|
5784
|
+
definitionId = featureDefinitionWrapper && featureDefinitionWrapper.id || values.featureId;
|
|
5793
5785
|
|
|
5794
5786
|
// Ensure fields array exists and preserves order property
|
|
5795
5787
|
if ((_definition = definition) !== null && _definition !== void 0 && _definition.fields) {
|
|
@@ -6642,8 +6634,8 @@ const ListingCTAInput = _ref => {
|
|
|
6642
6634
|
}));
|
|
6643
6635
|
};
|
|
6644
6636
|
|
|
6645
|
-
var css$6 = "/* Enhanced File Input Styles - Based on existing PlussCore FileInput styling */\n\n/* Main container for enhanced file input */\n.
|
|
6646
|
-
var modules_4fb4144d = {"listingFileInput":"
|
|
6637
|
+
var css$6 = "/* Enhanced File Input Styles - Based on existing PlussCore FileInput styling */\n\n/* Main container for enhanced file input */\n.ListingFileInput_module_listingFileInput__cb175742 {\n\twidth: 100%;\n}\n\n/* Drop zone area - reusing existing imageInput styling */\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 {\n\twidth: 100%;\n\tmargin-bottom: 1rem;\n}\n\n/* Drop zone error state */\n.ListingFileInput_module_listingFileInput__dropZone_Error__cb175742 {\n\tborder: 1px solid var(--colour-error);\n\tborder-radius: var(--border-radius-base);\n\tpadding: 0.25rem;\n\tbackground-color: var(--colour-error-light, rgba(220, 53, 69, 0.05));\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInputOuterSingle__cb175742 {\n\twidth: 100%;\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput__cb175742 {\n\twidth: 100%;\n\tmargin-right: 0;\n}\n\n/* File list container */\n.ListingFileInput_module_listingFileInput__fileList__cb175742 {\n\tmargin-top: 1rem;\n}\n\n/* Individual file item */\n.ListingFileInput_module_fileListItem__cb175742 {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.75rem;\n\tbackground-color: var(--bg-white);\n\ttransition: all var(--transition-base) ease-in-out;\n}\n\n.ListingFileInput_module_fileListItem__cb175742:hover {\n\tbox-shadow: 0 0 4px var(--colour-branding-action-alpha20);\n}\n\n.ListingFileInput_module_fileListItem__icon__cb175742 {\n\tcolor: var(--text-bluegrey);\n\t\tfont-size: 2rem;\n\tflex-shrink: 0;\n\ttext-align: center;\n}\n\n.ListingFileInput_module_fileListItem__input__cb175742 {\n\tmin-width: 0;\n\tflex-grow: 1;\n}\n\n.ListingFileInput_module_fileListItem__nameInput__cb175742 {\n}\n\n.ListingFileInput_module_fileListItem__originalName__cb175742 {\n\tflex: 0 1 200px;\n\tcolor: var(--text-bluegrey);\n\tfont-size: 16px;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.ListingFileInput_module_fileListItem__actions__cb175742 {\n\tflex-shrink: 0;\n}\n\n.ListingFileInput_module_fileListItem__removeButton__cb175742 {\n\tfont-size: 16px;\n\twidth: 1.75rem;\n\theight: 1.75rem;\n\tborder-radius: var(--border-radius-full);\n\tbackground-color: transparent;\n\tborder: none;\n\tdisplay: flex;\n\tcolor: var(--colour-branding-main);\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 0;\n\tmin-height: auto;\n\ttransition: all var(--transition-base) ease-in-out;\n}\n\n.ListingFileInput_module_fileListItem__removeButton__cb175742:hover:not(:disabled) {\n\tcolor: var(--colour-branding-light);\n}\n\n.ListingFileInput_module_fileListItem__removeButton__cb175742:disabled {\n\topacity: 0.5;\n\tcursor: not-allowed;\n}\n\n/* Add more files button */\n.ListingFileInput_module_listingFileInput__addMore__cb175742 {\n\tmargin-top: 0.75rem;\n\ttext-align: center;\n}\n\n.ListingFileInput_module_listingFileInput__addMoreButton__cb175742 {\n\tfont-size: var(--font-size-sm);\n\tpadding: 0.5rem 1rem;\n}\n\n/* Upload progress styling */\n.ListingFileInput_module_fileListItem_Uploading__cb175742 {\n\tbackground-color: var(--colour-branding-secondary-light);\n\tborder-color: var(--colour-branding-action);\n}\n\n.ListingFileInput_module_fileListItem__progress__cb175742 {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 0.5rem;\n\tfont-size: var(--font-size-xs);\n\tcolor: var(--colour-branding-action);\n}\n\n.ListingFileInput_module_fileListItem__spinner__cb175742 {\n\tcolor: var(--colour-branding-action);\n\tanimation: ListingFileInput_module_spin__cb175742 1s linear infinite;\n}\n\n@keyframes ListingFileInput_module_spin__cb175742 {\n\t0% {\n\t\ttransform: rotate(0deg);\n\t}\n\t100% {\n\t\ttransform: rotate(360deg);\n\t}\n}\n\n/* Responsive design */\n@media (max-width: 768px) {\n\t.ListingFileInput_module_fileListItem__cb175742 {\n\t\tflex-direction: column;\n\t\talign-items: stretch;\n\t\tgap: 0.5rem;\n\t}\n\n\t.ListingFileInput_module_fileListItem__icon__cb175742 {\n\t\ttext-align: center;\n\t}\n\n\t.ListingFileInput_module_fileListItem__actions__cb175742 {\n\t\ttext-align: center;\n\t}\n}\n\n/* Integration with existing field styling */\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_upload__cb175742 {\n\tbackground-color: var(--bg-white);\n\ttransition: all var(--transition-base) ease-in-out;\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_upload__cb175742:hover {\n\tbackground-color: var(--bg-bluegrey);\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_dropZoneActive__cb175742 {\n\tbackground-color: var(--bg-bluegrey);\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_icon__cb175742 {\n\twidth: 1.5rem;\n\theight: 2rem;\n\tmargin: 0 auto;\n\tdisplay: block;\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_helpText__cb175742 {\n\tfont-size: var(--font-size-base);\n\tcolor: var(--colour-lightgrey);\n\tmargin-top: 1.25rem;\n\ttext-align: center;\n}\n\n.ListingFileInput_module_listingFileInput__dropZone__cb175742 .ListingFileInput_module_imageInput_button__cb175742 {\n\ttext-align: center;\n\tmargin-top: 1rem;\n}\n";
|
|
6638
|
+
var modules_4fb4144d = {"listingFileInput":"ListingFileInput_module_listingFileInput__cb175742","listingFileInput__dropZone":"ListingFileInput_module_listingFileInput__dropZone__cb175742","listingFileInput__dropZone--error":"ListingFileInput_module_listingFileInput__dropZone_Error__cb175742","imageInputOuter-single":"ListingFileInput_module_imageInputOuterSingle__cb175742","imageInput":"ListingFileInput_module_imageInput__cb175742","listingFileInput__fileList":"ListingFileInput_module_listingFileInput__fileList__cb175742","fileListItem":"ListingFileInput_module_fileListItem__cb175742","fileListItem__icon":"ListingFileInput_module_fileListItem__icon__cb175742","fileListItem__input":"ListingFileInput_module_fileListItem__input__cb175742","fileListItem__nameInput":"ListingFileInput_module_fileListItem__nameInput__cb175742","fileListItem__originalName":"ListingFileInput_module_fileListItem__originalName__cb175742","fileListItem__actions":"ListingFileInput_module_fileListItem__actions__cb175742","fileListItem__removeButton":"ListingFileInput_module_fileListItem__removeButton__cb175742","listingFileInput__addMore":"ListingFileInput_module_listingFileInput__addMore__cb175742","listingFileInput__addMoreButton":"ListingFileInput_module_listingFileInput__addMoreButton__cb175742","fileListItem--uploading":"ListingFileInput_module_fileListItem_Uploading__cb175742","fileListItem__progress":"ListingFileInput_module_fileListItem__progress__cb175742","fileListItem__spinner":"ListingFileInput_module_fileListItem__spinner__cb175742","spin":"ListingFileInput_module_spin__cb175742","imageInput_upload":"ListingFileInput_module_imageInput_upload__cb175742","imageInput_dropZoneActive":"ListingFileInput_module_imageInput_dropZoneActive__cb175742","imageInput_icon":"ListingFileInput_module_imageInput_icon__cb175742","imageInput_helpText":"ListingFileInput_module_imageInput_helpText__cb175742","imageInput_button":"ListingFileInput_module_imageInput_button__cb175742"};
|
|
6647
6639
|
n(css$6,{});
|
|
6648
6640
|
|
|
6649
6641
|
const FileListItem = _ref => {
|
|
@@ -6733,109 +6725,153 @@ const ListingFileInput = _ref => {
|
|
|
6733
6725
|
errorMessage,
|
|
6734
6726
|
disabled = false
|
|
6735
6727
|
} = _ref;
|
|
6736
|
-
const
|
|
6737
|
-
|
|
6728
|
+
const [inputs, setInputs] = React.useState([{
|
|
6729
|
+
id: 0,
|
|
6730
|
+
fileUrl: null
|
|
6731
|
+
}]);
|
|
6732
|
+
const nextIdRef = React.useRef(1);
|
|
6738
6733
|
|
|
6739
|
-
//
|
|
6734
|
+
// Initialize inputs from value prop - map each existing file to an input
|
|
6740
6735
|
React.useEffect(() => {
|
|
6741
6736
|
if (value) {
|
|
6737
|
+
let fileData = [];
|
|
6738
|
+
|
|
6742
6739
|
// Handle multiple files - could be array of URLs or array of file objects
|
|
6743
6740
|
if (Array.isArray(value)) {
|
|
6744
|
-
|
|
6741
|
+
fileData = value.map(file => {
|
|
6745
6742
|
if (typeof file === "string") {
|
|
6746
|
-
// Backward compatibility: array of URLs
|
|
6747
6743
|
const fileName = file.split("/").pop() || "Unknown File";
|
|
6748
6744
|
return {
|
|
6749
6745
|
url: file,
|
|
6750
6746
|
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
6751
|
-
|
|
6752
|
-
originalName: fileName,
|
|
6753
|
-
uploading: false
|
|
6747
|
+
originalName: fileName
|
|
6754
6748
|
};
|
|
6755
6749
|
} else if (file && typeof file === "object" && file.url) {
|
|
6756
|
-
// Enhanced structure
|
|
6757
6750
|
return {
|
|
6758
6751
|
url: file.url,
|
|
6759
6752
|
name: file.name || file.originalName || "Unknown File",
|
|
6760
|
-
originalName: file.originalName || file.url.split("/").pop() || "Unknown File"
|
|
6761
|
-
uploading: false
|
|
6753
|
+
originalName: file.originalName || file.url.split("/").pop() || "Unknown File"
|
|
6762
6754
|
};
|
|
6763
6755
|
}
|
|
6764
6756
|
return null;
|
|
6765
6757
|
}).filter(Boolean);
|
|
6766
|
-
setInternalFiles(normalizedFiles);
|
|
6767
6758
|
} else {
|
|
6768
|
-
// Handle single file values
|
|
6759
|
+
// Handle single file values
|
|
6769
6760
|
let singleFile = null;
|
|
6770
6761
|
if (typeof value === "string") {
|
|
6771
|
-
// Backward compatibility: single URL
|
|
6772
6762
|
const fileName = value.split("/").pop() || "Unknown File";
|
|
6773
6763
|
singleFile = {
|
|
6774
6764
|
url: value,
|
|
6775
6765
|
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
6776
|
-
originalName: fileName
|
|
6777
|
-
uploading: false
|
|
6766
|
+
originalName: fileName
|
|
6778
6767
|
};
|
|
6779
6768
|
} else if (value && typeof value === "object" && value.url) {
|
|
6780
|
-
// Enhanced structure - preserve empty name if explicitly set
|
|
6781
6769
|
singleFile = {
|
|
6782
6770
|
url: value.url,
|
|
6783
6771
|
name: value.name !== undefined ? value.name : value.originalName || "Unknown File",
|
|
6784
|
-
originalName: value.originalName || value.url.split("/").pop() || "Unknown File"
|
|
6785
|
-
uploading: false
|
|
6772
|
+
originalName: value.originalName || value.url.split("/").pop() || "Unknown File"
|
|
6786
6773
|
};
|
|
6787
6774
|
}
|
|
6788
|
-
|
|
6775
|
+
fileData = singleFile ? [singleFile] : [];
|
|
6789
6776
|
}
|
|
6777
|
+
|
|
6778
|
+
// Create inputs for each existing file
|
|
6779
|
+
const existingInputs = fileData.map((file, index) => ({
|
|
6780
|
+
id: index,
|
|
6781
|
+
fileUrl: file.url,
|
|
6782
|
+
name: file.name,
|
|
6783
|
+
originalName: file.originalName
|
|
6784
|
+
}));
|
|
6785
|
+
// Add one empty input at the end
|
|
6786
|
+
nextIdRef.current = fileData.length;
|
|
6787
|
+
setInputs([...existingInputs, {
|
|
6788
|
+
id: nextIdRef.current,
|
|
6789
|
+
fileUrl: null
|
|
6790
|
+
}]);
|
|
6791
|
+
nextIdRef.current++;
|
|
6790
6792
|
} else {
|
|
6791
|
-
|
|
6793
|
+
setInputs([{
|
|
6794
|
+
id: 0,
|
|
6795
|
+
fileUrl: null
|
|
6796
|
+
}]);
|
|
6792
6797
|
}
|
|
6793
6798
|
}, [value]);
|
|
6794
6799
|
|
|
6795
6800
|
// Handle new file upload from FileInput component
|
|
6796
|
-
const handleFileUpload = uploadedUrl => {
|
|
6797
|
-
// Handle case where uploadedUrl might be undefined
|
|
6801
|
+
const handleFileUpload = (inputId, uploadedUrl) => {
|
|
6798
6802
|
if (!uploadedUrl) {
|
|
6799
6803
|
return;
|
|
6800
6804
|
}
|
|
6801
6805
|
const fileName = uploadedUrl.split("/").pop() || "Unknown File";
|
|
6802
|
-
const newFile = {
|
|
6803
|
-
url: uploadedUrl,
|
|
6804
|
-
name: "",
|
|
6805
|
-
// Start with empty name as requested
|
|
6806
|
-
originalName: fileName,
|
|
6807
|
-
uploading: false
|
|
6808
|
-
};
|
|
6809
6806
|
|
|
6810
|
-
//
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6807
|
+
// Find the input that triggered this upload and update it
|
|
6808
|
+
setInputs(prevInputs => {
|
|
6809
|
+
const updatedInputs = prevInputs.map(input => {
|
|
6810
|
+
if (input.id === inputId) {
|
|
6811
|
+
return _objectSpread$1(_objectSpread$1({}, input), {}, {
|
|
6812
|
+
fileUrl: uploadedUrl,
|
|
6813
|
+
name: "",
|
|
6814
|
+
originalName: fileName
|
|
6815
|
+
});
|
|
6816
|
+
}
|
|
6817
|
+
return input;
|
|
6818
|
+
});
|
|
6819
|
+
|
|
6820
|
+
// Add a new empty input for the next upload
|
|
6821
|
+
updatedInputs.push({
|
|
6822
|
+
id: nextIdRef.current,
|
|
6823
|
+
fileUrl: null
|
|
6824
|
+
});
|
|
6825
|
+
nextIdRef.current++;
|
|
6826
|
+
return updatedInputs;
|
|
6827
|
+
});
|
|
6814
6828
|
};
|
|
6815
6829
|
|
|
6816
6830
|
// Handle file name change
|
|
6817
6831
|
const handleNameChange = (url, newName) => {
|
|
6818
|
-
|
|
6832
|
+
setInputs(prevInputs => prevInputs.map(input => input.fileUrl === url ? _objectSpread$1(_objectSpread$1({}, input), {}, {
|
|
6819
6833
|
name: newName
|
|
6820
|
-
}) :
|
|
6821
|
-
setInternalFiles(updatedFiles);
|
|
6822
|
-
notifyParent(updatedFiles);
|
|
6834
|
+
}) : input));
|
|
6823
6835
|
};
|
|
6824
6836
|
|
|
6825
6837
|
// Handle file removal
|
|
6826
6838
|
const handleFileRemove = url => {
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6839
|
+
setInputs(prevInputs => {
|
|
6840
|
+
return prevInputs.map(input => {
|
|
6841
|
+
if (input.fileUrl === url) {
|
|
6842
|
+
return _objectSpread$1(_objectSpread$1({}, input), {}, {
|
|
6843
|
+
fileUrl: null
|
|
6844
|
+
});
|
|
6845
|
+
}
|
|
6846
|
+
return input;
|
|
6847
|
+
});
|
|
6848
|
+
});
|
|
6830
6849
|
};
|
|
6831
6850
|
|
|
6832
|
-
//
|
|
6833
|
-
const
|
|
6834
|
-
|
|
6835
|
-
|
|
6851
|
+
// Get all uploaded files from inputs
|
|
6852
|
+
const getUploadedFiles = () => {
|
|
6853
|
+
return inputs.filter(input => input.fileUrl !== null).map(input => ({
|
|
6854
|
+
url: input.fileUrl,
|
|
6855
|
+
name: input.name,
|
|
6856
|
+
originalName: input.originalName
|
|
6857
|
+
}));
|
|
6858
|
+
};
|
|
6859
|
+
|
|
6860
|
+
// Track previous uploaded files to prevent infinite loops
|
|
6861
|
+
const prevFilesRef = React.useRef([]);
|
|
6862
|
+
|
|
6863
|
+
// Sync with parent when uploaded files actually change
|
|
6864
|
+
React.useEffect(() => {
|
|
6865
|
+
const files = getUploadedFiles();
|
|
6866
|
+
const currentUrls = files.map(f => f.url).join(",");
|
|
6867
|
+
const prevUrls = prevFilesRef.current.map(f => f.url).join(",");
|
|
6868
|
+
|
|
6869
|
+
// Only call onChange if the file list actually changed
|
|
6870
|
+
if (currentUrls !== prevUrls && onChange) {
|
|
6871
|
+
prevFilesRef.current = files;
|
|
6836
6872
|
onChange(field.id, files);
|
|
6837
6873
|
}
|
|
6838
|
-
};
|
|
6874
|
+
}, [inputs]);
|
|
6839
6875
|
|
|
6840
6876
|
// Define acceptable file types for documents and common formats
|
|
6841
6877
|
const acceptTypes = {
|
|
@@ -6850,8 +6886,11 @@ const ListingFileInput = _ref => {
|
|
|
6850
6886
|
"image/png": [".png"]
|
|
6851
6887
|
};
|
|
6852
6888
|
|
|
6853
|
-
//
|
|
6889
|
+
// Get the last empty input (the one to show)
|
|
6890
|
+
const emptyInput = inputs.find(input => input.fileUrl === null);
|
|
6854
6891
|
|
|
6892
|
+
// Get uploaded files for display
|
|
6893
|
+
const uploadedFiles = getUploadedFiles();
|
|
6855
6894
|
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6856
6895
|
className: modules_4fb4144d.listingFileInput
|
|
6857
6896
|
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
@@ -6876,39 +6915,22 @@ const ListingFileInput = _ref => {
|
|
|
6876
6915
|
className: modules_2b7292f7.requiredAsterisk
|
|
6877
6916
|
}, "*")), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6878
6917
|
className: "".concat(modules_4fb4144d.listingFileInput__dropZone, " ").concat(showError ? modules_4fb4144d["listingFileInput__dropZone--error"] : "")
|
|
6879
|
-
}, /*#__PURE__*/React__default["default"].createElement(FileInput, {
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
refreshCallback: handleFileUpload,
|
|
6918
|
+
}, emptyInput && /*#__PURE__*/React__default["default"].createElement(FileInput, {
|
|
6919
|
+
key: emptyInput.id,
|
|
6920
|
+
refreshCallback: url => handleFileUpload(emptyInput.id, url),
|
|
6883
6921
|
hasDefault: null,
|
|
6884
6922
|
accept: acceptTypes,
|
|
6885
|
-
multiple: false
|
|
6886
|
-
,
|
|
6923
|
+
multiple: false,
|
|
6887
6924
|
disabled: disabled
|
|
6888
|
-
})),
|
|
6925
|
+
})), uploadedFiles.length > 0 && /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6889
6926
|
className: modules_4fb4144d.listingFileInput__fileList
|
|
6890
|
-
},
|
|
6927
|
+
}, uploadedFiles.map(file => /*#__PURE__*/React__default["default"].createElement(FileListItem, {
|
|
6891
6928
|
key: file.url,
|
|
6892
6929
|
file: file,
|
|
6893
6930
|
onNameChange: handleNameChange,
|
|
6894
6931
|
onRemove: handleFileRemove,
|
|
6895
6932
|
disabled: disabled
|
|
6896
|
-
}))),
|
|
6897
|
-
ref: fileInputRef,
|
|
6898
|
-
className: "imageInputOuter-single",
|
|
6899
|
-
refreshCallback: handleFileUpload,
|
|
6900
|
-
hasDefault: null,
|
|
6901
|
-
accept: acceptTypes,
|
|
6902
|
-
multiple: false,
|
|
6903
|
-
disabled: disabled,
|
|
6904
|
-
customButton: /*#__PURE__*/React__default["default"].createElement(Button$4, {
|
|
6905
|
-
buttonType: "secondary",
|
|
6906
|
-
className: modules_4fb4144d.listingFileInput__addMoreButton,
|
|
6907
|
-
disabled: disabled
|
|
6908
|
-
}, /*#__PURE__*/React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, {
|
|
6909
|
-
icon: iconImports.plus
|
|
6910
|
-
}), "Add More Files")
|
|
6911
|
-
})), showError && errorMessage && /*#__PURE__*/React__default["default"].createElement(Text$8, {
|
|
6933
|
+
}))), showError && errorMessage && /*#__PURE__*/React__default["default"].createElement(Text$8, {
|
|
6912
6934
|
type: "help",
|
|
6913
6935
|
style: {
|
|
6914
6936
|
color: "var(--colour-red)",
|
|
@@ -7215,9 +7237,11 @@ const ListingEditor = _ref => {
|
|
|
7215
7237
|
// formData always has structure { fields: {...} } after our fixes
|
|
7216
7238
|
const submissionData = isEditMode ? {
|
|
7217
7239
|
id: listingId,
|
|
7218
|
-
fields: formData.fields
|
|
7240
|
+
fields: formData.fields,
|
|
7241
|
+
site: auth.site
|
|
7219
7242
|
} : {
|
|
7220
|
-
fields: formData.fields
|
|
7243
|
+
fields: formData.fields,
|
|
7244
|
+
site: auth.site
|
|
7221
7245
|
};
|
|
7222
7246
|
const actionPromise = dispatch(isEditMode ? editListing(submissionData) : createListing(submissionData));
|
|
7223
7247
|
actionPromise.then(result => {
|
|
@@ -7762,12 +7786,12 @@ const Reducers = (() => {
|
|
|
7762
7786
|
})();
|
|
7763
7787
|
const Screens = (() => {
|
|
7764
7788
|
const screens = {};
|
|
7765
|
-
screens[
|
|
7766
|
-
screens[
|
|
7767
|
-
screens[
|
|
7768
|
-
screens[
|
|
7769
|
-
screens[
|
|
7770
|
-
screens[
|
|
7789
|
+
screens[values.screenFormOverviewStep] = FormOverviewStep;
|
|
7790
|
+
screens[values.screenFormFieldsStep] = FormFieldsStep;
|
|
7791
|
+
screens[values.screenFormLayoutStep] = FormLayoutStep;
|
|
7792
|
+
screens[values.screenListingScreen] = ListingScreen$1;
|
|
7793
|
+
screens[values.pageCreateListing] = CreateListingPage;
|
|
7794
|
+
screens[values.pageEditListing] = EditListingPage;
|
|
7771
7795
|
return screens;
|
|
7772
7796
|
})();
|
|
7773
7797
|
|
package/package.json
CHANGED
|
@@ -241,10 +241,10 @@ export const BaseFieldConfig = (props) => {
|
|
|
241
241
|
{setMaxImages && (
|
|
242
242
|
<GenericInput
|
|
243
243
|
type="number"
|
|
244
|
-
value={maxImages ||
|
|
245
|
-
placeholder="
|
|
244
|
+
value={maxImages || 16}
|
|
245
|
+
placeholder="16"
|
|
246
246
|
label="Maximum Images"
|
|
247
|
-
onChange={(e) => setMaxImages(parseInt(e.target.value) ||
|
|
247
|
+
onChange={(e) => setMaxImages(parseInt(e.target.value) || 16)}
|
|
248
248
|
className={styles.fieldSpacing}
|
|
249
249
|
alwaysShowLabel
|
|
250
250
|
/>
|
|
@@ -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
|
-
|
|
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
|
-
|
|
24
|
+
grid-template-columns: repeat(4, 160px);
|
|
24
25
|
gap: 1rem;
|
|
25
26
|
margin-top: 0.5rem;
|
|
26
27
|
}
|
package/src/index.js
CHANGED
|
@@ -18,12 +18,12 @@ export const Reducers = (() => {
|
|
|
18
18
|
export const Screens = (() => {
|
|
19
19
|
const screens = {};
|
|
20
20
|
|
|
21
|
-
screens[
|
|
22
|
-
screens[
|
|
23
|
-
screens[
|
|
24
|
-
screens[
|
|
25
|
-
screens[
|
|
26
|
-
screens[
|
|
21
|
+
screens[values.screenFormOverviewStep] = FormOverviewStep;
|
|
22
|
+
screens[values.screenFormFieldsStep] = FormFieldsStep;
|
|
23
|
+
screens[values.screenFormLayoutStep] = FormLayoutStep;
|
|
24
|
+
screens[values.screenListingScreen] = ListingScreen;
|
|
25
|
+
screens[values.pageCreateListing] = CreateListingPage;
|
|
26
|
+
screens[values.pageEditListing] = EditListingPage;
|
|
27
27
|
return screens;
|
|
28
28
|
})();
|
|
29
29
|
|
|
@@ -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,8 @@ 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 = payload
|
|
281
|
-
const definition = definitionWrapper
|
|
280
|
+
const definitionWrapper = (payload && payload.featureDefinition) || payload;
|
|
281
|
+
const definition = (definitionWrapper && definitionWrapper.definition) || definitionWrapper;
|
|
282
282
|
|
|
283
283
|
// Validate and map definition data to form state structure
|
|
284
284
|
const mappedValues = {
|
|
@@ -511,10 +511,10 @@ const definitionReducer = (state = INITIAL_STATE.definition, action) => {
|
|
|
511
511
|
// Extract from API response for edit mode
|
|
512
512
|
// Handle nested structure: data.featureDefinition.definition
|
|
513
513
|
const featureDefinitionWrapper =
|
|
514
|
-
data
|
|
514
|
+
(data && data.featureDefinition) || data;
|
|
515
515
|
definition =
|
|
516
|
-
featureDefinitionWrapper
|
|
517
|
-
definitionId = featureDefinitionWrapper
|
|
516
|
+
(featureDefinitionWrapper && featureDefinitionWrapper.definition) || featureDefinitionWrapper;
|
|
517
|
+
definitionId = (featureDefinitionWrapper && featureDefinitionWrapper.id) || values.featureId;
|
|
518
518
|
|
|
519
519
|
// Ensure fields array exists and preserves order property
|
|
520
520
|
if (definition?.fields) {
|
|
@@ -131,14 +131,9 @@ export const selectDefinitionMode = (state) => {
|
|
|
131
131
|
|
|
132
132
|
// Check if we have a definition loaded or if we've determined the mode
|
|
133
133
|
// This is used by hiddenFromFeaturePicker to determine if feature should be shown
|
|
134
|
-
// When mode is "create", definition is null but we've determined the feature doesn't exist
|
|
135
|
-
// When mode is "edit", definition exists and we've determined the feature exists
|
|
136
134
|
export const selectHasDefinition = (state) => {
|
|
137
135
|
const definition = selectDefinition(state);
|
|
138
|
-
|
|
139
|
-
// Consider feature "loaded" if definition exists OR mode is set
|
|
140
|
-
// Mode being set means we've successfully fetched and determined the state
|
|
141
|
-
return !!definition || !!mode;
|
|
136
|
+
return !!definition;
|
|
142
137
|
};
|
|
143
138
|
|
|
144
139
|
// ============ LISTINGS SELECTORS ============
|
|
@@ -431,13 +426,13 @@ export const selectIsStepAccessible = (step) => (state) => {
|
|
|
431
426
|
// Get current sort method
|
|
432
427
|
export const selectSortBy = (state) => {
|
|
433
428
|
const listingsState = selectListingsState(state);
|
|
434
|
-
return listingsState?.sortBy
|
|
429
|
+
return listingsState?.sortBy || "newest";
|
|
435
430
|
};
|
|
436
431
|
|
|
437
432
|
// Get show deleted toggle state
|
|
438
433
|
export const selectShowDeleted = (state) => {
|
|
439
434
|
const listingsState = selectListingsState(state);
|
|
440
|
-
return listingsState?.showDeleted
|
|
435
|
+
return listingsState?.showDeleted || false;
|
|
441
436
|
};
|
|
442
437
|
|
|
443
438
|
// Get sorted and filtered listings
|