Skip to content

dashboard: add Status column to devices table#474

Open
arnauorriols wants to merge 3 commits into
mainfrom
polecat/dust-mpcpjl2q
Open

dashboard: add Status column to devices table#474
arnauorriols wants to merge 3 commits into
mainfrom
polecat/dust-mpcpjl2q

Conversation

@arnauorriols
Copy link
Copy Markdown
Member

@arnauorriols arnauorriols commented May 19, 2026

Adds a per-row Status column to the existing v1 Devices table
(dashboard/src/components/AgentsTable.tsx).

Status column

  • State A — at least one declared credential needs setup:
    renders "{N} credentials not connected. Click to configure",
    linked to #/settings so the operator can connect in one click.
  • State B — every credential connected: green dot + the
    device's most recent action (method · endpoint/host · path · age).

State B is live: subscribes to the existing /api/events SSE
stream (same feed LiveRequests uses), batches updates via
requestAnimationFrame, and keys the latest event per agent_ip
so a row only updates when its own device produces a new action.

Notes

  • "Needs setup" is computed by the existing needsAction helper
    joining Agent.integrations against Integration.connected
    no new backend queries.
  • The previous tiny green/red status dot in the Device column is
    removed; the new Status column carries the same signal in a
    more actionable form.
  • No new backend endpoints.

Test plan

  • Open the dashboard, confirm the devices table shows the new
    Status column.
  • With a device whose profile binds at least one unconnected
    credential, confirm the Status cell renders the count + link
    and clicking it lands on #/settings.
  • With a fully-connected device, confirm the green dot renders
    and the last-action text updates as the device makes new
    requests.
  • cd dashboard && deno task format:check && deno task lint && deno task build are clean.

@arnauorriols
Copy link
Copy Markdown
Member Author

You need to do this to v1 dashboard! The devices table already exists in v1, you just have to add the new column. Rebase from main

State A — at least one declared credential needs setup: red link
to Settings ("{N} credentials not connected. Click to configure").
State B — every credential connected: green dot + the device's
most recent action (method · endpoint/host · path · age), live
from the existing /api/events SSE feed.

Folds the existing per-row status dot's signal into the new
column so the row carries the same info in a single, actionable
place (cl-zf15).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arnauorriols arnauorriols force-pushed the polecat/dust-mpcpjl2q branch from 25596fb to 166538d Compare May 19, 2026 23:26
@arnauorriols arnauorriols changed the base branch from polecat/v2-dashboard to main May 19, 2026 23:26
@arnauorriols arnauorriols changed the title v2 overview: devices table with Status column dashboard: add Status column to devices table May 19, 2026
@arnauorriols
Copy link
Copy Markdown
Member Author

[bot-reply]
Pushed 166538d addressing this comment:

  • Reset the PR onto main (rebased; force-push) and re-targeted the base from polecat/v2-dashboardmain.
  • Added the Status column to the existing v1 AgentsTable (dashboard/src/components/AgentsTable.tsx) instead of building a parallel v2 table:
    • State A — needs setup → red link "{N} credentials not connected. Click to configure" → #/settings.
    • State B — all connected → green dot + last action (method · endpoint/host · path · age), live via the existing /api/events SSE feed.
  • The previous tiny status dot in the Device column is folded into the new column (same signal, single place).

deno task format:check && deno task lint && deno task build are clean.

@arnauorriols
Copy link
Copy Markdown
Member Author

Start a local gateway, configure and seed it with realistic data, and take some screenshots. I want to see several devices in the devices table. I want to see some devices in state A, and some devices in state B (with the last action). You can fake all the data by writing directly to the sqlite

@arnauorriols
Copy link
Copy Markdown
Member Author

[bot-reply]
Started a local gateway (-tags dev build) seeded with the existing dev_seed.go corpus — 24 synthetic devices spread across the 5 profiles (default, ops-team, data-team, support, ci-bot), with credential_secrets present for the three bearer credentials and absent for the OAuth credentials. That gives the dashboard a real mix of state A (needs setup) and state B (connected · last action).

Devices table — Status column

The table renders both states inline, in source order:

  • State A (red link): rows where the device's profile references at least one unconnected credential. The cell reads e.g. 3 credentials not connected. Click to configure and links to #/settings.
  • State B (green dot + last action): rows where every declared credential is connected. The trailing text is the live method · endpoint/host · path · age pulled from the /api/events SSE feed.

Devices table — Status column

Full dashboard view

For context, the full page with HITL pending + Live Requests panels below the table:

Full devices page

Verified breakdown from /api/state:

  • default and support profile devices (bind only bearer credentials that have secrets) → state B.
  • ops-team, data-team, ci-bot profile devices (bind 1–3 OAuth credentials with no completed flow) → state A, with the correct unconnected-count in the link text.

Screenshots are committed to the screenshots-pr474 orphan branch so the PR diff stays clean.

@arnauorriols
Copy link
Copy Markdown
Member Author

  1. do not show action latency. Instead, show a logo with the state of the action. The statuses can be "parsing" (when the endpoint is parsing the request), "awaiting verdict" (when the rule is waiting for a human or llm approval), request forwarded (when the request has been forwarded upstream but no response has yet been received), response forwarded (when the response from upstream has been received and forwarded downstream), denied (when the rule verdict is deny). No text, just the logo. Chose the logo you think better fits each state.
  2. instead of the green dot, show the logo of the endpoint that processed the request.
  3. truncate the action summary (with ...) to prevent it from overflowing.
  4. make the column wider enough so that the "click to configure" phrase does not wrap

Update the screenshots after all these changes. Make sure in the screenshots there's at least one example of each action state.

Addresses PR #474 review:

- Latency text replaced with a state-of-action glyph: parsing
  (in-flight start), awaiting verdict (hitl_async_pending), request
  forwarded (terminal, no upstream status), response forwarded
  (terminal with status code), denied (rule/approver deny verdict).
- Green dot replaced with the brand logo of the credential bound to
  the endpoint that processed the request (falls back to a neutral
  globe glyph for unbound endpoints).
- Action body (endpoint/host + path) now truncates with an ellipsis
  inside a flex container so long URLs no longer push the row wide.
- Status column pinned to 380px (table minWidth bumped to 940) and
  the "Click to configure" link marked whitespace-nowrap, so the
  call-to-action stays on one line at every breakpoint.

State A "needs setup" path is unchanged. The SSE subscriber now also
surfaces phase="start" events so the row can flip live from parsing
to its terminal state without waiting on a phase="end" gap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arnauorriols
Copy link
Copy Markdown
Member Author

[bot-reply]
Pushed a53237a addressing this comment:

  • 1. Action latency → state logo. Removed the trailing age text; added a per-row state glyph derived from the SSE event: magnifier (parsing, phase=start), clock (awaiting verdict, hitl_async_pending), right-arrow (request forwarded, terminal with no upstream status), check (response forwarded, terminal with status code), X (denied, deny verdict). Tones picked to match the deny/butter/success/muted palette already in use.
  • 2. Green dot → endpoint logo. The dot is replaced with the brand icon of the credential bound to the endpoint that processed the request (Claude / GitHub / OpenAI / Postgres / …). Endpoints not bound to a credential fall back to a neutral globe glyph instead of empty space.
  • 3. Truncate action summary. The endpoint/host + path span now sits inside a flex row with min-w-0 truncate, so long URLs ellipsis-clip instead of pushing the row wide.
  • 4. Wider Status column. Pinned the column to 380 px (table minWidth bumped to 940) and added whitespace-nowrap to the "Click to configure" link, so the call-to-action stays on a single line at every breakpoint.

Behind the scenes, useLastActionByIp now also surfaces phase="start" events so a row can flip live from parsing → its terminal state without waiting on the next end event. deno task format:check && deno task lint && deno task build are clean.

Devices table — new Status column

Showcase rows pinned to each of the five action states (pinned via a dev-only seed override; reverted from the PR commit):

Devices table — Status column with state icons + endpoint logos

Top of the table (sorted by IP) shows macbook-sam (response forwarded → ✓ + OpenAI logo) and ubuntu-dev-1 (denied → ⊗ + GitHub logo). The remaining showcase devices are at the bottom of the visible range: macbook-jane (parsing → magnifier + Slack), macbook-alex (awaiting verdict → clock + GitHub), macbook-priya (request forwarded → → + Claude). All state-A rows now display the full "Click to configure" call-to-action without wrapping or clipping.

Full devices page

Full devices page with HITL pending + Live Requests panels

@arnauorriols
Copy link
Copy Markdown
Member Author

  1. for the parsing state, use an icon with code brackets and a magnifier on top (or similar; only the magnifier is not representative enough of what is being inspected)
  2. Careful with the ip column, it shows clipped in the screenshots now

- ParsingGlyph: code-bracket `</>` with a magnifier laid on top so the
  state reads as "request body being inspected" rather than a bare
  search icon.
- IP column: 140 → 200 px (table minWidth 940 → 1000) so longer IPv6
  external addresses stop ellipsis-clipping inside the cell.
@arnauorriols
Copy link
Copy Markdown
Member Author

[bot-reply]
Pushed eef7f2a addressing this comment:

  1. Parsing icon — code brackets + magnifier. ParsingGlyph now layers a </>-style angle-bracket pair underneath a magnifier (lens centred over the gap, handle off to the lower-right), so the state reads as "request body being inspected" rather than a bare search glyph. The other four state glyphs (clock / arrow / check / X) are unchanged.
  2. IP column no longer clips. The <col> for the IP cell goes from 140 → 200 px (table minWidth 940 → 1000), which fits the longest seeded external IPv6 (2001:db8:xxxx::yyyy) inside the cell without ellipsis. The column already had truncate + a full-IP title tooltip as the fallback, both kept.

deno task format:check && deno task lint && deno task build are clean.

Holding off on regenerating the screenshots in this round so the polecat finishes on the requested change — happy to refresh them in a follow-up if you'd like to see the new glyph + un-clipped IP column inline.

@arnauorriols
Copy link
Copy Markdown
Member Author

Refresh the screenshots

@arnauorriols
Copy link
Copy Markdown
Member Author

[bot-reply]
Refreshed the PR #474 screenshots on the screenshots-pr474 orphan branch (e46f436). Both shots now reflect the latest commit (eef7f2a):

Devices table — new parsing glyph + wider IP column

Devices table — Status column with state icons + endpoint logos

Showcase rows (pinned via a dev-only seed override, reverted from the PR commit) cover all five action states without overlap:

  • macbook-janeparsing (the new code-brackets + magnifier glyph) → PUT to aws/release-bucket/v0.42/...
  • ubuntu-dev-1denied (X) → DELETE api.github.com/repos/denoland/clawpatrol/branches/main
  • debian-stagingresponse forwarded (check) → GET aws/internal-artifacts/build-1742.tar.gz
  • ci-runner-2awaiting verdict (clock) → POST snyk/v1/org/acme/projects/release
  • ci-runner-3request forwarded (arrow) → POST grafana/api/datasources/proxy/uid/loki/...

IP column now renders the full 203.0.113.x external IPv4 on every row instead of clipping to 203.0.113.. The "Click to configure" call-to-action also stays on a single line for every State A row.

Full devices page

Full devices page

@arnauorriols
Copy link
Copy Markdown
Member Author

arnauorriols commented May 20, 2026

  1. The ip column now overflows the box. You can probably reduce the width of the profile column to make room
  2. the code-brackets with magnifier icon is too small and it's not understandable.

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