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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

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';
}

View File

@@ -274,6 +274,14 @@
font-size: 15px;
margin: 0;
}
h2 a {
color: #AFA;
text-decoration: none;
}
h2 a:hover {
color: #FFF;
text-decoration: underline;
}
img {
object-fit: cover;
width: 250px;
@@ -551,3 +559,188 @@ a.button {
a.button:hover {
background: #1ed760;
}
.edit-toggle {
cursor: pointer;
font-size: 0.8em;
margin-left: 8px;
color: #888;
vertical-align: middle;
}
.edit-toggle:hover {
color: #AFA;
}
.inline-edit-form {
display: inline-flex;
align-items: center;
gap: 5px;
margin-left: 8px;
}
.inline-edit-form input,
.inline-edit-form textarea {
padding: 5px 10px;
border: 1px solid #444;
border-radius: 4px;
background: #333;
color: #AFA;
font-size: inherit;
font-family: inherit;
}
.inline-edit-form textarea {
min-width: 200px;
min-height: 60px;
}
.inline-edit-form button {
padding: 5px 10px;
background: #1DB954;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
}
.inline-edit-form button:hover {
background: #1ed760;
}
.editable-image {
transition: opacity 0.2s;
}
.editable-image:hover {
opacity: 0.7;
}
.album-cover {
border-radius: 0 !important;
}
.edit-btn {
margin-left: 15px;
padding: 5px 15px;
background: #444;
color: #AFA;
border: 1px solid #AFA;
border-radius: 4px;
cursor: pointer;
font-size: 0.8em;
}
.edit-btn:hover {
background: #555;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
}
.modal-content {
background: #2a2a2a;
padding: 30px;
border-radius: 15px;
min-width: 400px;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.modal-content h2 {
margin-top: 0;
margin-bottom: 20px;
}
.modal-content form {
display: flex;
flex-direction: column;
gap: 15px;
}
.modal-content label {
display: flex;
flex-direction: column;
text-align: left;
gap: 5px;
}
.modal-content input,
.modal-content textarea {
padding: 10px;
border: 1px solid #444;
border-radius: 5px;
background: #333;
color: #AFA;
font-size: 1em;
font-family: inherit;
}
.modal-content textarea {
min-height: 100px;
resize: vertical;
}
.modal-buttons {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 10px;
}
.modal-buttons button {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.modal-buttons button[type="submit"] {
background: #1DB954;
color: #fff;
}
.modal-buttons button[type="submit"]:hover {
background: #1ed760;
}
.modal-buttons .cancel-btn {
background: #444;
color: #AFA;
}
.modal-buttons .cancel-btn:hover {
background: #555;
}
.bio-box {
margin-top: 40px;
padding: 20px;
background: #2a2a2a;
border-radius: 10px;
width: 100%;
text-align: left;
}
.bio-box h3 {
margin-top: 0;
border-bottom: 1px solid #444;
padding-bottom: 10px;
}
.bio-box p {
margin: 0;
line-height: 1.6;
}