fix: search usage by name or username (#23317)

## Summary

The search field on `/agents/settings/usage` previously only matched
against usernames. This updates the SQL query to also match against the
user's display name via `ILIKE`, and updates the frontend placeholder
and variable names to reflect the broader search scope.

## Changes

- **SQL** (`coderd/database/queries/chats.sql`,
`coderd/database/queries.sql.go`): Added `OR u.name ILIKE '%' ||
@username::text || '%'` to the `GetChatCostPerUser` query's WHERE
clause.
- **Frontend** (`site/src/pages/AgentsPage/SettingsPageContent.tsx`):
Renamed `usernameFilter`/`debouncedUsername` to
`searchFilter`/`debouncedSearch`, updated placeholder to "Search by name
or username".

---

PR generated with Coder Agents
This commit is contained in:
Matt Vollmer
2026-03-19 19:14:10 -04:00
committed by GitHub
parent 6afc1bac0b
commit 0c9771a38b
4 changed files with 11 additions and 9 deletions
+1
View File
@@ -4062,6 +4062,7 @@ WITH chat_cost_users AS (
AND (
$5::text = ''
OR u.username ILIKE '%' || $5::text || '%'
OR u.name ILIKE '%' || $5::text || '%'
)
GROUP BY
c.owner_id,
+1
View File
@@ -725,6 +725,7 @@ WITH chat_cost_users AS (
AND (
@username::text = ''
OR u.username ILIKE '%' || @username::text || '%'
OR u.name ILIKE '%' || @username::text || '%'
)
GROUP BY
c.owner_id,
@@ -203,7 +203,7 @@ export const UsageUserList: Story = {
// Verify the search field is present.
await expect(
canvas.getByPlaceholderText("Filter by username"),
canvas.getByPlaceholderText("Search by name or username"),
).toBeInTheDocument();
},
};
@@ -302,7 +302,7 @@ export const UsageUserDrillInAndBack: Story = {
// The search field should be present, confirming we're
// back on the list view.
await expect(
body.getByPlaceholderText("Filter by username"),
body.getByPlaceholderText("Search by name or username"),
).toBeInTheDocument();
},
};
@@ -125,8 +125,8 @@ interface UsageContentProps {
const UsageContent: FC<UsageContentProps> = ({ now }) => {
const [searchParams, setSearchParams] = useSearchParams();
const [usernameFilter, setUsernameFilter] = useState("");
const debouncedUsername = useDebouncedValue(usernameFilter, 300);
const [searchFilter, setSearchFilter] = useState("");
const debouncedSearch = useDebouncedValue(searchFilter, 300);
const [page, setPage] = useState(1);
const dateRange = useMemo(() => {
const end = now ?? dayjs();
@@ -143,7 +143,7 @@ const UsageContent: FC<UsageContentProps> = ({ now }) => {
...chatCostUsers({
start_date: dateRange.startDate,
end_date: dateRange.endDate,
username: debouncedUsername || undefined,
username: debouncedSearch || undefined,
limit: pageSize,
offset,
}),
@@ -283,13 +283,13 @@ const UsageContent: FC<UsageContentProps> = ({ now }) => {
<div className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div className="w-full md:max-w-sm">
<SearchField
value={usernameFilter}
value={searchFilter}
onChange={(value) => {
setUsernameFilter(value);
setSearchFilter(value);
setPage(1);
}}
placeholder="Filter by username"
aria-label="Filter usage by username"
placeholder="Search by name or username"
aria-label="Search usage by name or username"
/>
</div>
{usersQuery.data && (