3 Commits

Author SHA1 Message Date
fede 3e07252c6f refactor: enlineadas funciones que solo se usan una vez 2026-02-13 15:09:08 -03:00
fede ec9ec1f58a fix: añadida paginacion 2026-02-12 18:14:31 -03:00
Fran 4ca434da9e add UsersAdmin 2026-02-12 17:25:53 -03:00
6 changed files with 98 additions and 81 deletions
BIN
View File
Binary file not shown.
+44 -48
View File
@@ -33,10 +33,12 @@
interface Props { interface Props {
usuarios: UserResponseDto[]; usuarios: UserResponseDto[];
hayMas: boolean;
} }
let { usuarios = $bindable() }: Props = $props(); let { usuarios = $bindable(), hayMas }: Props = $props();
let hayMass = $state(hayMas);
let open = $state(false); let open = $state(false);
let openModificarUsuario = $state(false); let openModificarUsuario = $state(false);
let openDarAdmin = $state(false); let openDarAdmin = $state(false);
@@ -90,41 +92,20 @@
return sortDirection === 'asc' ? '↑' : '↓'; return sortDirection === 'asc' ? '↑' : '↓';
} }
function handleCambiarContraseña(usuario: UserResponseDto) {
open = true;
usuarioCambioPass = usuario;
}
function handleModificar(usuario: UserResponseDto) {
openModificarUsuario = true;
usuarioModificar = usuario;
}
function handleBorrar(usuario: UserResponseDto) {
openBorrar = true;
usuarioBorrar = usuario;
}
function handleDarAdmin(usuario: UserResponseDto) {
openDarAdmin = true;
usuarioDarAdmin = usuario;
}
// $inspect(usuarios); // $inspect(usuarios);
let timeoutId: ReturnType<typeof setTimeout> | number | undefined; let timeoutId: ReturnType<typeof setTimeout> | number | undefined;
function buscarUsuarios() { function buscarUsuarios() {
if (timeoutId) { if (timeoutId) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
} }
timeoutId = setTimeout(async () => { timeoutId = setTimeout(async () => {
paginaActual = 1;
if (search === '') { if (search === '') {
usuariosFiltrados = usuarios; search = '';
return;
} }
usuariosFiltrados = await busquedaAdminUsuarios(search); let ret = await busquedaAdminUsuarios(search, ITEMS_POR_PAGINA, paginaActual);
usuariosFiltrados = ret.usuarios;
hayMass = ret.hayMas;
}, 200); }, 200);
return () => { return () => {
@@ -135,11 +116,9 @@
let paginaActual = $state(1); let paginaActual = $state(1);
const totalPaginas = $derived(Math.ceil(usuariosFiltrados.length / ITEMS_POR_PAGINA)); // const usuariosPaginados = $derived(
// usuariosFiltrados.slice((paginaActual - 1) * ITEMS_POR_PAGINA, paginaActual * ITEMS_POR_PAGINA)
const usuariosPaginados = $derived( // );
usuariosFiltrados.slice((paginaActual - 1) * ITEMS_POR_PAGINA, paginaActual * ITEMS_POR_PAGINA)
);
</script> </script>
<div class="mb-4 flex gap-2"> <div class="mb-4 flex gap-2">
@@ -184,7 +163,7 @@
<p class="text-center">No hay usuarios por el nombre de: {search}</p> <p class="text-center">No hay usuarios por el nombre de: {search}</p>
</TableCell> </TableCell>
</TableRow>{:else} </TableRow>{:else}
{#each usuariosPaginados as usuario} {#each usuariosFiltrados as usuario}
<TableRow> <TableRow>
<TableCell <TableCell
>@<a href={'/' + usuario.username}> >@<a href={'/' + usuario.username}>
@@ -197,7 +176,11 @@
<TableCell class="flex gap-2"> <TableCell class="flex gap-2">
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<Button onclick={() => handleCambiarContraseña(usuario)}><KeyIcon></KeyIcon></Button <Button
onclick={() => {
open = true;
usuarioCambioPass = usuario;
}}><KeyIcon></KeyIcon></Button
> >
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
@@ -206,7 +189,12 @@
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<Button onclick={() => handleModificar(usuario)}><UserPen /></Button> <Button
onclick={() => {
openModificarUsuario = true;
usuarioModificar = usuario;
}}><UserPen /></Button
>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>Modificar Usuario</p> <p>Modificar Usuario</p>
@@ -216,7 +204,10 @@
<TooltipTrigger> <TooltipTrigger>
<Button <Button
disabled={usuario.isAdmin} disabled={usuario.isAdmin}
onclick={() => handleBorrar(usuario)} onclick={() => {
openBorrar = true;
usuarioBorrar = usuario;
}}
variant="destructive"><Trash_2 /></Button variant="destructive"><Trash_2 /></Button
> >
</TooltipTrigger> </TooltipTrigger>
@@ -232,7 +223,10 @@
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<Button <Button
onclick={() => handleDarAdmin(usuario)} onclick={() => {
openDarAdmin = true;
usuarioDarAdmin = usuario;
}}
variant={usuario.isAdmin ? 'destructive' : 'default'} variant={usuario.isAdmin ? 'destructive' : 'default'}
> >
<Shield /> <Shield />
@@ -253,23 +247,25 @@
</TableBody> </TableBody>
</Table> </Table>
<div class="mt-4 flex items-center justify-between"> <div class="mt-4 flex items-center justify-between">
<p class="text-sm text-muted-foreground"> <Button
Página {paginaActual} de {totalPaginas} disabled={paginaActual === 1}
</p> onclick={() => {
paginaActual--;
<div class="flex gap-2"> buscarUsuarios();
<Button disabled={paginaActual === 1} onclick={() => paginaActual--} variant="secondary"> }}
variant="secondary"
>
Anterior Anterior
</Button> </Button>
<Button <Button
disabled={paginaActual === totalPaginas || totalPaginas === 0} disabled={!hayMass}
onclick={() => paginaActual++} onclick={() => {
variant="secondary" paginaActual++;
buscarUsuarios();
}}
variant="secondary">Siguiente</Button
> >
Siguiente
</Button>
</div>
</div> </div>
<BorrarUsuario bind:open={openBorrar} usuario={usuarioBorrar} /> <BorrarUsuario bind:open={openBorrar} usuario={usuarioBorrar} />
<RecuperarContraseña bind:open usuario={usuarioCambioPass} /> <RecuperarContraseña bind:open usuario={usuarioCambioPass} />
+25
View File
@@ -0,0 +1,25 @@
import { apiBase } from '@/stores/url';
import { sesionStore } from '@/stores/usuario';
import { redirect } from '@sveltejs/kit';
import { get } from 'svelte/store';
import type { UserResponseDto } from '../../types';
export async function fetchUsuariosAdmin(page: number, limit: number) {
let response = await fetch(get(apiBase) + `/api/admin/users?page=${page}&pageSize=${limit}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${get(sesionStore)?.accessToken}`
}
});
if (response.status === 401) {
throw redirect(302, '/');
}
if (!response.ok) {
return { error: true };
}
const ret: { usuarios: UserResponseDto[]; hayMas: boolean } = await response.json();
return { ret, error: false };
}
+7 -3
View File
@@ -2,15 +2,19 @@ import { apiBase } from '@/stores/url';
import { sesionStore } from '@/stores/usuario'; import { sesionStore } from '@/stores/usuario';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
export async function busquedaAdminUsuarios(q: string) { export async function busquedaAdminUsuarios(q: string, limit = 5, page = 1) {
try { try {
const response = await fetch(get(apiBase) + '/api/admin/users?q=' + q, { const response = await fetch(
get(apiBase) +
`/api/admin/users${q ? `?q=${q}` : ''}${q ? '&' : '?'}page=${page}&pageSize=${limit}`,
{
method: 'GET', method: 'GET',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${get(sesionStore)?.accessToken}` Authorization: `Bearer ${get(sesionStore)?.accessToken}`
} }
}); }
);
if (response.ok) { if (response.ok) {
return await response.json(); return await response.json();
} }
+2 -1
View File
@@ -10,6 +10,7 @@
interface Prop { interface Prop {
data: { data: {
usuarios?: UserResponseDto[]; usuarios?: UserResponseDto[];
hayMas: boolean;
error: boolean; error: boolean;
}; };
} }
@@ -31,7 +32,7 @@
{#if data.usuarios?.length === 0} {#if data.usuarios?.length === 0}
<CardDescription>No hay usuarios que mostar</CardDescription> <CardDescription>No hay usuarios que mostar</CardDescription>
{:else} {:else}
<TablaUsuarios usuarios={data.usuarios || []}></TablaUsuarios> <TablaUsuarios usuarios={data.usuarios || []} hayMas={data.hayMas}></TablaUsuarios>
{/if} {/if}
</CardContent> </CardContent>
</Card> </Card>
+12 -21
View File
@@ -1,28 +1,19 @@
import { apiBase } from '@/stores/url.js'; import type { PageLoad } from './$types.js';
import { sesionStore } from '@/stores/usuario'; import { fetchUsuariosAdmin } from '@/hooks/UsuariosAdmin.js';
import { redirect } from '@sveltejs/kit';
import { get } from 'svelte/store';
import type { UserResponseDto } from '../../../types.js';
export const ssr = false; export const ssr = false;
export async function load({ depends, fetch }) { export const load: PageLoad = async ({ depends }) => {
depends('admin:load'); depends('admin:load');
const response = await fetch(get(apiBase) + '/api/admin/users', { const result = await fetchUsuariosAdmin(1, 5);
method: 'GET',
headers: { if (result.error) {
'Content-Type': 'application/json',
Authorization: `Bearer ${get(sesionStore)?.accessToken}`
}
});
if (response.status === 401) {
throw redirect(302, '/');
}
if (!response.ok) {
return { error: true }; return { error: true };
} }
const usuarios: UserResponseDto[] = await response.json(); return {
usuarios: result.ret?.usuarios,
return { usuarios, error: false }; hayMas: result.ret?.hayMas,
} error: false
};
};