fix name collisions and add better track/artist/album edit UX

This commit is contained in:
2026-02-28 23:31:26 -08:00
parent 99185499b1
commit 19ab88268e
11 changed files with 973 additions and 109 deletions

View File

@@ -23,10 +23,10 @@ document.addEventListener('DOMContentLoaded', function() {
menuOverlay.addEventListener('click', closeMenu);
}
// Close menu on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeMenu();
closeEditModal();
}
});
@@ -78,18 +78,123 @@ document.addEventListener('DOMContentLoaded', function() {
}, 300);
});
// Close search on escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
searchResults.classList.remove('active');
}
});
// Close search when clicking outside
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
searchResults.classList.remove('active');
}
});
}
// Image Upload Functionality
document.querySelectorAll('.editable-image').forEach(function(img) {
img.style.cursor = 'pointer';
img.addEventListener('click', function(e) {
var entityType = this.getAttribute('data-entity');
var entityId = this.getAttribute('data-id');
var field = this.getAttribute('data-field');
var input = document.createElement('input');
input.type = 'file';
input.accept = 'image/jpeg,image/png,image/gif,image/webp';
input.onchange = function(e) {
var file = e.target.files[0];
if (!file) return;
if (file.size > 5 * 1024 * 1024) {
alert('File exceeds 5MB limit');
return;
}
var formData = new FormData();
formData.append('file', file);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload/image', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var result = JSON.parse(xhr.responseText);
var patchXhr = new XMLHttpRequest();
patchXhr.open('PATCH', '/api/' + entityType + '/' + entityId + '/edit?field=' + field, true);
patchXhr.setRequestHeader('Content-Type', 'application/json');
patchXhr.onreadystatechange = function() {
if (patchXhr.readyState === 4) {
if (patchXhr.status === 200) {
img.src = result.url;
} else {
alert('Error updating image: ' + patchXhr.responseText);
}
}
};
patchXhr.send(JSON.stringify({ value: result.url }));
} else {
alert('Error uploading: ' + xhr.responseText);
}
}
};
xhr.send(formData);
};
input.click();
});
});
// Generic edit form handler
var editForm = document.getElementById('editForm');
if (editForm) {
editForm.addEventListener('submit', function(e) {
e.preventDefault();
var form = e.target;
var entityType = form.getAttribute('data-entity');
var entityId = form.getAttribute('data-id');
var data = {};
var elements = form.querySelectorAll('input, textarea');
for (var i = 0; i < elements.length; i++) {
var el = elements[i];
if (el.name) {
data[el.name] = el.value;
}
}
var xhr = new XMLHttpRequest();
xhr.open('PATCH', '/api/' + entityType + '/' + entityId + '/batch', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// Update bio display if it exists
var bioDisplay = document.getElementById('bio-display');
if (bioDisplay && data.bio !== undefined) {
bioDisplay.textContent = data.bio;
}
// Update info display if it exists
var infoDisplay = document.getElementById('info-display');
if (infoDisplay && data.title !== undefined) {
// Will be reloaded anyway, but close modal first
}
closeEditModal();
location.reload();
} else {
alert('Error saving: ' + xhr.responseText);
}
}
};
xhr.send(JSON.stringify(data));
});
}
});
function openEditModal() {
document.getElementById('editModal').style.display = 'flex';
}
function closeEditModal() {
document.getElementById('editModal').style.display = 'none';
}