mirror of
https://github.com/emailerfacu-spec/minix-front.git
synced 2026-04-20 16:17:32 -03:00
Añadido el editar usuarios
This commit is contained in:
@@ -15,19 +15,23 @@
|
|||||||
import RecuperarContraseña from './admin/RecuperarContraseña.svelte';
|
import RecuperarContraseña from './admin/RecuperarContraseña.svelte';
|
||||||
import { Dialog } from './ui/dialog';
|
import { Dialog } from './ui/dialog';
|
||||||
import DialogContent from './ui/dialog/dialog-content.svelte';
|
import DialogContent from './ui/dialog/dialog-content.svelte';
|
||||||
|
import ModificarUsuario from './admin/ModificarUsuario.svelte';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
usuarios: UserResponseDto[];
|
usuarios: UserResponseDto[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let { usuarios }: Props = $props();
|
let { usuarios = $bindable() }: Props = $props();
|
||||||
|
|
||||||
let open = $state(false);
|
let open = $state(false);
|
||||||
const openModificarUsuario = $state(false);
|
let openModificarUsuario = $state(false);
|
||||||
|
|
||||||
//si ponia contraseña en español quedaba muy largo el nombre
|
//si ponia contraseña en español quedaba muy largo el nombre
|
||||||
let usuarioCambioPass: UserResponseDto | null = $state(null);
|
let usuarioCambioPass: UserResponseDto | null = $state(null);
|
||||||
|
|
||||||
|
let usuarioModificar: UserResponseDto | null = $state(null);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
usuarioCambioPass = null;
|
usuarioCambioPass = null;
|
||||||
@@ -38,9 +42,13 @@
|
|||||||
open = true;
|
open = true;
|
||||||
usuarioCambioPass = usuario;
|
usuarioCambioPass = usuario;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleModificar(usuario: UserResponseDto) {
|
||||||
|
openModificarUsuario = true;
|
||||||
|
usuarioModificar = usuario;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- {$inspect(usuarios)} -->
|
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -73,7 +81,7 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Button><UserPen /></Button>
|
<Button onclick={() => handleModificar(usuario)}><UserPen /></Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>Modificar Usuario</p>
|
<p>Modificar Usuario</p>
|
||||||
@@ -86,3 +94,4 @@
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<RecuperarContraseña bind:open usuario={usuarioCambioPass} />
|
<RecuperarContraseña bind:open usuario={usuarioCambioPass} />
|
||||||
|
<ModificarUsuario bind:open={openModificarUsuario} bind:usuario={usuarioModificar} />
|
||||||
|
|||||||
101
src/lib/components/admin/ModificarUsuario.svelte
Normal file
101
src/lib/components/admin/ModificarUsuario.svelte
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { Dialog, DialogTitle, DialogContent, DialogHeader } from '../ui/dialog';
|
||||||
|
import InputGroup from '../ui/input-group/input-group.svelte';
|
||||||
|
import InputGroupInput from '../ui/input-group/input-group-input.svelte';
|
||||||
|
import InputGroupAddon from '../ui/input-group/input-group-addon.svelte';
|
||||||
|
import Button from '../ui/button/button.svelte';
|
||||||
|
import type { UserResponseDto } from '../../../types';
|
||||||
|
import Checkbox from '../ui/checkbox/checkbox.svelte';
|
||||||
|
import Label from '../ui/label/label.svelte';
|
||||||
|
import Spinner from '../ui/spinner/spinner.svelte';
|
||||||
|
import { updateUsuario } from '@/hooks/updateUsuario';
|
||||||
|
|
||||||
|
interface Prop {
|
||||||
|
open: boolean;
|
||||||
|
usuario: UserResponseDto | null;
|
||||||
|
}
|
||||||
|
let { open = $bindable(), usuario = $bindable() }: Prop = $props();
|
||||||
|
|
||||||
|
let imagen = $state(!!usuario?.profileImageUrl);
|
||||||
|
let fallback = usuario?.displayName;
|
||||||
|
|
||||||
|
let cargando = $state(false);
|
||||||
|
let error = $state('');
|
||||||
|
async function onsubmit(e: SubmitEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
cargando = true;
|
||||||
|
let ret: { displayName: string } | string = await updateUsuario({
|
||||||
|
id: usuario?.id || '',
|
||||||
|
bio: usuario?.bio || '',
|
||||||
|
displayName: usuario?.displayName || '',
|
||||||
|
oldImageUrl: usuario?.profileImageUrl || '',
|
||||||
|
profileImage: imagen
|
||||||
|
});
|
||||||
|
if (typeof ret === 'string') {
|
||||||
|
error = ret;
|
||||||
|
} else {
|
||||||
|
usuario!.displayName = ret.displayName;
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
cargando = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div transition:fade>
|
||||||
|
<Dialog
|
||||||
|
{open}
|
||||||
|
onOpenChange={() => {
|
||||||
|
open = !open;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Modificar Usuario</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form {onsubmit}>
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupInput disabled={cargando} bind:value={usuario!.displayName} />
|
||||||
|
<InputGroupAddon>Nombre</InputGroupAddon>
|
||||||
|
</InputGroup>
|
||||||
|
<InputGroup>
|
||||||
|
<InputGroupInput disabled={cargando} bind:value={usuario!.bio} />
|
||||||
|
<InputGroupAddon>Bio</InputGroupAddon>
|
||||||
|
</InputGroup>
|
||||||
|
<div class="ms-1 flex gap-2">
|
||||||
|
<Checkbox disabled={usuario?.profileImageUrl == null || cargando} bind:checked={imagen}
|
||||||
|
></Checkbox>
|
||||||
|
<Label>
|
||||||
|
{#if usuario?.profileImageUrl == null}
|
||||||
|
No tiene imagen de perfil
|
||||||
|
{:else if imagen}
|
||||||
|
Borrar imagen
|
||||||
|
{:else}
|
||||||
|
Mantener imagen
|
||||||
|
{/if}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<hr class="my-2" />
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<Button type="submit" disabled={cargando}>
|
||||||
|
{#if cargando}
|
||||||
|
<Spinner /> Cargando...
|
||||||
|
{:else}
|
||||||
|
Aceptar
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" disabled={cargando}>Cerrar</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
<div transition:fade>
|
||||||
|
<Dialog open={error != ''} onOpenChange={() => (error = '')}>
|
||||||
|
<DialogContent>
|
||||||
|
{error}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
36
src/lib/components/ui/checkbox/checkbox.svelte
Normal file
36
src/lib/components/ui/checkbox/checkbox.svelte
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||||
|
import CheckIcon from "@lucide/svelte/icons/check";
|
||||||
|
import MinusIcon from "@lucide/svelte/icons/minus";
|
||||||
|
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
checked = $bindable(false),
|
||||||
|
indeterminate = $bindable(false),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CheckboxPrimitive.Root
|
||||||
|
bind:ref
|
||||||
|
data-slot="checkbox"
|
||||||
|
class={cn(
|
||||||
|
"border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-xs peer flex size-4 shrink-0 items-center justify-center rounded-[4px] border outline-none transition-shadow focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
bind:checked
|
||||||
|
bind:indeterminate
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#snippet children({ checked, indeterminate })}
|
||||||
|
<div data-slot="checkbox-indicator" class="text-current transition-none">
|
||||||
|
{#if checked}
|
||||||
|
<CheckIcon class="size-3.5" />
|
||||||
|
{:else if indeterminate}
|
||||||
|
<MinusIcon class="size-3.5" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
</CheckboxPrimitive.Root>
|
||||||
6
src/lib/components/ui/checkbox/index.ts
Normal file
6
src/lib/components/ui/checkbox/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import Root from "./checkbox.svelte";
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
//
|
||||||
|
Root as Checkbox,
|
||||||
|
};
|
||||||
44
src/lib/hooks/updateUsuario.ts
Normal file
44
src/lib/hooks/updateUsuario.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { apiBase } from "@/stores/url"
|
||||||
|
import { sesionStore } from "@/stores/usuario"
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
|
export interface AdminUpdateUsuario {
|
||||||
|
id:string,
|
||||||
|
displayName: string,
|
||||||
|
bio: string,
|
||||||
|
profileImage:boolean,
|
||||||
|
oldImageUrl:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUsuario(usuario: AdminUpdateUsuario) {
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('displayName', usuario.displayName);
|
||||||
|
formData.append('bio', usuario.bio);
|
||||||
|
if (usuario.profileImage) {
|
||||||
|
formData.append('profileImageUrl', usuario.oldImageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const req = await fetch(get(apiBase) + "/api/users/"+usuario.id, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${get(sesionStore)?.accessToken}`
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
if (req.status === 204) {
|
||||||
|
let ret = {
|
||||||
|
// bio: usuario.bio,
|
||||||
|
displayName: usuario.displayName,
|
||||||
|
// oldImageUrl: usuario.oldImageUrl,
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
const dataa = await req.json();
|
||||||
|
|
||||||
|
return dataa.message;
|
||||||
|
} catch {
|
||||||
|
return "No se pudo alcanzar el servidor"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
import CardHeader from '@/components/ui/card/card-header.svelte';
|
import CardHeader from '@/components/ui/card/card-header.svelte';
|
||||||
|
|
||||||
let cargando = $state(true);
|
let cargando = $state(true);
|
||||||
|
let usuarios = $state(page.data.usuarios);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex min-h-fit w-full items-center justify-center p-6 md:p-10">
|
<div class="flex min-h-fit w-full items-center justify-center p-6 md:p-10">
|
||||||
@@ -22,15 +23,9 @@
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{#if page.data.usuarios.length === 0}
|
{#if page.data.usuarios.length === 0}
|
||||||
{#if page.data.error}
|
<CardDescription>No hay posts que mostar</CardDescription>
|
||||||
<CardDescription class="flex items-center justify-center space-x-2 text-destructive">
|
|
||||||
<span class="text-sm">Error al cargar usuarios.</span>
|
|
||||||
</CardDescription>
|
|
||||||
{:else}
|
|
||||||
<CardDescription>No hay posts que mostar</CardDescription>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
<TablaUsuarios usuarios={page.data.usuarios}></TablaUsuarios>
|
<TablaUsuarios bind:usuarios></TablaUsuarios>
|
||||||
{/if}
|
{/if}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Reference in New Issue
Block a user