Miscellaneous UI and pricing updates from prior sessions

- PricingCalculationService: powder coverage and specific gravity math fixes
- Dashboard/Index: minor widget updates
- Jobs/Details, Jobs/Intake: shop floor and intake view improvements
- Quotes/Details: detail view updates
- GiftCertificates/Details: detail view update
- job-photos.js: photo gallery improvements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:27:37 -04:00
parent 74414c6c71
commit 2e73cfab54
7 changed files with 226 additions and 38 deletions
@@ -4,6 +4,7 @@ const jobPhotoModule = {
allPhotos: [],
currentPhotoIndex: 0,
_tagApi: null,
_editTagApi: null,
init: function(jobId, tagSuggestions) {
this.jobId = jobId;
@@ -21,6 +22,17 @@ const jobPhotoModule = {
});
});
}
// Reset to view mode when the view modal closes
const viewModal = document.getElementById('viewPhotoModal');
if (viewModal) {
viewModal.addEventListener('hidden.bs.modal', () => {
const editPanel = document.getElementById('photoEditPanel');
if (editPanel && !editPanel.classList.contains('d-none')) {
this.cancelPhotoEdit();
}
});
}
},
loadJobPhotos: function() {
@@ -119,6 +131,11 @@ const jobPhotoModule = {
},
navigatePhoto: function(direction) {
// Exit edit mode before navigating to another photo
const editPanel = document.getElementById('photoEditPanel');
if (editPanel && !editPanel.classList.contains('d-none')) {
this.cancelPhotoEdit();
}
this.currentPhotoIndex += direction;
if (this.currentPhotoIndex < 0) this.currentPhotoIndex = this.allPhotos.length - 1;
if (this.currentPhotoIndex >= this.allPhotos.length) this.currentPhotoIndex = 0;
@@ -212,6 +229,73 @@ const jobPhotoModule = {
});
},
editPhoto: function() {
const photo = this.allPhotos[this.currentPhotoIndex];
document.getElementById('editPhotoType').value = photo.photoType;
document.getElementById('editPhotoCaption').value = photo.caption || '';
// Pre-populate tag hidden field so initTagInput picks it up
document.getElementById('editPhotoTagsHidden').value = photo.tags || '';
this._editTagApi = initTagInput('editPhotoTagsHidden', 'editPhotoTagsContainer', {
suggestions: this._tagSuggestions
});
document.getElementById('photoDetails').classList.add('d-none');
document.getElementById('photoEditPanel').classList.remove('d-none');
document.getElementById('viewModeButtons').classList.add('d-none');
document.getElementById('editModeButtons').classList.remove('d-none');
},
cancelPhotoEdit: function() {
document.getElementById('photoEditPanel').classList.add('d-none');
document.getElementById('photoDetails').classList.remove('d-none');
document.getElementById('editModeButtons').classList.add('d-none');
document.getElementById('viewModeButtons').classList.remove('d-none');
},
savePhotoEdit: function() {
const photo = this.allPhotos[this.currentPhotoIndex];
const photoType = parseInt(document.getElementById('editPhotoType').value, 10);
const caption = document.getElementById('editPhotoCaption').value.trim();
const tags = document.getElementById('editPhotoTagsHidden').value;
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;
fetch('/Jobs/UpdatePhoto', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'RequestVerificationToken': token
},
body: JSON.stringify({
id: photo.id,
caption: caption,
photoType: photoType,
tags: tags,
displayOrder: photo.displayOrder || 0
})
})
.then(r => r.json())
.then(data => {
if (data.success) {
photo.caption = caption || null;
photo.photoType = photoType;
photo.photoTypeDisplay = this.getPhotoTypeLabel(photoType);
photo.tags = tags || null;
photo.tagsList = tags ? tags.split(',').map(t => t.trim()).filter(t => t) : [];
this.cancelPhotoEdit();
this.viewPhoto(this.currentPhotoIndex, true);
this.renderPhotoGallery(this.allPhotos);
this.showToast('Photo updated successfully', 'success');
} else {
this.showToast(data.message || 'Error updating photo', 'danger');
}
})
.catch(() => this.showToast('An error occurred while updating', 'danger'));
},
getPhotoTypeLabel: function(type) {
const labels = { 0: 'Before', 1: 'Progress', 2: 'After', 3: 'Quality Check', 4: 'Issue', 5: 'Completed' };
return labels[type] || 'Unknown';
},
setupDragDrop: function() {
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('photoFile');