Skip to content

feat: add search and toggle verify button to user page#16

Merged
sirily11 merged 1 commit intomainfrom
search-verify
Jan 31, 2026
Merged

feat: add search and toggle verify button to user page#16
sirily11 merged 1 commit intomainfrom
search-verify

Conversation

@sirily11
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings January 31, 2026 06:13
@vercel
Copy link

vercel bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rxlab-auth Ready Ready Preview, Comment Jan 31, 2026 6:29am

Request Review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the admin Users page with debounced search and a UI/server flow to toggle a user’s verified status, plus end-to-end coverage for both behaviors.

Changes:

  • Introduces a generic useDebounce hook and a UserSearch component, wiring them into the admin UserList to support debounced searching with empty-state messaging and pagination-aware queries.
  • Adds a toggleUserVerified server action and updates UserRow to show a verified/unverified badge and a toggle item in the actions menu, with local state and parent callbacks to keep the list in sync.
  • Extends getUsers to accept an optional search term, filters and paginates results accordingly, and adds Playwright E2E tests for user search and the verification toggle flow.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
hooks/use-debounce.ts New reusable client hook to debounce arbitrary values, used for search input.
components/admin/user-search.tsx New presentational search input with icon, loading spinner, and clear button for the admin users list.
components/admin/user-row.tsx Adds local verified state, a toggle-verified dropdown item, and wiring to the new toggleUserVerified server action and parent callback.
components/admin/user-list.tsx Integrates search UI and debounced queries, threads search into pagination, adjusts empty state messaging, and updates list state on verification changes.
actions/admin/users/toggle-verified.ts New admin-only server action that flips a user’s emailVerified flag in the database and revalidates the users page.
actions/admin/users/list.ts Extends users listing to support search (email, username, display name) while preserving cursor-based pagination and count semantics.
e2e/admin/users.spec.ts Adds Playwright tests covering user search UX and the verified/unverified toggle in the admin users UI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 194 to 221
test("should update actions menu after toggle", async ({ page }) => {
// Find the test user's row (use last() to get desktop layout which is visible)
const userRow = page.locator(`[data-testid^="user-row-"]`).last();
const userId = (await userRow.getAttribute("data-testid"))?.replace(
"user-row-",
"",
);

// Check initial state (use exact match to avoid "Unverified" matching "Verified")
const verifiedBadge = userRow.getByText("Verified", { exact: true });

// Accept the confirmation dialog before clicking
page.on("dialog", (dialog) => dialog.accept());

// Open actions menu (use last() to get desktop layout which is visible)
await page.getByTestId(`user-actions-${userId}`).last().click();

// Click the toggle action
await page.getByTestId(`unmark-verified-${userId}`).click();

// Wait for the action to complete
await page.waitForTimeout(500);

// Open actions menu again (use last() to get desktop layout which is visible)
await page.getByTestId(`user-actions-${userId}`).last().click();

// The toggle action should now show the opposite option
await expect(page.getByTestId(`mark-verified-${userId}`)).toBeVisible();
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

This test both hardcodes a click on unmark-verified-${userId} and relies on the user starting in a verified state, but the same user is toggled in the previous test and there is no reset in beforeEach, so the outcome becomes dependent on test execution order and persisted DB state. Additionally, the verifiedBadge locator is created but never asserted, so the initial state mentioned in the comment is not actually being checked. It would be more robust to either give this test its own user or explicitly reset the verification status before running the assertions, and to assert the initial badge state while choosing the correct toggle action based on the current state.

Copilot uses AI. Check for mistakes.
Comment on lines +88 to 91
const handleVerificationChanged = (userId: string, verified: boolean) => {
setUsers(
users.map((u) => (u.id === userId ? { ...u, emailVerified: verified } : u))
);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

When updating users in response to a verification change, this uses the users value from the outer closure, which can become stale if multiple updates are queued close together (for example, a delete and a verification toggle). Using a functional state update (deriving the new array from the previous state passed into the setter) would make this more robust against concurrent state updates.

Copilot uses AI. Check for mistakes.
Comment on lines 169 to 193
test("should toggle verification status", async ({ page }) => {
// Find the test user's row (use last() to get desktop layout which is visible)
const userRow = page.locator(`[data-testid^="user-row-"]`).last();
const userId = (await userRow.getAttribute("data-testid"))?.replace(
"user-row-",
"",
);

// Accept the confirmation dialog before clicking
page.on("dialog", (dialog) => dialog.accept());

// Open actions menu (use last() to get desktop layout which is visible)
await page.getByTestId(`user-actions-${userId}`).last().click();

// Click the toggle action

await page.getByTestId(`unmark-verified-${userId}`).click();

// Wait for the action to complete
await page.waitForTimeout(500);

// Should now show Unverified badge
await expect(page.getByText("Unverified").nth(1)).toBeVisible();
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

This test unconditionally clicks the unmark-verified-${userId} action, which assumes the user is currently verified. Because the verification status can change (and the same user is reused across tests), this makes the test order-dependent and it will fail if the current state is "Unverified" (where only the mark-verified-* action exists). Consider either resetting the user's verification state before this test or branching on which toggle action is currently visible so the locator matches the actual state under test.

Copilot uses AI. Check for mistakes.
@sirily11 sirily11 merged commit 8aaf69d into main Jan 31, 2026
4 checks passed
@sirily11 sirily11 deleted the search-verify branch January 31, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant