ui: amber pending-edit markers for unapplied config changes
Add hoover/dirty.js: line-matching helpers that flag UI rows/cards edited (saved to config) but not yet applied, consuming the pending state the daemon already streams — status.pending_diff for hash subsystems, firewall pending zone+type for firewalld. Visual language is amber (.config-dirty + PendingDot), distinct from the red .pending-delete; orphanInfo surfaces removed entries (e.g. WireGuard peers) on their container table. Wired into the backends, dhcp, interfaces, nat, proxy, rules, wireguard, and zones pages; Card and Table gain cls/title props. Covered by 27 node tests (tests/test-dirty.js).
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { h, html, PageHeader, Badge, Empty, Table, renderGuard, renderGuardMulti, ConfirmDelete, esc, enc, $val, apiFetch, toast, definePage, getModel, modelFetch, ActionButton, ActionGroup } from '/static/hoover/index.js';
|
||||
import { h, html, PageHeader, Badge, Empty, Table, renderGuard, renderGuardMulti, ConfirmDelete, esc, enc, $val, apiFetch, toast, definePage, getModel, modelFetch, ActionButton, ActionGroup, PendingDot, dirtySet, dirtyInfo } from '/static/hoover/index.js';
|
||||
import { openModal, closeModal, isModalProcessing, setModalProcessing, refreshModals } from '/static/hoover/components/modal.js';
|
||||
import { _deleting } from '/static/hoover/components/data.js';
|
||||
|
||||
@@ -260,18 +260,22 @@ export default definePage({
|
||||
return {
|
||||
backends: getModel('backends'),
|
||||
dnsmasq: getModel('dnsmasq'),
|
||||
nginx: getModel('nginx'),
|
||||
};
|
||||
},
|
||||
render(state) {
|
||||
const guard = renderGuardMulti('Backends', 'Reusable proxy backend templates', state.backends);
|
||||
if (guard) return guard;
|
||||
|
||||
const set = dirtySet(state.nginx.data?.status);
|
||||
const backends = state.backends.data || {};
|
||||
const entries = Object.entries(backends);
|
||||
|
||||
const rows = entries.map(([name, b]) =>
|
||||
html`<tr key=${name} class=${_deleting.has(name) ? 'pending-delete' : ''}>
|
||||
<td><strong>${esc(name)}</strong></td>
|
||||
const rows = entries.map(([name, b]) => {
|
||||
const info = dirtyInfo(set, 'backends.' + name);
|
||||
const cls = (_deleting.has(name) ? 'pending-delete' : '') + (info.class ? ' ' + info.class : '');
|
||||
return html`<tr key=${name} class=${cls || undefined} title=${info.title || undefined}>
|
||||
<td>${info.dirty ? PendingDot({}) : ''}<strong>${esc(name)}</strong></td>
|
||||
<td>${esc(b.label || name)}</td>
|
||||
<td>${Object.keys(b.paths || {}).length}</td>
|
||||
<td>
|
||||
@@ -291,11 +295,11 @@ export default definePage({
|
||||
message=${'Remove backend ' + enc(name) + '?'}
|
||||
success="Backend removed"
|
||||
onComplete=${() => modelFetch('backends')}
|
||||
label="Delete" />`
|
||||
label="Delete" />`}
|
||||
}
|
||||
</td>
|
||||
</tr>`
|
||||
);
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
const actions = ActionGroup(
|
||||
h('button', { class: 'btn btn-primary', 'on:click': () => openBackendModal(state) }, 'Add Backend'),
|
||||
|
||||
Reference in New Issue
Block a user