webui: surface confd inference in the advanced tree editor

confd infers Infix-suggested values (DHCP option lists, related leaves,
normalised values) into the candidate when a change is written, but the
tree editor's write handlers only wrote — SaveLeaf echoed the typed value
back, SaveGroup returned 204 — so the inferred values never appeared
until a manual page reload.

After a save (cfgSaved) re-fetch the current tree page from the now
up-to-date candidate and re-render it, so inference surfaces wherever it
landed on the page.  SaveLeaf now emits cfgSaved + 204 like SaveGroup
(its form uses hx-swap="none") instead of echoing the raw input.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2026-06-15 19:45:26 +02:00
parent b5b310f3c8
commit cd19b626a7
3 changed files with 28 additions and 10 deletions
+8 -8
View File
@@ -1227,17 +1227,17 @@ func (h *TreeHandler) SaveLeaf(w http.ResponseWriter, r *http.Request) {
}
body := map[string]any{qualName: coerceLeafValue(rawValue, node)}
data := &nodeDetailData{Node: node}
// Emit cfgSaved/cfgError like SaveGroup and let the client re-fetch the
// whole current page from the fresh candidate (so confd inference and
// normalisation surface) — don't echo the raw input back, which hid it.
if putErr := h.RC.Put(r.Context(), candidateDS+path, body); putErr != nil {
data.Error = putErr.Error()
data.CurrentValue = rawValue
} else {
data.SavedOK = true
data.CurrentValue = rawValue
w.Header().Set("HX-Trigger", `{"cfgError":"`+node.Name+": "+putErr.Error()+`"}`)
w.WriteHeader(http.StatusUnprocessableEntity)
return
}
h.FragTmpl.ExecuteTemplate(w, "yang-node-detail", data)
w.Header().Set("HX-Trigger", `{"cfgSaved":"Saved `+node.Name+` to candidate"}`)
w.WriteHeader(http.StatusNoContent)
}
// DeleteLeaf serves DELETE /configure/tree/node?path=...
+16
View File
@@ -2121,6 +2121,22 @@ function renderCfgLog() {
span.classList.remove('saved');
}, 3000);
}
// Re-render the whole current tree page from the (now fresh) candidate.
// The save handlers only WRITE — they don't echo back what confd
// inferred from the change (DHCP option lists, related leaves,
// normalised values), and inference can land outside the edited node.
// Re-fetching the page the user is on surfaces all of it. Guarded on
// #yang-detail so curated pages that also emit cfgSaved are unaffected.
var detail = document.getElementById('yang-detail');
if (detail && window.htmx) {
var node = (history.state && history.state.yangDetailPath) ||
new URLSearchParams(window.location.search).get('node');
if (node) {
htmx.ajax('GET', '/configure/tree/node?path=' + encodeURIComponent(node),
{ target: '#yang-detail', swap: 'innerHTML' });
}
}
});
})();
@@ -21,10 +21,12 @@
{{/* ── Edit form — only for writable leaves in non-ReadOnly mode ── */}}
{{if and (not .ReadOnly) .Config (or (eq .Kind "leaf") (eq .Kind "leaf-list"))}}
{{/* hx-swap="none": SaveLeaf returns 204 + a cfgSaved trigger, and the
cfgSaved handler re-fetches the whole current tree page from the
fresh candidate so confd-inferred values surface. */}}
<form class="yt-edit-form"
hx-put="/configure/tree/node?path={{.Path | urlquery}}"
hx-target="#yang-detail"
hx-swap="innerHTML">
hx-swap="none">
<div class="cfg-card-footer">
<label class="yt-value-label">
{{if .UsingDefault -}}