Update samy.js

This commit is contained in:
2025-12-21 00:48:05 -05:00
committed by syelle
parent 241b6c126e
commit 7c00dd801b

146
samy.js
View File

@@ -82,10 +82,13 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
}); });
// Default tab from PS (onboard/offboard/tweaks/SVSApps) // Default tab from PS (onboard/offboard/devices)
const defaultTabId = `${defaultPage}Tab`; const defaultTabId = `${defaultPage}Tab`;
const defaultBtn = document.querySelector(`.tab-button[data-tab='${defaultTabId}']`); const defaultBtn = document.querySelector(
`.tab-button[data-tab='${defaultTabId}']`
);
const defaultTab = document.getElementById(defaultTabId); const defaultTab = document.getElementById(defaultTabId);
if (defaultBtn) defaultBtn.classList.add("active"); if (defaultBtn) defaultBtn.classList.add("active");
if (defaultTab) defaultTab.classList.add("active"); if (defaultTab) defaultTab.classList.add("active");
}); });
@@ -105,6 +108,7 @@ function toggleColumn(col) {
cb.checked = master.checked; cb.checked = master.checked;
}); });
// fire change handlers
setTimeout(() => { setTimeout(() => {
children.forEach((cb) => { children.forEach((cb) => {
cb.dispatchEvent(new Event("change")); cb.dispatchEvent(new Event("change"));
@@ -117,19 +121,21 @@ function updateSelectAll(col) {
`selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox` `selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox`
); );
const children = document.querySelectorAll( const children = document.querySelectorAll(
`#onboardTab input[type=checkbox][data-column=${col}]` `#onboardTab input[type=checkbox][data-column="${col}"]`
); );
if (!master) return;
master.checked = Array.from(children).every((cb) => cb.checked); master.checked = Array.from(children).every((cb) => cb.checked);
} }
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
["left", "right"].forEach((col) => { ["left", "right"].forEach((col) => {
document document
.querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`) .querySelectorAll(`#onboardTab input[type=checkbox][data-column="${col}"]`)
.forEach((cb) => .forEach((cb) =>
cb.addEventListener("change", () => updateSelectAll(col)) cb.addEventListener("change", () => updateSelectAll(col))
); );
updateSelectAll(col);
}); });
}); });
@@ -182,6 +188,7 @@ function toggleDattoRMMOptions() {
const master = document.getElementById("installDattoRMM"); const master = document.getElementById("installDattoRMM");
const container = document.getElementById("installDattoRMMOptionsContainer"); const container = document.getElementById("installDattoRMMOptionsContainer");
if (!container) return; if (!container) return;
const checked = master?.checked; const checked = master?.checked;
container.style.display = checked ? "block" : "none"; container.style.display = checked ? "block" : "none";
container container
@@ -200,9 +207,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (passwordField && goButton) { if (passwordField && goButton) {
passwordField.addEventListener("keydown", (e) => { passwordField.addEventListener("keydown", (e) => {
if (e.key === "Enter") { if (e.key === "Enter") goButton.click();
goButton.click();
}
}); });
} }
@@ -211,9 +216,7 @@ document.addEventListener("DOMContentLoaded", () => {
if (siteDropdown && runButton) { if (siteDropdown && runButton) {
siteDropdown.addEventListener("keydown", (e) => { siteDropdown.addEventListener("keydown", (e) => {
if (e.key === "Enter" && siteDropdown.value) { if (e.key === "Enter" && siteDropdown.value) runButton.click();
runButton.click();
}
}); });
} }
}); });
@@ -223,11 +226,11 @@ document.addEventListener("DOMContentLoaded", () => {
// ======================================================================= // =======================================================================
async function fetchSites() { async function fetchSites() {
const pwdInput = document.getElementById("Password"); const pwdInput = document.getElementById("Password");
const pwd = (pwdInput?.value ?? "").trim(); // allow blank, normalize whitespace const pwd = (pwdInput?.value ?? "").trim(); // allow blank
const dropdown = document.getElementById("dattoDropdown"); const dropdown = document.getElementById("dattoDropdown");
if (!dropdown) return;
dropdown.innerHTML = '<option disabled selected>Loading sites...</option>'; dropdown.innerHTML = '<option disabled selected>Loading sites...</option>';
try { try {
@@ -237,19 +240,19 @@ async function fetchSites() {
body: JSON.stringify({ password: pwd }), body: JSON.stringify({ password: pwd }),
}); });
if (!resp.ok) throw "HTTP " + resp.status; if (!resp.ok) throw new Error("HTTP " + resp.status);
const sites = await resp.json(); const sites = await resp.json();
if (!Array.isArray(sites) || sites.length === 0) { if (!Array.isArray(sites) || sites.length === 0) {
dropdown.innerHTML = dropdown.innerHTML = '<option disabled selected>No sites returned</option>';
'<option disabled selected>No sites returned</option>'; alert(
alert("No Datto sites returned. Verify credentials/allowlist, or try again in a moment."); "No Datto sites returned. Verify credentials/allowlist, or try again in a moment."
);
return; return;
} }
dropdown.innerHTML = "";
dropdown.innerHTML = "";
sites.forEach((site) => { sites.forEach((site) => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = site.UID; option.value = site.UID;
@@ -257,13 +260,14 @@ async function fetchSites() {
dropdown.appendChild(option); dropdown.appendChild(option);
}); });
document.getElementById("dattoRmmContainer").style.display = "block"; const rmmContainer = document.getElementById("dattoRmmContainer");
if (rmmContainer) rmmContainer.style.display = "block";
} catch (e) { } catch (e) {
console.error(e); console.error(e);
dropdown.innerHTML = dropdown.innerHTML = '<option disabled selected>Error loading sites</option>';
'<option disabled selected>Error loading sites</option>'; alert(
alert("Failed to fetch sites. Check password or confirm your public IP is allowlisted."); "Failed to fetch sites. Check password or confirm your public IP is allowlisted."
);
} }
} }
@@ -275,7 +279,6 @@ let allPrinters = [];
// POST /getprinters with password from Devices tab // POST /getprinters with password from Devices tab
async function fetchPrinters() { async function fetchPrinters() {
const pwdInput = document.getElementById("PrinterPassword"); const pwdInput = document.getElementById("PrinterPassword");
const pwd = (pwdInput?.value ?? ""); // allow blank const pwd = (pwdInput?.value ?? ""); // allow blank
const clientContainer = document.getElementById("printerClientContainer"); const clientContainer = document.getElementById("printerClientContainer");
@@ -283,12 +286,8 @@ async function fetchPrinters() {
const dropdown = document.getElementById("printerClientDropdown"); const dropdown = document.getElementById("printerClientDropdown");
const checkboxContainer = document.getElementById("printerCheckboxContainer"); const checkboxContainer = document.getElementById("printerCheckboxContainer");
if (dropdown) { if (dropdown) dropdown.innerHTML = '<option disabled selected>Loading clients...</option>';
dropdown.innerHTML = '<option disabled selected>Loading clients...</option>'; if (checkboxContainer) checkboxContainer.innerHTML = "";
}
if (checkboxContainer) {
checkboxContainer.innerHTML = "";
}
if (clientContainer) clientContainer.style.display = "none"; if (clientContainer) clientContainer.style.display = "none";
if (listContainer) listContainer.style.display = "none"; if (listContainer) listContainer.style.display = "none";
@@ -309,27 +308,22 @@ async function fetchPrinters() {
return; return;
} }
// Build unique sorted ClientCode list
const codes = [...new Set(allPrinters.map((p) => p.ClientCode))].sort(); const codes = [...new Set(allPrinters.map((p) => p.ClientCode))].sort();
dropdown.innerHTML = ""; if (dropdown) {
const defaultOpt = new Option("Select a client...", "", true, true); dropdown.innerHTML = "";
defaultOpt.disabled = true; const defaultOpt = new Option("Select a client...", "", true, true);
dropdown.appendChild(defaultOpt); defaultOpt.disabled = true;
dropdown.appendChild(defaultOpt);
codes.forEach((code) => { codes.forEach((code) => dropdown.appendChild(new Option(code, code)));
dropdown.appendChild(new Option(code, code)); }
});
if (clientContainer) clientContainer.style.display = "block"; if (clientContainer) clientContainer.style.display = "block";
} catch (e) { } catch (e) {
console.error("fetchPrinters error:", e); console.error("fetchPrinters error:", e);
if (dropdown) { if (dropdown) dropdown.innerHTML = '<option disabled selected>Error loading clients</option>';
dropdown.innerHTML =
'<option disabled selected>Error loading clients</option>';
}
alert("Failed to fetch printers. Check password or confirm your public IP is allowlisted."); alert("Failed to fetch printers. Check password or confirm your public IP is allowlisted.");
} }
} }
@@ -354,12 +348,10 @@ function renderPrintersForClient(clientCode) {
label.style.display = "block"; label.style.display = "block";
label.style.marginBottom = "4px"; label.style.marginBottom = "4px";
//Install-Checkbox
const cb = document.createElement("input"); const cb = document.createElement("input");
cb.type = "checkbox"; cb.type = "checkbox";
cb.id = id; cb.id = id;
// stash all fields we might need later
cb.dataset.clientCode = p.ClientCode; cb.dataset.clientCode = p.ClientCode;
cb.dataset.profileName = p.ProfileName; cb.dataset.profileName = p.ProfileName;
cb.dataset.displayName = p.DisplayName; cb.dataset.displayName = p.DisplayName;
@@ -373,14 +365,10 @@ function renderPrintersForClient(clientCode) {
const nameText = p.DisplayName || p.ProfileName || "Unnamed printer"; const nameText = p.DisplayName || p.ProfileName || "Unnamed printer";
const locText = p.Location || "Unknown location"; const locText = p.Location || "Unknown location";
// Line 1: install checkbox + printer label
label.appendChild(cb); label.appendChild(cb);
label.appendChild(document.createTextNode(" ")); label.appendChild(document.createTextNode(" "));
label.appendChild( label.appendChild(document.createTextNode(`${nameText} (${locText})`));
document.createTextNode(`${nameText} (${locText})`)
);
// Line 2: radio for "Make default"
const defaultWrapper = document.createElement("div"); const defaultWrapper = document.createElement("div");
defaultWrapper.style.marginLeft = "24px"; defaultWrapper.style.marginLeft = "24px";
defaultWrapper.style.fontSize = "0.85em"; defaultWrapper.style.fontSize = "0.85em";
@@ -389,7 +377,7 @@ function renderPrintersForClient(clientCode) {
const radio = document.createElement("input"); const radio = document.createElement("input");
radio.type = "radio"; radio.type = "radio";
radio.name = "defaultPrinter"; radio.name = "defaultPrinter";
radio.value = id; // associate default choice with this checkbox/printer radio.value = id;
const radioLabel = document.createElement("span"); const radioLabel = document.createElement("span");
radioLabel.textContent = " Make default"; radioLabel.textContent = " Make default";
@@ -416,7 +404,6 @@ async function installSelectedPrinters() {
return; return;
} }
// See which radio is checked for "Make default"
const defaultRadio = container.querySelector( const defaultRadio = container.querySelector(
'input[type=radio][name="defaultPrinter"]:checked' 'input[type=radio][name="defaultPrinter"]:checked'
); );
@@ -432,7 +419,6 @@ async function installSelectedPrinters() {
ShareName: cb.dataset.shareName, ShareName: cb.dataset.shareName,
DriverName: cb.dataset.driverName, DriverName: cb.dataset.driverName,
DriverInfPath: cb.dataset.driverInfPath, DriverInfPath: cb.dataset.driverInfPath,
// Only the printer whose checkbox id matches the selected radio gets SetAsDefault=true
SetAsDefault: defaultId !== null && cb.id === defaultId, SetAsDefault: defaultId !== null && cb.id === defaultId,
})); }));
@@ -446,20 +432,16 @@ async function installSelectedPrinters() {
if (!resp.ok) throw new Error("HTTP " + resp.status); if (!resp.ok) throw new Error("HTTP " + resp.status);
const result = await resp.json().catch(() => null); const result = await resp.json().catch(() => null);
console.log("Printer install result:", result); console.log("Printer install result:", result);
} catch (e) { } catch (e) {
console.error("installSelectedPrinters error:", e); console.error("installSelectedPrinters error:", e);
alert("Failed to trigger printer install."); alert("Failed to trigger printer install.");
} }
} }
// ======================================================================= // =======================================================================
// Run Selected (main trigger) // Run Selected (main trigger)
// ======================================================================= // =======================================================================
async function triggerInstall() { async function triggerInstall() {
const runBtn = document.querySelector(".run-button"); const runBtn = document.querySelector(".run-button");
if (!runBtn) return; if (!runBtn) return;
@@ -470,13 +452,13 @@ async function triggerInstall() {
if (statusBox) statusBox.innerHTML = ""; if (statusBox) statusBox.innerHTML = "";
try { try {
// Grab special-case elements ONCE // Special-case elements ONCE
const dattoCB = document.getElementById("installDattoRMM"); const dattoCB = document.getElementById("installDattoRMM");
const svsCB = document.getElementById("installSVSMSPModule"); const svsCB = document.getElementById("installSVSMSPModule");
const renameCB = document.getElementById("renameComputer"); const renameCB = document.getElementById("renameComputer");
const newNameInput = document.getElementById("txtNewComputerName"); const newNameInput = document.getElementById("txtNewComputerName");
// Standard tasks are all tasks EXCEPT special-case ones // Standard tasks = all tasks except special-case ones
const checkedTasks = tasks.filter((t) => { const checkedTasks = tasks.filter((t) => {
if (["installDattoRMM", "installSVSMSPModule", "renameComputer"].includes(t.id)) return false; if (["installDattoRMM", "installSVSMSPModule", "renameComputer"].includes(t.id)) return false;
const cb = document.getElementById(t.id); const cb = document.getElementById(t.id);
@@ -489,7 +471,6 @@ async function triggerInstall() {
if (svsCB && svsCB.checked) specialTasks++; if (svsCB && svsCB.checked) specialTasks++;
const extraTasks = (renameCB && renameCB.checked) ? 1 : 0; const extraTasks = (renameCB && renameCB.checked) ? 1 : 0;
const total = checkedTasks.length + specialTasks + extraTasks; const total = checkedTasks.length + specialTasks + extraTasks;
if (total === 0) { if (total === 0) {
@@ -554,15 +535,45 @@ async function triggerInstall() {
} }
} }
// 4) Rename computer (LAST) // 4) Rename computer LAST
if (renameCB && renameCB.checked && newNameInput) { if (renameCB && renameCB.checked && newNameInput) {
const newName = newNameInput.value.trim(); const newName = newNameInput.value.trim();
const nameIsValid = const nameIsValid =
newName.length > 0 && newName.length > 0 &&
newName.length <= 15 && newName.length <= 15 &&
/^[A-Za-z]() /^[A-Za-z0-9-]+$/.test(newName);
if (!nameIsValid) {
alert("Invalid computer name. Must be 1-15 characters and only letters, numbers, and hyphens.");
logProgress("Rename computer", false);
} else {
try {
await fetch("/renameComputer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ newName }),
});
logProgress("Rename computer", true);
} catch (e) {
console.error("Error calling /renameComputer:", e);
logProgress("Rename computer", false);
}
}
}
} catch (e) {
console.error("triggerInstall fatal error:", e);
} finally {
runBtn.disabled = false;
// Best-effort notification to the server
try {
await fetch("/tasksCompleted", { method: "POST" });
} catch (err) {
console.warn("Could not notify server about completion:", err);
}
}
}
// ======================================================================= // =======================================================================
// Shutdown handler (Exit button & window close) // Shutdown handler (Exit button & window close)
@@ -574,9 +585,7 @@ function endSession() {
// Sub-options auto-toggle, tagline rotation, and beforeunload hook // Sub-options auto-toggle, tagline rotation, and beforeunload hook
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// Sub-option containers // Sub-option containers
const tasksWithSubOptions = document.querySelectorAll( const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]');
'[id$="OptionsContainer"]'
);
tasksWithSubOptions.forEach((container) => { tasksWithSubOptions.forEach((container) => {
const taskId = container.id.replace("OptionsContainer", ""); const taskId = container.id.replace("OptionsContainer", "");
@@ -602,7 +611,7 @@ document.addEventListener("DOMContentLoaded", () => {
updateVisibility(); updateVisibility();
}); });
// NEW: Rename computer checkbox -> show/hide text box // Rename computer checkbox -> show/hide text box
const renameCheckbox = document.getElementById("renameComputer"); const renameCheckbox = document.getElementById("renameComputer");
const renameBlock = document.getElementById("renameComputerBlock"); const renameBlock = document.getElementById("renameComputerBlock");
@@ -654,8 +663,7 @@ document.addEventListener("DOMContentLoaded", () => {
} }
}); });
// notify server on window close // notify server on window close
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
fetch("/quit", { method: "GET", keepalive: true }); fetch("/quit", { method: "GET", keepalive: true });
}); });