mirror of
https://github.com/riwiwa/muzi.git
synced 2026-04-20 11:25:51 -07:00
fix name collisions and add better track/artist/album edit UX
This commit is contained in:
BIN
static/assets/pfps/default_album.png
Normal file
BIN
static/assets/pfps/default_album.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
111
static/menu.js
111
static/menu.js
@@ -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';
|
||||
}
|
||||
|
||||
193
static/style.css
193
static/style.css
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user