Skip to content

Add search to user management - #809

Merged
hedhoud merged 10 commits into
developfrom
agent/user-search
Jul 27, 2026
Merged

Add search to user management#809
hedhoud merged 10 commits into
developfrom
agent/user-search

Conversation

@hedhoud

@hedhoud hedhoud commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #776

Context

Administrators need to find an account quickly as the user directory grows. Scanning pages manually slows routine account management and makes selecting the wrong account more likely.

Change

The user-management view now filters the complete authorized user list by display name, email, external identity, or displayed user number. Search works across the existing pagination, reports the result count, can be cleared in one action, and distinguishes an empty directory from a search with no matches.

The existing admin-only permissions and user API remain unchanged.

Validation

All 161 Admin UI tests pass, including coverage for searches across paginated rows and the empty states. The production UI build succeeds, and linting reports no new warnings or errors.

Summary by CodeRabbit

  • New Features
    • Added client-side, case-insensitive search to the admin users list with live counts, clear-search, and distinct “no users yet” vs “no matches” messaging.
    • Partition member listings now show user display name and email, including “Not available” handling when missing.
    • Data tables can now show custom empty-state text and optionally reset pagination.
  • Bug Fixes
    • Legacy user listings now include email information.
  • Tests
    • Expanded UI and backend test coverage for search, empty/error states, pagination reset, batched user fetching, and identity enrichment.
  • Documentation
    • Updated API and TypeScript descriptions for partition member fields and endpoint responses.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The admin users page now supports case-insensitive client-side search, result counts, contextual empty states, and retryable loading errors. Partition members now expose display names and email addresses through bulk backend enrichment and updated UI components.

Changes

Admin user and partition updates

Layer / File(s) Summary
User search and empty-state flow
ui/src/components/shared/data-table.tsx, ui/src/pages/admin/users/list.tsx
Adds configurable empty messages, pagination resets, multi-field user search, result summaries, loading states, and retryable initial-load or refresh errors.
Bulk user identity lookup
openrag/core/ports/user_repo.py, openrag/services/persistence/user_repo.py, openrag/services/orchestrators/partition_service.py, openrag/api/routers/admin/partitions.py, openrag/api/routers/admin/users.py
Adds batched user retrieval and enriches partition membership responses with display names and email addresses, including null values for missing users.
Partition member contract and UI
ui/src/lib/api/partitions.ts, ui/src/pages/admin/partitions/partition-member*, ui/src/pages/admin/partitions/detail.tsx
Updates partition member types and renders identity, email, and descriptive removal-confirmation content.
Behavior validation
ui/src/pages/admin/users/list.test.tsx, tests/unit/services/persistence/test_user_repo_external_id.py, tests/unit/services/orchestrators/test_partition_service.py, ui/src/pages/admin/partitions/partition-member-identity.test.tsx
Covers search interactions, empty and error states, repository email and bulk-query behavior, and partition-member identity enrichment and fallbacks.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant UserListPage
  participant DataTable
  Admin->>UserListPage: Enter or clear search text
  UserListPage->>UserListPage: Normalize and filter users
  UserListPage->>DataTable: Pass filteredUsers and emptyMessage
  DataTable-->>Admin: Render rows, count, or empty state
Loading
sequenceDiagram
  participant PartitionService
  participant UserRepository
  participant PartitionUsersUI
  PartitionService->>PartitionService: Fetch partition membership rows
  PartitionService->>UserRepository: Bulk-fetch referenced users
  UserRepository-->>PartitionService: Return matching identities
  PartitionService-->>PartitionUsersUI: Return enriched member records
  PartitionUsersUI-->>PartitionUsersUI: Render identity and email fields
Loading

Possibly related PRs

Suggested labels: admin-ui

Suggested reviewers: andyne13, ahmath-gadji

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Several partition identity, user-repo, and backend admin endpoint changes are unrelated to the user-management search issue. Move the partition identity and backend API updates to a separate PR or issue, and keep this change focused on admin user search.
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding search to admin user management.
Linked Issues check ✅ Passed The PR adds search by visible identifiers, preserves pagination, and distinguishes empty-directory and no-result states.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/user-search

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hedhoud

hedhoud commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@hedhoud
hedhoud marked this pull request as ready for review July 27, 2026 09:15
@coderabbitai coderabbitai Bot added the fix Fix issue label Jul 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f616af6bea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/pages/admin/users/list.tsx
Comment thread ui/src/pages/admin/users/list.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
ui/src/pages/admin/users/list.test.tsx (1)

82-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for searching by displayed user number.

list.tsx matches String(user.id) and `User #${user.id}` for search, per the PR objective of filtering by "displayed user number", but no test here exercises that path (only name/email/external-id are covered).

✅ Suggested addition
     await userEvent.click(screen.getByRole("button", { name: "Clear user search" }));
     await userEvent.type(search, "zArA operator");

     expect(await screen.findByText("Zara Operator")).toBeTruthy();
+
+    await userEvent.click(screen.getByRole("button", { name: "Clear user search" }));
+    await userEvent.type(search, "User `#12`");
+
+    expect(await screen.findByText("Zara Operator")).toBeTruthy();
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/pages/admin/users/list.test.tsx` around lines 82 - 121, Extend the
“searches visible identifiers across paginated rows” test to search for a
displayed user number, such as “User `#12`” or its numeric identifier, and assert
that Zara Operator is returned with the expected single-user status. Keep the
existing name, email, and external-ID coverage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/pages/admin/users/list.tsx`:
- Around line 199-227: Update the user search Input in the users list to
suppress the browser’s native search cancel button while preserving the existing
custom clear Button behavior. Apply the appropriate WebKit search-cancel
pseudo-element styling through the Input’s className or established styling
mechanism.

---

Nitpick comments:
In `@ui/src/pages/admin/users/list.test.tsx`:
- Around line 82-121: Extend the “searches visible identifiers across paginated
rows” test to search for a displayed user number, such as “User `#12`” or its
numeric identifier, and assert that Zara Operator is returned with the expected
single-user status. Keep the existing name, email, and external-ID coverage
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0266618b-be23-4b08-97b9-84be3819449e

📥 Commits

Reviewing files that changed from the base of the PR and between b2ab6b1 and f616af6.

📒 Files selected for processing (3)
  • ui/src/components/shared/data-table.tsx
  • ui/src/pages/admin/users/list.test.tsx
  • ui/src/pages/admin/users/list.tsx

Comment thread ui/src/pages/admin/users/list.tsx
@hedhoud

hedhoud commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining non-inline review request in 653809f.

The displayed user number was a supported search key but had no regression coverage, so that contract could have been removed accidentally while the name/email/external-ID tests still passed. The existing cross-page search test now searches for User #12 and verifies both the matching user and the 1 of 11 users result summary.

Validation after all review fixes: 162 Admin UI tests passed, the production UI build passed, 32 focused backend tests passed, and Python lint passed. UI lint reports only the existing unrelated hook warning in the models page.

@coderabbitai coderabbitai Bot removed the fix Fix issue label Jul 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 653809f921

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/pages/admin/users/list.tsx Outdated
@hedhoud

hedhoud commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

Integrated the partition-member identity work from #757 and extended it with email while keeping the stable user ID visible. The original per-member concurrent lookup was replaced with one bulk lookup so large partitions do not put unnecessary pressure on the database connection pool. Missing profile data still falls back safely to the user ID.\n\nValidated with the focused Python tests, the complete UI test suite, the production UI build, Ruff, and ESLint.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 415db6aa2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread openrag/services/orchestrators/partition_service.py Outdated
@coderabbitai coderabbitai Bot added the admin-ui Admin UI label Jul 27, 2026
@Ahmath-Gadji Ahmath-Gadji self-assigned this Jul 27, 2026
Comment thread ui/src/pages/admin/users/list.tsx Outdated
Comment thread openrag/services/persistence/user_repo.py
@coderabbitai coderabbitai Bot added the fix Fix issue label Jul 27, 2026
@Ahmath-Gadji

Ahmath-Gadji commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Review pass — 1 fix, 2 remarks, 1 observation

Reviewed the full diff and ran the suites locally against the branch: 2215 backend unit tests, 168 UI tests, tsc -b, ESLint and Ruff all pass.

One commit pushed — bd9e494e, which adds email to the documented response of GET /users/ and GET /users/{user_id}. The payload gained the field here but the OpenAPI descriptions still listed only id, display_name, external_user_id, is_admin, created_at, and this PR had already set the opposite precedent by updating the partition-members description in the same change.

The two substantive findings are left as remarks in their threads rather than as pushed changes — both are design calls that belong to you:

  1. list_members() charges every authorization check for identity data it never reads. Three callers share the method; only GET /partition/{p}/users displays members. _enforce_editor_access (5 MCP write paths) and the indexing create-race check read role alone, and now pay an extra SELECT * FROM users WHERE id = ANY(...) plus N discarded model constructions per request.
  2. Searching from a later page leaves the table on that page of the results. autoResetPageIndex: false is deliberate for deletes, but the search filters data in the parent, so it applies there too. Verified on the branch: 100 users, navigate to page 5, type a matching query, and the first row is Match 41 under Page 5 of 10. When the query narrows instead, the clamp at data-table.tsx:131-135 pins you to the last page, so the status reads 12 of 100 users while two rows show. Either way the first match is off screen.

The rest of the change reads well. The bulk get_users_by_ids lookup is the right shape for the members list, _row_to_user never touches the token column so nothing sensitive is materialised, and the missing-user fallback to None identities is handled on both the service and component sides.

Observation, not a blocker and not in scope here: no workflow runs the ui/ suite — astro.yml is the docs site, and the other nine are Python. The 168 vitest tests, including the ones this PR adds, are only ever green because a human ran them. Worth a follow-up issue.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/unit/services/orchestrators/test_partition_service.py (1)

238-247: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the batching test distinguish one batch from per-member lookups.

The test at Line 875 contains only one member, so a per-member lookup still passes. Additionally, Line 239 overwrites the previous request, preventing the fake from proving that exactly one batch was used. Track all lookup batches, use at least two members, and assert one call containing both IDs.

Also applies to: 875-892

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/services/orchestrators/test_partition_service.py` around lines 238
- 247, Update the fake service’s get_users_by_ids method to accumulate every
requested batch instead of overwriting the prior request, then revise the
batching test to use at least two member IDs and assert exactly one lookup batch
containing both IDs. Keep the existing returned-user behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/unit/services/orchestrators/test_partition_service.py`:
- Around line 238-247: Update the fake service’s get_users_by_ids method to
accumulate every requested batch instead of overwriting the prior request, then
revise the batching test to use at least two member IDs and assert exactly one
lookup batch containing both IDs. Keep the existing returned-user behavior
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 484fe1fe-b49c-4b7d-98ea-b8f14ba9d765

📥 Commits

Reviewing files that changed from the base of the PR and between def1b46 and 2ee700e.

📒 Files selected for processing (7)
  • openrag/api/routers/admin/partitions.py
  • openrag/api/routers/admin/users.py
  • openrag/services/orchestrators/partition_service.py
  • tests/unit/services/orchestrators/test_partition_service.py
  • ui/src/components/shared/data-table.tsx
  • ui/src/pages/admin/users/list.test.tsx
  • ui/src/pages/admin/users/list.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • ui/src/pages/admin/users/list.tsx
  • ui/src/pages/admin/users/list.test.tsx

@coderabbitai coderabbitai Bot removed the fix Fix issue label Jul 27, 2026
Comment thread tests/unit/services/persistence/test_user_repo_external_id.py Outdated
Comment thread tests/unit/services/persistence/test_user_repo_external_id.py
@Ahmath-Gadji

Copy link
Copy Markdown
Collaborator

Re-review of 0d975fc7 — both findings resolved

Both remarks are addressed and verified. Details are in their threads; in short:

  • list_members() is back to the single membership query, with list_members_with_identities() serving the admin UI. The two authorization callers are at their original cost.
  • pageResetKey returns the table to the first page on a query change, including when the search is cleared, and leaves autoResetPageIndex: false untouched so delete behaviour is unchanged.

I checked both new tests actually catch their bug rather than passing vacuously — collapsing the service split, and removing pageResetKey from the users page, each turn the corresponding test red.

Full suites on this head: 2214 backend unit tests, 168 UI tests, tsc -b and ESLint pass (the one ESLint warning is the pre-existing models.tsx hook dependency). All six CI checks are green.

Two small nits posted inline on the test fake, neither blocking. Nothing else outstanding from my side.

One thing worth a decision before merge: 3ef20eb3 is titled "init". This repo merges rather than squashes — the five most recent commits on develop are all Merge pull request #… — so that message lands in the permanent history as-is. Either reword it or squash-merge this PR.

@Ahmath-Gadji Ahmath-Gadji left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Every finding I raised is addressed and verified on 251586f5, and all six CI checks are green.

For the record, what I checked rather than assumed: both fix commits were tested for vacuity — collapsing the list_members split, removing pageResetKey, and dropping the partition_memberships guard each turn the corresponding test red, so none of them pass by accident. Locally on this head: 2214 backend unit tests, 168 UI tests, tsc -b and ESLint pass.


One thing to settle before merge: the init commit

3ef20eb3 is titled "init". It is not a scratch commit — it is the original partition-member display-name work, authored by Baptiste (bnoleau@ik.me), and it is the seed of the identity feature this PR builds on. It still exists independently as #757, which is open.

That makes the obvious fix the wrong one. This repo merges rather than squashes, so the message lands in develop verbatim — but squash-merging to hide it would collapse Baptiste's authorship into a single commit attributed to this PR's author. The commit is worth attributing; only its message is the problem.

Suggested instead:

  1. Reword 3ef20eb3 in place (git rebase, keeping the Author: line intact) to something like "Show partition member display name in the partitions UI". Authorship is preserved, history stays readable.
  2. Close #757 with a note pointing at this PR, so it does not sit stale after the content merges here.

Neither is blocking from my side — flagging because both are easy to miss and awkward to undo after the merge.

@hedhoud
hedhoud merged commit db3482c into develop Jul 27, 2026
6 checks passed
@hedhoud
hedhoud deleted the agent/user-search branch July 27, 2026 13:29
@andyne13 andyne13 mentioned this pull request Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

admin-ui Admin UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin UI: add search to user management

3 participants