update account page, instance creation form, and config page,

fix issue with instance delete confirm popup,
fix issues with dialog exit handling without form element
This commit is contained in:
Arthur Lu 2023-11-15 20:18:01 +00:00
parent 6fe526be6e
commit 3521228366
5 changed files with 55 additions and 51 deletions

View File

@ -53,12 +53,12 @@ function populateResources (containerID, meta, resources) {
Object.keys(meta).forEach((resourceType) => { Object.keys(meta).forEach((resourceType) => {
if (meta[resourceType].display) { if (meta[resourceType].display) {
if (meta[resourceType].type === "list") { if (meta[resourceType].type === "list") {
resources[resourceType].forEach((listResource) => { resources[resourceType].total.forEach((listResource) => {
createResourceUsageChart(container, listResource.name, listResource.avail, listResource.used, listResource.max, null); createResourceUsageChart(container, listResource.name, listResource.avail, listResource.used, listResource.max, null);
}); });
} }
else { else {
createResourceUsageChart(container, meta[resourceType].name, resources[resourceType].avail, resources[resourceType].used, resources[resourceType].max, meta[resourceType]); createResourceUsageChart(container, meta[resourceType].name, resources[resourceType].total.avail, resources[resourceType].total.used, resources[resourceType].total.max, meta[resourceType]);
} }
} }
}); });
@ -125,28 +125,28 @@ function handlePasswordChangeForm () {
const body = ` const body = `
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form"> <form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
<label for="new-password">New Password</label> <label for="new-password">New Password</label>
<input class="w3-input w3-border" type="password" id="new-password" name="new-password"> <input class="w3-input w3-border" id="new-password" name="new-password" type="password"required>
<label for="confirm-password">Confirm Password</label> <label for="confirm-password">Confirm Password</label>
<input class="w3-input w3-border" type="password" id="confirm-password" name="confirm-password"> <input class="w3-input w3-border" id="confirm-password" name="confirm-password" type="password" required>
</form> </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) => { const d = dialog("Change Password", body, async (result, form) => {
if (result === "confirm") { if (result === "confirm") {
const result = await requestAPI("/auth/password", "POST", { password: form.get("new-password") }); const result = await requestAPI("/auth/password", "POST", { password: form.get("new-password") });
if (result.status !== 200) { if (result.status !== 200) {
alert(result.error); 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;
}
}); });
var password = d.querySelector("#new-password")
var confirm_password = d.querySelector("#confirm-password");
function validatePassword(){
confirm_password.setCustomValidity( password.value !=
confirm_password.value ? "Passwords Don't Match" : '');
}
password.addEventListener("change", validatePassword);
confirm_password.addEventListener("keyup", validatePassword);
} }

View File

@ -54,8 +54,10 @@ async function populateResources () {
const global = await requestAPI("/global/config/resources"); const global = await requestAPI("/global/config/resources");
const user = await requestAPI("/user/config/resources"); const user = await requestAPI("/user/config/resources");
let options = []; let options = [];
if (global.cpu.whitelist) { const globalCPU = global.cpu;
user.cpu.forEach((userType) => { const userCPU = node in user.cpu.nodes ? user.cpu.nodes[node] : user.cpu.global;
if (globalCPU.whitelist) {
userCPU.forEach((userType) => {
options.push(userType.name); options.push(userType.name);
}); });
options = options.sort((a, b) => { options = options.sort((a, b) => {
@ -65,7 +67,7 @@ async function populateResources () {
else { else {
const supported = await requestPVE(`/nodes/${node}/capabilities/qemu/cpu`); const supported = await requestPVE(`/nodes/${node}/capabilities/qemu/cpu`);
supported.data.forEach((supportedType) => { supported.data.forEach((supportedType) => {
if (!user.cpu.some((userType) => supportedType.name === userType.name)) { if (!userCPU.some((userType) => supportedType.name === userType.name)) {
options.push(supportedType.name); options.push(supportedType.name);
} }
}); });

View File

@ -1,6 +1,4 @@
export function dialog (header, body, onclose = async (result, form) => { }, validate = async (dialog, form) => { export function dialog (header, body, onclose = async (result, 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>
@ -15,29 +13,20 @@ export function dialog (header, body, onclose = async (result, form) => { }, val
dialog.querySelector("#body").innerHTML = body; dialog.querySelector("#body").innerHTML = body;
dialog.addEventListener("close", async () => { dialog.addEventListener("close", async () => {
const formElem = dialog.querySelector("form"); const formElem = dialog.querySelector("form");
let formData = null; const formData = formElem ? new FormData(formElem) : null;
if (formElem) {
formData = new FormData(formElem);
}
await onclose(dialog.returnValue, formData); await onclose(dialog.returnValue, formData);
dialog.parentElement.removeChild(dialog); dialog.parentElement.removeChild(dialog);
}); });
if (!dialog.querySelector("form")) {
dialog.querySelector("#confirm").addEventListener("click", async (e) => { dialog.querySelector("#confirm").addEventListener("click", async (e) => {
e.preventDefault(); 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.close(e.target.value);
}
}); });
dialog.querySelector("#cancel").addEventListener("click", async (e) => { dialog.querySelector("#cancel").addEventListener("click", async (e) => {
e.preventDefault(); e.preventDefault();
dialog.close(e.target.value); dialog.close(e.target.value);
}); });
}
document.body.append(dialog); document.body.append(dialog);
dialog.showModal(); dialog.showModal();
return dialog; return dialog;

View File

@ -163,6 +163,9 @@ async function handleInstanceAdd () {
const rootfsStorage = d.querySelector("#rootfs-storage"); const rootfsStorage = d.querySelector("#rootfs-storage");
rootfsStorage.selectedIndex = -1; rootfsStorage.selectedIndex = -1;
const userResources = await requestAPI("/user/dynamic/resources", "GET");
const userCluster = await requestAPI("/user/config/cluster", "GET");
const nodeSelect = d.querySelector("#node"); const nodeSelect = d.querySelector("#node");
const clusterNodes = await requestPVE("/nodes", "GET"); const clusterNodes = await requestPVE("/nodes", "GET");
const allowedNodes = await requestAPI("/user/config/nodes", "GET"); const allowedNodes = await requestAPI("/user/config/nodes", "GET");
@ -185,6 +188,23 @@ async function handleInstanceAdd () {
}); });
templateStorage.selectedIndex = -1; templateStorage.selectedIndex = -1;
rootfsStorage.selectedIndex = -1; rootfsStorage.selectedIndex = -1;
if (node in userResources.cores.nodes) {
d.querySelector("#cores").max = userResources.cores.nodes[node].avail;
}
else {
d.querySelector("#cores").max = userResources.cores.global.avail;
}
if (node in userResources.memory.nodes) {
d.querySelector("#memory").max = userResources.memory.nodes[node].avail;
}
else {
d.querySelector("#memory").max = userResources.memory.global.avail;
}
d.querySelector("#vmid").min = userCluster.vmid.min;
d.querySelector("#vmid").max = userCluster.vmid.max;
}); });
const templateImage = d.querySelector("#template-image"); // populate templateImage depending on selected image storage const templateImage = d.querySelector("#template-image"); // populate templateImage depending on selected image storage
@ -199,11 +219,4 @@ async function handleInstanceAdd () {
}); });
templateImage.selectedIndex = -1; templateImage.selectedIndex = -1;
}); });
const userResources = await requestAPI("/user/dynamic/resources", "GET");
const userCluster = await requestAPI("/user/config/cluster", "GET");
d.querySelector("#cores").max = userResources.cores.avail;
d.querySelector("#memory").max = userResources.memory.avail;
d.querySelector("#vmid").min = userCluster.vmid.min;
d.querySelector("#vmid").max = userCluster.vmid.max;
} }

View File

@ -219,7 +219,7 @@ class InstanceCard extends HTMLElement {
handleDeleteButton () { handleDeleteButton () {
if (!this.actionLock && this.status === "stopped") { if (!this.actionLock && this.status === "stopped") {
const header = `Delete VM ${this.vmid}`; const header = `Delete VM ${this.vmid}`;
const body = `<p>Are you sure you want to <strong>delete</strong> VM </p><p>${this.vmid}</p>`; const body = `<p>Are you sure you want to <strong>delete</strong> VM ${this.vmid}</p>`;
dialog(header, body, async (result, form) => { dialog(header, body, async (result, form) => {
if (result === "confirm") { if (result === "confirm") {