fix: sidebar and analytics UI tweaks (#23499)

<img width="684" height="540" alt="image"
src="https://github.com/user-attachments/assets/ccd09873-4640-4a54-b3ca-f740dd50b38d"
/>


## 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
This commit is contained in:
Matt Vollmer
2026-03-24 13:15:24 -04:00
committed by GitHub
parent b06d183a32
commit 0a5b28c538
5 changed files with 84 additions and 70 deletions
@@ -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(
@@ -96,7 +96,7 @@ export const AgentPageHeader: FC<AgentPageHeaderProps> = ({
</Link>
</Button>
</div>
)}
)}{" "}
{children && <div className="flex items-center gap-2">{children}</div>}
</div>
);
@@ -94,10 +94,10 @@ export const ChatCostSummaryView: FC<ChatCostSummaryViewProps> = ({
: 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
@@ -628,6 +628,15 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (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<AgentsSidebarProps> = (props) => {
<SettingsIcon />
</Link>
</Button>
<Button
asChild
variant="subtle"
size="icon"
aria-label="Analytics"
className={cn(
"h-7 w-7 min-w-0 text-content-secondary hover:text-content-primary",
sidebarView.panel === "analytics" && "text-content-primary",
)}
>
<Link to="/agents/analytics">
<BarChart3Icon />
</Link>
</Button>{" "}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="subtle"
size="icon"
aria-label="Filter agents"
className={cn(
"h-7 w-7 min-w-0 text-content-secondary hover:text-content-primary",
archivedFilter === "archived" && "text-content-primary",
)}
>
<FilterIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onSelect={() => onArchivedFilterChange?.("active")}
>
Active
{archivedFilter === "active" && (
<CheckIcon className="ml-auto h-3.5 w-3.5" />
)}
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() => onArchivedFilterChange?.("archived")}
>
Archived
{archivedFilter === "archived" && (
<CheckIcon className="ml-auto h-3.5 w-3.5" />
)}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{onCollapse && (
<Button
variant="subtle"
@@ -827,11 +789,22 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
<ChatTreeContext.Provider value={chatTreeCtx}>
{visibleRootIDs.length === 0 ? (
<div className="rounded-lg border border-dashed border-border-default bg-surface-primary p-4 text-center text-xs text-content-secondary">
{normalizedSearch
? "No matching agents"
: archivedFilter === "archived"
? "No archived agents"
: "No agents yet"}
<p className="m-0">
{normalizedSearch
? "No matching agents"
: archivedFilter === "archived"
? "No archived agents"
: "No agents yet"}
</p>
{archivedFilter === "archived" && (
<button
type="button"
className="mt-2 cursor-pointer border-none bg-transparent p-0 text-xs text-content-secondary hover:text-content-primary hover:underline"
onClick={() => onArchivedFilterChange?.("active")}
>
Back to active
</button>
)}
</div>
) : (
<div>
@@ -851,8 +824,48 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
key={group}
className="[&:not(:first-child)]:mt-3"
>
<div className="mb-1 ml-2.5 flex items-center justify-between text-xs font-medium text-content-secondary">
<div className="mb-1 ml-2.5 -mr-0.5 flex items-center justify-between text-xs font-medium text-content-secondary">
<span>{group}</span>
{group === firstNonEmptyGroup && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="subtle"
size="icon"
aria-label="Filter agents"
className={cn(
"h-7 w-7 min-w-0 text-content-secondary hover:text-content-primary",
archivedFilter === "archived" &&
"text-content-primary",
)}
>
<FilterIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onSelect={() =>
onArchivedFilterChange?.("active")
}
>
Active
{archivedFilter === "active" && (
<CheckIcon className="ml-auto h-3.5 w-3.5" />
)}
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() =>
onArchivedFilterChange?.("archived")
}
>
Archived
{archivedFilter === "archived" && (
<CheckIcon className="ml-auto h-3.5 w-3.5" />
)}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
<div className="flex flex-col gap-0.5">
{groupChats.map((chat) => (
@@ -865,7 +878,7 @@ export const AgentsSidebar: FC<AgentsSidebarProps> = (props) => {
</div>
</div>
);
})}
})}{" "}
</div>
)}
</div>
@@ -102,7 +102,7 @@ export const UsageIndicator: FC = () => {
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link to="/agents/analytics">View details</Link>
<Link to="/agents/analytics">View usage</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>