diff --git a/portal/menu-builder.html b/portal/menu-builder.html
index a24ae32..20111c5 100644
--- a/portal/menu-builder.html
+++ b/portal/menu-builder.html
@@ -2804,7 +2804,10 @@
}
},
- // Upload photo
+ // Pending photo upload info (for mobile camera flow)
+ _pendingPhotoUpload: null,
+
+ // Upload photo - mobile-friendly version
uploadPhoto(itemId) {
// Find the item and check it has a database ID
let targetItem = null;
@@ -2817,52 +2820,81 @@
return;
}
- const input = document.createElement('input');
- input.type = 'file';
- 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;
-
- // 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) {
- this.toast('Image must be under 5MB', 'error');
- return;
- }
-
- this.toast('Uploading photo...', 'info');
-
- try {
- const formData = new FormData();
- formData.append('photo', file);
- formData.append('ItemID', targetItem.dbId);
-
- const response = await fetch(`${this.config.apiBaseUrl}/menu/uploadItemPhoto.cfm`, {
- method: 'POST',
- body: formData,
- credentials: 'include'
- });
-
- const data = await response.json();
- if (data.OK) {
- targetItem.imageUrl = data.IMAGEURL + '?t=' + Date.now();
- this.render();
- this.toast('Photo uploaded!', 'success');
- } else {
- this.toast(data.MESSAGE || 'Failed to upload photo', 'error');
- }
- } catch (err) {
- console.error('Photo upload error:', err);
- this.toast('Failed to upload photo', 'error');
- }
+ // Store pending upload info (survives page context issues on mobile)
+ this._pendingPhotoUpload = {
+ itemId: itemId,
+ dbId: targetItem.dbId
};
+ sessionStorage.setItem('pendingPhotoUpload', JSON.stringify(this._pendingPhotoUpload));
+
+ // Get or create persistent file input (mobile browsers handle these better)
+ let input = document.getElementById('photoUploadInput');
+ if (!input) {
+ input = document.createElement('input');
+ input.type = 'file';
+ input.id = 'photoUploadInput';
+ input.accept = 'image/*'; // Simpler accept for better mobile compatibility
+ input.style.display = 'none';
+ document.body.appendChild(input);
+
+ input.addEventListener('change', async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ // Get pending upload info
+ const pending = this._pendingPhotoUpload || JSON.parse(sessionStorage.getItem('pendingPhotoUpload') || 'null');
+ if (!pending) {
+ this.toast('Upload context lost - please try again', 'error');
+ return;
+ }
+
+ if (file.size > 5 * 1024 * 1024) {
+ this.toast('Image must be under 5MB', 'error');
+ return;
+ }
+
+ this.toast('Uploading photo...', 'info');
+
+ try {
+ const formData = new FormData();
+ formData.append('photo', file);
+ formData.append('ItemID', pending.dbId);
+
+ const response = await fetch(`${this.config.apiBaseUrl}/menu/uploadItemPhoto.cfm`, {
+ method: 'POST',
+ body: formData,
+ credentials: 'include'
+ });
+
+ const data = await response.json();
+ if (data.OK) {
+ // Find and update the item
+ for (const cat of this.menu.categories) {
+ const item = cat.items.find(i => i.id === pending.itemId);
+ if (item) {
+ item.imageUrl = data.IMAGEURL + '?t=' + Date.now();
+ break;
+ }
+ }
+ this.render();
+ this.toast('Photo uploaded!', 'success');
+ } else {
+ this.toast(data.MESSAGE || 'Failed to upload photo', 'error');
+ }
+ } catch (err) {
+ console.error('Photo upload error:', err);
+ this.toast('Failed to upload photo', 'error');
+ }
+
+ // Clean up
+ sessionStorage.removeItem('pendingPhotoUpload');
+ this._pendingPhotoUpload = null;
+ input.value = ''; // Reset for next upload
+ });
+ }
+
+ // Reset and trigger
+ input.value = '';
input.click();
},