Fix broken action buttons and add delete confirmation modal
- shares.js, users.js: JSON.stringify() inside onclick="..." attributes produced unescaped " chars that broke HTML attribute parsing — all dynamic onclick JSON now passed through esc() so the browser decodes " back to " before executing the handler - Replace browser confirm() dialogs with a styled Bootstrap modal (#deleteModal) for share and user deletion — reusable across all tabs - users.js: rename shadow variable 'confirm' → 'pwConfirm' to avoid shadowing the now-removed confirm() call - main.js: auto-refresh now reloads shares and users every 30 s, not just the dashboard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
67dacd4044
commit
74737568b0
4 files changed
+92
-45
No files matched your search
+28
-15
@@ -7,8 +7,10 @@ async function loadUsers() {
|
||||
tb.innerHTML = `<tr><td colspan="4" style="color:var(--muted)">No users found.</td></tr>`;
|
||||
return;
|
||||
}
|
||||
tb.innerHTML = users.map(u => `
|
||||
<tr>
|
||||
tb.innerHTML = users.map(u => {
|
||||
// esc() HTML-encodes JSON so " inside onclick="..." doesn't break the attribute
|
||||
const nameJson = esc(JSON.stringify(u.name));
|
||||
return `<tr>
|
||||
<td>${esc(u.name)}</td>
|
||||
<td style="color:var(--muted)">${u.uid}</td>
|
||||
<td>
|
||||
@@ -19,11 +21,12 @@ async function loadUsers() {
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm me-1" style="border:1px solid var(--border); color:var(--muted)"
|
||||
onclick="openPwModal(${JSON.stringify(u.name)})">Password</button>
|
||||
onclick="openPwModal(${nameJson})">Password</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="deleteUser(${JSON.stringify(u.name)})">Delete</button>
|
||||
onclick="confirmDeleteUser(${nameJson})">Delete</button>
|
||||
</td>
|
||||
</tr>`).join('');
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function createUser() {
|
||||
@@ -44,14 +47,24 @@ async function createUser() {
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
async function deleteUser(name) {
|
||||
if (!confirm(`Delete user "${name}"? This cannot be undone.`)) return;
|
||||
const res = await apiFetch(`/api/users/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
||||
if (!res) return;
|
||||
const data = await res.json();
|
||||
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
|
||||
showToast(`User "${name}" deleted.`);
|
||||
loadUsers();
|
||||
function confirmDeleteUser(name) {
|
||||
document.getElementById('deleteModalMsg').textContent =
|
||||
`Are you sure you want to delete user "${name}"? This removes both the system account and SMB access.`;
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
|
||||
const btn = document.getElementById('deleteModalConfirm');
|
||||
const handler = async () => {
|
||||
btn.removeEventListener('click', handler);
|
||||
modal.hide();
|
||||
const res = await apiFetch(`/api/users/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
||||
if (!res) return;
|
||||
const data = await res.json();
|
||||
if (!res.ok) return showToast('Error: ' + (data.error || res.status), 'danger');
|
||||
showToast(`User "${name}" deleted.`);
|
||||
loadUsers();
|
||||
};
|
||||
btn.addEventListener('click', handler);
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function openPwModal(name) {
|
||||
@@ -64,9 +77,9 @@ function openPwModal(name) {
|
||||
async function submitPassword() {
|
||||
const name = document.getElementById('pwUsername').value;
|
||||
const pw = document.getElementById('pwNew').value;
|
||||
const confirm = document.getElementById('pwConfirm').value;
|
||||
const pwConfirm = document.getElementById('pwConfirm').value;
|
||||
if (pw.length < 8) return showToast('Password must be at least 8 characters.', 'danger');
|
||||
if (pw !== confirm) return showToast('Passwords do not match.', 'danger');
|
||||
if (pw !== pwConfirm) return showToast('Passwords do not match.', 'danger');
|
||||
|
||||
const res = await apiFetch(`/api/users/${encodeURIComponent(name)}/password`, {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
|
||||
Reference in new issue
Block a user