Added credential persistance

This commit is contained in:
Oliver
2026-05-25 12:13:39 -03:00
parent a27acf9357
commit 28345295b6
2 changed files with 226 additions and 14 deletions
+77 -14
View File
@@ -6,7 +6,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no"
/>
<title>Health Routine Product Scanner</title>
<title>Health Routine Package Scanner</title>
<style>
*,
*::before,
@@ -808,7 +808,7 @@
</div>
<span class="brand-name">Health Routine</span>
<div class="brand-divider"></div>
<span class="brand-subtitle">Product Scanner</span>
<span class="brand-subtitle">Package Scanner</span>
</div>
<!-- Controls bar -->
@@ -898,7 +898,7 @@
</div>
<div class="empty-title">No scans yet</div>
<div class="empty-hint">
Select a user, then scan or type a product code and
Select a user, then scan or type a package code and
press Enter
</div>
</div>
@@ -994,6 +994,51 @@
const modalError = document.getElementById("modalError");
const toast = document.getElementById("toast");
// ── Cookie helpers ────────────────────────────────────────────────────────
function saveCreds(user, pass) {
const val = btoa(
encodeURIComponent(JSON.stringify({ user, pass })),
);
document.cookie = `hr_auth=${val}; max-age=${30 * 24 * 3600}; SameSite=Strict; path=/`;
}
function loadCredsFromCookie() {
const match = document.cookie.match(
/(?:^|;\s*)hr_auth=([^;]+)/,
);
if (match) {
try {
return JSON.parse(decodeURIComponent(atob(match[1])));
} catch (_) {}
}
return null;
}
function clearCredsCookie() {
document.cookie = "hr_auth=; max-age=0; path=/";
}
// ── JSON parser (handles array, single object, and NDJSON) ───────────────
async function parseJson(res) {
const text = await res.text();
// Standard JSON (array or object)
try {
const parsed = JSON.parse(text);
return parsed;
} catch (_) {}
// NDJSON — n8n sometimes returns one object per line
try {
const lines = text
.trim()
.split("\n")
.filter((l) => l.trim());
if (lines.length > 0) {
return lines.map((l) => JSON.parse(l));
}
} catch (_) {}
throw new Error("Could not parse server response");
}
// ── Helpers ──────────────────────────────────────────────────────────────
function esc(str) {
return String(str ?? "")
@@ -1052,10 +1097,16 @@
function needAuth(retryCb) {
pendingAuthCb = retryCb;
modalError.style.display = "none";
authUser.value = "";
authPass.value = "";
// Pre-fill from saved cookie so user only needs to confirm
const saved = loadCredsFromCookie();
authUser.value = saved?.user || "";
authPass.value = saved?.pass || "";
authModal.classList.remove("hidden");
setTimeout(() => authUser.focus(), 50);
// Focus password if username already filled, otherwise username
setTimeout(
() => (saved ? authPass.focus() : authUser.focus()),
50,
);
}
function cancelAuth() {
@@ -1075,6 +1126,7 @@
}
creds = { user: u, pass: p };
saveCreds(u, p); // persist to cookie
authBtn.classList.add("loading");
authBtn.disabled = true;
modalError.style.display = "none";
@@ -1120,7 +1172,7 @@
return;
}
const data = await res.json();
const data = await parseJson(res);
buildDropdown(data);
hideBanner();
} catch (err) {
@@ -1164,7 +1216,7 @@
return;
}
showBanner("Looking up product…", "loading");
showBanner("Looking up package…", "loading");
try {
const res = await fetch(WEBHOOK, {
@@ -1182,16 +1234,24 @@
}
if (!res.ok) {
showBanner(
`Scan failed (HTTP ${res.status}).`,
"error",
5000,
);
// Try to extract a descriptive error from the response body
let errMsg = `Scan failed (HTTP ${res.status}).`;
try {
const errData = await parseJson(res);
if (errData?.error) errMsg = errData.error;
} catch (_) {}
showBanner(errMsg, "error", 0); // persistent until next action
scanInput.select();
return;
}
const item = await res.json();
const item = await parseJson(res);
// Webhook may return 200 but carry an error field
if (item?.error) {
showBanner(item.error, "error", 0);
scanInput.select();
return;
}
hideBanner();
addRow(item);
showToast(
@@ -1265,6 +1325,9 @@
// ── Init ─────────────────────────────────────────────────────────────────
window.addEventListener("DOMContentLoaded", () => {
// Restore saved credentials before the first network call
const saved = loadCredsFromCookie();
if (saved) creds = saved;
loadUsers();
scanInput.focus();
});