feat(dashboard): two-level nav, live ticker, and polish (PR-A)#151
Conversation
- Replace flat 8-tab bar with 3 primary tabs (Observe/Analyze/Ops) and contextual sub-navs; hash routing preserved unchanged - Add syncPrimaryNav()/switchGroup() to keep primary and sub-nav in sync on direct tab clicks and hash-driven navigation - Live pill now shows "Live · updated Xs ago" / "Disconnected · last seen Xs ago" via real setInterval ticker - Export buttons show "Preparing…" (honest; window.location.href gives no JS completion event) - Sessions/Workflows/Rejected metric cards are clickable deep-links - Actionable empty states (emit command hint, trace_id hint) - Type floor: .k and .panel-title raised from 10px to 11px - Drop "Demo" from page title and h1 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Revert role=tablist/role=tab (no paired tabpanels — incomplete ARIA) keeping flat aria-selected hint approach with a follow-up note - Wire pdot-ops in setBadge() and pdot-analyze in renderAnomalies() so primary-nav alert dots actually light up on rejections/anomalies - Remove redundant syncPrimaryNav() call from switchGroup() — switchMain already calls it; add comment explaining active-tab click reset behavior - Add role=status/aria-live=polite to live-label for screen reader support - Restore "Policy Analytics" and "Custom Analytics" sub-tab labels Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
surpradhan
left a comment
There was a problem hiding this comment.
R1 review response
Finding #1 — ARIA tablist without tabpanels (Major → Fixed)
Reverted role="tablist" and role="tab" from all elements. Restored the flat aria-selected hint approach that was on main, with a comment noting the full WAI-ARIA tablist pattern (roving tabindex, arrow keys, tabpanel wiring) remains a tracked follow-up.
Finding #2 — Alert dots unwired (Major → Fixed)
setBadge(total) now sets pdot-ops to alert-warn when total > 0. renderAnomalies(a) now sets pdot-analyze to alert-warn when a.anomaly_count > 0. Both clear the class when the count returns to zero.
Finding #3 — Redundant syncPrimaryNav in switchGroup (Minor → Fixed)
Removed the extra call. switchMain handles it. Added a comment explaining the intentional "clicking an active primary tab resets to group default" behavior.
Finding #4 — _tickerInterval never cleared (Minor → Comment added)
Added a comment in the interval guard noting the single-page context. A full teardown path is not in scope for this PR.
Finding #5 — live-label missing aria-live (Minor → Fixed)
Added role="status" aria-live="polite" aria-atomic="true" to #live-label.
Finding #6/#7 — "Policy" / "Custom" ambiguous labels (Nit → Fixed)
Restored "Policy Analytics" and "Custom Analytics" as sub-tab labels.
surpradhan
left a comment
There was a problem hiding this comment.
Round-2 code review: Approve. All R1 findings correctly addressed. Alert dots now wire up in setBadge/renderAnomalies (single source of truth for each count). switchGroup simplified — no redundant syncPrimaryNav call. ARIA role regression reverted. live-label gets role=status. Zero new issues introduced. Gates 239/239.
What does this PR do?
Redesigns
src/public/dashboard.htmlacross two axes: navigation structure and interaction polish. No backend changes, no new CI job.Changes
Nav restructure — flat 8-tab bar → 3 primary tabs (Observe / Analyze / Ops) with contextual sub-navs revealed per group.
switchGroup()handles primary-tab clicks;syncPrimaryNav()keeps both levels in sync. Hash routing (#sessions,#analytics, etc.) preserved unchanged — existing deep-links continue to work.Live pill — replaced static "Live" / "Reconnecting…" with a real
setIntervalticker:Live · updated Xs ago/Disconnected · last seen Xs ago. Timestamp resets on everyevent.receivedSSE tick.Honest export — export buttons show
Preparing…(disabled) for 1.8s then revert. Previously no feedback was given even thoughwindow.location.hrefhas no JS completion event.Clickable metric cards — Sessions, Rejected, Workflows cards act as deep-links to their respective views. Events Received and Accepted have no corresponding view and remain non-clickable.
Actionable empty states — sessions empty state now shows the emit command; workflows empty state explains
trace_idgrouping.Type floor —
.k(metric card label) and.panel-titleraised from 10px → 11px.Title — "AEP Demo Dashboard" → "AEP Dashboard".
Why?
Part of the UX redesign initiative (PR-A of 4). Reduces cognitive load from a flat 10-item tab bar and fixes several small interaction honesty issues.
How to test?
#analytics,#anomalies,#rejecteddirectly — correct tab highlightedChecklist
src/public/dashboard.html)