diff --git a/api/menu/uploadHeader.cfm b/api/menu/uploadHeader.cfm index 3c9c605..980f259 100644 --- a/api/menu/uploadHeader.cfm +++ b/api/menu/uploadHeader.cfm @@ -64,14 +64,19 @@ if (bizId LTE 0) { - - + + - #serializeJSON({ "OK": false, "ERROR": "invalid_type", "MESSAGE": "Only image files are accepted (jpg, jpeg, gif, png, webp)" })# + #serializeJSON({ "OK": false, "ERROR": "invalid_type", "MESSAGE": "Only image files are accepted (jpg, jpeg, gif, png, webp, heic)" })# + + + + + diff --git a/api/menu/uploadItemPhoto.cfm b/api/menu/uploadItemPhoto.cfm index e17c340..3150fa0 100644 --- a/api/menu/uploadItemPhoto.cfm +++ b/api/menu/uploadItemPhoto.cfm @@ -26,17 +26,23 @@ if (itemId LTE 0) { - - - + + + + - #serializeJSON({ "OK": false, "ERROR": "invalid_type", "MESSAGE": "Only image files are accepted (jpg, jpeg, gif, png, webp)" })# + #serializeJSON({ "OK": false, "ERROR": "invalid_type", "MESSAGE": "Only image files are accepted (jpg, jpeg, gif, png, webp, heic). Got: #actualExt#" })# + + + + + -for (ext in listToArray(allowedExtensions)) { +for (ext in listToArray("jpg,jpeg,gif,png,webp,heic,heif")) { oldFile = "#itemsDir#/#itemId#.#ext#"; if (fileExists(oldFile)) { try { fileDelete(oldFile); } catch (any e) {} @@ -44,15 +50,15 @@ for (ext in listToArray(allowedExtensions)) { } - - + + #serializeJSON({ "OK": true, "ERROR": "", "MESSAGE": "Photo uploaded successfully", - "IMAGEURL": "/uploads/items/#itemId#.#uploadResult.ClientFileExt#" + "IMAGEURL": "/uploads/items/#itemId#.#actualExt#" })# diff --git a/portal/menu-builder.html b/portal/menu-builder.html index 5cc9652..a24ae32 100644 --- a/portal/menu-builder.html +++ b/portal/menu-builder.html @@ -2819,14 +2819,17 @@ const input = document.createElement('input'); input.type = 'file'; - input.accept = 'image/jpeg,image/png,image/gif,image/webp'; + input.accept = 'image/jpeg,image/png,image/gif,image/webp,image/heic,image/heif,.heic,.heif'; + input.capture = 'environment'; // Prefer rear camera on mobile input.onchange = async (e) => { const file = e.target.files[0]; if (!file) return; - const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - if (!validTypes.includes(file.type)) { - this.toast('Please select a valid image (JPG, PNG, GIF, or WebP)', 'error'); + // Be permissive with file types - server will validate + // Mobile cameras may report inconsistent MIME types + const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/heic', 'image/heif', '']; + if (file.type && !validTypes.includes(file.type) && !file.type.startsWith('image/')) { + this.toast('Please select a valid image file', 'error'); return; } if (file.size > 5 * 1024 * 1024) { @@ -3115,15 +3118,15 @@ uploadHeader() { const input = document.createElement('input'); input.type = 'file'; - input.accept = 'image/jpeg,image/png,image/gif,image/webp'; + input.accept = 'image/jpeg,image/png,image/gif,image/webp,image/heic,image/heif,.heic,.heif'; input.onchange = async (e) => { const file = e.target.files[0]; if (!file) return; - // Validate file type - const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - if (!validTypes.includes(file.type)) { - this.toast('Please select a valid image (JPG, PNG, GIF, or WebP)', 'error'); + // Validate file type (be permissive for mobile cameras) + const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/heic', 'image/heif', '']; + if (file.type && !validTypes.includes(file.type) && !file.type.startsWith('image/')) { + this.toast('Please select a valid image file', 'error'); return; }