feat(admin): implement user search functionality in admin portal

This commit is contained in:
Jose Selesan
2026-06-10 09:44:02 -03:00
parent c1c2f18471
commit 93b3a82638
5 changed files with 46 additions and 16 deletions

View File

@@ -6,9 +6,9 @@ import { createPlanHandler } from '@/modules/admin/handlers/create-plan.handler'
import { deletePlanHandler } from '@/modules/admin/handlers/delete-plan.handler'; import { deletePlanHandler } from '@/modules/admin/handlers/delete-plan.handler';
import { getUserSessionsHandler } from '@/modules/admin/handlers/get-user-sessions.handler'; import { getUserSessionsHandler } from '@/modules/admin/handlers/get-user-sessions.handler';
import { listComplexesHandler } from '@/modules/admin/handlers/list-complexes.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 { listPlansAdminHandler } from '@/modules/admin/handlers/list-plans-admin.handler';
import { listUsersHandler } from '@/modules/admin/handlers/list-users.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 { unblockUserHandler } from '@/modules/admin/handlers/unblock-user.handler';
import { updatePlanHandler } from '@/modules/admin/handlers/update-plan.handler'; import { updatePlanHandler } from '@/modules/admin/handlers/update-plan.handler';
import type { AppEnv } from '@/types/hono'; import type { AppEnv } from '@/types/hono';

View File

@@ -2,6 +2,7 @@ import { listAdminUsers } from '@/modules/admin/services/admin-users.service';
import type { AppContext } from '@/types/hono'; import type { AppContext } from '@/types/hono';
export async function listUsersHandler(c: AppContext) { 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); return c.json(users);
} }

View File

@@ -13,8 +13,16 @@ type AdminUser = {
activeSessions: number; activeSessions: number;
}; };
export async function listAdminUsers(): Promise<AdminUser[]> { export async function listAdminUsers(search?: string): Promise<AdminUser[]> {
const users = await db.user.findMany({ const users = await db.user.findMany({
where: search
? {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
],
}
: undefined,
include: { include: {
_count: { _count: {
select: { complexes: true, sessions: true }, select: { complexes: true, sessions: true },

View File

@@ -21,10 +21,11 @@ import {
LogIn, LogIn,
Monitor, Monitor,
RefreshCw, RefreshCw,
Search,
ShieldAlert, ShieldAlert,
Users, Users,
} from 'lucide-react'; } from 'lucide-react';
import { useState } from 'react'; import { useEffect, useState } from 'react';
function SessionsDialog({ function SessionsDialog({
user, user,
@@ -284,10 +285,17 @@ export function UsersPage() {
const [blockingUser, setBlockingUser] = useState<AdminUser | null>(null); const [blockingUser, setBlockingUser] = useState<AdminUser | null>(null);
const [unblockingUser, setUnblockingUser] = useState<AdminUser | null>(null); const [unblockingUser, setUnblockingUser] = useState<AdminUser | null>(null);
const [sessionsUser, setSessionsUser] = useState<AdminUser | null>(null); const [sessionsUser, setSessionsUser] = useState<AdminUser | null>(null);
const [searchInput, setSearchInput] = useState('');
const [search, setSearch] = useState('');
useEffect(() => {
const timer = setTimeout(() => setSearch(searchInput), 300);
return () => clearTimeout(timer);
}, [searchInput]);
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
queryKey: ['admin-users'], queryKey: ['admin-users', search],
queryFn: () => apiClient.admin.listUsers(), queryFn: () => apiClient.admin.listUsers(search || undefined),
}); });
return ( return (
@@ -300,6 +308,16 @@ export function UsersPage() {
</p> </p>
</div> </div>
<div className="flex items-center gap-2">
<div className="relative">
<Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
placeholder="Buscar por nombre o email..."
className="h-9 w-56 rounded-xl pl-9 text-sm"
/>
</div>
<Button <Button
variant="outline" variant="outline"
size="icon" size="icon"
@@ -309,6 +327,7 @@ export function UsersPage() {
<RefreshCw className="size-4" /> <RefreshCw className="size-4" />
</Button> </Button>
</div> </div>
</div>
{blockingUser && <BlockDialog user={blockingUser} onClose={() => setBlockingUser(null)} />} {blockingUser && <BlockDialog user={blockingUser} onClose={() => setBlockingUser(null)} />}

View File

@@ -51,8 +51,10 @@ export async function deletePlan(code: string) {
return response.data; return response.data;
} }
export async function listUsers() { export async function listUsers(search?: string) {
const response = await http.get<AdminUser[]>('/api/admin/users'); const response = await http.get<AdminUser[]>('/api/admin/users', {
params: search ? { search } : undefined,
});
return response.data; return response.data;
} }