feat: agregar login y register funcionales

This commit is contained in:
Sofía Maturana 2026-05-18 09:44:14 -04:00
parent e02de65ec2
commit e2cdab8b11
3 changed files with 203 additions and 66 deletions

View file

@ -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>
);

View file

@ -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>
);
};

View file

@ -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>
);
};