summaryrefslogtreecommitdiff
path: root/web/js/admin.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/js/admin.js')
-rw-r--r--web/js/admin.js84
1 files changed, 81 insertions, 3 deletions
diff --git a/web/js/admin.js b/web/js/admin.js
index f60000e..bc2913a 100644
--- a/web/js/admin.js
+++ b/web/js/admin.js
@@ -69,8 +69,18 @@ function renderUsers(users) {
</ul>`;
el.querySelectorAll('button[data-id]').forEach((b) => {
b.addEventListener('click', async () => {
- try { await API.deleteUser(b.dataset.id); toast('User removed'); await refreshAdmin(); }
- catch (err) { toast(err.message, 'error'); }
+ try {
+ await API.deleteUser(b.dataset.id);
+ toast('User removed');
+ await refreshAdmin();
+ } catch (err) {
+ const msg = err.message || '';
+ if (msg.toLowerCase().includes('cannot delete yourself') || msg.toLowerCase().includes('self-delete')) {
+ toast('You cannot delete yourself', 'error');
+ } else {
+ toast(err.message, 'error');
+ }
+ }
});
});
}
@@ -78,18 +88,86 @@ function renderUsers(users) {
function renderPermissions(data) {
const el = document.getElementById('admin-permissions');
if (!el || !data) return;
- el.innerHTML = `<p class="text-muted text-xs">Use the API to manage permissions directly.</p>`;
+ if (!data.sets?.length || !data.users?.length) {
+ el.innerHTML = '<p class="text-muted text-xs">No sets or users to manage.</p>';
+ return;
+ }
+
+ // Build a lookup: setId -> userId -> role
+ const roleMap = {};
+ data.permissions?.forEach((p) => {
+ if (!roleMap[p.set_id]) roleMap[p.set_id] = {};
+ roleMap[p.set_id][p.user_id] = p.role;
+ });
+
+ let html = '<table class="admin-table"><thead><tr><th>Set</th>';
+ data.users.forEach((u) => {
+ html += `<th>${esc(u.username)}</th>`;
+ });
+ html += '</tr></thead><tbody>';
+
+ data.sets.forEach((s) => {
+ html += `<tr><td>${esc(s.name)}</td>`;
+ data.users.forEach((u) => {
+ const role = roleMap[s.id]?.[u.id] || '';
+ const selectId = `perm-${s.id}-${u.id}`;
+ html += `<td>
+ <select id="${selectId}" class="perm-select" data-set="${s.id}" data-user="${u.id}">
+ <option value="" ${!role ? 'selected' : ''}>—</option>
+ <option value="viewer" ${role === 'viewer' ? 'selected' : ''}>Viewer</option>
+ <option value="owner" ${role === 'owner' ? 'selected' : ''}>Owner</option>
+ </select>
+ </td>`;
+ });
+ html += '</tr>';
+ });
+
+ html += '</tbody></table>';
+ el.innerHTML = html;
+
+ el.querySelectorAll('.perm-select').forEach((sel) => {
+ sel.addEventListener('change', async () => {
+ const setId = sel.dataset.set;
+ const userId = sel.dataset.user;
+ const role = sel.value;
+ try {
+ if (role) {
+ await API.setPermissions({ set_id: parseInt(setId), user_id: parseInt(userId), role });
+ toast('Permission granted');
+ } else {
+ await API.delPermissions({ set_id: parseInt(setId), user_id: parseInt(userId) });
+ toast('Permission revoked');
+ }
+ } catch (err) { toast(err.message, 'error'); }
+ });
+ });
}
function renderTrash(data) {
const el = document.getElementById('admin-users');
if (!el || !Array.isArray(data)) return;
+ if (!data.length) {
+ el.innerHTML = '<h4 class="text-90">Trash</h4><p class="text-muted text-xs">No deleted items.</p>';
+ return;
+ }
el.innerHTML = `<h4 class="text-90">Trash</h4><ul class="admin-list">
${data.map((m) => `<li>
<span>${esc(m.file_name)}</span>
<span class="text-muted text-75">${esc(m.deleted_at || '')}</span>
+ <button class="btn btn-primary btn-sm" data-id="${m.id}">Restore</button>
</li>`).join('')}
</ul>`;
+
+ el.querySelectorAll('button[data-id]').forEach((b) => {
+ b.addEventListener('click', async () => {
+ try {
+ await API.restore(b.dataset.id);
+ toast('Item restored');
+ const data = await API.trash();
+ renderTrash(data);
+ } catch (err) { toast(err.message, 'error'); }
+ });
+ });
}
function toast(msg, type = 'info') {