646 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			646 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<html lang="en">
 | 
						|
	<head>
 | 
						|
		<meta charset="UTF-8" />
 | 
						|
		<title>PID Log Viewer</title>
 | 
						|
 | 
						|
		<!-- Link to CSS file -->
 | 
						|
		<link rel="stylesheet" href="/css/style.css" />
 | 
						|
	</head>
 | 
						|
	<body>
 | 
						|
		<div id="logToast" class="toast hidden"></div>
 | 
						|
 | 
						|
		<div class="sidebar">
 | 
						|
			<div class="sidebar-header">
 | 
						|
				<h3>PIDs (<span id="pidCount">0</span>)</h3>
 | 
						|
				<input
 | 
						|
					type="text"
 | 
						|
					id="pidSearch"
 | 
						|
					placeholder="🔍 Search PID..."
 | 
						|
				/>
 | 
						|
			</div>
 | 
						|
			<div class="sidebar-list" id="pidListQuick"></div>
 | 
						|
		</div>
 | 
						|
 | 
						|
		<div class="content">
 | 
						|
			<!-- <h3>📊 PID Log Viewer</h3> -->
 | 
						|
			<div
 | 
						|
				style="
 | 
						|
					margin-bottom: 0.5rem;
 | 
						|
					display: flex;
 | 
						|
					justify-content: end;
 | 
						|
				"
 | 
						|
			>
 | 
						|
				<div class="user-dropdown">
 | 
						|
					<button
 | 
						|
						id="welcome-user"
 | 
						|
						class="user-dropdown-toggle"
 | 
						|
					></button>
 | 
						|
 | 
						|
					<div class="user-dropdown-content">
 | 
						|
						<div id="confirm-count-user" class="item"></div>
 | 
						|
 | 
						|
						<button onclick="logout()">Logout</button>
 | 
						|
					</div>
 | 
						|
				</div>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div id="selectAndVersionDiv">
 | 
						|
				<div id="selectedPIDDiv">
 | 
						|
					<label for="pidList">PID</label>
 | 
						|
					<select id="pidList" disabled></select>
 | 
						|
				</div>
 | 
						|
				<div id="selectedVersionDiv">
 | 
						|
					<label for="versionList">Select Version</label>
 | 
						|
					<select id="versionList"></select>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<div style="width: 23%; align-items: center">
 | 
						|
					<button id="copyBtn" class="action-btn">📋 Copy</button>
 | 
						|
					<button id="downloadBtn" class="action-btn">
 | 
						|
						⬇️ Download
 | 
						|
					</button>
 | 
						|
				</div>
 | 
						|
 | 
						|
				<div>
 | 
						|
					<button
 | 
						|
						id="deviceConfirmBtn"
 | 
						|
						type="button"
 | 
						|
						class="primary-btn"
 | 
						|
					>
 | 
						|
						Confirm
 | 
						|
					</button>
 | 
						|
					<span
 | 
						|
						id="confirmUserText"
 | 
						|
						style="font-size: 12px; display: none"
 | 
						|
					>
 | 
						|
						Confirmed by <strong id="confirmedUserName"></strong>
 | 
						|
					</span>
 | 
						|
				</div>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div id="errorTableContainer" class="command-section">
 | 
						|
				<h3>⚠️ Error History for PID</h3>
 | 
						|
				<table border="1" cellspacing="0" cellpadding="5" width="100%">
 | 
						|
					<thead>
 | 
						|
						<tr>
 | 
						|
							<th style="text-align: left">Logging</th>
 | 
						|
							<th style="text-align: left">Pattern</th>
 | 
						|
							<th style="text-align: left">Description</th>
 | 
						|
						</tr>
 | 
						|
					</thead>
 | 
						|
					<tbody id="errorTableBody"></tbody>
 | 
						|
				</table>
 | 
						|
			</div>
 | 
						|
			<div id="logSections"></div>
 | 
						|
 | 
						|
			<div id="logDeleteModal" class="modal hidden">
 | 
						|
				<div style="max-width: 30%" class="modal-content">
 | 
						|
					<div id="deleteLogText"></div>
 | 
						|
					<div class="modal-actions">
 | 
						|
						<button id="logCancelDeleteBtn" class="secondary-btn">
 | 
						|
							Cancel
 | 
						|
						</button>
 | 
						|
						<button id="logDeleteBtn" class="danger-btn">
 | 
						|
							Delete
 | 
						|
						</button>
 | 
						|
					</div>
 | 
						|
				</div>
 | 
						|
			</div>
 | 
						|
 | 
						|
			<div id="deviceConfirmModal" class="modal hidden">
 | 
						|
				<div style="max-width: 30%" class="modal-content">
 | 
						|
					<div id="deviceConfirmText"></div>
 | 
						|
					<div class="modal-actions">
 | 
						|
						<button id="deviceCancelBtn" class="secondary-btn">
 | 
						|
							Cancel
 | 
						|
						</button>
 | 
						|
						<button id="submitDeviceConfirmBtn" class="danger-btn">
 | 
						|
							Confirm
 | 
						|
						</button>
 | 
						|
					</div>
 | 
						|
				</div>
 | 
						|
			</div>
 | 
						|
		</div>
 | 
						|
 | 
						|
		<!-- Authentication and get user info -->
 | 
						|
		<script>
 | 
						|
			if (!localStorage.getItem("token")) {
 | 
						|
				logout();
 | 
						|
			}
 | 
						|
 | 
						|
			function logout() {
 | 
						|
				localStorage.removeItem("token");
 | 
						|
				localStorage.removeItem("user");
 | 
						|
				window.location.href = "/login";
 | 
						|
			}
 | 
						|
 | 
						|
			async function getProfile() {
 | 
						|
				const token = localStorage.getItem("token");
 | 
						|
 | 
						|
				await fetch("/api/profile", {
 | 
						|
					method: "GET",
 | 
						|
					headers: {
 | 
						|
						"Content-Type": "application/json",
 | 
						|
						Authorization: `Bearer ${token}`,
 | 
						|
					},
 | 
						|
				})
 | 
						|
					.then((res) => res.json())
 | 
						|
					.then((data) => {
 | 
						|
						document.getElementById(
 | 
						|
							"welcome-user",
 | 
						|
						).innerHTML = `Welcome, <strong>${data?.user?.name}</strong> ▼`;
 | 
						|
 | 
						|
						document.getElementById(
 | 
						|
							"confirm-count-user",
 | 
						|
						).innerHTML = `Confirmed: <strong>${data?.user?.confirm_count}</strong>`;
 | 
						|
					})
 | 
						|
					.catch((err) => {
 | 
						|
						console.error(err);
 | 
						|
					});
 | 
						|
			}
 | 
						|
 | 
						|
			getProfile();
 | 
						|
		</script>
 | 
						|
 | 
						|
		<!-- Get PID - Version - Log -->
 | 
						|
		<script>
 | 
						|
			// Get param pid, version from URL
 | 
						|
			const params = new URLSearchParams(window.location.search);
 | 
						|
			const pidParam = params.get("pid");
 | 
						|
			const versionParam = params.get("version");
 | 
						|
 | 
						|
			const pidSearchInput = document.getElementById("pidSearch");
 | 
						|
			const pidList = document.getElementById("pidList");
 | 
						|
			const versionList = document.getElementById("versionList");
 | 
						|
			const pidListQuick = document.getElementById("pidListQuick");
 | 
						|
			const logSections = document.getElementById("logSections");
 | 
						|
			const commands = ["inventory", "version", "license", "logging"];
 | 
						|
 | 
						|
			const confirmUserText = document.getElementById("confirmUserText");
 | 
						|
			const confirmedUserName =
 | 
						|
				document.getElementById("confirmedUserName");
 | 
						|
 | 
						|
			const logsCache = {}; // Store logs and current index for each command
 | 
						|
			let versionsUser = [];
 | 
						|
 | 
						|
			async function loadPIDs(pidParam, versionParam) {
 | 
						|
				const res = await fetch("/api/pids");
 | 
						|
				const pids = await res.json();
 | 
						|
				document.getElementById("pidCount").textContent = pids.length;
 | 
						|
				pidListQuick.innerHTML = "";
 | 
						|
 | 
						|
				pids.forEach((pid) => {
 | 
						|
					const btn = document.createElement("button");
 | 
						|
					btn.className = "pid-button";
 | 
						|
					btn.textContent = pid;
 | 
						|
					btn.onclick = () => selectPID(pid);
 | 
						|
					pidListQuick.appendChild(btn);
 | 
						|
				});
 | 
						|
 | 
						|
				if (pidParam) {
 | 
						|
					selectPID(pidParam, versionParam);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			async function selectPID(pid, versionParam) {
 | 
						|
				document
 | 
						|
					.querySelectorAll(".pid-button")
 | 
						|
					.forEach((b) => b.classList.remove("selected"));
 | 
						|
				const target = [
 | 
						|
					...document.querySelectorAll(".pid-button"),
 | 
						|
				].find((b) => b.textContent === pid);
 | 
						|
				if (target) target.classList.add("selected");
 | 
						|
 | 
						|
				const res = await fetch(
 | 
						|
					`/api/pid/${encodeURIComponent(pid)}/versions`,
 | 
						|
				);
 | 
						|
				const versions = await res.json();
 | 
						|
 | 
						|
				versionsUser = versions
 | 
						|
					.filter((verItem) => verItem.user)
 | 
						|
					.map((verItem) => {
 | 
						|
						return {
 | 
						|
							version: verItem.version,
 | 
						|
							user: verItem.user,
 | 
						|
						};
 | 
						|
					});
 | 
						|
 | 
						|
				pidList.innerHTML = `<option value="${pid}">${pid}</option>`;
 | 
						|
				versionList.innerHTML = versions
 | 
						|
					.map((v) => {
 | 
						|
						const cmds = Object.entries(v.commands)
 | 
						|
							.filter(([_, count]) => count > 0)
 | 
						|
							.map(([cmd, count]) => `${cmd} (${count})`)
 | 
						|
							.join(", ");
 | 
						|
						return `<option value="${v.version}">${v.version} - ${
 | 
						|
							cmds || "No commands"
 | 
						|
						}</option>`;
 | 
						|
					})
 | 
						|
					.join("");
 | 
						|
 | 
						|
				// Set param pid, version to URL
 | 
						|
				const currentParams = new URLSearchParams(
 | 
						|
					window.location.search,
 | 
						|
				);
 | 
						|
				currentParams.set("pid", pid);
 | 
						|
 | 
						|
				// Có param version thì chọn
 | 
						|
				if (versionParam) {
 | 
						|
					versionList.value = versionParam;
 | 
						|
					versionList.dispatchEvent(new Event("change"));
 | 
						|
					currentParams.set("version", versionParam);
 | 
						|
				} else {
 | 
						|
					// Default chọn version đầu tiên
 | 
						|
					if (versions.length > 0) {
 | 
						|
						versionList.value = versions[0].version;
 | 
						|
						versionList.dispatchEvent(new Event("change"));
 | 
						|
						currentParams.set("version", versions[0].version);
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				history.replaceState(null, "", `?${currentParams.toString()}`);
 | 
						|
 | 
						|
				await loadErrorTable(pid);
 | 
						|
			}
 | 
						|
 | 
						|
			pidSearchInput.addEventListener("input", async () => {
 | 
						|
				const keyword = pidSearchInput.value.toLowerCase().trim();
 | 
						|
				const res = await fetch("/api/pids");
 | 
						|
				const pids = await res.json();
 | 
						|
				const filtered = pids.filter((pid) =>
 | 
						|
					pid.toLowerCase().includes(keyword),
 | 
						|
				);
 | 
						|
 | 
						|
				pidListQuick.innerHTML = "";
 | 
						|
				filtered.forEach((pid) => {
 | 
						|
					const btn = document.createElement("button");
 | 
						|
					btn.className = "pid-button";
 | 
						|
					btn.textContent = pid;
 | 
						|
					btn.onclick = () => selectPID(pid);
 | 
						|
					pidListQuick.appendChild(btn);
 | 
						|
				});
 | 
						|
 | 
						|
				pidList.innerHTML = filtered
 | 
						|
					.map((pid) => `<option value="${pid}">${pid}</option>`)
 | 
						|
					.join("");
 | 
						|
				if (filtered.length > 0) {
 | 
						|
					pidList.value = filtered[0];
 | 
						|
					selectPID(filtered[0]);
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			pidList.addEventListener("change", () => {
 | 
						|
				const pid = pidList.value;
 | 
						|
				if (pid) selectPID(pid);
 | 
						|
			});
 | 
						|
 | 
						|
			versionList.addEventListener("change", () => {
 | 
						|
				const pid = pidList.value;
 | 
						|
				const version = versionList.value;
 | 
						|
				if (pid && version) {
 | 
						|
					// Set param pid, version to URL
 | 
						|
					const currentParams = new URLSearchParams(
 | 
						|
						window.location.search,
 | 
						|
					);
 | 
						|
					currentParams.set("pid", pid);
 | 
						|
					currentParams.set("version", version);
 | 
						|
					history.replaceState(
 | 
						|
						null,
 | 
						|
						"",
 | 
						|
						`?${currentParams.toString()}`,
 | 
						|
					);
 | 
						|
 | 
						|
					// Render Confirm Section
 | 
						|
					const currentVerUser = versionsUser.find(
 | 
						|
						(verItem) => verItem.version === version,
 | 
						|
					);
 | 
						|
					if (currentVerUser) {
 | 
						|
						deviceConfirmBtn.style.display = "none";
 | 
						|
						confirmedUserName.textContent =
 | 
						|
							currentVerUser.user.name;
 | 
						|
						confirmUserText.style.display = "inline";
 | 
						|
					} else {
 | 
						|
						deviceConfirmBtn.style.display = "inline-block";
 | 
						|
						confirmUserText.style.display = "none";
 | 
						|
					}
 | 
						|
 | 
						|
					loadLogs(pid, version);
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			async function loadLogs(pid, version) {
 | 
						|
				logSections.innerHTML = "";
 | 
						|
				logsCache[`${pid}_${version}`] = {};
 | 
						|
 | 
						|
				for (const cmd of commands) {
 | 
						|
					const res = await fetch(
 | 
						|
						`/api/device/${encodeURIComponent(
 | 
						|
							pid,
 | 
						|
						)}/${encodeURIComponent(version)}/${cmd}`,
 | 
						|
					);
 | 
						|
					const data = await res.json();
 | 
						|
					if (!data || data.length === 0) continue;
 | 
						|
 | 
						|
					logsCache[`${pid}_${version}`][cmd] = {
 | 
						|
						logs: data,
 | 
						|
						index: 0,
 | 
						|
					};
 | 
						|
 | 
						|
					const section = document.createElement("div");
 | 
						|
					section.className = "command-section";
 | 
						|
					section.id = `section-${cmd}`;
 | 
						|
					section.innerHTML = `
 | 
						|
            <h3>
 | 
						|
                ${"show " + cmd} (${data.length})
 | 
						|
                <div>
 | 
						|
                <button class="next-btn" id="btn-prev-${cmd}">Previous</button>
 | 
						|
                <span id="page-info-${cmd}">Output 1/${data.length}</span>
 | 
						|
                <button class="next-btn" id="btn-next-${cmd}">Next</button>
 | 
						|
                </div>
 | 
						|
            </h3>
 | 
						|
            <div class="log-block" id="log-${cmd}">
 | 
						|
				<button type="button" class="danger-btn trash-btn" title="Add to delete list">🗑</button>
 | 
						|
                <b>${data[0].filename}</b>\n${data[0].output}
 | 
						|
            </div>
 | 
						|
            `;
 | 
						|
 | 
						|
					logSections.appendChild(section);
 | 
						|
 | 
						|
					// Navigation logic
 | 
						|
					const updateDisplay = () => {
 | 
						|
						const info = logsCache[`${pid}_${version}`][cmd];
 | 
						|
						const curr = info.logs[info.index];
 | 
						|
 | 
						|
						const logBlock = document.getElementById(`log-${cmd}`);
 | 
						|
						logBlock.innerHTML = `
 | 
						|
							<button type="button" class="danger-btn trash-btn" title="Add to delete list">🗑</button>
 | 
						|
							<b>${curr.filename}</b>\n${curr.output}
 | 
						|
						`;
 | 
						|
 | 
						|
						document.getElementById(
 | 
						|
							`page-info-${cmd}`,
 | 
						|
						).textContent = `Output ${info.index + 1}/${
 | 
						|
							info.logs.length
 | 
						|
						}`;
 | 
						|
						document.getElementById(`btn-prev-${cmd}`).disabled =
 | 
						|
							info.index === 0;
 | 
						|
					};
 | 
						|
 | 
						|
					document
 | 
						|
						.getElementById(`btn-next-${cmd}`)
 | 
						|
						.addEventListener("click", () => {
 | 
						|
							const info = logsCache[`${pid}_${version}`][cmd];
 | 
						|
							info.index = (info.index + 1) % info.logs.length;
 | 
						|
							updateDisplay();
 | 
						|
						});
 | 
						|
 | 
						|
					document
 | 
						|
						.getElementById(`btn-prev-${cmd}`)
 | 
						|
						.addEventListener("click", () => {
 | 
						|
							const info = logsCache[`${pid}_${version}`][cmd];
 | 
						|
							info.index =
 | 
						|
								(info.index - 1 + info.logs.length) %
 | 
						|
								info.logs.length;
 | 
						|
							updateDisplay();
 | 
						|
						});
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			loadPIDs(pidParam, versionParam);
 | 
						|
 | 
						|
			const copyBtn = document.getElementById("copyBtn");
 | 
						|
			const downloadBtn = document.getElementById("downloadBtn");
 | 
						|
 | 
						|
			function getExportText() {
 | 
						|
				const pid = pidList.value || "";
 | 
						|
				const version = versionList.value || "";
 | 
						|
				const inventory = getCommandOutput("inventory");
 | 
						|
				const versionOut = getCommandOutput("version");
 | 
						|
				const logging = getCommandOutput("logging");
 | 
						|
				const license = getCommandOutput("license");
 | 
						|
 | 
						|
				return `PID: ${pid}
 | 
						|
Version: ${version}
 | 
						|
 | 
						|
${inventory}
 | 
						|
 | 
						|
${versionOut}
 | 
						|
 | 
						|
${logging}
 | 
						|
 | 
						|
${license}`.trim();
 | 
						|
			}
 | 
						|
 | 
						|
			// Assume you store outputs in memory or fetch based on UI state
 | 
						|
			function getCommandOutput(command) {
 | 
						|
				const pid = pidList.value;
 | 
						|
				const version = versionList.value;
 | 
						|
				const key = `${pid}_${version}`;
 | 
						|
 | 
						|
				if (
 | 
						|
					logsCache[key] &&
 | 
						|
					logsCache[key][command] &&
 | 
						|
					logsCache[key][command].logs.length > 0
 | 
						|
				) {
 | 
						|
					const { logs, index } = logsCache[key][command];
 | 
						|
					return logs[index].output || "";
 | 
						|
				}
 | 
						|
 | 
						|
				return "No data";
 | 
						|
			}
 | 
						|
 | 
						|
			copyBtn.addEventListener("click", async () => {
 | 
						|
				try {
 | 
						|
					await navigator.clipboard.writeText(getExportText());
 | 
						|
					alert("Copied to clipboard!");
 | 
						|
				} catch (err) {
 | 
						|
					alert("Failed to copy.");
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			downloadBtn.addEventListener("click", () => {
 | 
						|
				const content = getExportText();
 | 
						|
				const blob = new Blob([content], { type: "text/plain" });
 | 
						|
				const a = document.createElement("a");
 | 
						|
				a.href = URL.createObjectURL(blob);
 | 
						|
				a.download = `log-${pidList.value || "unknown"}.txt`;
 | 
						|
				a.click();
 | 
						|
			});
 | 
						|
 | 
						|
			async function loadErrorTable(pid) {
 | 
						|
				const res = await fetch(
 | 
						|
					`/api/errors/${encodeURIComponent(pid)}`,
 | 
						|
				);
 | 
						|
				const data = await res.json();
 | 
						|
 | 
						|
				const tbody = document.getElementById("errorTableBody");
 | 
						|
				tbody.innerHTML = "";
 | 
						|
 | 
						|
				if (data.length === 0) {
 | 
						|
					tbody.innerHTML = `<tr><td colspan="5">✅ No known issues for this PID</td></tr>`;
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				for (const err of data) {
 | 
						|
					const tr = document.createElement("tr");
 | 
						|
					tr.innerHTML = `
 | 
						|
      <td><code>${err.highlighted_message}</code></td>
 | 
						|
      <td><code>${err.regex_pattern}</code></td>
 | 
						|
      <td><code>${err.description}</code></td>
 | 
						|
      `;
 | 
						|
					tbody.appendChild(tr);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		</script>
 | 
						|
 | 
						|
		<!-- Handle delete log -->
 | 
						|
		<script>
 | 
						|
			let pendingDeleteLog = null;
 | 
						|
 | 
						|
			document.addEventListener("click", async (e) => {
 | 
						|
				if (e.target.classList.contains("trash-btn")) {
 | 
						|
					const section = e.target.closest(".command-section");
 | 
						|
					const pid = pidList.value;
 | 
						|
					const version = versionList.value;
 | 
						|
					const command = section.id.replace("section-", "");
 | 
						|
					const index = logsCache[`${pid}_${version}`][command].index;
 | 
						|
					const log =
 | 
						|
						logsCache[`${pid}_${version}`][command].logs[index];
 | 
						|
 | 
						|
					pendingDeleteLog = { id: log.id, command, pid, version };
 | 
						|
 | 
						|
					const text = `Are you sure you want to delete this log? <br /><code>${pid} - ${version} - ${command}</code> <br /> <div style="font-size: 12px; font-weight: 700" >${log.filename}</div>`;
 | 
						|
					document.getElementById("deleteLogText").innerHTML = text;
 | 
						|
					showDeleteModal();
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			const logDeleteModal = document.getElementById("logDeleteModal");
 | 
						|
			const logCancelDeleteBtn =
 | 
						|
				document.getElementById("logCancelDeleteBtn");
 | 
						|
			const logDeleteBtn = document.getElementById("logDeleteBtn");
 | 
						|
 | 
						|
			function showDeleteModal() {
 | 
						|
				logDeleteModal.classList.remove("hidden");
 | 
						|
			}
 | 
						|
			function hideDeleteModal() {
 | 
						|
				logDeleteModal.classList.add("hidden");
 | 
						|
				pendingDeleteLog = null;
 | 
						|
			}
 | 
						|
 | 
						|
			logCancelDeleteBtn.addEventListener("click", hideDeleteModal);
 | 
						|
 | 
						|
			logDeleteBtn.addEventListener("click", async () => {
 | 
						|
				if (!pendingDeleteLog) return;
 | 
						|
 | 
						|
				const token = localStorage.getItem("token");
 | 
						|
				try {
 | 
						|
					const res = await fetch("/api/delete-log", {
 | 
						|
						method: "POST",
 | 
						|
						headers: {
 | 
						|
							"Content-Type": "application/json",
 | 
						|
							Authorization: `Bearer ${token}`,
 | 
						|
						},
 | 
						|
						body: JSON.stringify(pendingDeleteLog),
 | 
						|
					});
 | 
						|
					const data = await res.json();
 | 
						|
					if (data.success) {
 | 
						|
						alert("Deleted successfully!");
 | 
						|
						location.reload();
 | 
						|
					} else {
 | 
						|
						alert("Delete failed.");
 | 
						|
					}
 | 
						|
				} catch (err) {
 | 
						|
					console.error(err);
 | 
						|
					alert("Delete failed.");
 | 
						|
				} finally {
 | 
						|
					hideDeleteModal();
 | 
						|
					pendingDeleteLog = null;
 | 
						|
				}
 | 
						|
			});
 | 
						|
 | 
						|
			function showToast(message = "Announce something") {
 | 
						|
				const toast = document.getElementById("logToast");
 | 
						|
				toast.textContent = message;
 | 
						|
				toast.classList.add("show");
 | 
						|
				toast.classList.remove("hidden");
 | 
						|
 | 
						|
				setTimeout(() => {
 | 
						|
					toast.classList.remove("show");
 | 
						|
					toast.classList.add("hidden");
 | 
						|
				}, 2000);
 | 
						|
			}
 | 
						|
		</script>
 | 
						|
 | 
						|
		<!-- Handle confirm device -->
 | 
						|
		<script>
 | 
						|
			const deviceConfirmBtn =
 | 
						|
				document.getElementById("deviceConfirmBtn");
 | 
						|
			const deviceConfirmModal =
 | 
						|
				document.getElementById("deviceConfirmModal");
 | 
						|
			const deviceCancelBtn = document.getElementById("deviceCancelBtn");
 | 
						|
			const submitDeviceConfirmBtn = document.getElementById(
 | 
						|
				"submitDeviceConfirmBtn",
 | 
						|
			);
 | 
						|
			const deviceConfirmText =
 | 
						|
				document.getElementById("deviceConfirmText");
 | 
						|
 | 
						|
			let pendingDevice = null;
 | 
						|
 | 
						|
			deviceConfirmBtn.addEventListener("click", () => {
 | 
						|
				const pid = document.getElementById("pidList").value;
 | 
						|
				const version = document.getElementById("versionList").value;
 | 
						|
 | 
						|
				if (!pid || !version) {
 | 
						|
					alert("PID or version is missing.");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
 | 
						|
				pendingDevice = { pid, version };
 | 
						|
 | 
						|
				deviceConfirmText.innerHTML = `
 | 
						|
			Are you sure you want to confirm this device?<br />
 | 
						|
			<strong>${pid} - ${version}</strong>
 | 
						|
		`;
 | 
						|
 | 
						|
				deviceConfirmModal.classList.remove("hidden");
 | 
						|
			});
 | 
						|
 | 
						|
			deviceCancelBtn.addEventListener("click", () => {
 | 
						|
				deviceConfirmModal.classList.add("hidden");
 | 
						|
				pendingDevice = null;
 | 
						|
			});
 | 
						|
 | 
						|
			submitDeviceConfirmBtn.addEventListener("click", async () => {
 | 
						|
				if (!pendingDevice) return;
 | 
						|
 | 
						|
				const token = localStorage.getItem("token");
 | 
						|
				try {
 | 
						|
					const res = await fetch("/api/confirm-device", {
 | 
						|
						method: "POST",
 | 
						|
						headers: {
 | 
						|
							"Content-Type": "application/json",
 | 
						|
							Authorization: `Bearer ${token}`,
 | 
						|
						},
 | 
						|
						body: JSON.stringify(pendingDevice),
 | 
						|
					});
 | 
						|
					const data = await res.json();
 | 
						|
					if (data.success) {
 | 
						|
						alert(data.message || "Device confirmed successfully!");
 | 
						|
						location.reload();
 | 
						|
					} else {
 | 
						|
						alert(data.message || "Confirmation failed.");
 | 
						|
					}
 | 
						|
				} catch (err) {
 | 
						|
					console.error(err);
 | 
						|
					alert("Confirmation failed.");
 | 
						|
				} finally {
 | 
						|
					deviceConfirmModal.classList.add("hidden");
 | 
						|
					pendingDevice = null;
 | 
						|
				}
 | 
						|
			});
 | 
						|
		</script>
 | 
						|
	</body>
 | 
						|
</html>
 |