Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 102 additions & 19 deletions frontend/src/components/features/auth/LoginForm.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,102 @@
/**
* Componente: LoginForm
* Propósito: Formulario de inicio de sesión con campos de email, password, checkbox remember me y botones de Sign in/Register.
* Uso:
* - LoginPage.jsx (único lugar donde se renderiza)
* Dependencias:
* - auth.css (.auth-form, .auth-field, .auth-label, .auth-input, .auth-form-footer, .auth-remember, .auth-checkbox, .auth-actions, .auth-button-primary, .auth-button-secondary)
*/

// frontend/src/components/features/auth/LoginForm.jsx

import { useState } from "react";
import { Link } from "react-router-dom";

const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function LoginForm() {
const handleSubmit = (event) => {
event.preventDefault();
// TODO: send login data to backend
const [formData, setFormData] = useState({
email: "",
password: "",
remember: false,
});

const [errors, setErrors] = useState({});

const validateField = (name, value) => {
let error = "";

switch (name) {
case "email":
if (!value.trim()) {
error = "El email es obligatorio";
} else if (!EMAIL_REGEX.test(value)) {
error = "El formato del email no es válido";
}
break;

case "password":
if (!value) {
error = "La contraseña es obligatoria";
} else if (value.length < 8) {
error = "La contraseña debe tener al menos 8 caracteres";
}
break;

default:
break;
}

return error;
};

const handleChange = (e) => {
const { name, type, value, checked } = e.target;

const fieldValue = type === "checkbox" ? checked : value;

setFormData((prev) => ({
...prev,
[name]: fieldValue,
}));

if (name !== "remember") {
setErrors((prev) => ({
...prev,
[name]: validateField(name, fieldValue),
}));
}
};

const isFormValid =
formData.email.trim() !== "" &&
formData.password.trim() !== "" &&
!errors.email &&
!errors.password;

const handleSubmit = (e) => {
e.preventDefault();

const newErrors = {
email: validateField("email", formData.email),
password: validateField("password", formData.password),
};

setErrors(newErrors);

if (newErrors.email || newErrors.password) return;

// TODO: enviar datos al backend
console.log("Login válido:", formData);
};

return (
<form className="auth-form" onSubmit={handleSubmit}>
<form className="auth-form" onSubmit={handleSubmit} noValidate>
<div className="auth-field">
<label className="auth-label" htmlFor="login-email">
Email
</label>
<input
id="login-email"
name="email"
type="email"
className="auth-input"
placeholder="SomosLaHostia@SuperKode.com"
required
placeholder="usuario@email.com"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <p className="auth-error">{errors.email}</p>}
</div>

<div className="auth-field">
Expand All @@ -37,11 +105,16 @@ function LoginForm() {
</label>
<input
id="login-password"
name="password"
type="password"
className="auth-input"
placeholder="••••••••"
required
value={formData.password}
onChange={handleChange}
/>
{errors.password && (
<p className="auth-error">{errors.password}</p>
)}
</div>

<div className="auth-forgot">
Expand All @@ -52,15 +125,25 @@ function LoginForm() {

<div className="auth-form-footer">
<div className="auth-remember">
<input id="remember-me" type="checkbox" className="auth-checkbox" />
<input
id="remember-me"
name="remember"
type="checkbox"
className="auth-checkbox"
checked={formData.remember}
onChange={handleChange}
/>
<label htmlFor="remember-me" className="auth-remember-label">
Remember me
</label>
</div>

{/* Botones Sign in + Register en la misma fila */}
<div className="auth-actions">
<button type="submit" className="auth-button-primary">
<button
type="submit"
className="auth-button-primary"
disabled={!isFormValid}
>
Sign in
</button>

Expand Down
125 changes: 114 additions & 11 deletions frontend/src/components/features/auth/RegisterForm.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,112 @@
/**
* Componente: RegisterForm
* Propósito: Formulario de registro de nuevos usuarios con campos de nombre, email, password, confirmación y enlace a login.
* Uso:
* - RegisterPage.jsx (envuelto en AuthCard)
* Dependencias:
* - auth.css (.auth-form, .auth-field, .auth-label, .auth-input, .auth-form-footer, .auth-button-primary, .auth-footer-text, .auth-link)
*/

// frontend/src/components/features/auth/RegisterForm.jsx

import { useState } from "react";
import { Link } from "react-router-dom";

const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function RegisterForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
confirmPassword: "",
});

const [errors, setErrors] = useState({});

const validateField = (name, value) => {
let error = "";

switch (name) {
case "name":
if (!value.trim()) {
error = "El nombre es obligatorio";
}
break;

case "email":
if (!value.trim()) {
error = "El email es obligatorio";
} else if (!EMAIL_REGEX.test(value)) {
error = "El formato del email no es válido";
}
break;

case "password":
if (!value) {
error = "La contraseña es obligatoria";
} else if (value.length < 8) {
error = "La contraseña debe tener al menos 8 caracteres";
}
break;

case "confirmPassword":
if (!value) {
error = "Debes repetir la contraseña";
} else if (value !== formData.password) {
error = "Las contraseñas no coinciden";
}
break;

default:
break;
}

return error;
};

const handleChange = (e) => {
const { name, value } = e.target;

setFormData((prev) => ({
...prev,
[name]: value,
}));

setErrors((prev) => ({
...prev,
[name]: validateField(name, value),
}));
};

const isFormValid =
Object.values(formData).every((value) => value.trim() !== "") &&
Object.values(errors).every((error) => !error);

const handleSubmit = (e) => {
e.preventDefault();

const newErrors = {};
Object.keys(formData).forEach((field) => {
const error = validateField(field, formData[field]);
if (error) newErrors[field] = error;
});

setErrors(newErrors);

if (Object.keys(newErrors).length > 0) return;

// TODO: enviar datos al backend
console.log("Registro válido:", formData);
};

return (
<form className="auth-form">
<form className="auth-form" onSubmit={handleSubmit} noValidate>
<div className="auth-field">
<label htmlFor="name" className="auth-label">
Nombre
</label>
<input
id="name"
name="name"
type="text"
className="auth-input"
placeholder="Tu nombre"
value={formData.name}
onChange={handleChange}
/>
{errors.name && <p className="auth-error">{errors.name}</p>}
</div>

<div className="auth-field">
Expand All @@ -32,10 +115,14 @@ function RegisterForm() {
</label>
<input
id="email"
name="email"
type="email"
className="auth-input"
placeholder="tu@email.com"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <p className="auth-error">{errors.email}</p>}
</div>

<div className="auth-field">
Expand All @@ -44,10 +131,16 @@ function RegisterForm() {
</label>
<input
id="password"
name="password"
type="password"
className="auth-input"
placeholder="Elige una contraseña segura"
value={formData.password}
onChange={handleChange}
/>
{errors.password && (
<p className="auth-error">{errors.password}</p>
)}
</div>

<div className="auth-field">
Expand All @@ -56,14 +149,24 @@ function RegisterForm() {
</label>
<input
id="confirmPassword"
name="confirmPassword"
type="password"
className="auth-input"
placeholder="Vuelve a escribir la contraseña"
value={formData.confirmPassword}
onChange={handleChange}
/>
{errors.confirmPassword && (
<p className="auth-error">{errors.confirmPassword}</p>
)}
</div>

<div className="auth-form-footer">
<button type="submit" className="auth-button-primary">
<button
type="submit"
className="auth-button-primary"
disabled={!isFormValid}
>
Crear cuenta
</button>

Expand Down
2 changes: 0 additions & 2 deletions frontend/src/components/ui/HamburgerMenu/HamburgerMenu.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,4 @@

.hamburger-submenu-button:hover {
background: rgba(56,189,248,0.06);
=======
>>>>>>> origin/dev
}