Apply pending changes from the banner

An Apply now button on the pending-changes banner applies the draft and
stays on the page; if the changes are refused, a dialog says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-10 12:56:10 +00:00
1 parent d4756444ac
commit 02c393a6bb
3 files changed
+28 -3

No files matched your search

+24 -3
View File
@@ -1,7 +1,7 @@
// Jarl web UI: router, layout and login.
import { h, mount, toast, errorBox, plural, lastList } from "./ui.js";
import { h, mount, toast, errorBox, plural, lastList, modal } from "./ui.js";
import { wordmark, logo } from "./brand.js";
import { api, get, state, loadDraft, onChange, setUnauthorizedHandler, setImmediate, setCodePrompt } from "./api.js";
import { api, get, state, loadDraft, onChange, setUnauthorizedHandler, setImmediate, setCodePrompt, useDraft } from "./api.js";
import * as totp from "./totp.js";
import * as dashboard from "./pages/dashboard.js";
import * as zones from "./pages/zones.js";
@@ -207,7 +207,28 @@ function renderBar() {
mount(barEl, n > 0 ? h("div", { class: "pending-bar" },
h("strong", null, plural(n, "pending change")),
h("span", { class: "muted" }, "Your edits are collected in changeset #" + d.id + " and are not live yet."),
h("a", { class: "button primary small", href: "#/changes" }, "Review & apply")) : null);
h("a", { class: "button small", href: "#/changes" }, "Review"),
h("button", { class: "primary small apply-now", title: "Apply the pending changes now, without reviewing them first", onclick: (ev) => applyDraft(ev.target, d) }, "Apply now")) : null);
}
// applyDraft applies the draft changeset from the banner and stays on the
// page. If it is refused, a dialog says why and links to Changes.
async function applyDraft(btn, d) {
btn.disabled = true;
btn.textContent = "Applying…";
try {
const r = await api("POST", "/changesets/" + d.id + "/apply", { body: {} });
const res = r.result || {};
toast("Applied changeset #" + d.id + (res.reloaded && res.reloaded.length ? " — named reloaded" : ""));
(r.warnings || []).forEach((w) => toast(w, "warn", 10000));
useDraft(null);
rerender();
} catch (e) {
btn.disabled = false;
btn.textContent = "Apply now";
const review = await modal("The changes were not applied", errorBox(e), [{ label: "Close", value: false }, { label: "Review changes", kind: "primary", value: true }], { wide: true });
if (review) location.hash = "#/changes";
}
}
async function logout() {