diff options
Diffstat (limited to 'web/js/admin.js')
| -rw-r--r-- | web/js/admin.js | 84 |
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') { |
