fix(web): backup age in Templates card was frozen by hx-preserve
The backups <details> carried hx-preserve="true" to survive the card's 30s poll without collapsing if the operator had it open -- but hx-preserve freezes the whole node, so "newest X ago" inside it never updated either, same class of bug as the Started column before it got its own JS-side toggle state. Drop hx-preserve; track open/closed the same way (stable id, state re-applied after every swap) so the count/age text stays live.
This commit is contained in:
@@ -239,6 +239,33 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Templates card: each "N backup(s)" row is a <details> the operator may
|
||||||
|
// expand -- it must not collapse every 30s poll. Unlike the time-toggle
|
||||||
|
// buttons, its own content (backup count, "newest X ago") legitimately
|
||||||
|
// needs to keep updating, so this tracks only open/closed the same way
|
||||||
|
// (stable id, state re-applied after every swap) instead of freezing the
|
||||||
|
// whole node with hx-preserve, which used to freeze that text too.
|
||||||
|
var detailsState = {};
|
||||||
|
|
||||||
|
function wireDetails(root) {
|
||||||
|
var list = root.querySelectorAll
|
||||||
|
? root.querySelectorAll("details.backups-details[id]")
|
||||||
|
: [];
|
||||||
|
Array.prototype.forEach.call(list, function (el) {
|
||||||
|
var saved = detailsState[el.id];
|
||||||
|
if (saved !== undefined) {
|
||||||
|
el.open = saved;
|
||||||
|
}
|
||||||
|
if (el.dataset.wired === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el.dataset.wired = "true";
|
||||||
|
el.addEventListener("toggle", function () {
|
||||||
|
detailsState[el.id] = el.open;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.body.addEventListener("htmx:afterSwap", function (evt) {
|
document.body.addEventListener("htmx:afterSwap", function (evt) {
|
||||||
var target = evt.detail && evt.detail.target;
|
var target = evt.detail && evt.detail.target;
|
||||||
if (!target || !target.isConnected) {
|
if (!target || !target.isConnected) {
|
||||||
@@ -253,11 +280,13 @@
|
|||||||
}
|
}
|
||||||
if (target) {
|
if (target) {
|
||||||
restoreTimeToggles(target);
|
restoreTimeToggles(target);
|
||||||
|
wireDetails(target);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
observeStack();
|
observeStack();
|
||||||
processSlot(document.getElementById("action-slot"));
|
processSlot(document.getElementById("action-slot"));
|
||||||
|
wireDetails(document);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if bks %}
|
{% if bks %}
|
||||||
<details class="backups-details" id="backups-{{ tmpl.name }}" hx-preserve="true">
|
<details class="backups-details" id="backups-{{ tmpl.name }}">
|
||||||
<summary>{{ bks | length }} backup(s) <span class="text-dim">· newest {{ bks[0].mtime | relative_time }}</span></summary>
|
<summary>{{ bks | length }} backup(s) <span class="text-dim">· newest {{ bks[0].mtime | relative_time }}</span></summary>
|
||||||
<ul class="backups-list">
|
<ul class="backups-list">
|
||||||
{% for b in bks %}
|
{% for b in bks %}
|
||||||
|
|||||||
@@ -180,6 +180,38 @@ def test_templates_route_degrades_on_probe_failure(
|
|||||||
assert "unavailable" in resp.text.lower()
|
assert "unavailable" in resp.text.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_templates_backups_details_is_not_frozen_by_hx_preserve(
|
||||||
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
"""``hx-preserve`` freezes the *whole* node across the card's 30s poll --
|
||||||
|
including the "newest X ago" text inside it, which must keep updating.
|
||||||
|
Only the <details> open/closed state should persist (via app.js, the
|
||||||
|
same pattern already used for the Started column's click-to-toggle)."""
|
||||||
|
monkeypatch.setattr(app_module, "list_templates", lambda _config: [_TEMPLATE_ROW])
|
||||||
|
monkeypatch.setattr(
|
||||||
|
app_module,
|
||||||
|
"backups_by_template",
|
||||||
|
lambda _config: {
|
||||||
|
"LinuxBuild2404": [
|
||||||
|
FileEntry(
|
||||||
|
name="Template_LinuxBuild2404_20260101_000000.7z",
|
||||||
|
size_bytes=2048,
|
||||||
|
mtime=1_700_000_000.0,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
app = create_app(_make_config(tmp_path))
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
resp = client.get("/templates")
|
||||||
|
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert 'id="backups-LinuxBuild2404"' in resp.text
|
||||||
|
assert "newest" in resp.text
|
||||||
|
assert "hx-preserve" not in resp.text
|
||||||
|
|
||||||
|
|
||||||
def test_templates_route_shows_running_ram_snapshot_warning(
|
def test_templates_route_shows_running_ram_snapshot_warning(
|
||||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user