Implement admin back-pass flow and guarded admin actions

This commit is contained in:
2026-02-08 14:20:38 +01:00
parent 4ee327fb4e
commit 5595bfd3b1
25 changed files with 572 additions and 109 deletions

View File

@@ -80,6 +80,84 @@ export function openConfirmModal({
document.body.appendChild(overlay);
}
export function openPasswordConfirmModal({
title,
body,
confirmLabel,
cancelLabel = t("modal.cancel"),
onConfirm,
}) {
const overlay = document.createElement("div");
overlay.className = "edit-modal";
const panel = document.createElement("div");
panel.className = "edit-panel";
panel.innerHTML = `
<div class="edit-header">
<h3>${title}</h3>
<button class="lightbox-close" aria-label="${t("modal.close")}">x</button>
</div>
<div class="edit-body">
<p>${body}</p>
</div>
`;
const close = () => overlay.remove();
const bodyWrap = panel.querySelector(".edit-body");
const fieldWrap = document.createElement("label");
fieldWrap.className = "stack";
fieldWrap.innerHTML = `
<span class="label">${t("admin.passwordLabel")}</span>
<input type="password" autocomplete="current-password" />
`;
bodyWrap?.appendChild(fieldWrap);
const passwordInput = fieldWrap.querySelector("input");
const actions = document.createElement("div");
actions.className = "stack horizontal";
const confirmBtn = document.createElement("button");
confirmBtn.className = "danger";
confirmBtn.textContent = confirmLabel ?? t("modal.confirm");
actions.append(confirmBtn);
if (cancelLabel !== null && cancelLabel !== undefined) {
const cancelBtn = document.createElement("button");
cancelBtn.className = "ghost";
cancelBtn.type = "button";
cancelBtn.textContent = cancelLabel;
actions.append(cancelBtn);
cancelBtn.addEventListener("click", close);
}
bodyWrap?.appendChild(actions);
overlay.addEventListener("click", (e) => {
if (
e.target.classList.contains("edit-modal") ||
e.target.classList.contains("lightbox-close")
) {
close();
}
});
confirmBtn.addEventListener("click", async () => {
const password = passwordInput?.value ?? "";
if (!password.trim()) {
toast(t("admin.passwordRequired"), true);
passwordInput?.focus();
return;
}
try {
await onConfirm?.(password, close);
} catch (err) {
toast(err.message, true);
}
});
overlay.appendChild(panel);
document.body.appendChild(overlay);
passwordInput?.focus();
}
export function openResultsRelockModal() {
openConfirmModal({
title: t("results.relockedTitle"),