async function loadData (block) { const source = block.dataset.source; block.classList.remove ("loaded"); try { const response = await fetch (source); if (response.ok) { const items = await response.json(); if (Array.isArray (items)) { const template = block.querySelector ("template"); const tableBody = block.querySelector ("table tbody"); tableBody.innerHTML = ""; items.forEach (item => { const clone = template.content.cloneNode (true); window["populate" + block.dataset.item] (clone, item); tableBody.appendChild (clone); tableBody.lastElementChild.dataset.itemid = item.id; }); } else { window["populate" + block.dataset.item] (block, items); } block.classList.add ("loaded"); } else { if ((response.status == 401) || (response.status == 403)) { document.location.href = document.body.dataset.baseurl; } else { block.classList.add ("failed"); } } } catch (error) { block.classList.add ("failed"); } } function populateUser (row, user) { row.querySelector (".email").innerText = user.email; row.querySelector (".fullname").innerText = user.fullName; row.querySelector (".roles").innerText = user.roles.join (", "); row.querySelector (".active").innerText = user.isActive ? "✓" : ""; } async function displayUser (container, dialog, userUrl) { dialog.querySelectorAll (".error") .forEach (element => { element.hidden = true; }); const response = await fetch (userUrl); if (response.ok) { const user = await response.json(); dialog.querySelector ("input[name=userid]").value = user.id; dialog.querySelector ("input[name=email]").value = user.email; dialog.querySelector ("input[name=fullname]").value = user.fullName; dialog.querySelector ("input[name=active]").checked = user.isActive; dialog.querySelectorAll ("input.role") .forEach (checkbox => { checkbox.checked = user.roles.includes (checkbox.value); }); container.classList.add ("loaded"); } else { if ((response.status == 401) || (response.status == 403)) { document.location.href = document.body.dataset.baseurl; } else { container.classList.add ("failed"); } } } function emptyUser (dialog) { dialog.querySelector ("input[name=email]").value = null; dialog.querySelector ("input[name=fullname]").value = null; dialog.querySelectorAll ("input[name=role]") .forEach (checkbox => { checkbox.checked = false; }); dialog.querySelectorAll (".error") .forEach (element => { element.hidden = true; }); dialog.querySelector ("button.invite").disabled = false; } function selectUser (event) { const row = event.target.closest ("tbody tr"); if (row && this.contains (row)) { const dialog = document.querySelector (this.dataset.dialog); window["display" + this.dataset.item] (this, dialog, this.dataset.source + "/" + row.dataset.itemid); dialog.showModal(); dialog.querySelector ("input[name=email]").focus(); } else if (event.target.classList.contains ("new")) { const dialog = document.querySelector (event.target.dataset.dialog); window["empty" + this.dataset.item] (dialog); dialog.showModal(); dialog.querySelector ("input[name=email]").focus(); } } async function saveUser (event) { event.preventDefault(); const dialog = event.target.closest ("dialog"); if (event.submitter.classList.contains ("save")) { const userId = event.target.querySelector ("input[name=userid]").value; const email = event.target.querySelector ("input[name=email]").value; const fullname = event.target.querySelector ("input[name=fullname]").value; const isActive = event.target.querySelector ("input[name=active]").checked; const roles = Array.from (event.target.querySelectorAll ("input[name=role]:checked")).map (checkbox => { return checkbox.value }); let valid = true; if (email) { dialog.querySelector (".error[for=email]").hidden = true; } else { dialog.querySelector (".error[for=email]").hidden = false; valid = false; } if (fullname) { dialog.querySelector (".error[for=fullname]").hidden = true; } else { dialog.querySelector (".error[for=fullname]").hidden = false; valid = false; } if (valid) { event.target.querySelector ("button.save").disabled = true; try { const url = dialog.dataset.saveUrl + "/" + userId; const response = await fetch (url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify ({email: email, fullname: fullname, isActive: isActive, roles: roles}) }); if (response.ok) { dialog.close(); event.target.querySelector ("button.save").disabled = false; loadData (document.querySelector (".users.loading")); return true; } else { console.error (url + " returned " + response.status); dialog.querySelector (".error#failure").hidden = false; dialog.querySelector ("button.save").disabled = false; if ((response.status == 401) || (response.status == 403)) { document.location.href = document.body.dataset.baseurl; } return true; } } catch (error) { console.error (error); dialog.querySelector (".error#failure").hidden = false; dialog.querySelector ("button.save").disabled = false; } } else { return false; } } else { dialog.close(); } } async function inviteUser (event) { event.preventDefault(); const dialog = event.target.closest ("dialog"); if (event.submitter.classList.contains ("invite")) { const email = event.target.querySelector ("input[name=email]").value; const fullname = event.target.querySelector ("input[name=fullname").value; const roles = Array.from (event.target.querySelectorAll ("input[name=role]:checked")).map (checkbox => { return checkbox.value }); let valid = true; if (email) { dialog.querySelector (".error[for=email]").hidden = true; } else { dialog.querySelector (".error[for=email]").hidden = false; valid = false; } if (fullname) { dialog.querySelector (".error[for=fullname]").hidden = true; } else { dialog.querySelector (".error[for=fullname]").hidden = false; valid = false; } if (valid) { event.target.querySelector ("button.invite").disabled = true; try { const url = dialog.dataset.inviteUrl; const response = await fetch (url, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify ({email: email, fullname: fullname, roles: roles}) }); if (response.ok) { dialog.close(); event.target.querySelector ("button.invite").disabled = false; loadData (document.querySelector (".users.loading")); return true; } else { console.error (url + " returned " + response.status); dialog.querySelector (".error#failure").hidden = false; dialog.querySelector ("button.invite").disabled = false; if ((response.status == 401) || (response.status == 403)) { document.location.href = document.body.dataset.baseurl; } return true; } } catch (error) { console.error (error); dialog.querySelector (".error#failure").hidden = false; dialog.querySelector ("button.invite").disabled = false; } } else { return false; } } else { dialog.close(); } } function populatePassword (container, token) { container.querySelector (".email").innerText = token.email; container.querySelector ("button.save").disabled = false; container.querySelector (".invalidPassword").hidden = true; } async function savePassword (event) { event.preventDefault(); if (event.submitter.classList.contains ("save")) { const password = event.target.querySelector ("input[name=password]").value; const saveUrl = event.target.dataset.saveUrl; const loginUrl = event.target.dataset.loginUrl; if (password && (password.length >= 12)) { try { event.target.querySelector ("button.save").disabled = true; event.target.querySelector (".invalidPassword").hidden = true; const response = await fetch (saveUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify ({password: password}) }); if (response.ok) { document.location.href = loginUrl; return true; } else { console.error (saveUrl + " returned " + response.status); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button.save").disabled = false; return false; } } catch (error) { console.error (saveUrl + " returned " + error); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button.save").disabled = false; } } else { event.target.querySelector (".invalidPassword").hidden = false; return false; } } else { dialog.close(); } } async function login (event) { event.preventDefault(); const email = event.target.querySelector ("input[name=email]").value; const password = event.target.querySelector ("input[name=password]").value; const loginUrl = event.target.dataset.loginUrl; if (email && password) { try { event.target.querySelector ("button#login").disabled = true; event.target.querySelector (".error#failure").hidden = true; event.target.querySelector (".invalidEmail").hidden = true; event.target.querySelector (".invalidPassword").hidden = true; const response = await fetch (loginUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify ({email: email, password: password}) }); if (response.ok) { event.target.querySelector ("button#login").disabled = false; document.location.href = document.body.dataset.baseurl; return true; } else { console.error (loginUrl + " returned " + response.status); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button#login").disabled = false; return false; } } catch (error) { console.error (loginUrl + " returned " + error); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button#login").disabled = false; } return true; } else { event.target.querySelector (".invalidEmail").hidden = email; event.target.querySelector (".invalidPassword").hidden = password; return false; } } function showForgotPassword (event) { event.preventDefault(); document.querySelector ("form#login").hidden = true; document.querySelector ("form#forgot").hidden = false; } async function submitForgotPassword (event) { event.preventDefault(); const email = event.target.querySelector ("input[name=email]").value; const resetUrl = event.target.dataset.resetUrl; if (email) { try { event.target.querySelector ("button#reset-password").disabled = true; const response = await fetch (resetUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify ({email: email}) }); if (response.ok) { document.querySelector ("form#forgot").hidden = true; document.querySelector ("div#reset-info").hidden = false; return true; } else { console.error (saveUrl + " returned " + response.status); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button#reset-password").disabled = false; return false; } } catch (error) { console.error (saveUrl + " returned " + error); event.target.querySelector (".error#failure").hidden = false; event.target.querySelector ("button#reset-password").disabled = false; } } else { event.target.querySelector (".invalidEmail").hidden = false; return false; } } function setupGlobalEnvironment() { document.querySelectorAll (".loading") .forEach (function (block) { loadData (block); block.addEventListener ("click", window["select" + block.dataset.item]); }); document.querySelectorAll ("form#login") .forEach (function (form) { form.addEventListener ("submit", login); }); document.querySelectorAll ("button#forgot-password") .forEach (function (button) { button.addEventListener ("click", showForgotPassword); }); document.querySelectorAll ("form#forgot") .forEach (function (form) { form.addEventListener ("submit", submitForgotPassword); }); } document.addEventListener ("DOMContentLoaded", setupGlobalEnvironment);