feat: Add Library Visualizer (Slots & Drives) to Web UI

This commit is contained in:
2025-12-10 14:36:24 +00:00
parent 0b026aa11f
commit 79cf24cb8c
10 changed files with 491 additions and 1 deletions

View File

@@ -87,6 +87,11 @@ function initNavigation() {
this.classList.add('active');
document.getElementById(targetId).classList.add('active');
// Auto reload for Viz
if (targetId === 'manage-tapes') {
loadLibraryStatus();
}
});
});
}
@@ -1941,3 +1946,63 @@ async function changeUserPassword() {
resultDiv.innerHTML = `<strong>❌ Error:</strong> ${error.message}`;
}
}
// Library Visualizer
function loadLibraryStatus() {
const loading = document.getElementById('viz-loading');
const container = document.getElementById('library-viz');
const errorEl = document.getElementById('viz-error');
if (!loading || !container || !errorEl) return;
loading.style.display = 'block';
container.style.display = 'none';
errorEl.style.display = 'none';
fetch('api.php?action=library_status')
.then(response => response.json())
.then(data => {
loading.style.display = 'none';
if (data.success) {
container.style.display = 'flex';
renderVizGrid('viz-drives', data.data.drives, 'drive');
renderVizGrid('viz-maps', data.data.maps, 'map');
renderVizGrid('viz-slots', data.data.slots, 'slot');
} else {
errorEl.textContent = 'Error: ' + data.error;
errorEl.style.display = 'block';
}
})
.catch(err => {
loading.style.display = 'none';
errorEl.textContent = 'Fetch Error: ' + err.message;
errorEl.style.display = 'block';
});
}
function renderVizGrid(containerId, items, type) {
const grid = document.getElementById(containerId);
if (!grid) return;
grid.innerHTML = '';
if (!items || items.length === 0) {
grid.innerHTML = '<div style="color:#aaa; font-style:italic; grid-column: 1/-1;">No items</div>';
return;
}
items.forEach(item => {
const el = document.createElement('div');
el.className = `viz-slot ${item.full ? 'full' : 'empty'} ${type === 'drive' ? 'drive-slot' : ''}`;
const icon = type === 'drive' ? '💾' : (type === 'map' ? '📥' : '📼');
const label = item.full ? item.barcode : 'Empty';
el.innerHTML = `
<span class="slot-icon">${icon}</span>
<div class="slot-id">${type.toUpperCase()} ${item.id}</div>
<div class="tape-label" title="${label}">${label}</div>
`;
grid.appendChild(el);
});
}