diff --git a/cosiap_api/cosiap_api/.env b/cosiap_api/cosiap_api/.env
index 1b59abace1cfbdfced37e164e8a9617b5a7b145f..1b8c473086c249755da5edce9d772d86374b1358 100644
--- a/cosiap_api/cosiap_api/.env
+++ b/cosiap_api/cosiap_api/.env
@@ -14,8 +14,8 @@ DATABASES_DEFAULT_PORT="3306"
EMAIL_HOST="sandbox.smtp.mailtrap.io"
EMAIL_FROM="cosiap@example.com"
-EMAIL_HOST_USER="52e12fdcb768cc"
-EMAIL_HOST_PASSWORD="03520623807dea"
+EMAIL_HOST_USER="3b48193365f615"
+EMAIL_HOST_PASSWORD="37f89fc1d98f48"
EMAIL_PORT="2525"
EMAIL_USE_TLS=True
diff --git a/cosiap_api/users/serializers.py b/cosiap_api/users/serializers.py
index f7825c94373fa2eba6fb9417667ee594ce887f86..e71bed61253441ef51e28a9d5107bac3c28ed5ce 100644
--- a/cosiap_api/users/serializers.py
+++ b/cosiap_api/users/serializers.py
@@ -123,7 +123,7 @@ class SolicitanteSerializer(serializers.ModelSerializer):
# indicamos el modelo a utilziar
model = Solicitante
# indicamos los campos que debe ingresar el usuario
- fields = ['nombre','ap_paterno', 'ap_materno', 'telefono', 'RFC','sexo', 'direccion', 'codigo_postal', 'municipio', 'poblacion', 'INE']
+ fields = ['curp', 'email', 'nombre','ap_paterno', 'ap_materno', 'telefono', 'RFC','sexo', 'direccion', 'codigo_postal', 'municipio', 'poblacion', 'INE']
# Agregamos validadores para asegurar que los campos puedan estar vacíos
extra_kwargs = {'ap_paterno': {'required': False}, 'ap_materno': {'required': False},'telefono': {'required': False},'RFC': {'required': False}, 'sexo':{'required':False},
'direccion': {'required': False},'codigo_postal': {'required': False},'municipio': {'required': False},'poblacion': {'required': False},
@@ -145,6 +145,8 @@ class SolicitanteSerializer(serializers.ModelSerializer):
# Definimos una función para la actualización del solicitante, recibiendo una instancia
def update(self, instance, validated_data):
# Actualizar los campos del Solicitante
+ instance.curp = validated_data.get('curp', instance.curp)
+ instance.email = validated_data.get('email', instance.email)
instance.nombre = validated_data.get('nombre', instance.nombre)
instance.ap_paterno = validated_data.get('ap_paterno', instance.ap_paterno)
instance.ap_materno = validated_data.get('ap_materno', instance.ap_materno)
diff --git a/cosiap_frontend/src/App.css b/cosiap_frontend/src/App.css
index 695f37cfb1f7f4a1c72f152fce0294e7547a0fad..b6ec5a7be721fc86e95919ca14b935a61a79b0d0 100644
--- a/cosiap_frontend/src/App.css
+++ b/cosiap_frontend/src/App.css
@@ -809,4 +809,24 @@ font-size: 16px;
.search-btn:hover {
background-color: #555;
-}
\ No newline at end of file
+}
+
+.table-container {
+ overflow-x: auto;
+ max-width: 100%;
+ margin-top: 16px;
+}
+
+table {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+th, td {
+ padding: 8px;
+ text-align: left;
+ border-bottom: 1px solid #ddd;
+ white-space: nowrap;
+}
+
+
diff --git a/cosiap_frontend/src/components/admin/TablaUsuarios.jsx b/cosiap_frontend/src/components/admin/TablaUsuarios.jsx
index bd2411f1fa90b9b32c4ccec75911e3051f5e52b9..4f6cffa31235e7be5f38da673288e64720b3f237 100644
--- a/cosiap_frontend/src/components/admin/TablaUsuarios.jsx
+++ b/cosiap_frontend/src/components/admin/TablaUsuarios.jsx
@@ -1,73 +1,115 @@
-import { useState, useEffect } from "react";
+import { useState, useEffect, useRef } from "react";
import api from '../../api';
-import Tabla from "../common/utility/ReusableTable"; // Importa la tabla reutilizable
+import Tabla from "../common/utility/ReusableTable";
import MainContainer from "../common/utility/MainContainer";
import '@/App.css';
-
-// Componente para recuperar la lista de usuarios solicitantes del sistema
const ListaUsuarios = () => {
const [usuarios, setUsuarios] = useState([]);
const [searchQuery, setSearchQuery] = useState('');
+ const [editRow, setEditRow] = useState(null);
+ const [registerChange, setRegisterChange] = useState({});
+ const inputRefs = useRef({});
+ const tableContainerRef = useRef(null); // Referencia al contenedor con scroll
+ const [alertMessage, setAlertMessage] = useState('');
+ const [isSuccess, setIsSuccess] = useState(false);
-
- // obtenemos a los usuarios al cargar la página
useEffect(() => {
const fetchUsuarios = async () => {
try {
const response = await api.usuarios.solicitantes.get();
- setUsuarios(response.data.data)
+ setUsuarios(response.data.data);
} catch (error) {
- console.log("Error al recuperar la lista de usuarios.", error);
+ console.error("Error al recuperar la lista de usuarios.", error);
setUsuarios([]);
}
};
fetchUsuarios();
}, []);
- // Definimos las columnas a mostrar en la tabla
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (tableContainerRef.current?.contains(event.target)) {
+ return; // Ignorar el clic si es en el scroll
+ }
+
+ const isOutside = !Object.values(inputRefs.current).some((input) =>
+ input?.contains(event.target)
+ );
+
+ if (isOutside && editRow !== null) {
+ handleUpdate(editRow);
+ setEditRow(null);
+ setRegisterChange({});
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, [editRow, registerChange]);
+
+ const handleChange = (e, rowId, key) => {
+ const value = e.target.value;
+
+ setRegisterChange((prev) => ({
+ ...prev,
+ [rowId]: {
+ ...prev[rowId],
+ [key]: value === '' ? '' : value // Permitir valores vacíos
+ },
+ }));
+ };
+
const columnas = [
- {
- label: "CURP",
- render: (fila) => fila.curp
- },
- {
- label: "Nombre",
- render: (fila) => fila.nombre
- },
- {
- label: "Apellido Paterno",
- render: (fila) => fila.ap_paterno
- },
- {
- label: "Apellido Materno",
- render: (fila) => fila.ap_materno
- },
- {
- label: "RFC",
- render: (fila) => fila.RFC
- },
- {
- label: "E-mail",
- render: (fila) => fila.email
- },
- {
- label: "Estado",
- render: (fila) => fila.municipio__estado__nombre
- },
- {
- label: "Municipio",
- render: (fila) => fila.municipio__nombre
- }
+ { label: "CURP", render: (fila) => renderCell(fila, "curp") },
+ { label: "Nombre", render: (fila) => renderCell(fila, "nombre") },
+ { label: "Apellido Paterno", render: (fila) => renderCell(fila, "ap_paterno") },
+ { label: "Apellido Materno", render: (fila) => renderCell(fila, "ap_materno") },
+ { label: "RFC", render: (fila) => renderCell(fila, "RFC") },
+ { label: "E-mail", render: (fila) => renderCell(fila, "email") },
];
- // Actualiza el estado de `searchQuery` con cada cambio en el input
+ const renderCell = (fila, key) => {
+ const isEditing = editRow === fila.id;
+ return isEditing ? (
+ handleChange(e, fila.id, key)}
+ ref={(el) => (inputRefs.current[fila.id + key] = el)}
+ style={{
+ fontSize: "14px",
+ padding: "2px 4px",
+ borderRadius: "4px",
+ height: "1.6em",
+ lineHeight: "1",
+ border: "1px solid #d3d3d3",
+ display: "inline-block",
+ width: "auto",
+ maxWidth: "100px",
+ }}
+ autoFocus
+ />
+ ) : (
+ setEditRow(fila.id)}>{fila[key]}
+ );
+ };
+
+ const showAlert = (message, isSuccess) => {
+ setAlertMessage(message);
+ setIsSuccess(isSuccess);
+
+ setTimeout(() => {
+ setAlertMessage('');
+ }, 3000);
+ };
+
const handleInputChange = (e) => setSearchQuery(e.target.value);
const handleSearch = async () => {
try {
const response = await api.usuarios.solicitantes.get({
- params: { search_query: searchQuery , model_name: "Solicitante", columns: "__all__"}
+ params: { search_query: searchQuery, model_name: "Solicitante", columns: "__all__" },
});
setUsuarios(response.data.data);
} catch (error) {
@@ -75,26 +117,46 @@ const ListaUsuarios = () => {
}
};
+ const handleUpdate = async (id) => {
+ console.log(registerChange[id]);
+ try {
+ await api.usuarios.solicitantes.update(id, registerChange[id])
+ const response = await api.usuarios.solicitantes.get();
+ setUsuarios(response.data.data);
+ } catch (error) {
+ const errorData = error.response.data;
+ for (const key in errorData) {
+ if (errorData.hasOwnProperty(key)) {
+ showAlert(errorData[key], false);
+ }
+ }
+ }
+ };
+
return (
+ {alertMessage && (
+
+ {alertMessage}
+
+ )}
-
-
+ />
-
-
+
+
);
};
-export default ListaUsuarios;
\ No newline at end of file
+export default ListaUsuarios;