merge Sam

This commit is contained in:
IO
2024-06-30 13:13:29 -06:00
15 changed files with 259 additions and 17 deletions
+8 -10
View File
@@ -107,6 +107,7 @@ const FormProfesor = () => {
activo,
lineasInvestigacion,
proyectosAcademicos,
foto: fotoBase64,
};
const token = localStorage.getItem('Token');
@@ -143,7 +144,7 @@ const FormProfesor = () => {
const onDrop = useCallback(acceptedFiles => {
const file = acceptedFiles[0];
setFoto(file);
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result;
@@ -193,13 +194,11 @@ const FormProfesor = () => {
<h2>Datos Académicos</h2>
<div className="form-group">
<label>Grado Maximo <span className="required">*</span></label>
<input type="email" name="grado_maximo" required
onChange={handleInputChange} />
<input type="email" name="grado_maximo" required onChange={handleInputChange} />
</div>
<div className="form-group">
<label>Grados Obtenidos <span className="required">*</span></label>
<input type="email" name="grados_obtenidos" required
onChange={handleInputChange} />
<input type="email" name="grados_obtenidos" required onChange={handleInputChange} />
</div>
<div className="form-group">
<label>Correo Pcpuma <span className="required">*</span></label>
@@ -279,8 +278,8 @@ const FormProfesor = () => {
onChange={(e) => handleSelectChange(e, setActivo)}
className="form-control m-2"
>
<option value = {true} ></option>
<option value= {false} >No</option>
<option value={true}></option>
<option value={false}>No</option>
</select>
</div>
<div className="form-group">
@@ -294,7 +293,7 @@ const FormProfesor = () => {
)}
</div>
{foto && <p>Archivo seleccionado: {foto.name}</p>}
{fotoBase64 && <img src={fotoBase64} alt="Foto seleccionada" style={{ maxWidth: '200px', marginTop: '10px' }} />}
{fotoBase64 && <img src={fotoBase64} alt="Preview" style={{ width: '100px', height: '100px' }} />}
</div>
</div>
)}
@@ -398,5 +397,4 @@ const FormProfesor = () => {
);
};
export default FormProfesor;
export default FormProfesor;
+139
View File
@@ -0,0 +1,139 @@
/* components/LoginForm.css */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
}
.login-form {
max-width: 300px;
width: 100%;
margin: auto;
padding: 1.5em;
background: white;
box-shadow: 0 4px 8px 2px rgba(0, 0, 0, 0.1);
border-radius: 1em;
}
h1.text-warning {
color: #ffc107;
text-align: center;
}
h2.m-3 {
margin: 20px 0;
text-align: center;
}
.input-group {
margin-bottom: 0.75em;
}
label {
margin-bottom: 0.5em;
color: #333333;
display: block;
font-size: 0.9em;
}
.login input[type="text"],
.login input[type="password"] {
border: 1px solid #CCCCCC;
padding: 0.4em;
font-size: 0.9em;
width: 100%;
box-sizing: border-box;
border-radius: 5px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 2px 4px 1px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
.login input[type="text"]:focus,
.login input[type="password"]:focus {
box-shadow: 0 0 8px 2px rgba(0, 123, 255, 0.5);
outline: none;
}
.remember-me-group {
display: flex;
align-items: center;
margin-bottom: 1em;
}
.remember-me-label {
margin-left: 0.5em;
}
input[type="checkbox"] {
width: 18px !important; /* Incremento de tamaño */
height: 18px !important; /* Incremento de tamaño */
border: 1px solid #CCCCCC !important;
border-radius: 3px !important;
background-color: #fff;
appearance: none;
cursor: pointer !important;
position: relative !important;
transition: background-color 0.3s ease, border-color 0.3s ease !important;
}
input[type="checkbox"]:checked::before {
content: '✔';
font-size: 14px;
color: #007bff !important;
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -55%) !important; /* Ajuste para centrar la paloma */
}
input[type="checkbox"]:focus {
box-shadow: 0 0 5px 2px rgba(0, 123, 255, 0.5);
outline: none;
}
.btn-group .btn {
padding: 0.6em 1em;
border: 1px solid #007bff;
border-radius: 0.25em;
background-color: transparent;
color: #007bff;
cursor: pointer;
transition: background-color 0.3s, color 0.3s;
}
.btn-group .btn.active {
background-color: #007bff;
color: #fff;
}
button[type="submit"] {
width: 100%;
padding: 0.75em;
border: none;
border-radius: 1em;
background-color: #002B7A;
color: #fff;
font-size: 0.9em;
cursor: pointer;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
button[type="submit"]:hover {
background-color: #0056b3;
box-shadow: 0 6px 12px 4px rgba(0, 0, 0, 0.2);
}
button[type="submit"]:disabled {
background-color: #007bff;
cursor: not-allowed;
opacity: 0.6;
}
p {
text-align: center;
margin-top: 1em;
}
+100
View File
@@ -0,0 +1,100 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { PrivateRoutes } from '../../models/routes';
import './LoginForm.css';
const LoginForm = () => {
const [usuario, setUsuario] = useState('');
const [contraseña, setContraseña] = useState('');
const [rememberMe, setRememberMe] = useState(false); // Estado para el botón de mantener sesión
const [error, setError] = useState(null); // Estado para manejar errores
const [loading, setLoading] = useState(false); // Estado para manejar el estado de carga
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true); // Activar el estado de carga
setError(null); // Limpiar errores previos
// Validar si los campos están vacíos
if (!usuario || !contraseña) {
setError('Es necesario rellenar el usuario y la contraseña.');
setLoading(false);
return;
}
try {
const response = await fetch(`http://localhost:3000/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ usuario, contraseña }),
});
if (response.status === 401) {
setError('Usuario o contraseña inválido.');
} else if (response.status === 500) {
setError('Error con la conexión de la API.');
} else if (!response.ok) {
throw new Error('Error en el inicio de sesión');
} else {
const data = await response.json();
console.log('Response:', data);
window.location.href = PrivateRoutes.INTERFACE;
localStorage.setItem('Token', data.token);
}
} catch (error) {
console.error('Error:', error);
setError('Error en el inicio de sesión');
} finally {
setLoading(false); // Desactivar el estado de carga
}
};
return (
<div className="main-content">
<Link to={"/profesor/perfil/36"}>Hola mundo</Link>
<h1 className="text-warning">Bienvenido al directorio de profesores</h1>
<h2 className='m-3'>Iniciar sesión</h2>
<form onSubmit={handleSubmit} className="login-form" style={{ background: '#f9f9f9', borderRadius: '1em' }}>
<div className="input-group">
<label htmlFor="username">Nombre de Usuario:</label>
<input
type="text"
id="username"
value={usuario}
onChange={(e) => setUsuario(e.target.value)}
disabled={loading} // Desactivar input mientras carga
required
/>
</div>
<div className="input-group">
<label htmlFor="password">Contraseña:</label>
<input
type="password"
id="password"
value={contraseña}
onChange={(e) => setContraseña(e.target.value)}
disabled={loading} // Desactivar input mientras carga
required
/>
</div>
<div className="input-group remember-me-group">
<input
type="checkbox"
id="rememberMe"
checked={rememberMe}
onChange={() => setRememberMe(!rememberMe)}
disabled={loading} // Desactivar checkbox mientras carga
/>
<label htmlFor="rememberMe" className="remember-me-label">Mantener la sesión</label>
</div>
<button type="submit" disabled={loading}>Ingresar</button>
{loading && <p>Cargando...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
</div>
);
};
export default LoginForm;
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
import { useParams } from 'react-router-dom';
import { useDropzone } from 'react-dropzone';
import './FormEditProfesor.css';
const FormEditProfesor = () => {
@@ -13,13 +14,15 @@ const FormEditProfesor = () => {
const [edificio, setEdificio] = useState('');
const [adscripcion, setAdscripcion] = useState('');
const [categoria, setCategoria] = useState('');
const [activo, setActivo] = useState();
const [activo, setActivo] = useState(true);
const [foto, setFoto] = useState(null);
const [fotoBase64, setFotoBase64] = useState('');
const [lineasInvestigacion, setLineasInvestigacion] = useState([]);
const [proyectosAcademicos, setProyectosAcademicos] = useState([]);
const [photo, setPhoto] = useState(null);
const [preview, setPreview] = useState(null);
const handlePageChange = (newPage) => {
setPage(newPage);
};
@@ -60,7 +63,6 @@ const FormEditProfesor = () => {
setProyectosAcademicos(data.proyectosAcademicos);
})
.catch(error => console.error('Error fetching profesor:', error));
console.log(profesor)
}, [id_profesor]);
const handleInputChange = (event) => {
@@ -155,9 +157,13 @@ const FormEditProfesor = () => {
reader.readAsDataURL(file);
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: 'image/jpeg, image/png'
const { getRootProps, getInputProps } = useDropzone({
accept: '.jpg,.png',
onDrop: (acceptedFiles) => {
const file = acceptedFiles[0];
setPhoto(file);
setPreview(URL.createObjectURL(file));
}
});
return (
@@ -400,4 +406,3 @@ const FormEditProfesor = () => {
};
export default FormEditProfesor;