import { h, mount, table, toast, confirmDialog, diffView, time, fmtTime, badge, plural } from "../ui.js"; import { api, get, useDraft } from "../api.js"; export async function render(el, params) { if (params.id) return detail(el, params.id); const list = h("div"); let rows = []; const load = async (before) => { const page = await get("/changesets", { status: "applied", limit: 50, before }); rows = rows.concat(page); mount(list, table([ { label: "#", render: (c) => h("a", { href: "#/history/" + c.id }, String(c.id)) }, { label: "Applied", render: (c) => time(c.applied_at) }, { label: "By", render: (c) => c.applied_by || c.username }, { label: "Via", render: (c) => badge(c.via) }, { label: "Description", render: (c) => h("a", { href: "#/history/" + c.id }, c.description || "(no description)") }, { label: "Operations", render: (c) => String((c.ops || []).length), tdClass: "right" }, ], rows, { empty: "Nothing has been applied yet." }), page.length === 50 ? h("div", { style: { marginTop: "8px" } }, h("button", { onclick: () => load(rows[rows.length - 1].id) }, "Load older")) : null); }; mount(el, h("div", { class: "page-head" }, h("div", { class: "grow" }, h("h1", null, "History"), h("div", { class: "muted" }, "Every applied changeset, with the exact changes made to BIND's files. Any of them can be reverted."))), h("div", { class: "panel" }, list)); await load(); } async function detail(el, id) { const c = await get("/changesets/" + id); const res = c.result || {}; mount(el, h("div", { class: "page-head" }, h("div", { class: "grow" }, h("h1", null, "Changeset #" + c.id), h("div", { class: "muted" }, c.description || "(no description)")), h("div", { class: "actions" }, h("a", { class: "button", href: "#/history" }, "All changes"), c.status === "applied" && c.undoable ? h("button", { onclick: () => revert(c) }, "Revert…") : null)), h("div", { class: "panel" }, h("dl", { class: "kv" }, h("dt", null, "Status"), h("dd", null, badge(c.status, c.status === "applied" ? "ok" : "")), h("dt", null, "Created"), h("dd", null, fmtTime(c.created_at) + " by " + c.username + " via " + c.via), c.applied_at ? [h("dt", null, "Applied"), h("dd", null, fmtTime(c.applied_at) + " by " + c.applied_by)] : null, (res.serials || []).length ? [h("dt", null, "Serials"), h("dd", { class: "mono" }, res.serials.map((s) => s.zone.replace(/\.$/, "") + (s.view ? " [" + s.view + "]" : "") + ": " + s.old + " → " + s.new).join(", "))] : null, (res.warnings || []).length ? [h("dt", null, "Warnings"), h("dd", null, res.warnings.join("; "))] : null)), h("div", { class: "panel" }, h("h2", null, plural((c.ops || []).length, "operation")), table([ { label: "#", render: (o) => String(o.seq) }, { label: "Change", render: (o) => o.summary || o.op, tdClass: "mono small" }, ], c.ops || [])), h("div", { class: "panel" }, h("h2", null, "File changes"), diffView(c.diff))); } async function revert(c) { if (!(await confirmDialog("Revert changeset #" + c.id, "This creates a new changeset that undoes these changes. You can review it before applying.", "Create revert", "primary"))) return; try { const rev = await api("POST", "/changesets/" + c.id + "/revert"); useDraft(rev); toast("Created changeset #" + rev.id + "; review and apply it"); location.hash = "#/changes"; } catch (e) { toast(e.message, "err", 8000); } }