Add modal-based photo upload for better mobile support

- Show a modal with file input and preview
- Uses alert() instead of toast for error messages (more visible on mobile)
- Preview shows selected image before upload
- Submit button with loading state
- Keeps old uploadPhoto function for compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-02-08 13:08:24 -08:00
parent cab561a20e
commit 946c2ebdf0

View file

@ -1753,7 +1753,7 @@
`<span>No photo yet</span>`}
</div>
<div class="photo-actions">
<button class="btn btn-secondary" onclick="MenuBuilder.uploadPhoto('${item.id}')">
<button class="btn btn-secondary" onclick="MenuBuilder.showUploadModal('${item.id}', '${item.dbId || ''}')">
Upload
</button>
<button class="btn btn-secondary" onclick="MenuBuilder.createPhotoTask('${item.id}')">
@ -2804,10 +2804,108 @@
}
},
// Show upload modal - works better on mobile
showUploadModal(itemId, dbId) {
if (!dbId) {
alert('Please save the menu first before uploading photos.');
return;
}
document.getElementById('modalTitle').textContent = 'Upload Item Photo';
document.getElementById('modalBody').innerHTML = `
<form id="photoUploadForm" enctype="multipart/form-data" style="text-align: center;">
<input type="hidden" name="ItemID" value="${dbId}">
<input type="hidden" id="uploadItemId" value="${itemId}">
<p style="color: var(--gray-600); margin-bottom: 16px;">
Select or take a photo for this item
</p>
<div style="margin-bottom: 16px;">
<input type="file" name="photo" id="modalPhotoInput" accept="image/*"
style="font-size: 16px; padding: 12px; border: 2px dashed var(--gray-300);
border-radius: 8px; width: 100%; cursor: pointer;">
</div>
<div id="uploadPreview" style="margin-bottom: 16px; display: none;">
<img id="previewImg" style="max-width: 100%; max-height: 200px; border-radius: 8px;">
</div>
<button type="submit" class="btn btn-primary" id="uploadSubmitBtn" disabled
style="width: 100%; padding: 12px;">
Upload Photo
</button>
</form>
`;
this.showModal();
const fileInput = document.getElementById('modalPhotoInput');
const submitBtn = document.getElementById('uploadSubmitBtn');
const preview = document.getElementById('uploadPreview');
const previewImg = document.getElementById('previewImg');
const form = document.getElementById('photoUploadForm');
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
submitBtn.disabled = false;
// Show preview
const reader = new FileReader();
reader.onload = (ev) => {
previewImg.src = ev.target.result;
preview.style.display = 'block';
};
reader.readAsDataURL(file);
}
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
const file = fileInput.files[0];
if (!file) return;
submitBtn.disabled = true;
submitBtn.textContent = 'Uploading...';
try {
const formData = new FormData();
formData.append('photo', file);
formData.append('ItemID', 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) {
// Update the item
const itemIdVal = document.getElementById('uploadItemId').value;
for (const cat of this.menu.categories) {
const item = cat.items.find(i => i.id === itemIdVal);
if (item) {
item.imageUrl = data.IMAGEURL + '?t=' + Date.now();
break;
}
}
this.closeModal();
this.render();
this.toast('Photo uploaded!', 'success');
} else {
alert('Upload failed: ' + (data.MESSAGE || data.ERROR || 'Unknown error'));
submitBtn.disabled = false;
submitBtn.textContent = 'Upload Photo';
}
} catch (err) {
console.error('Upload error:', err);
alert('Upload failed: ' + err.message);
submitBtn.disabled = false;
submitBtn.textContent = 'Upload Photo';
}
});
},
// Pending photo upload info (for mobile camera flow)
_pendingPhotoUpload: null,
// Upload photo - mobile-friendly version
// Upload photo - legacy version (kept for compatibility)
uploadPhoto(itemId) {
// Find the item and check it has a database ID
let targetItem = null;