Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/doberman/dash/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@
#pending-list button.deny:hover { border-color: var(--block); background: var(--block-bg); }
#pending-list button.approve { background: var(--auth); border: 1px solid var(--auth); color: var(--ink-0); }
#pending-list button.approve:hover { background: var(--tan-hi); border-color: var(--tan-hi); }
.section-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-top: 1.4rem; }
#refresh-btn {
font-family: var(--font); font-size: .86rem; font-weight: 600; padding: .35rem 1rem;
border-radius: var(--r-sm); cursor: pointer;
background: transparent; border: 1px solid var(--rule); color: var(--fg);
transition: background-color var(--d), border-color var(--d);
}
#refresh-btn:hover { border-color: var(--tan-hi); color: var(--tan-hi); }
#feed {
max-height: 60vh; overflow-y: auto;
border: 1px solid var(--rule-2); border-radius: var(--r); background: var(--ink-1);
Expand Down Expand Up @@ -271,7 +279,10 @@
<h2>Pending approvals</h2>
<ul id="pending-list" aria-live="polite"></ul>
<div id="pending-empty" class="empty-state">Nothing pending. Doberman's watching.</div>
<h2>Recent decisions</h2>
<div class="section-head">
<h2>Recent decisions</h2>
<button type="button" id="refresh-btn">Refresh</button>
</div>
<ul id="feed"></ul>
<div id="feed-empty" class="empty-state">No decisions yet. Doberman's watching quietly.</div>
<script>
Expand Down Expand Up @@ -556,6 +567,13 @@
refreshPending();
setInterval(refreshPending, PENDING_POLL_MS);

// Manual refresh for the stats + pending views; both functions are safe
// to call at any time and no new endpoint is involved.
document.getElementById("refresh-btn").addEventListener("click", function () {
refreshStats();
refreshPending();
});

// EventSource cannot set request headers, so the token travels as a
// query param here only (see doberman.dash.app._feed_token_matches).
try {
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/test_dash_polish.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,11 @@ def test_feed_row_renders_a_risk_badge(tmp_path):
def test_feed_row_renders_source_context(tmp_path):
html = _index_html(tmp_path)
assert '" from:" + (row.source_context || "-") +' in html


def test_recent_decisions_header_has_a_refresh_button(tmp_path):
html = _index_html(tmp_path)
assert '<button type="button" id="refresh-btn">Refresh</button>' in html
assert 'document.getElementById("refresh-btn").addEventListener' in html
assert "refreshStats();" in html
assert "refreshPending();" in html
Loading