www.spaceplanner.app

Web client to the spaceplanner API
git clone git://jacobedwards.org/www.spaceplanner.app
Log | Files | Refs

commit 4e3f704e5015758cbe62ce017fe32fe538dfd4ea
parent 564ce65269c7c8c084335be078076d1ddbdd3b02
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date:   Fri, 30 Aug 2024 18:25:51 -0700

Update settings page

There were a lot of improvements and changes, but the core differences
are listing the user's name and verified email, and linking to email
verification page when email is not verified.

Diffstat:
Mfiles/settings/index.html | 8+++++++-
Mfiles/settings/main.js | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
2 files changed, 130 insertions(+), 26 deletions(-)

diff --git a/files/settings/index.html b/files/settings/index.html @@ -5,7 +5,13 @@ <script type="module" src="./main.js"></script> </head> <html> - <h1>Settings</h1> + <h1>Profile</h1> + + <dl id="userinfo"></dl> + + <a href="./billing/">Manage billing</a> + + <h2>Settings</h2> <form id="settings"></form> </html> diff --git a/files/settings/main.js b/files/settings/main.js @@ -1,5 +1,6 @@ import * as api from "/lib/api.js" import * as etc from "/lib/etc.js" +import * as ui from "/lib/ui.js" function init() { etc.authorize() @@ -8,6 +9,8 @@ function init() { } function main() { + update_info("Username", localStorage.getItem("username")) + let errfunc = function(err) { etc.error("Unable to get settings: " + err, document.querySelector("#settings")) } api.fetch("GET", "settings") .then(function(params) { @@ -18,9 +21,10 @@ function main() { }) .catch(errfunc) - let profile = document.createElement("h2") - profile.appendChild(document.createTextNode("Profile")) - document.body.append(profile) + let h = document.body.appendChild( + document.createElement("h2") + ) + h.appendChild(document.createTextNode("Delete Account")) let del = delete_form() del.onsubmit = delete_user document.body.append(del) @@ -30,9 +34,11 @@ function delete_form() { let form = document.createElement("form") form.id = "delete_user_form" + form.appendChild(ui.warning("This action cannot be undone.")) + let label = document.createElement("label") label.setAttribute("for", "delete_user_confirm") - label.appendChild(document.createTextNode("Confirm ")) + label.appendChild(document.createTextNode("Confirm: ")) form.append(label) let check = document.createElement("input") @@ -41,13 +47,13 @@ function delete_form() { check.setAttribute("required", true) form.append(check) + form.appendChild(document.createTextNode(" ")) + let submit = document.createElement("input") submit.type = "submit" - submit.value = "Delete User" + submit.value = "Delete Account" form.append(submit) - form.appendChild(document.createTextNode("This action cannot be undone.")) - return form } @@ -73,21 +79,14 @@ function show_settings(current, params) { let label = document.createElement("label") label.setAttribute("for", id) - label.appendChild(document.createTextNode(name[0].toUpperCase() + name.substring(1) + " ")) + label.appendChild(document.createTextNode(ui.prettyName(name, { title: false }) + " ")) item.append(label) let input = create_input(name, params[name], current[name]) input.id = id + input.setAttribute("title", params[name].description) item.append(input) - /* - *desc = document.createElement("label") - *desc.setAttribute("for", id) - *desc.append(document.createElement("br")) - *desc.appendChild(document.createTextNode(params[name].description)) - *item.append(desc) - */ - list.append(item) } @@ -96,34 +95,104 @@ function show_settings(current, params) { submit.type = "submit" form.append(submit) - form.onsubmit = function () { return update_settings(current, params) } + form.addEventListener("submit", function(event) { + event.preventDefault() + update_settings(current, params) + }) let current_form = document.querySelector("#settings") current_form.replaceWith(form) + + update_verified_email() } function update_settings(current, params) { let settings = Array.from(document.querySelectorAll("form#settings > ul > li > input")) let patch = [] - for (let name in settings) { - let newvalue = settings[name].value - if (params.default && newvalue == params.default) - continue + for (let i in settings) { + let name = settings[i].name let oldvalue = current[name] - if (oldvalue && newvalue == oldvalue) - continue - patch.push({ op: "add", path: settings[name].name, value: newvalue }) + let newvalue + if (settings[i].getAttribute("type") == "checkbox") { + newvalue = settings[i].checked + } else { + newvalue = settings[i].value + } + + if (params.default && newvalue == params.default) { + if (oldvalue != null) { + patch.push({ op: "remove", path: name }) + } + } else if (newvalue != oldvalue) { + if (name === "email") { + update_verified_email() + } + patch.push({ op: "add", path: name, value: newvalue }) + } } + if (patch.length == 0) { + return + } api.fetch("PATCH", "users/" + localStorage.getItem("username") + "/settings", patch) + .then(function(updated) { + for (let k in updated) { + current[k] = updated[k] + } + // Alert in a less annoying way + alert("Settings successfully updated") + }) .catch(function(err) { etc.error("Unable to update settings: " + err) }) - return false +} + +function update_verified_email() { + let setting_form = document.querySelector('input[name="email"]') + if (!setting_form) { + throw new Error("Expected email setting") + } + + api.fetch("GET", "users/" + localStorage.getItem("username") + "/email/verified") + .then(function(verified) { + update_info("Email", verified) + let old_warning = document.getElementById("unverified_email_warning") + if (verified == setting_form.value) { + if (old_warning != null) { + old_warning.remove() + } + } else if (verified != setting_form.value) { + let content + if (verified == null) + content = ui.warning("This email is not verified.") + else { + content = document.createElement("p") + content.appendChild(document.createTextNode("This email is not verified. Please verify it ")) + let a = content.appendChild(document.createElement("a")) + a.href = "./verify-email" + a.appendChild(document.createTextNode("here.")) + } + + let warning = ui.warning(content) + warning.id = "unverified_email_warning" + warning.classList.add("small") + + if (old_warning != null) { + old_warning.replaceWith(warning) + } else { + setting_form.after(warning) + } + } + }) } function create_input(name, setting, current_value) { let input = document.createElement("input") if (setting.type == "string") { input.type = "text" + } else if (setting.type == "bool") { + input.type = "checkbox" + if (current_value) { + input.setAttribute("checked", "true") + } } else { throw new Error("Unexpected setting type") } @@ -136,4 +205,33 @@ function create_input(name, setting, current_value) { return input } +function update_info(key, value) { + let dl = document.getElementById("userinfo") + + let v = get_info_element(key) + if (v != undefined) { + v.textContent = value + return + } + + let k = dl.appendChild(document.createElement("dt")) + k.appendChild(document.createTextNode(key)) + v = dl.appendChild(document.createElement("dd")) + v.appendChild(document.createTextNode(value)) +} + +function get_info_element(key) { + let dl = document.getElementById("userinfo") + + let k + let keys = Array.from(dl.querySelectorAll("dt")) + for (let i in keys) { + if (keys[i].textContent == key) { + return keys[i].nextElementSibling + } + } + + return undefined +} + window.onload = etc.handle_wrap(init)