feat: agregar login y register funcionales
This commit is contained in:
parent
e02de65ec2
commit
e2cdab8b11
3 changed files with 203 additions and 66 deletions
|
|
@ -3,12 +3,47 @@ import { createContext, useState } from "react";
|
|||
export const UserContext = createContext();
|
||||
|
||||
const UserProvider = ({ children }) => {
|
||||
const [token, setToken] = useState(true);
|
||||
const [token, setToken] = useState(localStorage.getItem("loginToken"));
|
||||
const [email, setEmail] = useState(null);
|
||||
|
||||
const storeData = (email, token) => {
|
||||
setEmail(email);
|
||||
setToken(token);
|
||||
localStorage.setItem("loginToken", data.token);
|
||||
};
|
||||
const login = async (user, password) => {
|
||||
const res = await fetch("http://localhost:5000/api/auth/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email: user, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Error al iniciar sesión");
|
||||
}
|
||||
const data = await res.json();
|
||||
storeData(email, data.token);
|
||||
};
|
||||
const register = async (user, password) => {
|
||||
const res = await fetch("http://localhost:5000/api/auth/register", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email: user, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error("Error al registrarse");
|
||||
}
|
||||
const data = await res.json();
|
||||
storeData(email, data.token);
|
||||
};
|
||||
const logout = () => {
|
||||
setToken(false);
|
||||
localStorage.removeItem("loginToken");
|
||||
setToken(null);
|
||||
setEmail(null);
|
||||
};
|
||||
return (
|
||||
<UserContext.Provider value={{ token, setToken, logout }}>
|
||||
<UserContext.Provider
|
||||
value={{ token, setToken, email, setEmail, login, register, logout }}
|
||||
>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,39 +1,90 @@
|
|||
import { useState } from "react";
|
||||
// Librería recomendada en tutoría
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { useContext } from "react";
|
||||
// Librería recomendada en tutoría
|
||||
import { z } from "zod";
|
||||
import { UserContext } from "../context/UserContext";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
// Esquema de validación de login
|
||||
const loginSchema = z.object({
|
||||
user: z
|
||||
.email({ error: "Email inválido" })
|
||||
.nonempty({ error: "Se requiere un email" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { error: "La contraseña debe tener al menos 6 caracteres" }),
|
||||
});
|
||||
|
||||
const Login = () => {
|
||||
const [user, setUser] = useState("");
|
||||
const [pass, setPass] = useState("");
|
||||
const { login } = useContext(UserContext);
|
||||
const form = useForm({
|
||||
defaultValues: { user: "", password: "" },
|
||||
validators: { onChange: loginSchema },
|
||||
onSubmit: async ({ value: { user, password } }) => {
|
||||
try {
|
||||
await login(user, password);
|
||||
} catch (error) {
|
||||
alert("Error de login");
|
||||
}
|
||||
},
|
||||
});
|
||||
/**
|
||||
* @type {React.SubmitEventHandler<HTMLFormElement>}
|
||||
*/
|
||||
return (
|
||||
<div>
|
||||
<p>Usuario</p>
|
||||
<input
|
||||
type="text"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => setUser(ev.target.value)}
|
||||
<form
|
||||
onSubmit={(ev) => {
|
||||
ev.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<form.Field
|
||||
name="user"
|
||||
children={(field) => {
|
||||
return (
|
||||
<>
|
||||
<p>Usuario</p>
|
||||
<input
|
||||
type="text"
|
||||
className="bg-gray-200"
|
||||
value={field.value}
|
||||
onChange={(ev) => field.handleChange(ev.target.value)}
|
||||
/>
|
||||
{!field.state.meta.isValid && (
|
||||
<p className="text-red-300">
|
||||
{field.state.meta.errors.map((e) => e.message).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<p>Contraseña</p>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => setPass(ev.target.value)}
|
||||
/>
|
||||
<p>Confirmar contraseña</p>
|
||||
<button
|
||||
className="p-2 rounded-md bg-teal-400"
|
||||
onClick={() => {
|
||||
if (pass.length < 6) {
|
||||
alert("La contraseña debe tener por lo menos 6 caracteres");
|
||||
return;
|
||||
}
|
||||
if (user.length === 0) {
|
||||
alert("Se requiere un nombre de usuario");
|
||||
}
|
||||
alert("Autenticacion exitosa");
|
||||
<form.Field
|
||||
name="password"
|
||||
children={(field) => {
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
value={field.value}
|
||||
onChange={(ev) => field.handleChange(ev.target.value)}
|
||||
/>
|
||||
{!field.state.meta.isValid && (
|
||||
<p className="text-red-300">
|
||||
{field.state.meta.errors.map((e) => e.message).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
>
|
||||
/>
|
||||
<button className="p-2 rounded-md bg-teal-400" type="submit">
|
||||
Iniciar sesión
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +1,101 @@
|
|||
import { useState } from "react";
|
||||
import { useForm } from "@tanstack/react-form";
|
||||
import { useContext, useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { UserContext } from "../context/UserContext";
|
||||
|
||||
const registerSchema = z
|
||||
.object({
|
||||
user: z.email().nonempty({ error: "Se requiere un email" }),
|
||||
password: z
|
||||
.string()
|
||||
.min(6, { error: "la contraseña debe ser de al menos 6 caracteres" }),
|
||||
confirmPassword: z.string(),
|
||||
})
|
||||
.refine((data) => data.password === data.confirmPassword, {
|
||||
error: "las contraseñas no coinciden",
|
||||
path: ["confirmPassword"],
|
||||
});
|
||||
|
||||
const Register = () => {
|
||||
const [user, setUser] = useState("");
|
||||
const [pass, setPass] = useState("");
|
||||
const [confirmPass, setConfirmPass] = useState("");
|
||||
const { register } = useContext(UserContext);
|
||||
const form = useForm({
|
||||
defaultValues: { user: "", password: "", confirmPassword: "" },
|
||||
validators: { onChange: registerSchema },
|
||||
onSubmit: ({ value: { user, password, confirmPassword } }) => {
|
||||
try {
|
||||
register(user, password);
|
||||
} catch (error) {
|
||||
alert("Error de registro");
|
||||
}
|
||||
},
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<form
|
||||
onSubmit={(ev) => {
|
||||
ev.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<p>Usuario</p>
|
||||
<input
|
||||
type="text"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => setUser(ev.target.value)}
|
||||
<form.Field
|
||||
name="user"
|
||||
children={(field) => (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
className="bg-gray-200"
|
||||
value={field.value}
|
||||
onChange={(ev) => field.handleChange(ev.target.value)}
|
||||
/>
|
||||
{!field.state.meta.isValid && (
|
||||
<p className="text-red-300">
|
||||
{field.state.meta.errors.map((e) => e.message).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<p>Contraseña</p>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => setPass(ev.target.value)}
|
||||
<form.Field
|
||||
name="password"
|
||||
children={(field) => (
|
||||
<>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => field.handleChange(ev.target.value)}
|
||||
/>
|
||||
{!field.state.meta.isValid && (
|
||||
<p className="text-red-300">
|
||||
{field.state.meta.errors.map((e) => e.message).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<p>Confirmar contraseña</p>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => setConfirmPass(ev.target.value)}
|
||||
<form.Field
|
||||
name="confirmPassword"
|
||||
children={(field) => (
|
||||
<>
|
||||
<input
|
||||
type="password"
|
||||
className="bg-gray-200"
|
||||
onChange={(ev) => field.handleChange(ev.target.value)}
|
||||
/>
|
||||
{!field.state.meta.isValid && (
|
||||
<p className="text-red-300">
|
||||
{field.state.meta.errors.map((e) => e.message).join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<br />
|
||||
<button
|
||||
className="p-2 rounded-md bg-teal-400"
|
||||
onClick={() => {
|
||||
if (pass.length < 6) {
|
||||
alert("La contraseña debe tener por lo menos 6 caracteres");
|
||||
return;
|
||||
}
|
||||
if (pass !== confirmPass) {
|
||||
alert("Las contraseñas no coinciden");
|
||||
return;
|
||||
}
|
||||
if (user.length === 0) {
|
||||
alert("Se requiere un nombre de usuario");
|
||||
}
|
||||
alert("Autenticacion exitosa");
|
||||
}}
|
||||
>
|
||||
<button className="p-2 rounded-md bg-teal-400" type="submit">
|
||||
Registrarse
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue