@plusscommunities/pluss-feature-builder-web-a 1.0.2-beta.7 → 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 +119 -92
- 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/reducers/featureBuilderReducer.js +2 -2
- package/src/selectors/featureBuilderSelectors.js +1 -6
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 ============
|
|
@@ -1779,10 +1774,10 @@ const BaseFieldConfig = props => {
|
|
|
1779
1774
|
alwaysShowLabel: true
|
|
1780
1775
|
}), setMaxImages && /*#__PURE__*/React__default["default"].createElement(GenericInput$1, {
|
|
1781
1776
|
type: "number",
|
|
1782
|
-
value: maxImages ||
|
|
1783
|
-
placeholder: "
|
|
1777
|
+
value: maxImages || 16,
|
|
1778
|
+
placeholder: "16",
|
|
1784
1779
|
label: "Maximum Images",
|
|
1785
|
-
onChange: e => setMaxImages(parseInt(e.target.value) ||
|
|
1780
|
+
onChange: e => setMaxImages(parseInt(e.target.value) || 16),
|
|
1786
1781
|
className: modules_18fc9e14.fieldSpacing,
|
|
1787
1782
|
alwaysShowLabel: true
|
|
1788
1783
|
}), setMaxFileSize && /*#__PURE__*/React__default["default"].createElement(GenericInput$1, {
|
|
@@ -1908,8 +1903,8 @@ var css$g = "/* Simple listing field styling - clean vertical layout */\n\n.List
|
|
|
1908
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"};
|
|
1909
1904
|
n(css$g,{});
|
|
1910
1905
|
|
|
1911
|
-
var css$f = "/* ListingGalleryInput BEM CSS Module */\n\n/* Gallery container styles */\n.
|
|
1912
|
-
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"};
|
|
1913
1908
|
n(css$f,{});
|
|
1914
1909
|
|
|
1915
1910
|
const ListingGalleryInput = _ref => {
|
|
@@ -2814,7 +2809,7 @@ const SideBarInner = props => {
|
|
|
2814
2809
|
|
|
2815
2810
|
// Build sidebar items based on mode
|
|
2816
2811
|
const buildSidebarItems = () => {
|
|
2817
|
-
const isWizardMode = mode === "create"
|
|
2812
|
+
const isWizardMode = mode === "create";
|
|
2818
2813
|
return steps.map((step, index) => {
|
|
2819
2814
|
const isCompleted = selectIsStepComplete(step.key);
|
|
2820
2815
|
const isAccessible = selectIsStepAccessible(step.key);
|
|
@@ -2833,8 +2828,8 @@ const SideBarInner = props => {
|
|
|
2833
2828
|
isFontAwesome: true,
|
|
2834
2829
|
// Enhanced completion indicator
|
|
2835
2830
|
completed: isCompleted,
|
|
2836
|
-
// Disable all navigation in
|
|
2837
|
-
disabled:
|
|
2831
|
+
// Disable all navigation in create mode, otherwise respect accessibility
|
|
2832
|
+
disabled: mode === "create" || !isAccessible
|
|
2838
2833
|
};
|
|
2839
2834
|
return itemProps;
|
|
2840
2835
|
});
|
|
@@ -2858,7 +2853,7 @@ const SideBarInner = props => {
|
|
|
2858
2853
|
|
|
2859
2854
|
// Add effect to manually attach click handlers since HubSidebar might not use onclick properly
|
|
2860
2855
|
React.useEffect(() => {
|
|
2861
|
-
const isWizardMode = mode === "create"
|
|
2856
|
+
const isWizardMode = mode === "create";
|
|
2862
2857
|
const attachClickHandlers = () => {
|
|
2863
2858
|
const stepsWithUrls = [{
|
|
2864
2859
|
key: "overview",
|
|
@@ -5367,7 +5362,7 @@ const DEFAULT_NEW_FIELD = {
|
|
|
5367
5362
|
// Default to not using as summary for description fields
|
|
5368
5363
|
minImages: 1,
|
|
5369
5364
|
// Gallery-specific default
|
|
5370
|
-
maxImages:
|
|
5365
|
+
maxImages: 16,
|
|
5371
5366
|
// Gallery-specific default
|
|
5372
5367
|
maxFileSize: "5MB",
|
|
5373
5368
|
// Gallery-specific default
|
|
@@ -5420,7 +5415,7 @@ function createNewField(id) {
|
|
|
5420
5415
|
baseField.values.label = "Image Gallery";
|
|
5421
5416
|
baseField.values.helpText = "Upload multiple images to create a gallery";
|
|
5422
5417
|
baseField.values.minImages = 1;
|
|
5423
|
-
baseField.values.maxImages =
|
|
5418
|
+
baseField.values.maxImages = 16;
|
|
5424
5419
|
baseField.values.maxFileSize = "5MB";
|
|
5425
5420
|
baseField.values.allowedTypes = ["image/jpeg", "image/png", "image/webp"];
|
|
5426
5421
|
const _baseField$values2 = baseField.values,
|
|
@@ -6639,8 +6634,8 @@ const ListingCTAInput = _ref => {
|
|
|
6639
6634
|
}));
|
|
6640
6635
|
};
|
|
6641
6636
|
|
|
6642
|
-
var css$6 = "/* Enhanced File Input Styles - Based on existing PlussCore FileInput styling */\n\n/* Main container for enhanced file input */\n.
|
|
6643
|
-
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"};
|
|
6644
6639
|
n(css$6,{});
|
|
6645
6640
|
|
|
6646
6641
|
const FileListItem = _ref => {
|
|
@@ -6730,109 +6725,153 @@ const ListingFileInput = _ref => {
|
|
|
6730
6725
|
errorMessage,
|
|
6731
6726
|
disabled = false
|
|
6732
6727
|
} = _ref;
|
|
6733
|
-
const
|
|
6734
|
-
|
|
6728
|
+
const [inputs, setInputs] = React.useState([{
|
|
6729
|
+
id: 0,
|
|
6730
|
+
fileUrl: null
|
|
6731
|
+
}]);
|
|
6732
|
+
const nextIdRef = React.useRef(1);
|
|
6735
6733
|
|
|
6736
|
-
//
|
|
6734
|
+
// Initialize inputs from value prop - map each existing file to an input
|
|
6737
6735
|
React.useEffect(() => {
|
|
6738
6736
|
if (value) {
|
|
6737
|
+
let fileData = [];
|
|
6738
|
+
|
|
6739
6739
|
// Handle multiple files - could be array of URLs or array of file objects
|
|
6740
6740
|
if (Array.isArray(value)) {
|
|
6741
|
-
|
|
6741
|
+
fileData = value.map(file => {
|
|
6742
6742
|
if (typeof file === "string") {
|
|
6743
|
-
// Backward compatibility: array of URLs
|
|
6744
6743
|
const fileName = file.split("/").pop() || "Unknown File";
|
|
6745
6744
|
return {
|
|
6746
6745
|
url: file,
|
|
6747
6746
|
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
6748
|
-
|
|
6749
|
-
originalName: fileName,
|
|
6750
|
-
uploading: false
|
|
6747
|
+
originalName: fileName
|
|
6751
6748
|
};
|
|
6752
6749
|
} else if (file && typeof file === "object" && file.url) {
|
|
6753
|
-
// Enhanced structure
|
|
6754
6750
|
return {
|
|
6755
6751
|
url: file.url,
|
|
6756
6752
|
name: file.name || file.originalName || "Unknown File",
|
|
6757
|
-
originalName: file.originalName || file.url.split("/").pop() || "Unknown File"
|
|
6758
|
-
uploading: false
|
|
6753
|
+
originalName: file.originalName || file.url.split("/").pop() || "Unknown File"
|
|
6759
6754
|
};
|
|
6760
6755
|
}
|
|
6761
6756
|
return null;
|
|
6762
6757
|
}).filter(Boolean);
|
|
6763
|
-
setInternalFiles(normalizedFiles);
|
|
6764
6758
|
} else {
|
|
6765
|
-
// Handle single file values
|
|
6759
|
+
// Handle single file values
|
|
6766
6760
|
let singleFile = null;
|
|
6767
6761
|
if (typeof value === "string") {
|
|
6768
|
-
// Backward compatibility: single URL
|
|
6769
6762
|
const fileName = value.split("/").pop() || "Unknown File";
|
|
6770
6763
|
singleFile = {
|
|
6771
6764
|
url: value,
|
|
6772
6765
|
name: fileName.replace(/\.[^/.]+$/, ""),
|
|
6773
|
-
originalName: fileName
|
|
6774
|
-
uploading: false
|
|
6766
|
+
originalName: fileName
|
|
6775
6767
|
};
|
|
6776
6768
|
} else if (value && typeof value === "object" && value.url) {
|
|
6777
|
-
// Enhanced structure - preserve empty name if explicitly set
|
|
6778
6769
|
singleFile = {
|
|
6779
6770
|
url: value.url,
|
|
6780
6771
|
name: value.name !== undefined ? value.name : value.originalName || "Unknown File",
|
|
6781
|
-
originalName: value.originalName || value.url.split("/").pop() || "Unknown File"
|
|
6782
|
-
uploading: false
|
|
6772
|
+
originalName: value.originalName || value.url.split("/").pop() || "Unknown File"
|
|
6783
6773
|
};
|
|
6784
6774
|
}
|
|
6785
|
-
|
|
6775
|
+
fileData = singleFile ? [singleFile] : [];
|
|
6786
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++;
|
|
6787
6792
|
} else {
|
|
6788
|
-
|
|
6793
|
+
setInputs([{
|
|
6794
|
+
id: 0,
|
|
6795
|
+
fileUrl: null
|
|
6796
|
+
}]);
|
|
6789
6797
|
}
|
|
6790
6798
|
}, [value]);
|
|
6791
6799
|
|
|
6792
6800
|
// Handle new file upload from FileInput component
|
|
6793
|
-
const handleFileUpload = uploadedUrl => {
|
|
6794
|
-
// Handle case where uploadedUrl might be undefined
|
|
6801
|
+
const handleFileUpload = (inputId, uploadedUrl) => {
|
|
6795
6802
|
if (!uploadedUrl) {
|
|
6796
6803
|
return;
|
|
6797
6804
|
}
|
|
6798
6805
|
const fileName = uploadedUrl.split("/").pop() || "Unknown File";
|
|
6799
|
-
const newFile = {
|
|
6800
|
-
url: uploadedUrl,
|
|
6801
|
-
name: "",
|
|
6802
|
-
// Start with empty name as requested
|
|
6803
|
-
originalName: fileName,
|
|
6804
|
-
uploading: false
|
|
6805
|
-
};
|
|
6806
6806
|
|
|
6807
|
-
//
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
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
|
+
});
|
|
6811
6828
|
};
|
|
6812
6829
|
|
|
6813
6830
|
// Handle file name change
|
|
6814
6831
|
const handleNameChange = (url, newName) => {
|
|
6815
|
-
|
|
6832
|
+
setInputs(prevInputs => prevInputs.map(input => input.fileUrl === url ? _objectSpread$1(_objectSpread$1({}, input), {}, {
|
|
6816
6833
|
name: newName
|
|
6817
|
-
}) :
|
|
6818
|
-
setInternalFiles(updatedFiles);
|
|
6819
|
-
notifyParent(updatedFiles);
|
|
6834
|
+
}) : input));
|
|
6820
6835
|
};
|
|
6821
6836
|
|
|
6822
6837
|
// Handle file removal
|
|
6823
6838
|
const handleFileRemove = url => {
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
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
|
+
});
|
|
6827
6849
|
};
|
|
6828
6850
|
|
|
6829
|
-
//
|
|
6830
|
-
const
|
|
6831
|
-
|
|
6832
|
-
|
|
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;
|
|
6833
6872
|
onChange(field.id, files);
|
|
6834
6873
|
}
|
|
6835
|
-
};
|
|
6874
|
+
}, [inputs]);
|
|
6836
6875
|
|
|
6837
6876
|
// Define acceptable file types for documents and common formats
|
|
6838
6877
|
const acceptTypes = {
|
|
@@ -6847,8 +6886,11 @@ const ListingFileInput = _ref => {
|
|
|
6847
6886
|
"image/png": [".png"]
|
|
6848
6887
|
};
|
|
6849
6888
|
|
|
6850
|
-
//
|
|
6889
|
+
// Get the last empty input (the one to show)
|
|
6890
|
+
const emptyInput = inputs.find(input => input.fileUrl === null);
|
|
6851
6891
|
|
|
6892
|
+
// Get uploaded files for display
|
|
6893
|
+
const uploadedFiles = getUploadedFiles();
|
|
6852
6894
|
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6853
6895
|
className: modules_4fb4144d.listingFileInput
|
|
6854
6896
|
}, /*#__PURE__*/React__default["default"].createElement("div", {
|
|
@@ -6873,39 +6915,22 @@ const ListingFileInput = _ref => {
|
|
|
6873
6915
|
className: modules_2b7292f7.requiredAsterisk
|
|
6874
6916
|
}, "*")), /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6875
6917
|
className: "".concat(modules_4fb4144d.listingFileInput__dropZone, " ").concat(showError ? modules_4fb4144d["listingFileInput__dropZone--error"] : "")
|
|
6876
|
-
}, /*#__PURE__*/React__default["default"].createElement(FileInput, {
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
refreshCallback: handleFileUpload,
|
|
6918
|
+
}, emptyInput && /*#__PURE__*/React__default["default"].createElement(FileInput, {
|
|
6919
|
+
key: emptyInput.id,
|
|
6920
|
+
refreshCallback: url => handleFileUpload(emptyInput.id, url),
|
|
6880
6921
|
hasDefault: null,
|
|
6881
6922
|
accept: acceptTypes,
|
|
6882
|
-
multiple: false
|
|
6883
|
-
,
|
|
6923
|
+
multiple: false,
|
|
6884
6924
|
disabled: disabled
|
|
6885
|
-
})),
|
|
6925
|
+
})), uploadedFiles.length > 0 && /*#__PURE__*/React__default["default"].createElement("div", {
|
|
6886
6926
|
className: modules_4fb4144d.listingFileInput__fileList
|
|
6887
|
-
},
|
|
6927
|
+
}, uploadedFiles.map(file => /*#__PURE__*/React__default["default"].createElement(FileListItem, {
|
|
6888
6928
|
key: file.url,
|
|
6889
6929
|
file: file,
|
|
6890
6930
|
onNameChange: handleNameChange,
|
|
6891
6931
|
onRemove: handleFileRemove,
|
|
6892
6932
|
disabled: disabled
|
|
6893
|
-
}))),
|
|
6894
|
-
ref: fileInputRef,
|
|
6895
|
-
className: "imageInputOuter-single",
|
|
6896
|
-
refreshCallback: handleFileUpload,
|
|
6897
|
-
hasDefault: null,
|
|
6898
|
-
accept: acceptTypes,
|
|
6899
|
-
multiple: false,
|
|
6900
|
-
disabled: disabled,
|
|
6901
|
-
customButton: /*#__PURE__*/React__default["default"].createElement(Button$4, {
|
|
6902
|
-
buttonType: "secondary",
|
|
6903
|
-
className: modules_4fb4144d.listingFileInput__addMoreButton,
|
|
6904
|
-
disabled: disabled
|
|
6905
|
-
}, /*#__PURE__*/React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, {
|
|
6906
|
-
icon: iconImports.plus
|
|
6907
|
-
}), "Add More Files")
|
|
6908
|
-
})), showError && errorMessage && /*#__PURE__*/React__default["default"].createElement(Text$8, {
|
|
6933
|
+
}))), showError && errorMessage && /*#__PURE__*/React__default["default"].createElement(Text$8, {
|
|
6909
6934
|
type: "help",
|
|
6910
6935
|
style: {
|
|
6911
6936
|
color: "var(--colour-red)",
|
|
@@ -7212,9 +7237,11 @@ const ListingEditor = _ref => {
|
|
|
7212
7237
|
// formData always has structure { fields: {...} } after our fixes
|
|
7213
7238
|
const submissionData = isEditMode ? {
|
|
7214
7239
|
id: listingId,
|
|
7215
|
-
fields: formData.fields
|
|
7240
|
+
fields: formData.fields,
|
|
7241
|
+
site: auth.site
|
|
7216
7242
|
} : {
|
|
7217
|
-
fields: formData.fields
|
|
7243
|
+
fields: formData.fields,
|
|
7244
|
+
site: auth.site
|
|
7218
7245
|
};
|
|
7219
7246
|
const actionPromise = dispatch(isEditMode ? editListing(submissionData) : createListing(submissionData));
|
|
7220
7247
|
actionPromise.then(result => {
|
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
|
}
|
|
@@ -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 {
|
|
@@ -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 ============
|