Change : display

This commit is contained in:
2026-06-21 18:05:52 +01:00
parent 6ee29a462e
commit 9b2bc907aa
4 changed files with 227 additions and 41 deletions

View File

@@ -382,6 +382,10 @@ async function refreshBackupsList() {
} catch (_) {}
}
function jsStr(s) {
return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
}
function renderBackupList(items, id, source) {
const node = el(id);
if (!node) return;
@@ -389,15 +393,26 @@ function renderBackupList(items, id, source) {
node.innerHTML = '<div class="empty-state"><i class="fas fa-inbox"></i>No backups</div>';
return;
}
node.innerHTML = items.map((b) => `
<div class="backup-item" id="bk-item-${source}-${b.replace(/[^a-z0-9]/gi,'_')}">
<span class="backup-name">${b}</span>
node.innerHTML = items.map((b) => {
const safe = jsStr(b);
const uploadBtn = source === 'local' && typeof quickUploadR2 === 'function'
? `<button class="btn btn-ghost btn-sm" onclick="quickUploadR2('${safe}',this)" title="Upload to R2"><i class="fas fa-cloud-upload-alt"></i></button>`
: '';
const detailsBtn = typeof showBackupDetails === 'function'
? `<button class="btn btn-ghost btn-sm" style="color:var(--text3);" onclick="showBackupDetails('${source}','${safe}',this)" title="Quick details"><i class="fas fa-circle-info"></i></button>`
: '';
return `
<div class="backup-item" id="bk-item-${source}-${b.replace(/[^a-z0-9]/gi, '_')}">
<span class="backup-name">${escapeHtml(b)}</span>
<div class="backup-actions">
<button class="btn btn-ghost btn-sm btn-audit" onclick="auditBackup('${source}','${b}',this)"><i class="fas fa-shield-check"></i> Audit</button>
<button class="btn btn-ghost btn-sm" onclick="quickRestore('${source}','${b}')">↩ Restore</button>
<button class="btn btn-ghost btn-sm btn-delete-backup" onclick="deleteBackup('${source}','${b}',this)"><i class="fas fa-trash"></i></button>
${detailsBtn}
<button class="btn btn-ghost btn-sm btn-audit" onclick="auditBackup('${source}','${safe}',this)"><i class="fas fa-shield-check"></i> Audit</button>
<button class="btn btn-ghost btn-sm" onclick="quickRestore('${source}','${safe}')">↩ Restore</button>
${uploadBtn}
<button class="btn btn-ghost btn-sm btn-delete-backup" onclick="deleteBackup('${source}','${safe}',this)"><i class="fas fa-trash"></i></button>
</div>
</div>`).join('');
</div>`;
}).join('');
}
async function auditBackup(source, filename, btn) {
@@ -537,13 +552,19 @@ async function loadUsers() {
grid.innerHTML = '<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-user-slash"></i>No users</div>';
return;
}
grid.innerHTML = users.map((u, i) => `<div class="user-card" onclick="loadUserContainers('${u.name}')"><div class="user-card-top"><div class="user-avatar" style="background:${userColors[i % userColors.length]}">${u.name[0].toUpperCase()}</div><div><div class="user-name">${u.name}</div><div class="user-uid">uid ${u.uid}</div></div></div><div class="user-tags">${u.has_docker ? '<span class="user-tag docker"><i class="fab fa-docker"></i> docker</span>' : ''}${u.linger ? '<span class="user-tag linger">linger</span>' : ''}${u.has_vdisk ? '<span class="user-tag" style="background:rgba(34,211,238,0.12);color:var(--cyan)">💾 vdisk</span>' : ''}</div><div class="user-stats"><div class="user-stat">Disk: <strong>${u.disk_used}</strong></div><div class="user-stat">Ctrs: <strong>${u.container_count}</strong></div></div></div>`).join('');
grid.innerHTML = users.map((u, i) => `<div class="user-card" data-user="${escapeHtml(u.name)}" onclick="loadUserContainers('${jsStr(u.name)}')"><div class="user-card-top"><div class="user-avatar" style="background:${userColors[i % userColors.length]}">${escapeHtml(u.name[0].toUpperCase())}</div><div><div class="user-name">${escapeHtml(u.name)}</div><div class="user-uid">uid ${u.uid}</div></div></div><div class="user-tags">${u.has_docker ? '<span class="user-tag docker"><i class="fab fa-docker"></i> docker</span>' : ''}${u.linger ? '<span class="user-tag linger">linger</span>' : ''}${u.has_vdisk ? '<span class="user-tag vdisk"><i class="fas fa-hard-drive"></i> vdisk</span>' : ''}</div><div class="user-stats"><div class="user-stat"><span class="user-stat-label">Disk</span><strong>${escapeHtml(u.disk_used)}</strong></div><div class="user-stat"><span class="user-stat-label">Containers</span><strong>${u.container_count}</strong></div></div></div>`).join('');
} catch (_) {}
}
async function loadUserContainers(username) {
selectedUser = username;
if (el('user-detail-panel')) el('user-detail-panel').style.display = '';
document.querySelectorAll('.user-card').forEach((card) => {
card.classList.toggle('selected', card.dataset.user === username);
});
if (el('user-detail-panel')) {
el('user-detail-panel').style.display = '';
el('user-detail-panel').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
setText('user-detail-title', `${username}'s Containers`);
const body = el('user-containers-body');
if (!body) return;