From 0a5b28c538335abe0640da501bda63f78dd9bc09 Mon Sep 17 00:00:00 2001 From: Matt Vollmer Date: Tue, 24 Mar 2026 13:15:24 -0400 Subject: [PATCH] fix: sidebar and analytics UI tweaks (#23499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit image ## Changes - Move filter dropdown from top nav bar to inline with the first time group header (e.g. "Today") - Remove analytics icon from desktop sidebar nav bar - Change "View details" to "View usage" in the usage indicator dropdown - Fix green progress bar visibility in dark mode (`bg-surface-green` → `bg-content-success`) - Fix missing space before date in "Resets" text --- PR generated with Coder Agents --- .../AgentsPage/AgentsPageView.stories.tsx | 23 ++-- .../AgentsPage/components/AgentPageHeader.tsx | 2 +- .../components/ChatCostSummaryView.tsx | 6 +- .../components/Sidebar/AgentsSidebar.tsx | 121 ++++++++++-------- .../AgentsPage/components/UsageIndicator.tsx | 2 +- 5 files changed, 84 insertions(+), 70 deletions(-) diff --git a/site/src/pages/AgentsPage/AgentsPageView.stories.tsx b/site/src/pages/AgentsPage/AgentsPageView.stories.tsx index 56019b5de1..2789a64565 100644 --- a/site/src/pages/AgentsPage/AgentsPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentsPageView.stories.tsx @@ -486,11 +486,6 @@ export const WithErrorReasons: Story = { }, }; -const openAnalyticsView = async (canvasElement: HTMLElement) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("link", { name: "Analytics" })); -}; - const openSettingsView = async (canvasElement: HTMLElement) => { const canvas = within(canvasElement); const link = await waitFor(() => @@ -503,9 +498,13 @@ export const OpensAnalyticsForAdmins: Story = { args: { isAgentsAdmin: true, }, - play: async ({ canvasElement }) => { - await openAnalyticsView(canvasElement); - + parameters: { + reactRouter: reactRouterParameters({ + location: { path: "/agents/analytics" }, + routing: agentsRouting, + }), + }, + play: async () => { await waitFor(() => { expect( screen.getByText( @@ -522,10 +521,12 @@ export const OpensAnalyticsForNonAdmins: Story = { }, parameters: { permissions: MockNoPermissions, + reactRouter: reactRouterParameters({ + location: { path: "/agents/analytics" }, + routing: agentsRouting, + }), }, - play: async ({ canvasElement }) => { - await openAnalyticsView(canvasElement); - + play: async () => { await waitFor(() => { expect( screen.getByText( diff --git a/site/src/pages/AgentsPage/components/AgentPageHeader.tsx b/site/src/pages/AgentsPage/components/AgentPageHeader.tsx index bc5917469d..0cf345cc8b 100644 --- a/site/src/pages/AgentsPage/components/AgentPageHeader.tsx +++ b/site/src/pages/AgentsPage/components/AgentPageHeader.tsx @@ -96,7 +96,7 @@ export const AgentPageHeader: FC = ({ - )} + )}{" "} {children &&
{children}
} ); diff --git a/site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx b/site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx index 5f4adfe0ae..2735481a61 100644 --- a/site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx +++ b/site/src/pages/AgentsPage/components/ChatCostSummaryView.tsx @@ -94,10 +94,10 @@ export const ChatCostSummaryView: FC = ({ : 0; const usageProgressBarClass = usageProgressPercentage > 90 - ? "bg-surface-red" + ? "bg-content-destructive" : usageProgressPercentage >= 75 - ? "bg-surface-orange" - : "bg-surface-green"; + ? "bg-content-warning" + : "bg-content-success"; const usageLimitExceeded = showUsageLimitCard && usageLimitCurrentSpend >= usageLimitSpendMicros; const usageLimitStatusText = usageLimitExceeded diff --git a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx index 067e5a55dd..dcee40d04e 100644 --- a/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx +++ b/site/src/pages/AgentsPage/components/Sidebar/AgentsSidebar.tsx @@ -628,6 +628,15 @@ export const AgentsSidebar: FC = (props) => { visibleChatIDs.has(chatID), ); + // Pre-compute the first non-empty time group so the filter + // dropdown renders next to it without needing a mutable IIFE. + const firstNonEmptyGroup = TIME_GROUPS.find((group) => + visibleRootIDs.some((id) => { + const chat = chatById.get(id); + return chat !== undefined && getTimeGroup(chat.updated_at) === group; + }), + ); + // Auto-expand ancestors of the active chat so it's always visible. // Only runs when activeChatId changes — not on every parentById // recalculation — so user-initiated collapse is preserved. @@ -719,53 +728,6 @@ export const AgentsSidebar: FC = (props) => { - {" "} - - - - - - onArchivedFilterChange?.("active")} - > - Active - {archivedFilter === "active" && ( - - )} - - onArchivedFilterChange?.("archived")} - > - Archived - {archivedFilter === "archived" && ( - - )} - - - {onCollapse && ( + )} ) : (
@@ -851,8 +824,48 @@ export const AgentsSidebar: FC = (props) => { key={group} className="[&:not(:first-child)]:mt-3" > -
+
{group} + {group === firstNonEmptyGroup && ( + + + + + + + onArchivedFilterChange?.("active") + } + > + Active + {archivedFilter === "active" && ( + + )} + + + onArchivedFilterChange?.("archived") + } + > + Archived + {archivedFilter === "archived" && ( + + )} + + + + )}
{groupChats.map((chat) => ( @@ -865,7 +878,7 @@ export const AgentsSidebar: FC = (props) => {
); - })} + })}{" "}
)} diff --git a/site/src/pages/AgentsPage/components/UsageIndicator.tsx b/site/src/pages/AgentsPage/components/UsageIndicator.tsx index 2598ff1087..4d0084babd 100644 --- a/site/src/pages/AgentsPage/components/UsageIndicator.tsx +++ b/site/src/pages/AgentsPage/components/UsageIndicator.tsx @@ -102,7 +102,7 @@ export const UsageIndicator: FC = () => { - View details + View usage