feat: Add Library Visualizer (Slots & Drives) to Web UI
This commit is contained in:
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user