feat(dash): visual polish - verdict badges, mode header, empty state, dark theme (D5) - #136
Merged
Conversation
… dark theme (D5)
Formalizes the dashboard's dark-by-default look into CSS custom properties,
adds verdict/risk/enforcement color badges to the live feed and pending
approval cards, a header bar surfacing current mode + enforcement state
(from build_stats(), no stats.py changes needed), and CSS-only designed
empty states for both the feed and pending-approvals list (replacing the
old JS-driven display toggle). Pending cards now surface risk prominently,
mirroring the terminal's "[RISK: {risk.upper()}]" convention. No new
endpoints, no auth/token-flow changes, no redaction/decision-path changes;
every row-derived field is still rendered via .textContent only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Slice
What this PR does
Final polish pass on the
doberman dashStarlette dashboard before recording a README hero GIF:(enforce/monitor/off), driven by
build_stats()'s existingmode/enforcementfields(
stats.pyneeded no changes).the pending-approvals list (previously JS-toggled; now the same CSS-only pattern via
:not(:empty) ~sibling combinators, so a JS variable/toggle could be removed entirely).--bg,--surface,--border,--ink,--pass/--auth/--block/--neutralfamilies), with a@media (prefers-color-scheme: light)override. Still fully self-contained: inline CSS in theexisting HTML shell, system font stack, no external assets/CDNs/fonts.
RISK: {risk}badge), mirroring theterminal's existing
[RISK: {risk.upper()}]convention (src/doberman/auth/provider.py).No new HTTP endpoints, no new dependencies, no auth/token-flow changes, no redaction or
decision-path changes. All row-derived fields are still rendered via
.textContentonly (neverinnerHTML/markup interpolation).Tests added (run in CI)
tests/unit/test_dash_polish.py(new, 10 tests): verdict/risk/enforcement badge-class lookuptables are present and exact-substring matchable; the header bar renders mode + enforcement
from
/api/stats; the feed and pending list both carry a CSS-only designed empty state (and theold JS
pendingEmptytoggle is gone); the dark-theme custom-property palette is present with alight-mode media override; the pending-card risk badge mirrors the terminal's
"RISK: " + risk.toUpperCase()convention; the served shell still never leaks the bearer token;no
.innerHTMLassignment appears anywhere in the shell.test_dash_serve.py,test_dash_feed_stats.py,test_dash_approve_deny.py(D1–D3)re-run unmodified and confirmed still green — they assert on JSON field contracts and element
IDs/behavior, not on CSS class names, so the restyle doesn't touch their assertions.
pytest tests/unit/test_dash* -q).Public-release safety (doberman-core only)
Security checklist
.textContent-only)Edge cases covered / Deviations from plan / Risks introduced
designed (not blank) states driven purely by CSS sibling-combinator rules — no JS timing/race
possible since there's no JS toggle left to race.
early assertion checked for the literal string
"innerHTML"anywhere in the HTML, whichfalse-failed against a JS comment documenting the textContent-only discipline; fixed to assert
on the
.innerHTMLproperty-assignment form specifically.stats.pywas re-confirmed to need no changes — it alreadyexposed the
modeandenforcementfields this slice's header bar reads.