last changes

This commit is contained in:
TheManus88
2024-08-30 16:32:51 -06:00
20 changed files with 267 additions and 103 deletions
+1
View File
@@ -1 +1,2 @@
REACT_APP_API_URL=https://venus.acatlan.unam.mx/directorio_test
REACT_APP_PORT=3001
+1 -1
View File
@@ -423,7 +423,7 @@ p .text-center .mt-3 {
justify-content: space-evenly;
width: 400px;
max-width: 90vw;
margin: 10px;
margin: 10px auto;
}
.cargando {
Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+24 -2
View File
@@ -1,7 +1,7 @@
.card {
max-width: 90vw;
width: 400px;
height: 175px;
height: 170px;
margin: auto;
padding: 0.5em;
border-radius: 1em !important;
@@ -11,7 +11,22 @@
flex-direction: row !important;
justify-content: left;
text-align: center;
transition: box-shadow 0.3s ease, transform 0.3s ease;
transition: box-shadow 0.3s ease-out, transform 0.3s ease-out ;
}
.transition{
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease-out, transform 0.5s ease-out;
}
.transition.show {
display: flex;
justify-content: center;
width: 40vw;
min-width: 340px;
opacity: 1;
transform: translateY(0);
}
.card:hover {
@@ -37,6 +52,7 @@
display: flex;
justify-content: center;
align-items: center;
max-width: 130px;
}
@@ -56,4 +72,10 @@
.status-label.inactive {
background-color: red;
}
.profesores-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
@@ -3,13 +3,19 @@ import "../Card.css";
import { PrivateRoutes, PublicRoutes } from "../../../models/routes";
import { useDeleteTeacher } from "../../../services/teacher.services";
const CardProfesor = ({ profesor, edificioNombre, permissions }) => {
const API_URL = process.env.REACT_APP_API_URL;
const CardProfesor = ({
profesor,
edificioNombre,
permissions,
currentPage,
}) => {
const [showDialog, setShowDialog] = useState(false);
const [confirmText, setConfirmText] = useState("");
const { deleteTeacher } = useDeleteTeacher();
const defaultImage =
"https://static.vecteezy.com/system/resources/thumbnails/020/765/399/small_2x/default-profile-account-unknown-icon-black-silhouette-free-vector.jpg";
const fotoProfesor = profesor.fotografia || defaultImage;
const handleDeleteClick = () => {
setShowDialog(true);
@@ -28,20 +34,28 @@ const CardProfesor = ({ profesor, edificioNombre, permissions }) => {
};
const handleViewProfesor = () => {
localStorage.setItem("lastPage", currentPage);
window.location.href = `${PublicRoutes.DATAPROFESOR}${profesor.id_profesor}`;
};
const handleImageError = (e) => {
e.target.src = defaultImage;
};
return (
<div className="card m-1">
<div className="image">
{fotoProfesor && fotoProfesor.startsWith("http") ? (
<img
src={fotoProfesor}
className="imgProfe"
alt={profesor.nombre}
onClick={handleViewProfesor}
/>
) : null}
<img
src={
profesor.fotografia && profesor.fotografia.startsWith("http")
? profesor.fotografia
: `${API_URL}/imagenes/${profesor.fotografia}`
}
className="imgProfe"
alt={profesor.nombre}
onClick={handleViewProfesor}
onError={handleImageError}
/>
{permissions ? (
<div
@@ -16,6 +16,7 @@ function FilterProfesor() {
const categorias = useCategoria();
const edificios = useEdificio();
const [filters, setFilters] = useState({});
const [currentPage, setCurrentPage] = useState(Number(localStorage.getItem("lastPage")) || 1);
const handleSubmit = (e) => {
e.preventDefault();
@@ -25,6 +26,7 @@ function FilterProfesor() {
categoria,
edificio,
});
setCurrentPage(1);
};
return (
@@ -104,7 +106,12 @@ function FilterProfesor() {
</form>
</div>
</nav>
<Mapeo filters={filters} edificios={edificios} />
<Mapeo
filters={filters}
edificios={edificios}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
</div>
);
}
+72 -24
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import "../../../App.css";
import CardProfesor from "./CardProfesor";
import Guard from "../../../guards/route.guard";
@@ -8,8 +8,8 @@ import {
} from "../../../services/teacher.services";
import { PrivateRoutes } from "../../../models/routes";
const Mapeo = ({ filters, edificios }) => {
const [currentPage, setCurrentPage] = useState(1);
const Mapeo = ({ filters, edificios, currentPage, setCurrentPage }) => {
const [shownCards, setShownCards] = useState([]);
const permissions = Guard();
const {
@@ -25,6 +25,50 @@ const Mapeo = ({ filters, edificios }) => {
error: errorActives,
} = useTeachersActives(currentPage, filters);
const { profesores, totalPages } = permissions
? { profesores: profesoresData, totalPages: totalDataPages }
: { profesores: profesoresActivesData, totalPages: totalActivePages };
const [maxPagesToShow, setMaxPagesToShow] = useState(10);
useEffect(() => {
const updateMaxPagesToShow = () => {
if (window.innerWidth < 650) {
setMaxPagesToShow(5);
} else {
setMaxPagesToShow(10);
}
};
window.addEventListener("resize", updateMaxPagesToShow);
updateMaxPagesToShow();
return () => window.removeEventListener("resize", updateMaxPagesToShow);
}, []);
useEffect(() => {
const storedPage = localStorage.getItem("lastPage");
if (storedPage) {
localStorage.removeItem("lastPage");
}
}, [setCurrentPage]);
useEffect(() => {
if (!loadingData && !loadingActives && profesores) {
setShownCards([]);
profesores.forEach((_, index) => {
setTimeout(() => {
setShownCards((prev) => [...prev, index]);
}, index * 120);
});
}
}, [loadingData, loadingActives, profesores, currentPage]);
useEffect(() => {
setTimeout(() => {
window.scrollTo({ top: 0, behavior: "smooth" });
}, 100);
}, [currentPage]);
if (loadingData || loadingActives) {
return (
<div className="perfil-profesor">
@@ -49,25 +93,20 @@ const Mapeo = ({ filters, edificios }) => {
);
}
const { profesores, totalPages } = permissions
? { profesores: profesoresData, totalPages: totalDataPages }
: { profesores: profesoresActivesData, totalPages: totalActivePages };
const handleNextPage = () => {
if (currentPage < totalPages) {
setCurrentPage((prevPage) => prevPage + 1);
}
};
const handlePrevPage = () => {
if (currentPage > 1) {
setCurrentPage((prevPage) => prevPage - 1);
}
};
};
const renderPageNumbers = () => {
const pageNumbers = [];
const maxPagesToShow = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
@@ -115,20 +154,29 @@ const Mapeo = ({ filters, edificios }) => {
</div>
) : null}
{profesores.map((profesor) => {
const edificio = edificios.find(
(e) => e.id_edificio === profesor.id_edificio
);
const edificioNombre = edificio ? edificio.edificio : "Desconocido";
return (
<CardProfesor
key={profesor.id_profesor}
profesor={profesor}
edificioNombre={edificioNombre}
permissions={permissions}
/>
);
})}
<div className="profesores-container">
{profesores.map((profesor, index) => {
const edificio = edificios.find(
(e) => e.id_edificio === profesor.id_edificio
);
const edificioNombre = edificio ? edificio.edificio : "Desconocido";
return (
<div
key={profesor.id_profesor}
className={`card-wrapper transition ${
shownCards.includes(index) ? "show" : ""
}`}
>
<CardProfesor
profesor={profesor}
edificioNombre={edificioNombre}
permissions={permissions}
currentPage={currentPage}
/>
</div>
);
})}
</div>
<nav aria-label="Page navigation example">
<ul className="pagination justify-content-center">
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import "../../../App.css";
import CardUsuario from "./CardUsuario";
import { PrivateRoutes } from "../../../models/routes";
@@ -8,6 +8,25 @@ const MapeoUsuario = ({ filters }) => {
const [currentPage, setCurrentPage] = useState(1);
const { usuarios, totalPages } = useUsers(currentPage, filters);
const [maxPagesToShow, setMaxPagesToShow] = useState(10);
useEffect(() => {
const updateMaxPagesToShow = () => {
if (window.innerWidth < 650) {
setMaxPagesToShow(5);
} else {
setMaxPagesToShow(10);
}
};
window.addEventListener("resize", updateMaxPagesToShow);
updateMaxPagesToShow();
return () => window.removeEventListener("resize", updateMaxPagesToShow);
}, []);
if (totalPages === 0) {
return (
<div className="perfil-profesor">
@@ -36,7 +55,6 @@ const MapeoUsuario = ({ filters }) => {
const renderPageNumbers = () => {
const pageNumbers = [];
const maxPagesToShow = 5;
let startPage = Math.max(1, currentPage - Math.floor(maxPagesToShow / 2));
let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
+4 -10
View File
@@ -1,23 +1,17 @@
import React from "react";
import "./style.css";
import logo1 from "../assets/img/escudo-b2.png"; // Ajusta la ruta si es necesario
import logo2 from "../assets/img/logo_unam-b.png"; // Ajusta la ruta si es necesario
const Header = () => {
return (
<header className="header">
<a href="https://www.unam.mx/">
<img
className="logo"
alt="Logo 1"
src="https://www.unam.mx/sites/all/themes/unam/logo.png"
/>
<img className="logo" alt="Logo 1" src={logo1} />
</a>
<a href="https://www.acatlan.unam.mx/">
<img
className="logo"
alt="Logo 2"
src="https://www.acatlan.unam.mx/img/logo_fesa.png"
/>
<img className="logo" alt="Logo 2" src={logo2} />
</a>
</header>
);
@@ -7,7 +7,8 @@ import {
useEdificio,
useUploadImage,
} from "../../../services/auth.services";
import { useRegisterTeacher } from "../../../services/teacher.services";
import { useRegisterTeacher, useUpdateTeacherImage } from "../../../services/teacher.services";
import { PublicRoutes } from "../../../models/routes";
const FormProfesor = () => {
const [page, setPage] = useState(1);
@@ -40,6 +41,7 @@ const FormProfesor = () => {
const [grado_maximo, setGrado_maximo] = useState("");
const { registerTeacher } = useRegisterTeacher();
const { updateTeacherImage } = useUpdateTeacherImage();
const { uploadImage } = useUploadImage();
const handlePageChange = (newPage) => {
@@ -131,24 +133,15 @@ const FormProfesor = () => {
const handleSubmit = async (event) => {
event.preventDefault();
if (!validateForm()) {
return;
}
try {
let imageUrl = "";
if (fotoBase64) {
imageUrl = await uploadImage(fotoBase64, nombre);
}
const updatedDatosAcademicos = datosAcademicos.map(dato => ({
...dato,
grado_maximo: grado_maximo || dato.grado_maximo
}));
const data = {
const id_profesor = await registerTeacher({
nombre,
id_usuario,
num_trabajador,
@@ -167,12 +160,23 @@ const FormProfesor = () => {
activo,
lineasInvestigacion,
proyectosAcademicos,
datosAcademicos: updatedDatosAcademicos,
datosAcademicos: datosAcademicos.map(dato => ({
...dato,
grado_maximo: grado_maximo || dato.grado_maximo
})),
fotografia: imageUrl,
};
console.log(data)
registerTeacher(data);
});
if (fotoBase64) {
imageUrl = await uploadImage(fotoBase64, id_profesor);
}
if (imageUrl) {
await updateTeacherImage(id_profesor, imageUrl);
}
window.location.href = PublicRoutes.FILTER;
} catch (error) {
if (error.response && error.response.status === 403) {
if (error.response.data.message === "RFC already exists") {
@@ -194,6 +198,7 @@ const FormProfesor = () => {
}
}
};
const onDrop = useCallback((acceptedFiles) => {
const file = acceptedFiles[0];
@@ -136,7 +136,7 @@ const FormEditProfesor = () => {
try {
if (fotoBase64) {
imageUrl = await updateImage(fotoBase64, nombre);
imageUrl = await updateImage(fotoBase64, id_profesor);
}
const updatedDatosAcademicos = datosAcademicos.map((dato) => ({
@@ -3,6 +3,7 @@
align-items: center;
justify-content: space-evenly;
margin-bottom: 20px;
gap: 20px;
}
.foto {
@@ -27,7 +28,7 @@
border: 1px solid #ccc;
border-radius: 5px;
background-color: white;
margin: 20px auto;
margin: 10px auto;
width: 800px;
max-width: 90vw;
}
@@ -13,6 +13,8 @@ import {
useTeacher,
} from "../../../services/teacher.services";
const API_URL = process.env.REACT_APP_API_URL;
const defaultImage =
"https://static.vecteezy.com/system/resources/thumbnails/020/765/399/small_2x/default-profile-account-unknown-icon-black-silhouette-free-vector.jpg";
@@ -86,6 +88,10 @@ const PerfilProfesor = () => {
});
};
const handleImageError = (e) => {
e.target.src = defaultImage;
};
return (
<div className="form-container">
<div className="containerButton">
@@ -96,8 +102,20 @@ const PerfilProfesor = () => {
<div className="perfil-profesor">
<div className="foto-nombre">
{fotoProfesor && fotoProfesor.startsWith("http") ? (
<img src={fotoProfesor} alt={profesor.nombre} className="foto" />
) : null}
<img
src={fotoProfesor}
alt={profesor.nombre}
className="foto"
onError={handleImageError}
/>
) : (
<img
src={`${API_URL}/imagenes/${profesor.fotografia}`}
alt={profesor.nombre}
className="foto"
onError={handleImageError}
/>
)}
<div className="info">
<div className="nombre">
@@ -134,7 +152,7 @@ const PerfilProfesor = () => {
</div>
<ul>
<li>
<strong>Grado Máximo:</strong>
<strong>Grado Máximo:</strong>
{profesor.datosAcademicos.length > 0 ? (
profesor.datosAcademicos[0].grado_maximo ? (
<>{profesor.datosAcademicos[0].grado_maximo}</>
@@ -240,7 +258,7 @@ const PerfilProfesor = () => {
</div>
{showDialog && (
<div className="dialog" style={{position:'fixed'}}>
<div className="dialog" style={{ position: "fixed" }}>
<p>
¿Deseas eliminar al profesor <strong>{profesor.nombre}</strong>?
</p>
+6 -3
View File
@@ -4,12 +4,14 @@
align-items: center;
background-color: #1e3c70 !important;
padding: 10px;
padding-left: 40px;
padding-right: 40px;
text-align: center;
}
.logo {
width: 300px;
max-width: 40vw;
width: 50px;
max-width: 10vw;
}
.footer {
@@ -20,9 +22,10 @@
text-align: center;
bottom: 0;
margin-top: auto;
padding: 10px;
}
.footer .container {
max-width: 1200px;
max-width: 90%;
margin: 0 auto;
}
-1
View File
@@ -7,7 +7,6 @@ const Id = () => {
const jwtPermissions = () => {
const jwtToken = localStorage.getItem("Token");
if (!jwtToken) {
console.error("No se encontró el token en localStorage");
return;
}
-1
View File
@@ -7,7 +7,6 @@ const Guard = () => {
const jwtPermissions = () => {
const jwtToken = localStorage.getItem("Token");
if (!jwtToken) {
console.error("No se encontró el token en localStorage");
return;
}
+3
View File
@@ -11,3 +11,6 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
*{
scrollbar-width: none;
}
+2 -2
View File
@@ -77,7 +77,7 @@ export const useUploadImage = () => {
}
const result = await response.json();
return `${API_URL}${result.imageUrl}`;
return result.imageUrl;
} catch (error) {
throw error;
}
@@ -112,7 +112,7 @@ export const useUpdateImage = () => {
}
const result = await response.json();
return `${API_URL}${result.imageUrl}`;
return result.imageUrl;
} catch (error) {
throw error;
}
+54 -22
View File
@@ -117,34 +117,37 @@ export const useTeachers = (currentPage, filters) => {
export const useRegisterTeacher = () => {
const token = useToken();
const registerTeacher = (teacherData) => {
return fetch(`${API_URL}/profesor`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(teacherData),
})
.then((response) => {
if (!response.ok) {
console.error("Error:");
}
return response.json();
})
.then((data) => {
console.log("Success:", data);
window.location.href = PublicRoutes.FILTER;
})
.catch((error) => {
console.error("Error:", error);
throw error;
const registerTeacher = async (teacherData) => {
try {
const response = await fetch(`${API_URL}/profesor`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(teacherData),
});
if (!response.ok) {
console.error("Error:");
throw new Error("Error registering teacher");
}
const data = await response.json();
console.log("Success:", data);
return data.id_profesor;
} catch (error) {
console.error("Error:", error);
throw error;
}
};
return { registerTeacher };
};
export const useTeacher = (id_profesor) => {
const [teacher, setTeacher] = useState(null);
@@ -196,3 +199,32 @@ export const useUpdateTeacher = () => {
return { updateTeacher };
};
export const useUpdateTeacherImage = () => {
const token = useToken();
const updateTeacherImage = async (id_profesor, imageUrl) => {
try {
const response = await fetch(`${API_URL}/profesor/${id_profesor}/image`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ fotografia: imageUrl }),
});
if (!response.ok) {
console.error("Error updating teacher image");
throw new Error("Error updating teacher image");
}
console.log("Image URL updated successfully");
} catch (error) {
console.error("Error:", error);
throw error;
}
};
return { updateTeacherImage };
};