fix bugs in network config change,
generalize dialog layout, add form validation callback, add password change form
This commit is contained in:
parent
46773d290d
commit
bce774d48c
11
account.html
11
account.html
@ -64,17 +64,10 @@
|
|||||||
<p id="nodes">Nodes:</p>
|
<p id="nodes">Nodes:</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-card w3-padding">
|
<div class="w3-card w3-padding">
|
||||||
|
<div class="flex row nowrap">
|
||||||
<h3>Password</h3>
|
<h3>Password</h3>
|
||||||
<form id="password-form">
|
<button class="w3-button w3-margin" id="change-password">Change Password</button>
|
||||||
<div class="input-grid" style="grid-template-columns: auto auto 1fr;">
|
|
||||||
<label for="new-password">New Password</label>
|
|
||||||
<input class="w3-input w3-border" type="password" id="new-password">
|
|
||||||
<span></span>
|
|
||||||
<label for="confirm-password">Confirm Password</label>
|
|
||||||
<input class="w3-input w3-border" type="password" id="confirm-password">
|
|
||||||
<span></span>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-card w3-padding">
|
<div class="w3-card w3-padding">
|
||||||
<h3>Cluster Resources</h3>
|
<h3>Cluster Resources</h3>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { dialog } from "./dialog.js";
|
||||||
import { requestAPI, goToPage, getCookie, setTitleAndHeader } from "./utils.js";
|
import { requestAPI, goToPage, getCookie, setTitleAndHeader } from "./utils.js";
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", init);
|
window.addEventListener("DOMContentLoaded", init);
|
||||||
@ -42,6 +43,8 @@ async function init () {
|
|||||||
document.querySelector("#nodes").innerText = `Nodes: ${nodes.toString()}`;
|
document.querySelector("#nodes").innerText = `Nodes: ${nodes.toString()}`;
|
||||||
|
|
||||||
populateResources("#resource-container", meta, resources);
|
populateResources("#resource-container", meta, resources);
|
||||||
|
|
||||||
|
document.querySelector("#change-password").addEventListener("click", handlePasswordChangeForm);
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateResources (containerID, meta, resources) {
|
function populateResources (containerID, meta, resources) {
|
||||||
@ -117,3 +120,33 @@ function parseNumber (value, unitData) {
|
|||||||
return `${value} ${unit}`;
|
return `${value} ${unit}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePasswordChangeForm () {
|
||||||
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="new-password">New Password</label>
|
||||||
|
<input class="w3-input w3-border" type="password" id="new-password" name="new-password">
|
||||||
|
<label for="confirm-password">Confirm Password</label>
|
||||||
|
<input class="w3-input w3-border" type="password" id="confirm-password" name="confirm-password">
|
||||||
|
</form>
|
||||||
|
<p class="w3-large" id="error-message" style="text-align: center; color: var(--negative-color); margin-top: 0.5em; margin-bottom: 0;"></p>
|
||||||
|
`;
|
||||||
|
dialog("Change Password", body, async (result, form) => {
|
||||||
|
if (result === "confirm") {
|
||||||
|
const result = await requestAPI("/auth/password", "POST", {password: form.get("new-password")});
|
||||||
|
if (result.status !== 200) {
|
||||||
|
alert(result.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (dialog, form) => {
|
||||||
|
const pass = form.get("new-password");
|
||||||
|
const conf = form.get("confirm-password");
|
||||||
|
if (pass !== conf) {
|
||||||
|
dialog.querySelector("#error-message").innerText = "Passwords must match";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -233,7 +233,7 @@ function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) {
|
|||||||
async function handleDiskDetach () {
|
async function handleDiskDetach () {
|
||||||
const disk = this.dataset.disk;
|
const disk = this.dataset.disk;
|
||||||
const header = `Detach ${disk}`;
|
const header = `Detach ${disk}`;
|
||||||
const body = `<p>Are you sure you want to detach disk</p><p>${disk}</p>`;
|
const body = `<p>Are you sure you want to detach disk ${disk}</p>`;
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg";
|
document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg";
|
||||||
@ -250,7 +250,12 @@ async function handleDiskDetach () {
|
|||||||
|
|
||||||
async function handleDiskAttach () {
|
async function handleDiskAttach () {
|
||||||
const header = `Attach ${this.dataset.disk}`;
|
const header = `Attach ${this.dataset.disk}`;
|
||||||
const body = `<label for="device">${type === "qemu" ? "SATA" : "MP"}</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" required></input>`;
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="device">${type === "qemu" ? "SATA" : "MP"}</label>
|
||||||
|
<input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" required></input>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -274,7 +279,12 @@ async function handleDiskAttach () {
|
|||||||
|
|
||||||
async function handleDiskResize () {
|
async function handleDiskResize () {
|
||||||
const header = `Resize ${this.dataset.disk}`;
|
const header = `Resize ${this.dataset.disk}`;
|
||||||
const body = "<label for=\"size-increment\">Size Increment (GiB)</label><input class=\"w3-input w3-border\" name=\"size-increment\" id=\"size-increment\" type=\"number\" min=\"0\" max=\"131072\"></input>";
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="size-increment">Size Increment (GiB)</label>
|
||||||
|
<input class="w3-input w3-border" name="size-increment" id="size-increment" type="number" min="0" max="131072"></input>
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -310,8 +320,10 @@ async function handleDiskMove () {
|
|||||||
const select = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select"><option hidden disabled selected value></option>${options}</select>`;
|
const select = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select"><option hidden disabled selected value></option>${options}</select>`;
|
||||||
|
|
||||||
const body = `
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
${select}
|
${select}
|
||||||
<label for="delete-check">Delete Source</label><input class="w3-input w3-border" name="delete-check" id="delete-check" type="checkbox" checked required>
|
<label for="delete-check">Delete Source</label><input class="w3-input w3-border" name="delete-check" id="delete-check" type="checkbox" checked required>
|
||||||
|
</form>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
@ -337,7 +349,7 @@ async function handleDiskMove () {
|
|||||||
async function handleDiskDelete () {
|
async function handleDiskDelete () {
|
||||||
const disk = this.dataset.disk;
|
const disk = this.dataset.disk;
|
||||||
const header = `Delete ${disk}`;
|
const header = `Delete ${disk}`;
|
||||||
const body = `<p>Are you sure you want to <strong>delete</strong> disk</p><p>${disk}</p>`;
|
const body = `<p>Are you sure you want to <strong>delete</strong> disk${disk}</p>`;
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg";
|
document.querySelector(`img[data-disk="${disk}"]`).src = "images/status/loading.svg";
|
||||||
@ -367,9 +379,11 @@ async function handleDiskAdd () {
|
|||||||
const select = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select" required><option hidden disabled selected value></option>${options}</select>`;
|
const select = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select" required><option hidden disabled selected value></option>${options}</select>`;
|
||||||
|
|
||||||
const body = `
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
<label for="device">${type === "qemu" ? "SATA" : "MP"}</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" value="0" required></input>
|
<label for="device">${type === "qemu" ? "SATA" : "MP"}</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="${type === "qemu" ? "5" : "255"}" value="0" required></input>
|
||||||
${select}
|
${select}
|
||||||
<label for="size">Size (GiB)</label><input class="w3-input w3-border" name="size" id="size" type="number" min="0" max="131072" required></input>
|
<label for="size">Size (GiB)</label><input class="w3-input w3-border" name="size" id="size" type="number" min="0" max="131072" required></input>
|
||||||
|
</form>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
@ -407,9 +421,11 @@ async function handleCDAdd () {
|
|||||||
const storageSelect = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select" required><option hidden disabled selected value></option>${storageOptions}</select>`;
|
const storageSelect = `<label for="storage-select">Storage</label><select class="w3-select w3-border" name="storage-select" id="storage-select" required><option hidden disabled selected value></option>${storageOptions}</select>`;
|
||||||
|
|
||||||
const body = `
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
<label for="device">IDE</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="3" required></input>
|
<label for="device">IDE</label><input class="w3-input w3-border" name="device" id="device" type="number" min="0" max="3" required></input>
|
||||||
${storageSelect}
|
${storageSelect}
|
||||||
<label for="iso-select">Image</label><select class="w3-select w3-border" name="iso-select" id="iso-select" required></select>
|
<label for="iso-select">Image</label><select class="w3-select w3-border" name="iso-select" id="iso-select" required></select>
|
||||||
|
</form>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const d = dialog(header, body, async (result, form) => {
|
const d = dialog(header, body, async (result, form) => {
|
||||||
@ -509,7 +525,11 @@ async function handleNetworkConfig () {
|
|||||||
const netID = this.dataset.network;
|
const netID = this.dataset.network;
|
||||||
const netDetails = this.dataset.values;
|
const netDetails = this.dataset.values;
|
||||||
const header = `Edit net${netID}`;
|
const header = `Edit net${netID}`;
|
||||||
const body = "<label for=\"rate\">Rate Limit (MB/s)</label><input type=\"number\" id=\"rate\" name=\"rate\" class=\"w3-input w3-border\">";
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="rate">Rate Limit (MB/s)</label><input type="number" id="rate" name="rate" class="w3-input w3-border">
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
const d = dialog(header, body, async (result, form) => {
|
const d = dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -523,7 +543,8 @@ async function handleNetworkConfig () {
|
|||||||
}
|
}
|
||||||
await getConfig();
|
await getConfig();
|
||||||
populateNetworks();
|
populateNetworks();
|
||||||
updateBootLine(`boot-net${netID}`, { id: `net${netID}`, prefix: "net", value: config.data[`net${netID}`] });
|
const id = `net${netID}`;
|
||||||
|
updateBootLine(`boot-net${netID}`, { id, prefix: "net", value: id, detail: config.data[`net${netID}`] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -551,10 +572,15 @@ async function handleNetworkDelete () {
|
|||||||
|
|
||||||
async function handleNetworkAdd () {
|
async function handleNetworkAdd () {
|
||||||
const header = "Create Network Interface";
|
const header = "Create Network Interface";
|
||||||
let body = "<label for=\"netid\">Interface ID</label><input type=\"number\" id=\"netid\" name=\"netid\" class=\"w3-input w3-border\"><label for=\"rate\">Rate Limit (MB/s)</label><input type=\"number\" id=\"rate\" name=\"rate\" class=\"w3-input w3-border\">";
|
let body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="netid">Interface ID</label><input type="number" id="netid" name="netid" class="w3-input w3-border">
|
||||||
|
<label for="rate">Rate Limit (MB/s)</label><input type="number" id="rate" name="rate" class="w3-input w3-border">
|
||||||
|
`;
|
||||||
if (type === "lxc") {
|
if (type === "lxc") {
|
||||||
body += "<label for=\"name\">Interface Name</label><input type=\"text\" id=\"name\" name=\"name\" class=\"w3-input w3-border\"></input>";
|
body += "<label for=\"name\">Interface Name</label><input type=\"text\" id=\"name\" name=\"name\" class=\"w3-input w3-border\"></input>";
|
||||||
}
|
}
|
||||||
|
body += "</form>";
|
||||||
|
|
||||||
dialog(header, body, async (result, form) => {
|
dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -648,7 +674,11 @@ async function handleDeviceConfig () {
|
|||||||
const deviceDetails = this.dataset.values;
|
const deviceDetails = this.dataset.values;
|
||||||
const deviceName = this.dataset.name;
|
const deviceName = this.dataset.name;
|
||||||
const header = `Edit Expansion Card ${deviceID}`;
|
const header = `Edit Expansion Card ${deviceID}`;
|
||||||
const body = "<label for=\"device\">Device</label><select id=\"device\" name=\"device\" required></select><label for=\"pcie\">PCI-Express</label><input type=\"checkbox\" id=\"pcie\" name=\"pcie\" class=\"w3-input w3-border\">";
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="device">Device</label><select id="device" name="device" required></select><label for="pcie">PCI-Express</label><input type="checkbox" id="pcie" name="pcie" class="w3-input w3-border">
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
const d = dialog(header, body, async (result, form) => {
|
const d = dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -694,7 +724,11 @@ async function handleDeviceDelete () {
|
|||||||
|
|
||||||
async function handleDeviceAdd () {
|
async function handleDeviceAdd () {
|
||||||
const header = "Add Expansion Card";
|
const header = "Add Expansion Card";
|
||||||
const body = "<label for=\"device\">Device</label><select id=\"device\" name=\"device\" required></select><label for=\"pcie\">PCI-Express</label><input type=\"checkbox\" id=\"pcie\" name=\"pcie\" class=\"w3-input w3-border\">";
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<label for="device">Device</label><select id="device" name="device" required></select><label for="pcie">PCI-Express</label><input type="checkbox" id="pcie" name="pcie" class="w3-input w3-border">
|
||||||
|
</form>
|
||||||
|
`;
|
||||||
|
|
||||||
const d = dialog(header, body, async (result, form) => {
|
const d = dialog(header, body, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
@ -798,11 +832,11 @@ function updateBootLine (id, newData) {
|
|||||||
const enabled = document.querySelector("#enabled");
|
const enabled = document.querySelector("#enabled");
|
||||||
const disabled = document.querySelector("#disabled");
|
const disabled = document.querySelector("#disabled");
|
||||||
let element = null;
|
let element = null;
|
||||||
if (enabled.getItemByID(id)) {
|
if (enabled.querySelector(`#${id}`)) {
|
||||||
element = enabled.getItemByID(id);
|
element = enabled.querySelector(`#${id}`);
|
||||||
}
|
}
|
||||||
if (disabled.getItemByID(id)) {
|
if (disabled.querySelector(`#${id}`)) {
|
||||||
element = disabled.getItemByID(id);
|
element = disabled.querySelector(`#${id}`);
|
||||||
}
|
}
|
||||||
if (element) {
|
if (element) {
|
||||||
const container = element.container;
|
const container = element.container;
|
||||||
|
@ -1,20 +1,43 @@
|
|||||||
export function dialog (header, body, callback = async (result, form) => { }) {
|
export function dialog (header, body, onclose = async (result, form) => { }, validate = async (dialog, form) => {
|
||||||
|
return true;
|
||||||
|
}) {
|
||||||
const dialog = document.createElement("dialog");
|
const dialog = document.createElement("dialog");
|
||||||
dialog.innerHTML = `
|
dialog.innerHTML = `
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;"></p>
|
<p class="w3-large" id="prompt" style="text-align: center;"></p>
|
||||||
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form"></form>
|
<div id="body"></div>
|
||||||
<div class="w3-center w3-container">
|
<div class="w3-center w3-container">
|
||||||
<button value="cancel" form="form" class="w3-button w3-margin" style="background-color: var(--negative-color, #f00); color: var(--lightbg-text-color, black);" formnovalidate>CANCEL</button>
|
<button id="cancel" value="cancel" form="form" class="w3-button w3-margin" style="background-color: var(--negative-color, #f00); color: var(--lightbg-text-color, black);" formnovalidate>CANCEL</button>
|
||||||
<button value="confirm" form="form" class="w3-button w3-margin" style="background-color: var(--positive-color, #0f0); color: var(--lightbg-text-color, black);">CONFIRM</button>
|
<button id="confirm" value="confirm" form="form" class="w3-button w3-margin" style="background-color: var(--positive-color, #0f0); color: var(--lightbg-text-color, black);">CONFIRM</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
dialog.className = "w3-container w3-card w3-border-0";
|
dialog.className = "w3-container w3-card w3-border-0";
|
||||||
dialog.querySelector("#prompt").innerText = header;
|
dialog.querySelector("#prompt").innerText = header;
|
||||||
dialog.querySelector("form").innerHTML = body;
|
dialog.querySelector("#body").innerHTML = body;
|
||||||
dialog.addEventListener("close", async () => {
|
dialog.addEventListener("close", async () => {
|
||||||
await callback(dialog.returnValue, new FormData(dialog.querySelector("form")));
|
const formElem = dialog.querySelector("form");
|
||||||
|
let formData = null;
|
||||||
|
if (formElem) {
|
||||||
|
formData = new FormData(formElem);
|
||||||
|
}
|
||||||
|
await onclose(dialog.returnValue, formData);
|
||||||
dialog.parentElement.removeChild(dialog);
|
dialog.parentElement.removeChild(dialog);
|
||||||
});
|
});
|
||||||
|
dialog.querySelector("#confirm").addEventListener("click", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let valid = true;
|
||||||
|
const formElem = dialog.querySelector("form");
|
||||||
|
if (formElem) {
|
||||||
|
const form = new FormData(formElem);
|
||||||
|
valid = await validate(dialog, form);
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
dialog.close(e.target.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.querySelector("#cancel").addEventListener("click", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dialog.close(e.target.value);
|
||||||
|
});
|
||||||
document.body.append(dialog);
|
document.body.append(dialog);
|
||||||
dialog.showModal();
|
dialog.showModal();
|
||||||
return dialog;
|
return dialog;
|
||||||
|
@ -78,6 +78,7 @@ async function handleInstanceAdd () {
|
|||||||
const header = "Create New Instance";
|
const header = "Create New Instance";
|
||||||
|
|
||||||
const body = `
|
const body = `
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
<label for="type">Instance Type</label>
|
<label for="type">Instance Type</label>
|
||||||
<select class="w3-select w3-border" name="type" id="type" required>
|
<select class="w3-select w3-border" name="type" id="type" required>
|
||||||
<option value="lxc">Container</option>
|
<option value="lxc">Container</option>
|
||||||
@ -106,6 +107,7 @@ async function handleInstanceAdd () {
|
|||||||
<input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled></input>
|
<input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled></input>
|
||||||
<label class="container-specific none" for="password">Password</label>
|
<label class="container-specific none" for="password">Password</label>
|
||||||
<input class="w3-input w3-border container-specific none" name="password" id="password" type="password" required disabled></input>
|
<input class="w3-input w3-border container-specific none" name="password" id="password" type="password" required disabled></input>
|
||||||
|
</form>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const d = dialog(header, body, async (result, form) => {
|
const d = dialog(header, body, async (result, form) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user