revert ssr dialog interface
This commit is contained in:
@@ -64,55 +64,50 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<modal-dialog id="create-instance-dialog">
|
<template id="create-instance-dialog">
|
||||||
<template shadowrootmode="open">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
Create New Instance
|
||||||
<link rel="stylesheet" href="css/form.css">
|
</p>
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<div id="body">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
Create New Instance
|
<label for="type">Instance Type</label>
|
||||||
</p>
|
<select class="w3-select w3-border" name="type" id="type" selected-index="-1" required>
|
||||||
<div id="body">
|
<option value="lxc">Container</option>
|
||||||
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
<option value="qemu">Virtual Machine</option>
|
||||||
<label for="type">Instance Type</label>
|
</select>
|
||||||
<select class="w3-select w3-border" name="type" id="type" selected-index="-1" required>
|
<label for="node">Node</label>
|
||||||
<option value="lxc">Container</option>
|
<select class="w3-select w3-border" name="node" id="node" required></select>
|
||||||
<option value="qemu">Virtual Machine</option>
|
<label for="name">Name</label>
|
||||||
</select>
|
<input class="w3-input w3-border" name="name" id="name" type="text" required>
|
||||||
<label for="node">Node</label>
|
<label for="vmid">ID</label>
|
||||||
<select class="w3-select w3-border" name="node" id="node" required></select>
|
<input class="w3-input w3-border" name="vmid" id="vmid" type="number" required>
|
||||||
<label for="name">Name</label>
|
<label for="pool">Pool</label>
|
||||||
<input class="w3-input w3-border" name="name" id="name" type="text" required>
|
<select class="w3-select w3-border" name="pool" id="pool" required></select>
|
||||||
<label for="vmid">ID</label>
|
<label for="cores">Cores (Threads)</label>
|
||||||
<input class="w3-input w3-border" name="vmid" id="vmid" type="number" required>
|
<input class="w3-input w3-border" name="cores" id="cores" type="number" min="1" max="8192" required>
|
||||||
<label for="pool">Pool</label>
|
<label for="memory">Memory (MiB)</label>
|
||||||
<select class="w3-select w3-border" name="pool" id="pool" required></select>
|
<input class="w3-input w3-border" name="memory" id="memory" type="number" min="16" step="1" required>
|
||||||
<label for="cores">Cores (Threads)</label>
|
<p class="container-specific none" style="grid-column: 1 / span 2; text-align: center;">Container Options</p>
|
||||||
<input class="w3-input w3-border" name="cores" id="cores" type="number" min="1" max="8192" required>
|
<label class="container-specific none" for="swap">Swap (MiB)</label>
|
||||||
<label for="memory">Memory (MiB)</label>
|
<input class="w3-input w3-border container-specific none" name="swap" id="swap" type="number" min="0" step="1" required disabled>
|
||||||
<input class="w3-input w3-border" name="memory" id="memory" type="number" min="16" step="1" required>
|
<label class="container-specific none" for="template-image">Template Image</label>
|
||||||
<p class="container-specific none" style="grid-column: 1 / span 2; text-align: center;">Container Options</p>
|
<select class="w3-select w3-border container-specific none" name="template-image" id="template-image" required disabled></select>
|
||||||
<label class="container-specific none" for="swap">Swap (MiB)</label>
|
<label class="container-specific none" for="rootfs-storage">ROOTFS Storage</label>
|
||||||
<input class="w3-input w3-border container-specific none" name="swap" id="swap" type="number" min="0" step="1" required disabled>
|
<select class="w3-select w3-border container-specific none" name="rootfs-storage" id="rootfs-storage" required disabled></select>
|
||||||
<label class="container-specific none" for="template-image">Template Image</label>
|
<label class="container-specific none" for="rootfs-size">ROOTFS Size (GiB)</label>
|
||||||
<select class="w3-select w3-border container-specific none" name="template-image" id="template-image" required disabled></select>
|
<input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled>
|
||||||
<label class="container-specific none" for="rootfs-storage">ROOTFS Storage</label>
|
<label class="container-specific none" for="password">Password</label>
|
||||||
<select class="w3-select w3-border container-specific none" name="rootfs-storage" id="rootfs-storage" required disabled></select>
|
<input class="w3-input w3-border container-specific none" name="password" id="password" type="password" required disabled>
|
||||||
<label class="container-specific none" for="rootfs-size">ROOTFS Size (GiB)</label>
|
<label class="container-specific none" for="confirm-password">Confirm Password</label>
|
||||||
<input class="w3-input w3-border container-specific none" name="rootfs-size" id="rootfs-size" type="number" min="0" max="131072" required disabled>
|
<input class="w3-input w3-border container-specific none" name="confirm-password" id="confirm-password" type="password" required disabled>
|
||||||
<label class="container-specific none" for="password">Password</label>
|
</form>
|
||||||
<input class="w3-input w3-border container-specific none" name="password" id="password" type="password" required disabled>
|
</div>
|
||||||
<label class="container-specific none" for="confirm-password">Confirm Password</label>
|
<div id="controls" class="w3-center w3-container">
|
||||||
<input class="w3-input w3-border container-specific none" name="confirm-password" id="confirm-password" type="password" required disabled>
|
<button id="cancel" type="submit" 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>
|
||||||
</form>
|
<button id="confirm" type="submit" 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>
|
||||||
<div id="controls" class="w3-center w3-container">
|
</dialog>
|
||||||
<button id="cancel" type="submit" 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>
|
</template>
|
||||||
<button id="confirm" type="submit" 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>
|
|
||||||
</dialog>
|
|
||||||
</template>
|
|
||||||
</modal-dialog>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -1,18 +1,17 @@
|
|||||||
import { requestAPI, setAppearance } from "./utils.js";
|
import { requestAPI, setAppearance } from "./utils.js";
|
||||||
import "./dialog.js";
|
import { dialog } from "./dialog.js";
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", init);
|
window.addEventListener("DOMContentLoaded", init);
|
||||||
|
|
||||||
async function init () {
|
async function init () {
|
||||||
setAppearance();
|
setAppearance();
|
||||||
|
|
||||||
initPasswordChangeForm();
|
|
||||||
document.querySelector("#change-password").addEventListener("click", handlePasswordChangeButton);
|
document.querySelector("#change-password").addEventListener("click", handlePasswordChangeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPasswordChangeForm () {
|
function handlePasswordChangeButton () {
|
||||||
const d = document.querySelector("#change-password-dialog");
|
const template = document.querySelector("#change-password-dialog");
|
||||||
d.setOnClose(async (result, form) => {
|
const d = dialog(template, async (result, form) => {
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
const result = await requestAPI("/access/password", "POST", { password: form.get("new-password") });
|
const result = await requestAPI("/access/password", "POST", { password: form.get("new-password") });
|
||||||
if (result.status !== 200) {
|
if (result.status !== 200) {
|
||||||
@@ -23,16 +22,9 @@ function initPasswordChangeForm () {
|
|||||||
|
|
||||||
const password = d.querySelector("#new-password");
|
const password = d.querySelector("#new-password");
|
||||||
const confirmPassword = d.querySelector("#confirm-password");
|
const confirmPassword = d.querySelector("#confirm-password");
|
||||||
|
|
||||||
function validatePassword () {
|
function validatePassword () {
|
||||||
confirmPassword.setCustomValidity(password.value !== confirmPassword.value ? "Passwords Don't Match" : "");
|
confirmPassword.setCustomValidity(password.value !== confirmPassword.value ? "Passwords Don't Match" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
password.addEventListener("change", validatePassword);
|
password.addEventListener("change", validatePassword);
|
||||||
confirmPassword.addEventListener("keyup", validatePassword);
|
confirmPassword.addEventListener("keyup", validatePassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePasswordChangeButton () {
|
|
||||||
const d = document.querySelector("#change-password-dialog");
|
|
||||||
d.showModal();
|
|
||||||
}
|
|
||||||
|
@@ -1,84 +1,40 @@
|
|||||||
/**
|
/**
|
||||||
* Custom modal dialog with form support. Assumes the following structure:
|
* Spawn modal dialog from template node. Assumes the following structure:
|
||||||
* <modal-dialog><template shadowrootmode="open">
|
* <template>
|
||||||
|
* <dialog>
|
||||||
* <p id="prompt"></p>
|
* <p id="prompt"></p>
|
||||||
* <div id="body">
|
* <div id="body">
|
||||||
* <form id="form"> ... </form>
|
* <form id="form"> ... </form>
|
||||||
* </div>
|
* </div>
|
||||||
* <div id="controls">
|
* <div id="controls">
|
||||||
* <button value="..." form=""
|
* <button value="..." form="form"
|
||||||
|
* <button value="..." form="form"
|
||||||
|
* ...
|
||||||
* </div>
|
* </div>
|
||||||
* </modal-dialog></template>
|
* </dialog>
|
||||||
|
* </template>
|
||||||
* Where prompt is the modal dialog's prompt or header,
|
* Where prompt is the modal dialog's prompt or header,
|
||||||
* body contains an optional form or other information,
|
* body contains an optional form or other information,
|
||||||
* and controls contains a series of buttons which controls the form
|
* and controls contains a series of buttons which controls the form
|
||||||
*/
|
*/
|
||||||
class ModalDialog extends HTMLElement {
|
export function dialog (template, onclose = async (result, form) => { }) {
|
||||||
shadowRoot = null;
|
const dialog = template.content.querySelector("dialog").cloneNode(true);
|
||||||
dialog = null;
|
document.body.append(dialog);
|
||||||
|
|
||||||
constructor () {
|
|
||||||
super();
|
|
||||||
// setup shadowDOM
|
|
||||||
const internals = this.attachInternals();
|
|
||||||
this.shadowRoot = internals.shadowRoot;
|
|
||||||
this.dialog = this.shadowRoot.querySelector("dialog");
|
|
||||||
}
|
|
||||||
|
|
||||||
showModal () {
|
|
||||||
this.dialog.showModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
querySelector (query) {
|
|
||||||
return this.shadowRoot.querySelector(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
querySelectorAll (query) {
|
|
||||||
return this.shadowRoot.querySelectorAll(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is usually not safe to call this on each dialog invocation
|
|
||||||
setOnClose (callback = (result, form) => {}) {
|
|
||||||
this.dialog.addEventListener("close", () => {
|
|
||||||
const formElem = this.dialog.querySelector("form");
|
|
||||||
const formData = formElem ? new FormData(formElem) : null;
|
|
||||||
callback(this.dialog.returnValue, formData);
|
|
||||||
formElem.reset();
|
|
||||||
this.dialog.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("modal-dialog", ModalDialog);
|
|
||||||
|
|
||||||
export function dialog (header, body, onclose = async (result, form) => { }) {
|
|
||||||
const dialog = document.createElement("dialog");
|
|
||||||
dialog.innerHTML = `
|
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;"></p>
|
|
||||||
<div id="body"></div>
|
|
||||||
<div class="w3-center w3-container">
|
|
||||||
<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 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>
|
|
||||||
`;
|
|
||||||
dialog.className = "w3-container w3-card w3-border-0";
|
|
||||||
dialog.querySelector("#prompt").innerText = header;
|
|
||||||
dialog.querySelector("#body").innerHTML = body;
|
|
||||||
dialog.addEventListener("close", async () => {
|
dialog.addEventListener("close", async () => {
|
||||||
const formElem = dialog.querySelector("form");
|
const formElem = dialog.querySelector("form");
|
||||||
const formData = formElem ? new FormData(formElem) : null;
|
const formData = formElem ? new FormData(formElem) : null;
|
||||||
await onclose(dialog.returnValue, formData);
|
await onclose(dialog.returnValue, formData);
|
||||||
|
formElem.reset();
|
||||||
|
dialog.close();
|
||||||
dialog.parentElement.removeChild(dialog);
|
dialog.parentElement.removeChild(dialog);
|
||||||
});
|
});
|
||||||
if (!dialog.querySelector("form")) {
|
if (!dialog.querySelector("form")) {
|
||||||
dialog.querySelector("#confirm").addEventListener("click", async (e) => {
|
for (const control of dialog.querySelector("#controls").childNodes) {
|
||||||
e.preventDefault();
|
control.addEventListener("click", async (e) => {
|
||||||
dialog.close(e.target.value);
|
e.preventDefault();
|
||||||
});
|
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();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setSVGSrc, setSVGAlt } from "./utils.js";
|
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setSVGSrc, setSVGAlt } from "./utils.js";
|
||||||
import { alert } from "./dialog.js";
|
import { alert, dialog } from "./dialog.js";
|
||||||
import { setupClientSync } from "./clientsync.js";
|
import { setupClientSync } from "./clientsync.js";
|
||||||
import wfaInit from "../modules/wfa.js";
|
import wfaInit from "../modules/wfa.js";
|
||||||
|
|
||||||
@@ -11,7 +11,6 @@ async function init () {
|
|||||||
wfaInit("modules/wfa.wasm");
|
wfaInit("modules/wfa.wasm");
|
||||||
initInstances();
|
initInstances();
|
||||||
|
|
||||||
initInstanceAddForm();
|
|
||||||
document.querySelector("#instance-add").addEventListener("click", handleInstanceAddButton);
|
document.querySelector("#instance-add").addEventListener("click", handleInstanceAddButton);
|
||||||
document.querySelector("#vm-search").addEventListener("input", sortInstances);
|
document.querySelector("#vm-search").addEventListener("input", sortInstances);
|
||||||
|
|
||||||
@@ -120,7 +119,6 @@ class InstanceCard extends HTMLElement {
|
|||||||
nameParagraph.innerHTML = this.name ? this.name : " ";
|
nameParagraph.innerHTML = this.name ? this.name : " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initPowerForm();
|
|
||||||
const powerButton = this.shadowRoot.querySelector("#power-btn");
|
const powerButton = this.shadowRoot.querySelector("#power-btn");
|
||||||
if (powerButton.classList.contains("clickable")) {
|
if (powerButton.classList.contains("clickable")) {
|
||||||
powerButton.onclick = this.handlePowerButton.bind(this);
|
powerButton.onclick = this.handlePowerButton.bind(this);
|
||||||
@@ -133,7 +131,6 @@ class InstanceCard extends HTMLElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initDeleteForm();
|
|
||||||
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
||||||
if (deleteButton.classList.contains("clickable")) {
|
if (deleteButton.classList.contains("clickable")) {
|
||||||
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
||||||
@@ -156,70 +153,60 @@ class InstanceCard extends HTMLElement {
|
|||||||
setSVGAlt(powerbtn, "");
|
setSVGAlt(powerbtn, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
async initPowerForm () {
|
|
||||||
const dialog = this.shadowRoot.querySelector("#power-dialog");
|
|
||||||
dialog.setOnClose(async (result, form) => {
|
|
||||||
if (result === "confirm") {
|
|
||||||
this.actionLock = true;
|
|
||||||
const targetAction = this.status === "running" ? "stop" : "start";
|
|
||||||
|
|
||||||
const result = await requestPVE(`/nodes/${this.node.name}/${this.type}/${this.vmid}/status/${targetAction}`, "POST", { node: this.node.name, vmid: this.vmid });
|
|
||||||
this.setStatusLoading();
|
|
||||||
|
|
||||||
const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const taskStatus = await requestPVE(`/nodes/${this.node.name}/tasks/${result.data}/status`, "GET");
|
|
||||||
if (taskStatus.data.status === "stopped" && taskStatus.data.exitstatus === "OK") { // task stopped and was successful
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (taskStatus.data.status === "stopped") { // task stopped but was not successful
|
|
||||||
alert(`Attempted to ${targetAction} ${this.vmid} but got: ${taskStatus.data.exitstatus}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else { // task has not stopped
|
|
||||||
await waitFor(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actionLock = false;
|
|
||||||
refreshInstances();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async handlePowerButton () {
|
async handlePowerButton () {
|
||||||
if (!this.actionLock) {
|
if (!this.actionLock) {
|
||||||
const dialog = this.shadowRoot.querySelector("#power-dialog");
|
const template = this.shadowRoot.querySelector("#power-dialog");
|
||||||
dialog.showModal();
|
dialog(template, async (result, form) => {
|
||||||
}
|
if (result === "confirm") {
|
||||||
}
|
this.actionLock = true;
|
||||||
|
const targetAction = this.status === "running" ? "stop" : "start";
|
||||||
|
|
||||||
initDeleteForm () {
|
const result = await requestPVE(`/nodes/${this.node.name}/${this.type}/${this.vmid}/status/${targetAction}`, "POST", { node: this.node.name, vmid: this.vmid });
|
||||||
const dialog = this.shadowRoot.querySelector("#delete-dialog");
|
this.setStatusLoading();
|
||||||
dialog.setOnClose(async (result, form) => {
|
|
||||||
if (result === "confirm") {
|
|
||||||
this.actionLock = true;
|
|
||||||
|
|
||||||
const action = {};
|
const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay));
|
||||||
action.purge = 1;
|
|
||||||
action["destroy-unreferenced-disks"] = 1;
|
|
||||||
|
|
||||||
const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE");
|
while (true) {
|
||||||
if (result.status !== 200) {
|
const taskStatus = await requestPVE(`/nodes/${this.node.name}/tasks/${result.data}/status`, "GET");
|
||||||
alert(`Attempted to delete ${this.vmid} but got: ${result.error}`);
|
if (taskStatus.data.status === "stopped" && taskStatus.data.exitstatus === "OK") { // task stopped and was successful
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (taskStatus.data.status === "stopped") { // task stopped but was not successful
|
||||||
|
alert(`Attempted to ${targetAction} ${this.vmid} but got: ${taskStatus.data.exitstatus}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else { // task has not stopped
|
||||||
|
await waitFor(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actionLock = false;
|
||||||
|
refreshInstances();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
this.actionLock = false;
|
}
|
||||||
refreshInstances();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteButton () {
|
handleDeleteButton () {
|
||||||
if (!this.actionLock && this.status === "stopped") {
|
if (!this.actionLock && this.status === "stopped") {
|
||||||
const dialog = this.shadowRoot.querySelector("#delete-dialog");
|
const template = this.shadowRoot.querySelector("#delete-dialog");
|
||||||
dialog.showModal();
|
dialog(template, async (result, form) => {
|
||||||
|
if (result === "confirm") {
|
||||||
|
this.actionLock = true;
|
||||||
|
|
||||||
|
const action = {};
|
||||||
|
action.purge = 1;
|
||||||
|
action["destroy-unreferenced-disks"] = 1;
|
||||||
|
|
||||||
|
const result = await requestAPI(`/cluster/${this.node.name}/${this.type}/${this.vmid}/delete`, "DELETE");
|
||||||
|
if (result.status !== 200) {
|
||||||
|
alert(`Attempted to delete ${this.vmid} but got: ${result.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.actionLock = false;
|
||||||
|
refreshInstances();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,10 +309,9 @@ function sortInstances () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initInstanceAddForm () {
|
async function handleInstanceAddButton () {
|
||||||
// form submit logic
|
const template = document.querySelector("#create-instance-dialog");
|
||||||
const d = document.querySelector("#create-instance-dialog");
|
const d = dialog(template, async (result, form) => {
|
||||||
d.setOnClose(async (result, form) => {
|
|
||||||
if (result === "confirm") {
|
if (result === "confirm") {
|
||||||
const body = {
|
const body = {
|
||||||
name: form.get("name"),
|
name: form.get("name"),
|
||||||
@@ -354,19 +340,6 @@ async function initInstanceAddForm () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// custom password validation checker
|
|
||||||
const password = d.querySelector("#password");
|
|
||||||
const confirmPassword = d.querySelector("#confirm-password");
|
|
||||||
function validatePassword () {
|
|
||||||
confirmPassword.setCustomValidity(password.value !== confirmPassword.value ? "Passwords Don't Match" : "");
|
|
||||||
}
|
|
||||||
password.addEventListener("change", validatePassword);
|
|
||||||
confirmPassword.addEventListener("keyup", validatePassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleInstanceAddButton () {
|
|
||||||
const d = document.querySelector("#create-instance-dialog");
|
|
||||||
|
|
||||||
const templates = await requestAPI("/user/ct-templates", "GET");
|
const templates = await requestAPI("/user/ct-templates", "GET");
|
||||||
|
|
||||||
const typeSelect = d.querySelector("#type");
|
const typeSelect = d.querySelector("#type");
|
||||||
@@ -454,5 +427,14 @@ async function handleInstanceAddButton () {
|
|||||||
}
|
}
|
||||||
templateImage.selectedIndex = -1;
|
templateImage.selectedIndex = -1;
|
||||||
|
|
||||||
|
// setup custom password checker for containers
|
||||||
|
const password = d.querySelector("#password");
|
||||||
|
const confirmPassword = d.querySelector("#confirm-password");
|
||||||
|
function validatePassword () {
|
||||||
|
confirmPassword.setCustomValidity(password.value !== confirmPassword.value ? "Passwords Don't Match" : "");
|
||||||
|
}
|
||||||
|
password.addEventListener("change", validatePassword);
|
||||||
|
confirmPassword.addEventListener("keyup", validatePassword);
|
||||||
|
|
||||||
d.showModal();
|
d.showModal();
|
||||||
}
|
}
|
||||||
|
@@ -61,64 +61,60 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<modal-dialog id="power-dialog">
|
<template id="power-dialog">
|
||||||
<template shadowrootmode="open">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/form.css">
|
||||||
<link rel="stylesheet" href="css/form.css">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
{{if eq .Status "running"}}
|
||||||
{{if eq .Status "running"}}
|
Stop {{.VMID}}
|
||||||
Stop {{.VMID}}
|
{{else if eq .Status "stopped"}}
|
||||||
{{else if eq .Status "stopped"}}
|
Start {{.VMID}}
|
||||||
Start {{.VMID}}
|
{{else}}
|
||||||
{{else}}
|
{{end}}
|
||||||
{{end}}
|
</p>
|
||||||
|
<div id="body">
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<p>
|
||||||
|
{{if eq .Status "running"}}
|
||||||
|
Are you sure you want to <strong>stop</strong> {{.VMID}}?
|
||||||
|
{{else if eq .Status "stopped"}}
|
||||||
|
Are you sure you want to <strong>start</strong> {{.VMID}}?
|
||||||
|
{{else}}
|
||||||
|
{{end}}
|
||||||
</p>
|
</p>
|
||||||
<div id="body">
|
</form>
|
||||||
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
</div>
|
||||||
<p>
|
<div id="controls" class="w3-center w3-container">
|
||||||
{{if eq .Status "running"}}
|
<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>
|
||||||
Are you sure you want to <strong>stop</strong> {{.VMID}}?
|
<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>
|
||||||
{{else if eq .Status "stopped"}}
|
</div>
|
||||||
Are you sure you want to <strong>start</strong> {{.VMID}}?
|
</dialog>
|
||||||
{{else}}
|
</template>
|
||||||
{{end}}
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div id="controls" class="w3-center w3-container">
|
|
||||||
<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 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>
|
|
||||||
</dialog>
|
|
||||||
</template>
|
|
||||||
</modal-dialog>
|
|
||||||
|
|
||||||
<modal-dialog id="delete-dialog">
|
<template id="delete-dialog">
|
||||||
<template shadowrootmode="open">
|
<link rel="stylesheet" href="modules/w3.css">
|
||||||
<link rel="stylesheet" href="modules/w3.css">
|
<link rel="stylesheet" href="css/style.css">
|
||||||
<link rel="stylesheet" href="css/style.css">
|
<link rel="stylesheet" href="css/form.css">
|
||||||
<link rel="stylesheet" href="css/form.css">
|
<dialog class="w3-container w3-card w3-border-0">
|
||||||
<dialog class="w3-container w3-card w3-border-0">
|
<p class="w3-large" id="prompt" style="text-align: center;">
|
||||||
<p class="w3-large" id="prompt" style="text-align: center;">
|
Delete {{.VMID}}
|
||||||
Delete {{.VMID}}
|
</p>
|
||||||
|
<div id="body">
|
||||||
|
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
||||||
|
<p>
|
||||||
|
Are you sure you want to <strong>delete</strong> {{.VMID}}
|
||||||
</p>
|
</p>
|
||||||
<div id="body">
|
</form>
|
||||||
<form method="dialog" class="input-grid" style="grid-template-columns: auto 1fr;" id="form">
|
</div>
|
||||||
<p>
|
<div id="controls" class="w3-center w3-container">
|
||||||
Are you sure you want to <strong>delete</strong> {{.VMID}}
|
<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>
|
||||||
</p>
|
<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>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</dialog>
|
||||||
<div id="controls" class="w3-center w3-container">
|
</template>
|
||||||
<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 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>
|
|
||||||
</dialog>
|
|
||||||
</template>
|
|
||||||
</modal-dialog>
|
|
||||||
</template>
|
</template>
|
||||||
</instance-card>
|
</instance-card>
|
||||||
{{end}}
|
{{end}}
|
Reference in New Issue
Block a user