Skip to content

feat(dashboard): apply G2 Barrier Membrane redesign to all routes - #75

Merged
dannyna merged 9 commits into
mainfrom
feat/dashboard-g2-redesign
Sep 4, 2026
Merged

feat(dashboard): apply G2 Barrier Membrane redesign to all routes#75
dannyna merged 9 commits into
mainfrom
feat/dashboard-g2-redesign

Conversation

@dannyna

@dannyna dannyna commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Applies the G2 "Barrier Membrane" visual redesign to all four existing dashboard hash routes (Overview #/, Audit #/audit, Policies #/policies, Approvals #/approvals), on top of the existing components, api.ts, usePolling, and hash routing. No new routes, no new dependencies, and the demo shim is untouched.

Changes

  • Tokens/shell: oklch tokens in index.css (dark primary theme, light secondary theme with reduced shadow depth), Tailwind @theme bridge, double-bezel Panel, mono verdict pills with glyphs (✓ ALLOWED, ✓ APPROVED, ‖ PAUSED, ✕ DENIED, ✕ REJECTED), cyan primary Approve / red-outline Deny, global :focus-visible, reduced-motion-aware reveal animation.
  • Shell chrome: lowercase mono honmoon wordmark, right-side command capsule with nav, live pending badge, gateway reachability derived from the approvals poll.
  • Overview: status hero and KPIs derived only from live API data; API errors stay explicit (hero reads "Gateway status is unknown", tiles show dashes instead of guessing); pending-approval strip shares the per-id busy actions with the Approvals route.
  • Audit: same Time/Decision/Request/Rule columns; loading, error, empty, and populated states; truncated requests keep the full value in title.
  • Policies: Prism + react-simple-code-editor kept (including the CJS unwrap workaround), gutter line numbers added, G2 Prism token colours replace prism-tomorrow; edits remain local only, "Reset to active" only appears when dirty, Phase 5 note kept, no Save/Deploy/Validate.
  • Approvals: new useApprovalActions hook (Set-based per-id busy tracking) shared with Overview; approve/reject + refresh, empty/error states preserved.
  • Shared pieces: components/ui.tsx (Panel, PageHead, SectionHead, ErrorNote, PanelState), components/ApprovalActions.tsx. AGENTS.md structure table updated. Nav label "Audit Log" → "Audit".

Test Plan

  • bun run --filter '@honmoon/dashboard' typecheck
  • bun run --filter '@honmoon/dashboard' build
  • bun run --filter '@honmoon/dashboard' build:demo
  • bunx eslint apps/dashboard/src
  • Manual verification in a headless browser at 1440x940 (dark and light) plus 1024 and 760 widths: populated, loading, empty, and API-error states on every route
  • Approval busy state locks only its own row; approving updates the pending badge and inserts ✓ APPROVED into Audit
  • Policy dirty/reset behavior; keyboard focus rings

Notes for Reviewers

  • Light-mode --*-ink text variants were added because the prototype's 81%-lightness cyan lacked contrast on white.
  • Hero copy avoids guarantees ("Nothing is waiting on you" instead of "flowing safely").
  • The prototype's decorative grid background was dropped per DESIGN.md's single-flourish rule.
  • Geist Mono is declared in the font stack only (no webfont request).
  • Follow-ups: locale-dependent formatTime width, no dashboard component tests yet.

Related Issues

No linked issue for this work.


Summary by cubic

Applies the G2 Barrier Membrane redesign to Overview, Audit, Policies, and Approvals on top of the existing API client, polling, hash routing, and demo flow.

Shared shell

  • Adds oklch design tokens, Tailwind mappings, light/dark variants, double-bezel panels, verdict pills, focus rings, and reduced-motion support.
  • Moves navigation into a right-side command capsule with a live pending badge; gateway reachability now requires both the approvals and audit polls to answer. Below md the capsule wraps to its own full-width row and scrolls horizontally as a fallback.
  • Shares per-approval busy tracking and actions between Overview and Approvals, with per-id error notes that never outlive their row. On phones the action pair wraps to its own full-width row, and light-mode focus rings use a darkened accent to stay visible on white.
  • Renames “Audit Log” to “Audit” and improves dark-mode metadata contrast.
  • Aligns page bodies with the header’s content edge at wide viewports and matches every route’s first-panel offset below the header.
  • usePolling commits a run newer than the last committed response, so a slow API shows stale-but-real data instead of loading forever.

Route behavior

  • Overview derives its hero, KPIs, decision mix, and approval strip from live API data without presenting unavailable data as healthy zeros; the hero waits for the approvals poll and audit panels key off the audit poll alone.
  • Audit keeps the existing columns and adds loading, error, empty, and populated states with full request values on hover.
  • Policies keeps Prism and react-simple-code-editor, adds G2 token colors and line numbers, labels the YAML editor accessibly, and limits edits to local state.
  • Approvals adds queue metrics, oldest-held information, explicit unavailable states, and shared approve/reject actions; busy buttons expose “Resolving…” to assistive tech and action errors render per row.
  • Policy posture uses the configured default verdict labels, including the correct “paused” wording, and describes the egress fallback as applying when neither the deny nor allow list matches.
  • DecisionBadge renders unknown verdicts as “?” instead of failing.
  • Typechecking, builds, linting, demo packaging, and responsive dark/light state checks pass; bun test covers useApprovalActions per-id busy, error, and refresh behavior plus usePolling commit ordering.

Written for commit 9545d0e. Summary will update on new commits.

Redesign Overview, Audit, Policies, and Approvals on top of the existing
components, API client, polling hooks, and routing; no new routes.

- index.css: oklch design tokens (dark primary, light secondary with
  reduced depth), Tailwind @theme bridge, double-bezel Panel, mono verdict
  pills with glyphs (checkmark ALLOWED/APPROVED, pause PAUSED, cross
  DENIED/REJECTED), cyan primary Approve + red-outline Deny actions,
  global :focus-visible, reduced-motion-aware reveal
- App shell: lowercase mono "honmoon" wordmark + cyan mark, right command
  capsule (nav, live pending badge, gateway reachability derived from the
  approvals poll, account area)
- Overview: status hero derived from live data, pending-approval strip
  with shared per-id busy actions, KPI tiles, latest 8 decisions,
  decision mix; API errors stay explicit and never look like a healthy
  empty state
- Audit: same Time/Decision/Request/Rule columns; loading/error/empty/
  populated states; request truncation keeps the full value in title
- Policies: keeps Prism + react-simple-code-editor (and the CJS unwrap
  workaround), adds gutter line numbers and G2 Prism token colours
  (prism-tomorrow theme dropped), local-only edits, "Reset to active"
  only when dirty, Phase 5 hot-reload note; no save/deploy actions
- Approvals: useApprovalActions hook (Set-based per-id busy) shared with
  Overview; approve/reject + refresh, empty/error states preserved
- Nav label "Audit Log" renamed to "Audit"

Verified: typecheck, build, build:demo, and eslint all pass; manual
dark/light browser verification of all states.
@codspeed-hq

codspeed-hq Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 50.18%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 16 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
parse_k8s_path[/api/v1/namespaces/prod/secrets/db-password] 8.7 µs 5.8 µs +50.18%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing feat/dashboard-g2-redesign (9545d0e) with main (3ea2879)

Open in CodSpeed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a major visual redesign of the dashboard using G2 'Barrier Membrane' tokens, OKLCH colors, and custom UI components, alongside refactoring the approval actions into a reusable hook and component. The review feedback highlights a layout-breaking bug in PolicyView due to an invalid padding object format passed to react-simple-code-editor. Additionally, the reviewer points out a significant DRY violation where the approvals endpoint is polled redundantly across multiple components, recommending that the polling state be lifted to the parent App component and shared via props.

Comment thread apps/dashboard/src/components/PolicyView.tsx Outdated
Comment thread apps/dashboard/src/components/Overview.tsx Outdated
Comment thread apps/dashboard/src/components/Approvals.tsx Outdated
Comment thread apps/dashboard/src/App.tsx Outdated
Comment thread apps/dashboard/src/App.tsx
dannyna and others added 4 commits September 3, 2026 21:42
- AGENTS.md: list `SectionHead` among the `ui` exports
- PolicyView: promote the Posture heading to `h2` so the Policy route no
  longer skips from h1 to h3 (WCAG 1.3.1)
- Record the review's rejected findings in the ledger

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
react-simple-code-editor spreads unknown props onto its wrapper div, so the
`aria-label` on `<Editor>` never reached the textarea. Bind a visually hidden
`<label>` to `textareaId` instead (WCAG 4.1.2 / 3.3.2) and record the
review-fix-loop iteration 2 rejections in the ledger.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Raise dark-mode `--dim` to oklch(60%) so metadata text clears WCAG 4.5:1
  against every backdrop it sits on (was 3.1:1 on `--bg`)
- Render the posture headline through a Verdict lookup so a `pause` default
  reads "paused", not "pauseed"
- Use `DEFAULT_EGRESS_VERDICT` from @honmoon/policy instead of a `'deny'` literal
- Correct the useApprovalActions docstring: refresh runs only after success
- Record the iteration 3 rejections in the review ledger

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Track approve/reject failures per approval id (`actionErrors` Map) and
  render each note on its own card / next to its own row, so one row's
  failure is never hidden by another row's action.
- Derive the header's gateway state from both live polls: approvals plus a
  one-row audit probe, so an audit-only outage no longer reads as "live".
- Overview hero waits for the approvals poll before claiming the queue is
  empty; audit-only panels key off the audit poll alone and receive only
  the audit error.
- Approve/Deny buttons expose "Resolving…" in their accessible name while
  busy, matching the visible label.
- DecisionBadge renders an unknown verdict as "?" instead of throwing.
- Fix the `--*-ink` token comment and the AGENTS.md theming note.
- Add `bun test` for the dashboard with a happy-dom + `act` harness and
  three `useApprovalActions` tests (per-id busy, per-id error, refresh
  only on success). CI does not run this suite yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Sep 4, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​types/​bun@​1.4.01001004890100
Addednpm/​@​happy-dom/​global-registrator@​20.14.01001008796100

View full report

dannyna and others added 2 commits September 4, 2026 10:04
Every route put `max-w-[1440px]` and `px-10` on the same element, so the
page body sat 40px inside the header's content edge at wide viewports.
Use the header's pattern instead — padding on the outer element, a
`max-w-[1440px]` wrapper inside — on Overview, Audit, Policies and
Approvals, and drop the now-redundant max-width from PageHead. Measured
header and body edges match on all four routes at 1536px.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w hero

Audit, Policies and Approvals sat 30px below the app header and 24px
above their first panel, while the Overview hero uses 32px and 50px.
Use `pt-8` on the three sections and `mb-[50px]` on PageHead so every
route lands its first panel at the same offset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dannyna
dannyna marked this pull request as ready for review September 4, 2026 01:22
@greptile-apps

greptile-apps Bot commented Sep 4, 2026

Copy link
Copy Markdown

Greptile Summary

Applies the Barrier Membrane redesign across all four dashboard routes while adding shared panels, approval controls, live status presentation, policy posture details, and hook tests.

  • Reworks the dashboard shell, responsive navigation, themes, status states, and verdict styling.
  • Shares per-approval action state between Overview and Approvals.
  • Adds policy gutter highlighting and active-policy posture.
  • Changes polling freshness handling to reject superseded results.

Confidence Score: 4/5

The slow-response polling failure should be fixed before merging because it can leave every live dashboard surface permanently loading despite completed API responses.

The new latest-run check combines with unconditional fixed-interval requests to discard every response whenever API latency remains above the polling interval, preventing data, errors, and settled state from reaching the UI.

Files Needing Attention: apps/dashboard/src/hooks.ts

Important Files Changed

Filename Overview
apps/dashboard/src/hooks.ts Adds shared approval actions and stale-run polling protection, but slow responses can be invalidated forever by subsequent interval ticks.
apps/dashboard/src/App.tsx Rebuilds the shell and derives gateway reachability from approvals and audit polling.
apps/dashboard/src/components/Overview.tsx Reworks live KPI, status, decision, and approval presentation while preserving explicit unknown/error states.
apps/dashboard/src/components/Approvals.tsx Redesigns the queue and adopts per-ID action state and error rendering.
apps/dashboard/src/components/PolicyView.tsx Adds line-numbered Prism highlighting and active-policy posture while keeping edits local.
apps/dashboard/src/index.css Introduces the Barrier Membrane token system, themes, shared component styling, focus treatment, and reduced-motion behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  API[Management API] --> Poll[usePolling]
  Poll --> Shell[Gateway status]
  Poll --> Overview[Overview]
  Poll --> Audit[Audit log]
  Poll --> Approvals[Approval queue]
  Actions[Approve or deny] --> API
Loading

Comments Outside Diff (1)

  1. apps/dashboard/src/hooks.ts, line 33-36 (link)

    P1 Polling can discard every response

    If management API latency consistently exceeds the 1.5- or 2-second polling interval, each interval increments latestRun before the preceding request settles. Every successful response and error is then rejected as stale, leaving the dashboard permanently on Loading/Connecting instead of displaying data or an API failure.

    Knowledge Base Used: Operations dashboard

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/dashboard/src/hooks.ts
    Line: 33-36
    
    Comment:
    **Polling can discard every response**
    
    If management API latency consistently exceeds the 1.5- or 2-second polling interval, each interval increments `latestRun` before the preceding request settles. Every successful response and error is then rejected as stale, leaving the dashboard permanently on Loading/Connecting instead of displaying data or an API failure.
    
    **Knowledge Base Used:** [Operations dashboard](https://app.greptile.com/passionfactory/-/custom-context/knowledge-base/pleaseai/honmoon/-/docs/operations-dashboard.md)
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

    Fix in Claude Code

Fix all with Greploop Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
apps/dashboard/src/hooks.ts:33-36
**Polling can discard every response**

If management API latency consistently exceeds the 1.5- or 2-second polling interval, each interval increments `latestRun` before the preceding request settles. Every successful response and error is then rejected as stale, leaving the dashboard permanently on Loading/Connecting instead of displaying data or an API failure.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(dashboard): match secondary pages' v..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c12d69e6ed

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/dashboard/src/App.tsx Outdated
Comment thread apps/dashboard/src/components/PolicyView.tsx Outdated
- usePolling: commit a run when it is newer than the last *committed* run
  rather than only when it is the latest *started* run. An API that is
  consistently slower than the poll interval now shows stale-but-real data
  instead of discarding every response and sitting on Loading forever.
  Add an ordering test covering the slow-first, newer-over-older and
  late-older-dropped cases.
- Header: below `md` the nav capsule wraps onto its own full-width row and
  scrolls horizontally as a last resort, so every route stays reachable at
  360-430px instead of overflowing the viewport.
- Policy posture: describe the egress fallback as applying when neither the
  deny nor the allow list matches; the engine checks deny before allow, so
  "not on the allow list" was wrong for policies with a deny list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@dannyna

dannyna commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Re Greptile's usePolling finding (slow responses discarded forever): fixed in 8e782dc. A run now commits when it is newer than the last committed run instead of only when it is the latest started run, so a consistently slow API degrades to stale-but-real data rather than an endless Loading state. Covered by a new ordering test in apps/dashboard/src/hooks.test.tsx.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/dashboard/src/components/Approvals.tsx
Comment thread apps/dashboard/src/index.css Outdated
…fix light-mode focus contrast

- ApprovalActions accepts placement classes; the approval card passes
  `max-md:col-span-full max-md:justify-self-end` so the Deny/Approve pair
  gets a full-width row below `md` instead of being auto-placed into the
  44px icon track.
- The shared `:focus-visible` ring uses `--accent-ink`, which equals
  `--accent` in dark mode and is the darkened variant in light mode
  (~6:1 against the near-white surfaces instead of ~1.6:1).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@dannyna
dannyna merged commit 778d87a into main Sep 4, 2026
12 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9545d0e6a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

type="button"
className="action action-deny"
disabled={busy}
onClick={onReject}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add interaction coverage for the new dashboard surfaces

The new dashboard behavior is only covered through direct hook tests in hooks.test.tsx; none of the added routes or components is rendered in a test. In particular, this user-facing Deny control has no interaction coverage proving it dispatches the rejection, reflects busy/error state, or remains correctly wired in its card and overview hosts. Add happy-dom component tests for the newly built surfaces rather than relying solely on hook tests.

AGENTS.md reference: apps/dashboard/AGENTS.md:L61-L63

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant