Improved dashboard and music queue
This commit is contained in:
+287
@@ -0,0 +1,287 @@
|
||||
// DOM Elements
|
||||
const queueTab = document.getElementById('queueTab');
|
||||
const usersTab = document.getElementById('usersTab');
|
||||
const cacheTab = document.getElementById('cacheTab');
|
||||
const logoutButton = document.getElementById('logoutButton');
|
||||
|
||||
const queueSection = document.getElementById('queueSection');
|
||||
const usersSection = document.getElementById('usersSection');
|
||||
const cacheSection = document.getElementById('cacheSection');
|
||||
|
||||
const nowPlaying = document.getElementById('nowPlaying');
|
||||
const queueList = document.getElementById('queueList');
|
||||
const clearQueueButton = document.getElementById('clearQueueButton');
|
||||
|
||||
const usersTable = document.getElementById('usersTable');
|
||||
const addUserButton = document.getElementById('addUserButton');
|
||||
const addUserModal = document.getElementById('addUserModal');
|
||||
const addUserForm = document.getElementById('addUserForm');
|
||||
const cancelAddUser = document.getElementById('cancelAddUser');
|
||||
|
||||
const cacheSongCount = document.getElementById('cacheSongCount');
|
||||
const cacheSize = document.getElementById('cacheSize');
|
||||
const cacheMaxSize = document.getElementById('cacheMaxSize');
|
||||
const cacheUsage = document.getElementById('cacheUsage');
|
||||
const clearCacheButton = document.getElementById('clearCacheButton');
|
||||
|
||||
// Tab switching
|
||||
function switchTab(tab, section) {
|
||||
// Remove active class from all tabs and sections
|
||||
[queueTab, usersTab, cacheTab].forEach(t => t.classList.remove('active'));
|
||||
[queueSection, usersSection, cacheSection].forEach(s => s.classList.remove('active'));
|
||||
|
||||
// Add active class to selected tab and section
|
||||
tab.classList.add('active');
|
||||
section.classList.add('active');
|
||||
}
|
||||
|
||||
queueTab.addEventListener('click', () => switchTab(queueTab, queueSection));
|
||||
usersTab.addEventListener('click', () => switchTab(usersTab, usersSection));
|
||||
cacheTab.addEventListener('click', () => switchTab(cacheTab, cacheSection));
|
||||
|
||||
// Logout
|
||||
logoutButton.addEventListener('click', async () => {
|
||||
try {
|
||||
await fetch('/api/logout', {
|
||||
method: 'POST',
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
window.location.href = '/login';
|
||||
} catch (error) {
|
||||
console.error('Logout failed:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Queue management
|
||||
function formatDuration(seconds) {
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
async function loadQueue() {
|
||||
try {
|
||||
const response = await fetch('/api/queue', {
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
const responseData = await response.json();
|
||||
|
||||
// Update now playing
|
||||
if (responseData.current_song) {
|
||||
nowPlaying.innerHTML = `
|
||||
<div class="song-info">
|
||||
<h4>${responseData.current_song.title}</h4>
|
||||
<p>${formatDuration(responseData.current_song.duration)}</p>
|
||||
</div>
|
||||
<div class="song-status">
|
||||
<span class="badge">${responseData.state}</span>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
nowPlaying.innerHTML = '<p>Nothing playing</p>';
|
||||
}
|
||||
|
||||
// Update queue
|
||||
if (responseData.queue && responseData.queue.length > 0) {
|
||||
queueList.innerHTML = responseData.queue.map((songItem, idx) => {
|
||||
if (songItem.is_current) {
|
||||
return `
|
||||
<li class="now-playing">
|
||||
<div class="song-info">
|
||||
<strong>${songItem.title}</strong>
|
||||
<span>${formatDuration(songItem.duration)}</span>
|
||||
</div>
|
||||
<div class="song-status">
|
||||
<span class="badge">Now Playing</span>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
} else {
|
||||
return `
|
||||
<li>
|
||||
<div class="song-info">
|
||||
<strong>${idx + 1}. ${songItem.title}</strong>
|
||||
<span>${formatDuration(songItem.duration)}</span>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}).join('');
|
||||
} else {
|
||||
queueList.innerHTML = '<li class="empty-message">Queue is empty</li>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load queue:', error);
|
||||
queueList.innerHTML = '<li class="empty-message">Failed to load queue</li>';
|
||||
}
|
||||
}
|
||||
|
||||
clearQueueButton.addEventListener('click', async () => {
|
||||
if (!confirm('Are you sure you want to clear the queue?')) return;
|
||||
|
||||
try {
|
||||
await fetch('/api/queue', {
|
||||
method: 'DELETE',
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
loadQueue();
|
||||
} catch (error) {
|
||||
console.error('Failed to clear queue:', error);
|
||||
alert('Failed to clear queue');
|
||||
}
|
||||
});
|
||||
|
||||
// User management
|
||||
async function loadUsers() {
|
||||
try {
|
||||
const response = await fetch('/api/users', {
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
const responseData = await response.json();
|
||||
|
||||
if (responseData.users && responseData.users.length > 0) {
|
||||
const tbody = usersTable.querySelector('tbody');
|
||||
tbody.innerHTML = responseData.users.map(userItem => `
|
||||
<tr>
|
||||
<td>${userItem.username}</td>
|
||||
<td>
|
||||
<select class="permission-select" data-username="${userItem.username}">
|
||||
<option value="0" ${userItem.permission_level == 0 ? 'selected' : ''}>None</option>
|
||||
<option value="1" ${userItem.permission_level == 1 ? 'selected' : ''}>View Only</option>
|
||||
<option value="2" ${userItem.permission_level == 2 ? 'selected' : ''}>Read/Write</option>
|
||||
<option value="3" ${userItem.permission_level == 3 ? 'selected' : ''}>Admin</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button class="save-permission" data-username="${userItem.username}">Save</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// Add event listeners to save buttons
|
||||
document.querySelectorAll('.save-permission').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const username = button.dataset.username;
|
||||
const select = document.querySelector(`.permission-select[data-username="${username}"]`);
|
||||
const permissionLevel = parseInt(select.value);
|
||||
|
||||
try {
|
||||
await fetch(`/api/users/${username}/permissions`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ permission_level: permissionLevel }),
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
alert(`Permission updated for ${username}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to update permission:', error);
|
||||
alert('Failed to update permission');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
usersTable.querySelector('tbody').innerHTML = '<tr class="empty-message"><td colspan="3">No users found</td></tr>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load users:', error);
|
||||
usersTable.querySelector('tbody').innerHTML = '<tr class="empty-message"><td colspan="3">Failed to load users</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
// Add user modal
|
||||
addUserButton.addEventListener('click', () => {
|
||||
addUserModal.classList.add('active');
|
||||
});
|
||||
|
||||
cancelAddUser.addEventListener('click', () => {
|
||||
addUserModal.classList.remove('active');
|
||||
addUserForm.reset();
|
||||
});
|
||||
|
||||
addUserForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const username = document.getElementById('newUsername').value;
|
||||
const permissionLevel = parseInt(document.getElementById('newPermissionLevel').value);
|
||||
|
||||
try {
|
||||
await fetch('/api/users', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, permission_level: permissionLevel }),
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
|
||||
addUserModal.classList.remove('active');
|
||||
addUserForm.reset();
|
||||
loadUsers();
|
||||
} catch (error) {
|
||||
console.error('Failed to add user:', error);
|
||||
alert('Failed to add user');
|
||||
}
|
||||
});
|
||||
|
||||
// Cache management
|
||||
async function loadCacheStats() {
|
||||
try {
|
||||
const response = await fetch('/api/cache', {
|
||||
credentials: 'include', // Important for cookies to work
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
const stats = await response.json();
|
||||
|
||||
cacheSongCount.textContent = stats.songCount;
|
||||
cacheSize.textContent = `${stats.totalSizeMb} MB`;
|
||||
cacheMaxSize.textContent = `${stats.maxSizeMb} MB`;
|
||||
|
||||
const usagePercent = stats.maxSizeMb > 0
|
||||
? (stats.totalSizeMb / stats.maxSizeMb) * 100
|
||||
: 0;
|
||||
|
||||
cacheUsage.style.width = `${Math.min(usagePercent, 100)}%`;
|
||||
|
||||
// Change color based on usage
|
||||
if (usagePercent > 90) {
|
||||
cacheUsage.style.backgroundColor = 'var(--danger-color)';
|
||||
} else if (usagePercent > 70) {
|
||||
cacheUsage.style.backgroundColor = 'var(--accent-color)';
|
||||
} else {
|
||||
cacheUsage.style.backgroundColor = 'var(--primary-color)';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load cache stats:', error);
|
||||
}
|
||||
}
|
||||
|
||||
clearCacheButton.addEventListener('click', async () => {
|
||||
if (!confirm('Are you sure you want to clear the cache? This will delete all downloaded audio files.')) return;
|
||||
|
||||
try {
|
||||
await fetch('/api/cache', {
|
||||
method: 'DELETE',
|
||||
credentials: 'include', // Important for cookies to work
|
||||
});
|
||||
loadCacheStats();
|
||||
} catch (error) {
|
||||
console.error('Failed to clear cache:', error);
|
||||
alert('Failed to clear cache');
|
||||
}
|
||||
});
|
||||
|
||||
// Initial load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadQueue();
|
||||
loadUsers();
|
||||
loadCacheStats();
|
||||
|
||||
// Refresh data periodically
|
||||
setInterval(loadQueue, 10000); // Every 10 seconds
|
||||
setInterval(loadCacheStats, 30000); // Every 30 seconds
|
||||
});
|
||||
Reference in New Issue
Block a user