feat: borrar usaurios

This commit is contained in:
2025-12-29 21:58:56 -03:00
parent 98b1b0d9f2
commit b7cfa77a91
7 changed files with 110 additions and 15 deletions

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import { Dialog } from './ui/dialog';
import DialogContent from './ui/dialog/dialog-content.svelte';
import DialogHeader from './ui/dialog/dialog-header.svelte';
import DialogTitle from './ui/dialog/dialog-title.svelte';
import Button from './ui/button/button.svelte';
import Spinner from './ui/spinner/spinner.svelte';
import { borrarUsuario } from '@/hooks/borrarUsuario';
import { invalidate } from '$app/navigation';
let cargando = $state(false);
let { usuario, open = $bindable() } = $props();
async function handleBorrar() {
if (!usuario) return;
await borrarUsuario(usuario.id);
open = false;
invalidate('admin:load');
}
</script>
<div transition:fade>
<Dialog {open} onOpenChange={() => (open = false)}>
<DialogContent>
<DialogHeader>
<DialogTitle>Borrar Usuario</DialogTitle>
</DialogHeader>
<p>¿Está seguro que desea borrar al usuario {usuario?.displayName}?</p>
<div class="mt-4 flex justify-between">
<Button variant="destructive" onclick={handleBorrar} disabled={cargando}>
{#if cargando}
<Spinner />
{:else}
Borrar
{/if}
</Button>
<Button variant="secondary" onclick={() => (open = false)} disabled={cargando}>
Cancelar
</Button>
</div>
</DialogContent>
</Dialog>
</div>

View File

@@ -99,12 +99,14 @@
<div class="flex flex-col">
<div class="flex items-center justify-between">
<div class="flex gap-3">
<a href={`/${post.authorName}`}>
<Avatar>
<AvatarImage src={post.authorImageUrl}></AvatarImage>
<AvatarFallback>{post.authorDisplayName[0].toUpperCase()}</AvatarFallback>
</Avatar>
</a>
{#if post.authorName !== '[deleted]'}
<a href={`/${post.authorName}`}>
<Avatar>
<AvatarImage src={post.authorImageUrl}></AvatarImage>
<AvatarFallback>{post.authorDisplayName[0].toUpperCase()}</AvatarFallback>
</Avatar>
</a>
{/if}
<div class="flex space-x-2">
<span class="text-lg font-medium">{post.authorDisplayName}</span>
<span class="text-lg text-muted-foreground">@{post.authorName}</span>

View File

@@ -19,6 +19,8 @@
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 BorrarUsuario from './BorrarUsuario.svelte';
interface Props {
usuarios: UserResponseDto[];
@@ -29,6 +31,9 @@
let open = $state(false);
let openModificarUsuario = $state(false);
let openBorrar = $state(false);
let usuarioBorrar: UserResponseDto | null = $state(null);
//si ponia contraseña en español quedaba muy largo el nombre
let usuarioCambioPass: UserResponseDto | null = $state(null);
@@ -84,12 +89,6 @@
return sortDirection === 'asc' ? '↑' : '↓';
}
$effect(() => {
if (!open) {
usuarioCambioPass = null;
}
});
function handleCambiarContraseña(usuario: UserResponseDto) {
open = true;
usuarioCambioPass = usuario;
@@ -99,6 +98,13 @@
openModificarUsuario = true;
usuarioModificar = usuario;
}
function handleBorrar(usuario: UserResponseDto) {
openBorrar = true;
usuarioBorrar = usuario;
}
// $inspect(usuarios);
</script>
<div class="mb-4">
@@ -156,11 +162,27 @@
<p>Modificar Usuario</p>
</TooltipContent>
</Tooltip>
<Button></Button>
<Tooltip>
<TooltipTrigger>
<Button
disabled={usuario.isAdmin}
onclick={() => handleBorrar(usuario)}
variant="destructive"><Trash_2 /></Button
>
</TooltipTrigger>
<TooltipContent>
{#if usuario.isAdmin}
No se pueden eliminar usuarios Admin
{:else}
Eliminar Usuario
{/if}
</TooltipContent>
</Tooltip>
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
<BorrarUsuario bind:open={openBorrar} usuario={usuarioBorrar} />
<RecuperarContraseña bind:open usuario={usuarioCambioPass} />
<ModificarUsuario bind:open={openModificarUsuario} bind:usuario={usuarioModificar} />

View File

@@ -0,0 +1,21 @@
import { apiBase } from '@/stores/url';
import { sesionStore } from '@/stores/usuario';
import { get } from 'svelte/store';
export async function borrarUsuario(id: string) {
try {
const req = await fetch(`${get(apiBase)}/api/users/${id}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${get(sesionStore)?.accessToken}`
}
});
if (req.ok) {
return true;
}
return false;
} catch {
return false;
}
}

View File

@@ -25,7 +25,9 @@
{#if page.data.usuarios.length === 0}
<CardDescription>No hay usuarios que mostar</CardDescription>
{:else}
<TablaUsuarios bind:usuarios></TablaUsuarios>
{#key page.data.usuarios}
<TablaUsuarios bind:usuarios></TablaUsuarios>
{/key}
{/if}
</CardContent>
</Card>

View File

@@ -6,7 +6,8 @@ import type { UserResponseDto } from '../../../types.js';
export const ssr = false;
export async function load({}) {
export async function load({ depends }) {
depends('admin:load');
const response = await fetch(get(apiBase) + '/api/admin/users', {
method: 'GET',
headers: {

1
src/types.d.ts vendored
View File

@@ -90,6 +90,7 @@ export interface UserResponseDto {
followingCount: number;
createdAt: string;
postsCount: number;
isAdmin?: bool;
}
export interface UsersResponseDto {
response: {