35 Commits

Author SHA1 Message Date
emailerfacu-spec 8683996d32 Cambios correspondientes a la primera devolucion
refactor: eliminar estilos que no seguían la guía de diseño
2026-02-23 13:17:29 -03:00
fede 3b4800d0d9 refactor: eliminar estilos que no seguían la guía de diseño 2026-02-23 13:08:36 -03:00
emailerfacu-spec bb06dade59 Merge pull request #95 from emailerfacu-spec/dev
fix panel de admin + eliminadas imports obsoletos
2026-02-16 18:42:44 -03:00
fede 022623e22c fix: Arregladas updates del panel de admin 2026-02-16 18:41:21 -03:00
emailerfacu-spec 7d0e76eff4 Merge pull request #94 from emailerfacu-spec/dev
hotfix
2026-02-16 18:40:09 -03:00
fede c96df5af92 chore: eliminados import redundantes 2026-02-14 21:10:18 -03:00
Fran d278c75688 fix race condition in isfollowing 2026-02-14 19:17:50 -03:00
fede 3e07252c6f refactor: enlineadas funciones que solo se usan una vez 2026-02-13 15:09:08 -03:00
emailerfacu-spec 77aee661a8 Merge pull request #90 from emailerfacu-spec/dev
Dev
2026-02-12 18:19:04 -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
emailerfacu-spec 398592ea1d Merge pull request #85 from emailerfacu-spec/dev
Dev
2026-01-29 23:29:29 -03:00
emailerfacu-spec 4d99695a59 Merge pull request #72 from emailerfacu-spec/dev
Dev
2026-01-09 22:39:01 -03:00
emailerfacu-spec 1f2f26f780 Merge pull request #67 from emailerfacu-spec/dev
otra try
2026-01-06 13:55:55 -03:00
emailerfacu-spec 3dc0cfc8a4 Merge pull request #66 from emailerfacu-spec/dev
cambios en el path
2026-01-06 13:47:28 -03:00
emailerfacu-spec 22ecdb6e9d Merge pull request #65 from emailerfacu-spec/dev
renderiza una imagen del post en og:image
2026-01-06 13:40:37 -03:00
emailerfacu-spec e785b60935 Merge pull request #63 from emailerfacu-spec/dev
Dev
2026-01-05 21:22:13 -03:00
emailerfacu-spec c47b9956b9 Merge pull request #62 from emailerfacu-spec/dev
Dev
2026-01-02 19:04:14 -03:00
emailerfacu-spec 61232ada05 Merge pull request #58 from emailerfacu-spec/dev
Dev
2026-01-01 22:07:06 -03:00
emailerfacu-spec fae9a676e2 Merge pull request #54 from emailerfacu-spec/dev
otras correcciones
2025-12-26 19:24:55 -03:00
emailerfacu-spec 9b0937b731 Merge pull request #53 from emailerfacu-spec/dev
otro fix
2025-12-26 19:16:58 -03:00
emailerfacu-spec 77f3901cb3 Merge pull request #52 from emailerfacu-spec/dev
el regex no era global
2025-12-26 19:13:00 -03:00
emailerfacu-spec bd09ae005b Merge pull request #51 from emailerfacu-spec/dev
modificadas las reglas de como renderizo los posts
2025-12-26 19:08:53 -03:00
emailerfacu-spec d1a26cc132 Merge pull request #50 from emailerfacu-spec/dev
Dev
2025-12-26 16:55:09 -03:00
emailerfacu-spec 670f8ae3e2 Merge pull request #48 from emailerfacu-spec/dev
Hotfix
2025-12-26 16:41:08 -03:00
emailerfacu-spec b4382b361a Merge pull request #46 from emailerfacu-spec/dev
Dev
2025-12-26 11:48:23 -03:00
emailerfacu-spec 17f7bed1d9 Merge pull request #45 from emailerfacu-spec/dev
Hotfix-2: hotfix siguie llamando al length
2025-12-26 11:37:28 -03:00
emailerfacu-spec 29b7effd57 Merge pull request #44 from emailerfacu-spec/dev
Hotfix-1
2025-12-26 11:24:24 -03:00
emailerfacu-spec f425dd13a7 Merge pull request #38 from emailerfacu-spec/dev
algunos refactors y caracteristicas como:

    añadida pagina para ver htags
    añadida pagina para ver resultados de busquedas
    Ahora se puede editar el perfil propio en la pagina de perfil
2025-12-26 01:54:11 -03:00
emailerfacu-spec 09ddb0800c Merge pull request #33 from emailerfacu-spec/dev
Dev
2025-12-20 19:02:56 -03:00
emailerfacu-spec d60daa624c Merge pull request #24 from emailerfacu-spec/dev
Dev
2025-12-05 13:30:11 -03:00
emailerfacu-spec ee5535dbc6 Merge pull request #12 from emailerfacu-spec/dev
Dev
2025-11-26 20:14:27 -03:00
emailerfacu-spec 8c0da761e6 Merge pull request #6 from emailerfacu-spec/dev
snapshot 25/11/2025 - B
2025-11-25 19:21:35 -03:00
emailerfacu-spec ef16f649dd Merge pull request #5 from emailerfacu-spec/dev
snapshot 25/11/2025
2025-11-25 19:18:22 -03:00
emailerfacu-spec 8decf85d3b Merge pull request #1 from emailerfacu-spec/dev
Login, Register y Logout Terminado
2025-11-14 22:24:55 -03:00
12 changed files with 275 additions and 220 deletions
BIN
View File
Binary file not shown.
+6 -9
View File
@@ -11,7 +11,7 @@
import { seguirUsuario } from '@/hooks/seguirUsuario'; import { seguirUsuario } from '@/hooks/seguirUsuario';
import type { Post } from '../../types'; import type { Post } from '../../types';
import CardError from './CardError.svelte'; import CardError from './CardError.svelte';
import { cacheSeguidos } from '@/stores/cacheSeguidos.svelte'; import { cacheSeguidos } from '@/stores/cacheSeguidos.js';
let { let {
post, post,
@@ -41,16 +41,13 @@
}); });
async function cargarSeguido() { async function cargarSeguido() {
let a = cacheSeguidos.get(post.authorId); seguido = await cacheSeguidos.getOrFetch(
if (a === undefined) { post.authorId,
async () => {
const seguidoStatus = await esSeguido(post as Post); const seguidoStatus = await esSeguido(post as Post);
if (seguidoStatus) { return seguidoStatus?.isFollowing || false;
cacheSeguidos.set(post.authorId, seguidoStatus.isFollowing || false);
seguido = seguidoStatus.isFollowing || false;
} }
return; );
}
seguido = a;
} }
let mensajeError: string | null = $state(null); let mensajeError: string | null = $state(null);
+67 -59
View File
@@ -16,12 +16,7 @@
import TooltipTrigger from './ui/tooltip/tooltip-trigger.svelte'; import TooltipTrigger from './ui/tooltip/tooltip-trigger.svelte';
import TooltipContent from './ui/tooltip/tooltip-content.svelte'; import TooltipContent from './ui/tooltip/tooltip-content.svelte';
import RecuperarContraseña from './admin/RecuperarContraseña.svelte'; import RecuperarContraseña from './admin/RecuperarContraseña.svelte';
import { Dialog } from './ui/dialog';
import DialogContent from './ui/dialog/dialog-content.svelte';
import ModificarUsuario from './admin/ModificarUsuario.svelte'; import ModificarUsuario from './admin/ModificarUsuario.svelte';
import { fade } from 'svelte/transition';
import type { Unsubscriber } from 'svelte/store';
import Input from './ui/input/input.svelte';
import Trash_2 from '@lucide/svelte/icons/trash-2'; import Trash_2 from '@lucide/svelte/icons/trash-2';
import BorrarUsuario from './BorrarUsuario.svelte'; import BorrarUsuario from './BorrarUsuario.svelte';
import InputGroup from './ui/input-group/input-group.svelte'; import InputGroup from './ui/input-group/input-group.svelte';
@@ -30,12 +25,25 @@
import AgregarUsuario from './admin/AgregarUsuario.svelte'; import AgregarUsuario from './admin/AgregarUsuario.svelte';
import DarAdmin from './admin/DarAdmin.svelte'; import DarAdmin from './admin/DarAdmin.svelte';
import { busquedaAdminUsuarios } from '@/hooks/busquedaAdminUsuarios'; import { busquedaAdminUsuarios } from '@/hooks/busquedaAdminUsuarios';
import { invalidate, replaceState } from '$app/navigation';
interface Props { interface Props {
usuarios: UserResponseDto[]; usuarios: UserResponseDto[];
hayMas: boolean;
} }
let { usuarios = $bindable() }: Props = $props(); let { usuarios = $bindable(), hayMas }: Props = $props();
let paginaActual = $derived.by(() => {
const url = new URL(window.location.href);
return Number(url.searchParams.get('p')) || 1;
});
let search = $derived.by(() => {
const url = new URL(window.location.href);
let ret = url.searchParams.get('q') || '';
return ret;
});
let hayMass = $derived(hayMas);
let open = $state(false); let open = $state(false);
let openModificarUsuario = $state(false); let openModificarUsuario = $state(false);
@@ -48,13 +56,11 @@
let usuarioModificar: UserResponseDto | null = $state(null); let usuarioModificar: UserResponseDto | null = $state(null);
let usuarioDarAdmin: UserResponseDto | null = $state(null); let usuarioDarAdmin: UserResponseDto | null = $state(null);
let search = $state('');
type SortKey = 'username' | 'displayName' | 'postsCount' | 'createdAt'; type SortKey = 'username' | 'displayName' | 'postsCount' | 'createdAt';
let sortBy = $state<SortKey | null>(null); let sortBy = $state<SortKey | null>(null);
let sortDirection = $state<'asc' | 'desc'>('asc'); let sortDirection = $state<'asc' | 'desc'>('asc');
let usuariosFiltrados = $state(usuarios); let usuariosFiltrados = $derived(usuarios);
function ordenarPor(campo: SortKey) { function ordenarPor(campo: SortKey) {
if (sortBy === campo) { if (sortBy === campo) {
@@ -90,41 +96,26 @@
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; const url = new URL(window.location.href);
if (search === '') { if (!search.trim()) {
usuariosFiltrados = usuarios; url.searchParams.delete('q');
return; } else {
url.searchParams.set('q', search);
} }
usuariosFiltrados = await busquedaAdminUsuarios(search); replaceState(url, {});
let ret = await busquedaAdminUsuarios(search, ITEMS_POR_PAGINA, paginaActual);
usuariosFiltrados = ret.usuarios;
// invalidate('admin:load');
hayMass = ret.hayMas;
}, 200); }, 200);
return () => { return () => {
@@ -133,13 +124,9 @@
} }
const ITEMS_POR_PAGINA = 5; const ITEMS_POR_PAGINA = 5;
let paginaActual = $state(1); // const usuariosPaginados = $derived(
// usuariosFiltrados.slice((paginaActual - 1) * ITEMS_POR_PAGINA, paginaActual * ITEMS_POR_PAGINA)
const totalPaginas = $derived(Math.ceil(usuariosFiltrados.length / 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 +171,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 +184,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 +197,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 +212,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 +231,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 +255,29 @@
</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={() => {
const url = new URL(window.location.href);
<div class="flex gap-2"> url.searchParams.set('p', String(--paginaActual));
<Button disabled={paginaActual === 1} onclick={() => paginaActual--} variant="secondary"> replaceState(url, {});
buscarUsuarios();
}}
variant="secondary"
>
Anterior Anterior
</Button> </Button>
<Button <Button
disabled={paginaActual === totalPaginas || totalPaginas === 0} disabled={!hayMass}
onclick={() => paginaActual++} onclick={() => {
variant="secondary" const url = new URL(window.location.href);
url.searchParams.set('p', String(++paginaActual));
replaceState(url, {});
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} />
-6
View File
@@ -30,13 +30,7 @@
}} }}
> >
<DialogContent> <DialogContent>
<div
class={esExitoso
? 'rounded border border-green-400 bg-green-100/10 px-4 py-3 text-green-700'
: 'rounded border border-red-400 bg-red-100/10 px-4 py-3 text-red-700'}
>
{mensajeResultado} {mensajeResultado}
</div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{/if} {/if}
@@ -10,6 +10,7 @@
import Label from '../ui/label/label.svelte'; import Label from '../ui/label/label.svelte';
import Spinner from '../ui/spinner/spinner.svelte'; import Spinner from '../ui/spinner/spinner.svelte';
import { updateUsuario } from '@/hooks/updateUsuario'; import { updateUsuario } from '@/hooks/updateUsuario';
import { invalidate } from '$app/navigation';
interface Prop { interface Prop {
open: boolean; open: boolean;
@@ -38,6 +39,7 @@
error = ret; error = ret;
} else { } else {
usuario!.displayName = ret.displayName; usuario!.displayName = ret.displayName;
invalidate('admin:load');
open = false; open = false;
} }
cargando = false; cargando = false;
+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 };
}
+8 -3
View File
@@ -2,15 +2,20 @@ 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, fetch2?: Function) {
try { try {
const response = await fetch(get(apiBase) + '/api/admin/users?q=' + q, { const fetchFn = fetch2 ? fetch2 : fetch;
const response = await fetchFn(
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();
} }
+1 -1
View File
@@ -1,5 +1,5 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { cacheSeguidos } from '@/stores/cacheSeguidos.svelte'; import { cacheSeguidos } from '@/stores/cacheSeguidos';
import { apiBase } from '@/stores/url'; 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';
+134
View File
@@ -0,0 +1,134 @@
import { browser } from '$app/environment';
import { writable } from 'svelte/store';
class FollowCache {
constructor() {
if (browser) {
this.loadFromStorage();
}
}
/** @type {Map<string, boolean | Promise<boolean>>} */
#cache = new Map();
/** @type {import('svelte/store').Writable<Map<string, boolean>>} */
store = writable(new Map());
/** @param {string} userId */
get(userId) {
const value = this.#cache.get(userId);
return value instanceof Promise ? undefined : value;
}
/** @param {string} userId */
has(userId) {
return this.#cache.has(userId);
}
/**
* @param {string} userId
* @param {() => Promise<boolean>} fetchFn
*/
async getOrFetch(userId, fetchFn) {
const existing = this.#cache.get(userId);
if (existing !== undefined) {
if (existing instanceof Promise) {
return existing;
}
return existing;
}
const promise = fetchFn()
.then((result) => {
this.#setFinal(userId, result);
return result;
})
.catch((err) => {
this.#cache.delete(userId);
this.#updateStore();
throw err;
});
this.#cache.set(userId, promise);
this.#updateStore();
return promise;
}
/**
* @param {string} userId
* @param {boolean} isFollowed
*/
set(userId, isFollowed) {
this.#setFinal(userId, isFollowed);
}
/**
* @param {string} userId
* @param {boolean} value
*/
#setFinal(userId, value) {
this.#cache.set(userId, value);
this.#updateStore();
this.saveToStorage();
if (browser) {
window.dispatchEvent(
new CustomEvent('followCacheUpdated', {
detail: { userId, isFollowed: value }
})
);
}
}
#updateStore() {
const filtered = Array.from(this.#cache.entries())
.filter(([_, v]) => typeof v === 'boolean');
this.store.set(
/** @type {Map<string, boolean>} */
(new Map(filtered))
);
}
/** @param {string} userId */
delete(userId) {
this.#cache.delete(userId);
this.#updateStore();
this.saveToStorage();
}
clear() {
this.#cache.clear();
this.store.set(new Map());
this.saveToStorage();
}
saveToStorage() {
if (!browser) return;
const filtered = Array.from(this.#cache.entries())
.filter(([_, v]) => typeof v === 'boolean');
const data = Object.fromEntries(filtered);
sessionStorage.setItem('follow-cache', JSON.stringify(data));
}
loadFromStorage() {
if (!browser) return;
try {
const stored = sessionStorage.getItem('follow-cache');
if (!stored) return;
const data = JSON.parse(stored);
this.#cache = new Map(Object.entries(data));
this.#updateStore();
} catch (err) {
console.error('Error cargando follow-cache:', err);
}
}
}
export const cacheSeguidos = new FollowCache();
-105
View File
@@ -1,105 +0,0 @@
import { browser } from '$app/environment';
import { writable } from 'svelte/store';
class FollowCache {
constructor() {
if (browser) {
this.loadFromStorage();
}
}
/** @type {Map<string, boolean>} */
#cache = new Map();
/** @type {import('svelte/store').Writable<Map<string, boolean>>} */
store = writable(this.#cache);
/**
* @param {string} userId
* @returns {boolean | undefined}
*/
get(userId) {
return this.#cache.get(userId);
}
/**
* @param {string} userId
* @param {boolean} isFollowed
*/
set(userId, isFollowed) {
this.#cache.set(userId, isFollowed);
this.store.set(this.#cache);
this.saveToStorage();
if (browser) {
window.dispatchEvent(
new CustomEvent('followCacheUpdated', {
detail: { userId, isFollowed }
})
);
}
}
/**
* @param {string} userId
* @returns {boolean}
*/
has(userId) {
return this.#cache.has(userId);
}
/**
* @param {string} userId
*/
delete(userId) {
this.#cache.delete(userId);
this.store.set(this.#cache);
this.saveToStorage();
if (browser) {
window.dispatchEvent(
new CustomEvent('followCacheUpdated', {
detail: { userId, isFollowed: false }
})
);
}
}
clear() {
this.#cache.clear();
this.store.set(this.#cache);
this.saveToStorage();
if (browser) {
window.dispatchEvent(
new CustomEvent('followCacheUpdated', {
detail: { clearAll: true }
})
);
}
}
saveToStorage() {
if (browser) {
const data = Object.fromEntries(this.#cache);
sessionStorage.setItem('follow-cache', JSON.stringify(data));
}
}
loadFromStorage() {
if (browser) {
try {
const stored = sessionStorage.getItem('follow-cache');
if (stored) {
const data = JSON.parse(stored);
this.#cache = new Map(Object.entries(data));
this.store.set(this.#cache);
}
} catch (error) {
console.error('Error cargando desde sesion:', error);
}
}
}
}
export const cacheSeguidos = new FollowCache();
+3 -7
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import CardContent from '@/components/ui/card/card-content.svelte'; import CardContent from '@/components/ui/card/card-content.svelte';
import Card from '@/components/ui/card/card.svelte'; import Card from '@/components/ui/card/card.svelte';
import CardDescription from '@/components/ui/card/card-description.svelte';
import TablaUsuarios from '@/components/TablaUsuarios.svelte'; import TablaUsuarios from '@/components/TablaUsuarios.svelte';
import CardTitle from '@/components/ui/card/card-title.svelte'; import CardTitle from '@/components/ui/card/card-title.svelte';
import CardHeader from '@/components/ui/card/card-header.svelte'; import CardHeader from '@/components/ui/card/card-header.svelte';
@@ -9,7 +8,8 @@
interface Prop { interface Prop {
data: { data: {
usuarios?: UserResponseDto[]; usuarios: UserResponseDto[];
hayMas: boolean;
error: boolean; error: boolean;
}; };
} }
@@ -28,11 +28,7 @@
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{#if data.usuarios?.length === 0} <TablaUsuarios usuarios={data.usuarios} hayMas={data.hayMas}></TablaUsuarios>
<CardDescription>No hay usuarios que mostar</CardDescription>
{:else}
<TablaUsuarios usuarios={data.usuarios || []}></TablaUsuarios>
{/if}
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
+19 -20
View File
@@ -1,28 +1,27 @@
import { apiBase } from '@/stores/url.js'; import { busquedaAdminUsuarios } from '@/hooks/busquedaAdminUsuarios.js';
import { sesionStore } from '@/stores/usuario'; import type { PageLoad } from './$types.js';
import { redirect } from '@sveltejs/kit'; import { fetchUsuariosAdmin } from '@/hooks/UsuariosAdmin.js';
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, fetch }) => {
depends('admin:load'); depends('admin:load');
const response = await fetch(get(apiBase) + '/api/admin/users', { let url = new URL(location.href);
method: 'GET', let query = url.searchParams.get('q') ?? '';
headers: { let page = Number(url.searchParams.get('p'));
'Content-Type': 'application/json', if (isNaN(page) || page < 1) {
Authorization: `Bearer ${get(sesionStore)?.accessToken}` page = 1;
} }
});
if (response.status === 401) { const result = await busquedaAdminUsuarios(query, 5, page, fetch);
throw redirect(302, '/');
} if (result.error) {
if (!response.ok) {
return { error: true }; return { error: true };
} }
const usuarios: UserResponseDto[] = await response.json(); return {
usuarios: result.usuarios,
return { usuarios, error: false }; hayMas: result.hayMas,
} error: false
};
};