Files
snowspeeder 59645e59b5 Add green-bean inventory and cupping features
Ports inventory management and SCA-style cupping scoring from
hope_roaster: lot tracking with audit-logged consumption, cupping
sessions with server-authoritative scoring and a live radar chart,
and links from the planner (draw-from-lot, open-cupping-session).

Also fixes the Blend/Single-origin toggle layout, replaces tooltips
with an in-context "why" teaching layer, and stages the planner UI
into pre-roast vs. post-roast phases.
2026-07-30 14:47:15 -04:00

52 lines
1.5 KiB
JavaScript

const form = document.querySelector("#reset-form");
const message = document.querySelector("#message");
const resetToken = new URLSearchParams(location.search).get("token") || "";
function showError(text) {
message.textContent = text;
message.className = "auth-message error";
}
if (!resetToken) {
form.classList.add("hidden");
showError(
"This reset link is missing its token. Request a new one from the forgot-password page.",
);
}
form.addEventListener("submit", async (event) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(form));
if (data.password !== data.confirmPassword) {
showError("Passwords do not match.");
return;
}
const submitButton = form.querySelector("button[type=submit]");
submitButton.disabled = true;
message.textContent = "";
try {
const response = await fetch("/api/auth/reset", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ token: resetToken, password: data.password }),
});
const body = await response.json();
if (!response.ok) {
showError(
{
invalid_token:
"This reset link is invalid or has expired. Request a new one.",
account_disabled:
"Your password was updated, but this account is disabled. Contact an administrator.",
}[body.code] || body.error || "Could not reset the password.",
);
return;
}
location.assign("/app");
} catch {
showError("Could not reach the server. Check your connection and try again.");
} finally {
submitButton.disabled = false;
}
});