feat(site): add server-side search and pagination for groups page - #27994
Merged
Conversation
Add a paginated, server-side searchable groups endpoint and wire the
organization Groups page to it, matching the organization Members page
pattern (useSearchParams + usePaginatedQuery + useFilter with a
presentational view rendering Filter + PaginationContainer).
- database: new PaginatedOrganizationGroups query with COUNT(*) OVER(),
deterministic ORDER BY LOWER(name), OFFSET/LIMIT. dbauthz wrapper does a
single org-wide ActionRead check (no post-filter) to keep LIMIT/OFFSET and
the count consistent, mirroring PaginatedOrganizationMembers.
- codersdk: PaginatedGroupsResponse + GroupsPaginated client method.
- searchquery: minimal Groups(q) parser for free-text search.
- enterprise: paginatedGroups handler + /organizations/{organization}/paginated-groups route.
- frontend: getOrganizationPaginatedGroups API, paginatedGroupsByOrganization
query, GroupsFilter (search-only), GroupsPageView filter + pagination,
updated stories.
The legacy /groups endpoint and its post-filter are left untouched.
Generated with Coder Agents.
fix: hide Filter preset menu if no presets are provided
fix: move "Create group" button to GroupsPageView to match OrganizationMembersPageView
Merge remote-tracking branch 'origin/main' into aqandrew/devex-434-groups-search-pagination
docs: regenerate enterprise API reference after merging main
fix(site/src/pages/GroupsPage): keep GroupsPageView presentational
Gate the Create group button on the canCreateGroup prop the container
already passes, instead of re-deriving permissions via useGroupsSettings +
organizationsPermissions inside the view. Calling useGroupsSettings coupled
the presentational view to GroupsPageContext, which threw "useGroupsSettings
should be used inside of GroupsPageContext" in Storybook.
test: set correct pagination number labels for WithSearchAndPagination story
refactor: replace GroupsParams struct with string
fix: order PaginatedOrganizationGroups by name, then id
style: add spaces around cast operators
Merge remote-tracking branch 'origin/main' into aqandrew/devex-434-groups-search-pagination
# Conflicts:
# site/src/api/api.ts
# site/src/api/queries/groups.ts
# site/src/pages/GroupsPage/GroupsPage.tsx
# site/src/pages/GroupsPage/GroupsPageView.stories.tsx
# site/src/pages/GroupsPage/GroupsPageView.tsx
Merge remote-tracking branch 'origin/aqandrew/devex-434-groups-search-pagination' into aqandrew/devex-434-groups-search-pagination
fix(coderd/database): restore OFFSET in PaginatedOrganizationGroups
The name+id tiebreaker commit (7f6e800) accidentally dropped the
OFFSET @offset_opt clause when it was collapsed onto the ORDER BY line,
which disabled offset pagination and removed OffsetOpt from the generated
params. Restore it alongside the id tiebreaker and regenerate.
refactor(codersdk): add PaginatedGroupsRequest and rename OrganizationGroupsPaginated
Replace UsersRequest with a dedicated PaginatedGroupsRequest (pagination +
search only) so the paginated groups client no longer advertises key:value
filters the endpoint rejects with a 400. Rename GroupsPaginated to
OrganizationGroupsPaginated for parity with OrganizationMembersPaginated and
the DB/frontend names. Addresses review comment (codersdk/groups.go).
docs(codersdk): document org-wide read requirement on OrganizationGroupsPaginated
The paginated endpoint requires organization-wide group read and is not a
drop-in for Groups (GET /groups), which authorizes per-group. Document this
on the published SDK method so consumers do not assume equivalent behavior.
Addresses review comment (dbauthz.go).
fix(coderd/searchquery): support multi-word group search
Bare search terms were added as separate 'search' values, so a multi-word
query like 'front end' tripped the duplicate-param check and returned a 400
instead of a substring match the SQL supports. Concatenate bare terms into a
single search value. Addresses review comment (searchquery/search.go).
fix(enterprise/coderd): drop dead sql.ErrNoRows check in paginatedGroups
PaginatedOrganizationGroups is a :many query that returns an empty slice,
never sql.ErrNoRows, so the errors.Is guard was dead code. The empty result
is already handled by the len(groups) == 0 branch. Addresses review comment
(groups.go).
test(enterprise/coderd): assert paginated group member hydration
test(coderd): strengthen paginated groups query and search coverage
feat(site/src/pages/GroupsPage): show filter-aware empty state
refactor(site/src/components/Filter): drop unused GroupsFilter error prop
refactor(site/src/pages/GroupsPage): use Array.from map callback in story
Merge branch 'main' into aqandrew/devex-434-groups-search-pagination
…to aqandrew/devex-434-groups-search-pagination-frontend
…to aqandrew/devex-434-groups-search-pagination-frontend
…-pagination-backend' into aqandrew/devex-434-groups-search-pagination-frontend # Conflicts: # site/src/pages/GroupsPage/GroupsPage.tsx
…to aqandrew/devex-434-groups-search-pagination-frontend
…-pagination-backend' into aqandrew/devex-434-groups-search-pagination-frontend
…r row The paginated groups endpoint no longer returns member rosters, so each group row now uses total_member_count for the count and lazily fetches a capped avatar preview via the group members endpoint. Handles loading (skeletons), error and empty (em dash), and success (avatars plus a +N badge) states. Adds a shared groupMemberAvatars query nested under the group members key, and Storybook coverage seeding the preview query.
…to aqandrew/devex-434-groups-search-pagination-frontend
The paginated groups endpoint now returns codersdk.PaginatedGroup, which omits the member roster. Point GroupWithSpend and joinGroupsSpend at the generated PaginatedGroup type and drop the obsolete members field from the story fixtures.
…to aqandrew/devex-434-groups-search-pagination-frontend
…to aqandrew/devex-434-groups-search-pagination-frontend
…to aqandrew/devex-434-groups-search-pagination-frontend
jeremyruppel
approved these changes
Aug 10, 2026
aqandrew
deleted the
aqandrew/devex-434-groups-search-pagination-frontend
branch
August 10, 2026 20:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Repeat of #27604, which I had mistakenly manually merged into #27603, instead of letting Graphite handle it after #27603 merges into main. Reattempting with GitHub stacks now instead of Graphite!
See #27604 for details/review.