diff --git a/samy.js b/samy.js index aec1fa9..f735a18 100644 --- a/samy.js +++ b/samy.js @@ -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 defaultBtn = document.querySelector(`.tab-button[data-tab='${defaultTabId}']`); + const defaultBtn = document.querySelector( + `.tab-button[data-tab='${defaultTabId}']` + ); const defaultTab = document.getElementById(defaultTabId); + if (defaultBtn) defaultBtn.classList.add("active"); if (defaultTab) defaultTab.classList.add("active"); }); @@ -105,6 +108,7 @@ function toggleColumn(col) { cb.checked = master.checked; }); + // fire change handlers setTimeout(() => { children.forEach((cb) => { cb.dispatchEvent(new Event("change")); @@ -117,19 +121,21 @@ function updateSelectAll(col) { `selectAll${col[0].toUpperCase() + col.slice(1)}Checkbox` ); 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); } document.addEventListener("DOMContentLoaded", () => { ["left", "right"].forEach((col) => { document - .querySelectorAll(`#onboardTab input[type=checkbox][data-column=${col}]`) + .querySelectorAll(`#onboardTab input[type=checkbox][data-column="${col}"]`) .forEach((cb) => cb.addEventListener("change", () => updateSelectAll(col)) ); + updateSelectAll(col); }); }); @@ -182,6 +188,7 @@ function toggleDattoRMMOptions() { const master = document.getElementById("installDattoRMM"); const container = document.getElementById("installDattoRMMOptionsContainer"); if (!container) return; + const checked = master?.checked; container.style.display = checked ? "block" : "none"; container @@ -200,9 +207,7 @@ document.addEventListener("DOMContentLoaded", () => { if (passwordField && goButton) { passwordField.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - goButton.click(); - } + if (e.key === "Enter") goButton.click(); }); } @@ -211,9 +216,7 @@ document.addEventListener("DOMContentLoaded", () => { if (siteDropdown && runButton) { siteDropdown.addEventListener("keydown", (e) => { - if (e.key === "Enter" && siteDropdown.value) { - runButton.click(); - } + if (e.key === "Enter" && siteDropdown.value) runButton.click(); }); } }); @@ -223,11 +226,11 @@ document.addEventListener("DOMContentLoaded", () => { // ======================================================================= async function fetchSites() { 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"); + if (!dropdown) return; + dropdown.innerHTML = ''; try { @@ -237,19 +240,19 @@ async function fetchSites() { 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(); if (!Array.isArray(sites) || sites.length === 0) { - dropdown.innerHTML = - ''; - alert("No Datto sites returned. Verify credentials/allowlist, or try again in a moment."); + dropdown.innerHTML = ''; + alert( + "No Datto sites returned. Verify credentials/allowlist, or try again in a moment." + ); return; } - - dropdown.innerHTML = ""; + dropdown.innerHTML = ""; sites.forEach((site) => { const option = document.createElement("option"); option.value = site.UID; @@ -257,13 +260,14 @@ async function fetchSites() { dropdown.appendChild(option); }); - document.getElementById("dattoRmmContainer").style.display = "block"; + const rmmContainer = document.getElementById("dattoRmmContainer"); + if (rmmContainer) rmmContainer.style.display = "block"; } catch (e) { console.error(e); - dropdown.innerHTML = - ''; - alert("Failed to fetch sites. Check password or confirm your public IP is allowlisted."); - + dropdown.innerHTML = ''; + alert( + "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 async function fetchPrinters() { const pwdInput = document.getElementById("PrinterPassword"); - const pwd = (pwdInput?.value ?? ""); // allow blank const clientContainer = document.getElementById("printerClientContainer"); @@ -283,12 +286,8 @@ async function fetchPrinters() { const dropdown = document.getElementById("printerClientDropdown"); const checkboxContainer = document.getElementById("printerCheckboxContainer"); - if (dropdown) { - dropdown.innerHTML = ''; - } - if (checkboxContainer) { - checkboxContainer.innerHTML = ""; - } + if (dropdown) dropdown.innerHTML = ''; + if (checkboxContainer) checkboxContainer.innerHTML = ""; if (clientContainer) clientContainer.style.display = "none"; if (listContainer) listContainer.style.display = "none"; @@ -309,27 +308,22 @@ async function fetchPrinters() { return; } - // Build unique sorted ClientCode list const codes = [...new Set(allPrinters.map((p) => p.ClientCode))].sort(); - dropdown.innerHTML = ""; - const defaultOpt = new Option("Select a client...", "", true, true); - defaultOpt.disabled = true; - dropdown.appendChild(defaultOpt); + if (dropdown) { + dropdown.innerHTML = ""; + const defaultOpt = new Option("Select a client...", "", true, true); + defaultOpt.disabled = true; + dropdown.appendChild(defaultOpt); - codes.forEach((code) => { - dropdown.appendChild(new Option(code, code)); - }); + codes.forEach((code) => dropdown.appendChild(new Option(code, code))); + } if (clientContainer) clientContainer.style.display = "block"; } catch (e) { console.error("fetchPrinters error:", e); - if (dropdown) { - dropdown.innerHTML = - ''; - } + if (dropdown) dropdown.innerHTML = ''; 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.marginBottom = "4px"; - //Install-Checkbox const cb = document.createElement("input"); cb.type = "checkbox"; cb.id = id; - // stash all fields we might need later cb.dataset.clientCode = p.ClientCode; cb.dataset.profileName = p.ProfileName; cb.dataset.displayName = p.DisplayName; @@ -373,14 +365,10 @@ function renderPrintersForClient(clientCode) { const nameText = p.DisplayName || p.ProfileName || "Unnamed printer"; const locText = p.Location || "Unknown location"; - // Line 1: install checkbox + printer label label.appendChild(cb); label.appendChild(document.createTextNode(" ")); - label.appendChild( - document.createTextNode(`${nameText} (${locText})`) - ); + label.appendChild(document.createTextNode(`${nameText} (${locText})`)); - // Line 2: radio for "Make default" const defaultWrapper = document.createElement("div"); defaultWrapper.style.marginLeft = "24px"; defaultWrapper.style.fontSize = "0.85em"; @@ -389,7 +377,7 @@ function renderPrintersForClient(clientCode) { const radio = document.createElement("input"); radio.type = "radio"; radio.name = "defaultPrinter"; - radio.value = id; // associate default choice with this checkbox/printer + radio.value = id; const radioLabel = document.createElement("span"); radioLabel.textContent = " Make default"; @@ -416,7 +404,6 @@ async function installSelectedPrinters() { return; } - // See which radio is checked for "Make default" const defaultRadio = container.querySelector( 'input[type=radio][name="defaultPrinter"]:checked' ); @@ -432,7 +419,6 @@ async function installSelectedPrinters() { ShareName: cb.dataset.shareName, DriverName: cb.dataset.driverName, DriverInfPath: cb.dataset.driverInfPath, - // Only the printer whose checkbox id matches the selected radio gets SetAsDefault=true SetAsDefault: defaultId !== null && cb.id === defaultId, })); @@ -446,20 +432,16 @@ async function installSelectedPrinters() { if (!resp.ok) throw new Error("HTTP " + resp.status); const result = await resp.json().catch(() => null); - console.log("Printer install result:", result); - } catch (e) { console.error("installSelectedPrinters error:", e); alert("Failed to trigger printer install."); } } - // ======================================================================= // Run Selected (main trigger) // ======================================================================= - async function triggerInstall() { const runBtn = document.querySelector(".run-button"); if (!runBtn) return; @@ -470,13 +452,13 @@ async function triggerInstall() { if (statusBox) statusBox.innerHTML = ""; try { - // Grab special-case elements ONCE + // Special-case elements ONCE const dattoCB = document.getElementById("installDattoRMM"); const svsCB = document.getElementById("installSVSMSPModule"); const renameCB = document.getElementById("renameComputer"); 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) => { if (["installDattoRMM", "installSVSMSPModule", "renameComputer"].includes(t.id)) return false; const cb = document.getElementById(t.id); @@ -489,7 +471,6 @@ async function triggerInstall() { if (svsCB && svsCB.checked) specialTasks++; const extraTasks = (renameCB && renameCB.checked) ? 1 : 0; - const total = checkedTasks.length + specialTasks + extraTasks; if (total === 0) { @@ -554,15 +535,45 @@ async function triggerInstall() { } } - // 4) Rename computer (LAST) + // 4) Rename computer LAST if (renameCB && renameCB.checked && newNameInput) { const newName = newNameInput.value.trim(); const nameIsValid = newName.length > 0 && 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) @@ -574,9 +585,7 @@ function endSession() { // Sub-options auto-toggle, tagline rotation, and beforeunload hook document.addEventListener("DOMContentLoaded", () => { // Sub-option containers - const tasksWithSubOptions = document.querySelectorAll( - '[id$="OptionsContainer"]' - ); + const tasksWithSubOptions = document.querySelectorAll('[id$="OptionsContainer"]'); tasksWithSubOptions.forEach((container) => { const taskId = container.id.replace("OptionsContainer", ""); @@ -602,7 +611,7 @@ document.addEventListener("DOMContentLoaded", () => { updateVisibility(); }); - // NEW: Rename computer checkbox -> show/hide text box + // Rename computer checkbox -> show/hide text box const renameCheckbox = document.getElementById("renameComputer"); const renameBlock = document.getElementById("renameComputerBlock"); @@ -654,8 +663,7 @@ document.addEventListener("DOMContentLoaded", () => { } }); - // notify server on window close window.addEventListener("beforeunload", () => { fetch("/quit", { method: "GET", keepalive: true }); -}); \ No newline at end of file +});