mirror of
https://github.com/emailerfacu-spec/minix-front.git
synced 2026-04-17 15:47:31 -03:00
hecha funcionalidad completa del cambio de contraseña
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { goto, replaceState } from '$app/navigation';
|
||||||
import Card from '@/components/ui/card/card.svelte';
|
import Card from '@/components/ui/card/card.svelte';
|
||||||
import { Content } from '@/components/ui/card';
|
import { Content } from '@/components/ui/card';
|
||||||
import { sesionStore } from '@/stores/usuario';
|
import { sesionStore } from '@/stores/usuario';
|
||||||
@@ -11,6 +12,9 @@
|
|||||||
import { fade, slide } from 'svelte/transition';
|
import { fade, slide } from 'svelte/transition';
|
||||||
import { getPosts } from '@/hooks/getPosts';
|
import { getPosts } from '@/hooks/getPosts';
|
||||||
import Spinner from '@/components/ui/spinner/spinner.svelte';
|
import Spinner from '@/components/ui/spinner/spinner.svelte';
|
||||||
|
import { page } from '$app/state';
|
||||||
|
import Dialog from '@/components/ui/dialog/dialog.svelte';
|
||||||
|
import DialogContent from '@/components/ui/dialog/dialog-content.svelte';
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
resetPosts();
|
resetPosts();
|
||||||
@@ -33,8 +37,23 @@
|
|||||||
);
|
);
|
||||||
postAModificar = null;
|
postAModificar = null;
|
||||||
}
|
}
|
||||||
|
let from = $state(page.url.searchParams.get('from'));
|
||||||
|
$effect(() => {
|
||||||
|
goto('/', { replaceState: true });
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if from == 'cambio_contraseña'}
|
||||||
|
<Dialog
|
||||||
|
open={true}
|
||||||
|
onOpenChange={() => {
|
||||||
|
from = '';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent>Se cambio la contraseña del usuario exitosamente</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<meta property="og:title" content="Mini-x" />
|
<meta property="og:title" content="Mini-x" />
|
||||||
<meta property="og:description" content="Pagina Principal" />
|
<meta property="og:description" content="Pagina Principal" />
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
let estado: 'email' | 'otp' | 'nuevapass' = $state('email');
|
let estado: 'email' | 'otp' | 'nuevapass' = $state('email');
|
||||||
let email: string = $state('');
|
let email: string = $state('');
|
||||||
|
let otp: string = $state('');
|
||||||
</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,10 +23,12 @@
|
|||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
{#if estado === 'email'}
|
{#if estado === 'email'}
|
||||||
<IngresarEmail bind:estado bind:email />
|
<IngresarEmail bind:estado bind:email />
|
||||||
{:else if estado === 'otp'}
|
{/if}
|
||||||
<Otp bind:estado />
|
{#if estado === 'otp'}
|
||||||
{:else if estado === 'nuevapass'}
|
<Otp bind:estado {email} bind:otp />
|
||||||
<NuevaPass />
|
{/if}
|
||||||
|
{#if estado === 'nuevapass'}
|
||||||
|
<NuevaPass {otp} {email} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import CardError from '@/components/CardError.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
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 DialogContent from '@/components/ui/dialog/dialog-content.svelte';
|
||||||
|
import Dialog from '@/components/ui/dialog/dialog.svelte';
|
||||||
import Input from '@/components/ui/input/input.svelte';
|
import Input from '@/components/ui/input/input.svelte';
|
||||||
import Spinner from '@/components/ui/spinner/spinner.svelte';
|
import Spinner from '@/components/ui/spinner/spinner.svelte';
|
||||||
import { checkEmail } from '@/hooks/checkEmail';
|
import { checkEmail } from '@/hooks/checkEmail';
|
||||||
|
import { apiBase } from '@/stores/url';
|
||||||
import Check from '@lucide/svelte/icons/check';
|
import Check from '@lucide/svelte/icons/check';
|
||||||
import Cross from '@lucide/svelte/icons/x';
|
import Cross from '@lucide/svelte/icons/x';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
@@ -13,24 +17,43 @@
|
|||||||
|
|
||||||
let checkeado = $state<Boolean | null>(null);
|
let checkeado = $state<Boolean | null>(null);
|
||||||
let esEmailExistente = $state<boolean>(false);
|
let esEmailExistente = $state<boolean>(false);
|
||||||
|
let mensajeError = $state('');
|
||||||
|
let lastemail: string;
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (email == '') {
|
if (email == '' || !email.includes('@')) {
|
||||||
checkeado = null;
|
checkeado = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
||||||
(async () => {
|
(async () => {
|
||||||
checkeado = true;
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
await Promise.all([checkEmaill(), new Promise((resolve) => setTimeout(resolve, 100))]);
|
timeoutId = setTimeout(async () => {
|
||||||
checkeado = false;
|
checkeado = true;
|
||||||
|
await checkEmaill();
|
||||||
|
checkeado = false;
|
||||||
|
}, 1000);
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function checkEmaill() {
|
async function checkEmaill() {
|
||||||
esEmailExistente = !(await checkEmail(email));
|
try {
|
||||||
|
if (lastemail == email) return;
|
||||||
|
lastemail = email;
|
||||||
|
esEmailExistente = !(await checkEmail(email));
|
||||||
|
} catch {
|
||||||
|
esEmailExistente = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if mensajeError}
|
||||||
|
<Dialog open={!!mensajeError} onOpenChange={() => (mensajeError = '')}>
|
||||||
|
<DialogContent>
|
||||||
|
<CardError {mensajeError} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
||||||
<div transition:slide>
|
<div transition:slide>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -47,16 +70,31 @@
|
|||||||
<Cross class="text-red-500" />
|
<Cross class="text-red-500" />
|
||||||
{/if}
|
{/if}
|
||||||
</h2>
|
</h2>
|
||||||
<Input type="email" placeholder="correo@ejemplo.com" bind:value={email} />
|
<form
|
||||||
<Button
|
onsubmit={async (e) => {
|
||||||
disabled={!esEmailExistente}
|
e.preventDefault();
|
||||||
onclick={async () => {
|
try {
|
||||||
///WIP
|
const formData = new FormData();
|
||||||
estado = 'otp';
|
formData.append('email', email);
|
||||||
|
const req = await fetch(`${$apiBase}/api/password-reset/otp`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
if (req.ok) {
|
||||||
|
estado = 'otp';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mensajeError = await req.text();
|
||||||
|
} catch {
|
||||||
|
mensajeError = 'No se pudo alcanzar el servidor';
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Enviar código
|
<div class="flex flex-col gap-2">
|
||||||
</Button>
|
<Input type="email" placeholder="correo@ejemplo.com" bind:value={email} />
|
||||||
|
<Button type="submit" disabled={!esEmailExistente}>Enviar código</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,17 +1,66 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import CardError from '@/components/CardError.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
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 DialogContent from '@/components/ui/dialog/dialog-content.svelte';
|
||||||
|
import Dialog from '@/components/ui/dialog/dialog.svelte';
|
||||||
import Input from '@/components/ui/input/input.svelte';
|
import Input from '@/components/ui/input/input.svelte';
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
|
import { apiBase } from '@/stores/url';
|
||||||
|
import { sesionStore } from '@/stores/usuario';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
|
|
||||||
|
let { otp, email } = $props();
|
||||||
|
|
||||||
|
let pass = $state('');
|
||||||
|
let pass2 = $state('');
|
||||||
|
let coinsiden = $derived(
|
||||||
|
pass === pass2 &&
|
||||||
|
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9])[A-Za-z\d\W_]*$/.test(pass) &&
|
||||||
|
pass.length > 8
|
||||||
|
);
|
||||||
|
let mensajeError = $state('');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if mensajeError}
|
||||||
|
<Dialog open={!!mensajeError} onOpenChange={() => (mensajeError = '')}>
|
||||||
|
<DialogContent>
|
||||||
|
<CardError {mensajeError} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div transition:slide>
|
<div transition:slide>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<h2 class="mb-4 text-2xl font-bold">Crear una Nueva Contraseña</h2>
|
<h2 class="mb-4 text-2xl font-bold">Crear una Nueva Contraseña</h2>
|
||||||
<form onsubmit={() => {}}>
|
<form
|
||||||
|
onsubmit={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('otp', otp);
|
||||||
|
formData.append('email', email);
|
||||||
|
formData.append('newpass', pass);
|
||||||
|
try {
|
||||||
|
const req = await fetch(`${$apiBase}/api/password-reset/change`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
if (req.ok) {
|
||||||
|
const token = await req.json();
|
||||||
|
sesionStore.set(token);
|
||||||
|
goto('/?from=cambio_contraseña');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = await req.text();
|
||||||
|
mensajeError = data;
|
||||||
|
} catch {
|
||||||
|
mensajeError = 'No se pudo alcanzar el servidor';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<Label for="password" class="mb-1 block text-sm font-medium">Contraseña</Label>
|
<Label for="password" class="mb-1 block text-sm font-medium">Contraseña</Label>
|
||||||
@@ -20,7 +69,12 @@
|
|||||||
id="password"
|
id="password"
|
||||||
class="w-full px-3 py-2"
|
class="w-full px-3 py-2"
|
||||||
placeholder="Ingresa tu nueva contraseña"
|
placeholder="Ingresa tu nueva contraseña"
|
||||||
|
bind:value={pass}
|
||||||
/>
|
/>
|
||||||
|
<p class="mt-2 text-sm text-gray-500">
|
||||||
|
La contraseña debe contener al menos una mayúscula, una minúscula, un número y un
|
||||||
|
carácter especial. Además de 8 chars de longitud.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label for="confirmPassword" class="mb-1 block text-sm font-medium"
|
<Label for="confirmPassword" class="mb-1 block text-sm font-medium"
|
||||||
@@ -31,9 +85,10 @@
|
|||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
class="w-full px-3 py-2"
|
class="w-full px-3 py-2"
|
||||||
placeholder="Repite tu nueva contraseña"
|
placeholder="Repite tu nueva contraseña"
|
||||||
|
bind:value={pass2}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit">Establecer Contraseña</Button>
|
<Button disabled={!coinsiden} type="submit">Establecer Contraseña</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -1,15 +1,38 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import CardError from '@/components/CardError.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
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 DialogContent from '@/components/ui/dialog/dialog-content.svelte';
|
||||||
|
import Dialog from '@/components/ui/dialog/dialog.svelte';
|
||||||
import InputOtpGroup from '@/components/ui/input-otp/input-otp-group.svelte';
|
import InputOtpGroup from '@/components/ui/input-otp/input-otp-group.svelte';
|
||||||
import InputOtpSlot from '@/components/ui/input-otp/input-otp-slot.svelte';
|
import InputOtpSlot from '@/components/ui/input-otp/input-otp-slot.svelte';
|
||||||
import InputOtp from '@/components/ui/input-otp/input-otp.svelte';
|
import InputOtp from '@/components/ui/input-otp/input-otp.svelte';
|
||||||
|
import { apiBase } from '@/stores/url';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
|
|
||||||
let { estado = $bindable() } = $props();
|
let { estado = $bindable(), email, otp = $bindable() } = $props();
|
||||||
|
|
||||||
|
let checkeado = $state(false);
|
||||||
|
let mensajeError = $state('');
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (otp && otp.length === 6) {
|
||||||
|
checkeado = true;
|
||||||
|
} else {
|
||||||
|
checkeado = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if mensajeError}
|
||||||
|
<Dialog open={!!mensajeError} onOpenChange={() => (mensajeError = '')}>
|
||||||
|
<DialogContent>
|
||||||
|
<CardError {mensajeError} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div transition:slide>
|
<div transition:slide>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -21,10 +44,10 @@
|
|||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<InputOtp maxlength={6}>
|
<InputOtp maxlength={6} bind:value={otp}>
|
||||||
{#snippet children({ cells })}
|
{#snippet children({ cells })}
|
||||||
<InputOtpGroup>
|
<InputOtpGroup>
|
||||||
{#each cells as cell, i}
|
{#each cells as cell}
|
||||||
<InputOtpSlot class="p-3!" {cell}></InputOtpSlot>
|
<InputOtpSlot class="p-3!" {cell}></InputOtpSlot>
|
||||||
{/each}
|
{/each}
|
||||||
</InputOtpGroup>
|
</InputOtpGroup>
|
||||||
@@ -33,7 +56,30 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<Button onclick={() => (estado = 'nuevapass')}>Verificar</Button>
|
<Button
|
||||||
|
disabled={!checkeado}
|
||||||
|
onclick={async () => {
|
||||||
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('otp', otp);
|
||||||
|
formData.append('email', email);
|
||||||
|
let req = await fetch(`${$apiBase}/api/password-reset/otp/check`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
if (req.ok) {
|
||||||
|
estado = 'nuevapass';
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
mensajeError = await req.text();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
mensajeError = 'No se pudo alcanzar el servidor';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Verificar
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button variant="link" onclick={() => console.log('Reenviar código')}>
|
<Button variant="link" onclick={() => console.log('Reenviar código')}>
|
||||||
Reenviar código de verificación
|
Reenviar código de verificación
|
||||||
|
|||||||
Reference in New Issue
Block a user