From 93b3a826381a63ad91d0c11c5aaa2715791fc52b Mon Sep 17 00:00:00 2001 From: Jose Selesan Date: Wed, 10 Jun 2026 09:44:02 -0300 Subject: [PATCH] feat(admin): implement user search functionality in admin portal --- .../backend/src/modules/admin/admin.routes.ts | 2 +- .../admin/handlers/list-users.handler.ts | 3 +- .../admin/services/admin-users.service.ts | 10 ++++- .../src/features/admin/users-page.tsx | 41 ++++++++++++++----- apps/frontend/src/lib/api/resources/admin.ts | 6 ++- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/apps/backend/src/modules/admin/admin.routes.ts b/apps/backend/src/modules/admin/admin.routes.ts index 1de3e12..9148f1c 100644 --- a/apps/backend/src/modules/admin/admin.routes.ts +++ b/apps/backend/src/modules/admin/admin.routes.ts @@ -6,9 +6,9 @@ import { createPlanHandler } from '@/modules/admin/handlers/create-plan.handler' import { deletePlanHandler } from '@/modules/admin/handlers/delete-plan.handler'; import { getUserSessionsHandler } from '@/modules/admin/handlers/get-user-sessions.handler'; import { listComplexesHandler } from '@/modules/admin/handlers/list-complexes.handler'; -import { revokeAllSessionsHandler } from '@/modules/admin/handlers/revoke-all-sessions.handler'; import { listPlansAdminHandler } from '@/modules/admin/handlers/list-plans-admin.handler'; import { listUsersHandler } from '@/modules/admin/handlers/list-users.handler'; +import { revokeAllSessionsHandler } from '@/modules/admin/handlers/revoke-all-sessions.handler'; import { unblockUserHandler } from '@/modules/admin/handlers/unblock-user.handler'; import { updatePlanHandler } from '@/modules/admin/handlers/update-plan.handler'; import type { AppEnv } from '@/types/hono'; diff --git a/apps/backend/src/modules/admin/handlers/list-users.handler.ts b/apps/backend/src/modules/admin/handlers/list-users.handler.ts index f6a757c..deaef65 100644 --- a/apps/backend/src/modules/admin/handlers/list-users.handler.ts +++ b/apps/backend/src/modules/admin/handlers/list-users.handler.ts @@ -2,6 +2,7 @@ import { listAdminUsers } from '@/modules/admin/services/admin-users.service'; import type { AppContext } from '@/types/hono'; export async function listUsersHandler(c: AppContext) { - const users = await listAdminUsers(); + const search = c.req.query('search'); + const users = await listAdminUsers(search); return c.json(users); } diff --git a/apps/backend/src/modules/admin/services/admin-users.service.ts b/apps/backend/src/modules/admin/services/admin-users.service.ts index 99ded34..5c01a43 100644 --- a/apps/backend/src/modules/admin/services/admin-users.service.ts +++ b/apps/backend/src/modules/admin/services/admin-users.service.ts @@ -13,8 +13,16 @@ type AdminUser = { activeSessions: number; }; -export async function listAdminUsers(): Promise { +export async function listAdminUsers(search?: string): Promise { const users = await db.user.findMany({ + where: search + ? { + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { email: { contains: search, mode: 'insensitive' } }, + ], + } + : undefined, include: { _count: { select: { complexes: true, sessions: true }, diff --git a/apps/frontend/src/features/admin/users-page.tsx b/apps/frontend/src/features/admin/users-page.tsx index eb4560c..7bc39f9 100644 --- a/apps/frontend/src/features/admin/users-page.tsx +++ b/apps/frontend/src/features/admin/users-page.tsx @@ -21,10 +21,11 @@ import { LogIn, Monitor, RefreshCw, + Search, ShieldAlert, Users, } from 'lucide-react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; function SessionsDialog({ user, @@ -284,10 +285,17 @@ export function UsersPage() { const [blockingUser, setBlockingUser] = useState(null); const [unblockingUser, setUnblockingUser] = useState(null); const [sessionsUser, setSessionsUser] = useState(null); + const [searchInput, setSearchInput] = useState(''); + const [search, setSearch] = useState(''); + + useEffect(() => { + const timer = setTimeout(() => setSearch(searchInput), 300); + return () => clearTimeout(timer); + }, [searchInput]); const { data, isLoading } = useQuery({ - queryKey: ['admin-users'], - queryFn: () => apiClient.admin.listUsers(), + queryKey: ['admin-users', search], + queryFn: () => apiClient.admin.listUsers(search || undefined), }); return ( @@ -300,14 +308,25 @@ export function UsersPage() {

- +
+
+ + setSearchInput(e.target.value)} + placeholder="Buscar por nombre o email..." + className="h-9 w-56 rounded-xl pl-9 text-sm" + /> +
+ +
{blockingUser && setBlockingUser(null)} />} diff --git a/apps/frontend/src/lib/api/resources/admin.ts b/apps/frontend/src/lib/api/resources/admin.ts index 4d7409a..39cb380 100644 --- a/apps/frontend/src/lib/api/resources/admin.ts +++ b/apps/frontend/src/lib/api/resources/admin.ts @@ -51,8 +51,10 @@ export async function deletePlan(code: string) { return response.data; } -export async function listUsers() { - const response = await http.get('/api/admin/users'); +export async function listUsers(search?: string) { + const response = await http.get('/api/admin/users', { + params: search ? { search } : undefined, + }); return response.data; }