From 9daaa115149c89de0f4ae2094e3d019790044cfd Mon Sep 17 00:00:00 2001 From: Elliot Axel Noriega Date: Wed, 9 Oct 2024 13:50:30 -0600 Subject: [PATCH 1/2] Recepcion de apoyo funcionalidad --- cosiap_frontend/src/App.jsx | 5 +- cosiap_frontend/src/api.js | 7 + .../src/components/FormsValidations.jsx | 38 ++- .../common/layouts/LayoutBaseNavigation.jsx | 4 +- .../Perfil/InformacionIdentificacion.jsx | 9 +- .../users/Perfil/Label_InputFile.jsx | 7 +- .../users/RecepcionApoyo/Convenio.jsx | 39 +++ .../users/RecepcionApoyo/DatosBancarios.jsx | 315 ++++++++++++++++++ .../users/RecepcionApoyo/RecepcionApoyo.jsx | 77 +++++ 9 files changed, 486 insertions(+), 15 deletions(-) create mode 100644 cosiap_frontend/src/components/users/RecepcionApoyo/Convenio.jsx create mode 100644 cosiap_frontend/src/components/users/RecepcionApoyo/DatosBancarios.jsx create mode 100644 cosiap_frontend/src/components/users/RecepcionApoyo/RecepcionApoyo.jsx diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx index 93d6d55..66ba431 100644 --- a/cosiap_frontend/src/App.jsx +++ b/cosiap_frontend/src/App.jsx @@ -29,6 +29,7 @@ import EditModalidad from "./components/modalidades/EditarModalidad"; import SolicitarModalidad from "./components/modalidades/Modalidad"; import Perfil from '@/components/users/Perfil/Perfil'; import ListaSolicitudes from "./components/solicitudes/HistorialSolicitudes"; +import RecepcionApoyo from "./components/users/RecepcionApoyo/RecepcionApoyo"; function App() { const [viewPageLoader, setViewPageLoader] = useState(false); @@ -100,7 +101,7 @@ function RoutesApp({ setViewPageLoader }) { /> } /> } /> - } /> + } /> } /> } /> {/* Solo administradores pueden acceder a estas url */} @@ -109,6 +110,8 @@ function RoutesApp({ setViewPageLoader }) { } > + + } /> } /> } /> diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js index 202a4d7..2631aa3 100644 --- a/cosiap_frontend/src/api.js +++ b/cosiap_frontend/src/api.js @@ -59,6 +59,13 @@ const api = { getId: () => ax.get('api/usuarios/uid/'), getIsCompleteData: () => ax.get('api/usuarios/user-complete-data/'), + datos_bancarios: { + get: () => ax.get('api/usuarios/datos-bancarios'), + post: (data) => ax.post('api/usuarios/datos-bancarios/', data), + update: (id, data) => ax.put(`api/usuarios/datos-bancarios/${id}`, data), + delete: (id) => ax.delete(`api/usuarios/datos-bancarios/${id}`), + }, + administradores: { get: () => ax.get('api/usuarios/administradores'), post: (data) => ax.post('api/usuarios/administradores/', data), diff --git a/cosiap_frontend/src/components/FormsValidations.jsx b/cosiap_frontend/src/components/FormsValidations.jsx index a9ec178..934eaae 100644 --- a/cosiap_frontend/src/components/FormsValidations.jsx +++ b/cosiap_frontend/src/components/FormsValidations.jsx @@ -116,4 +116,40 @@ export const IdentificationValidationSchema = Yup.object().shape({ .test("fileSize", "El archivo supera el tamaño máximo", (value) => { return value && value[0] && value[0].size <= FILE_SIZE; }) -}); \ No newline at end of file +}); + +export const DatosBancariosValidationSchema = Yup.object().shape({ + nombre_banco: Yup.string() + .required('El nombre del banco es obligatorio') + .max(20, 'El nombre del banco no puede exceder los 20 caracteres'), + + cuenta_bancaria: Yup.string() + .required('El número de cuenta es obligatorio') + .matches(/^\d{10}$/, 'El número de cuenta debe tener 10 dígitos'), + + clabe_bancaria: Yup.string() + .required('La CLABE bancaria es obligatoria') + .matches(/^\d{16}$/, 'La CLABE bancaria debe tener 16 dígitos'), + + doc_estado_cuenta: Yup.mixed() + .nullable() + .test('fileType', 'Solo se permiten archivos PDF', value => { + return !value || (value && value[0] && value[0].type === 'application/pdf'); + }), + + doc_constancia_sat: Yup.mixed() + .nullable() + .test('fileType', 'Solo se permiten archivos PDF', value => { + return !value || (value && value[0] && value[0].type === 'application/pdf'); + }), + + codigo_postal_fiscal: Yup.string() + .required('El código postal fiscal es obligatorio') + .matches(/^\d{5}$/, 'El código postal debe tener 5 dígitos'), + + regimen: Yup.string() + .required('El régimen fiscal es obligatorio') + .oneOf([ + '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12' + ], 'Selecciona un régimen válido') +}); diff --git a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx index d24a7f6..bd0869a 100644 --- a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx +++ b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx @@ -42,8 +42,8 @@ const linksItemsSolicitante = [ }, { nameIcon: 'mark_as_unread', - nameItem: 'Recepción de ayuda', - navigate: '/recepcion_ayuda', + nameItem: 'Recepción de apoyo', + navigate: '/recepcion_apoyo', isSelected: false }, { diff --git a/cosiap_frontend/src/components/users/Perfil/InformacionIdentificacion.jsx b/cosiap_frontend/src/components/users/Perfil/InformacionIdentificacion.jsx index 06557d2..194a402 100644 --- a/cosiap_frontend/src/components/users/Perfil/InformacionIdentificacion.jsx +++ b/cosiap_frontend/src/components/users/Perfil/InformacionIdentificacion.jsx @@ -13,7 +13,6 @@ export default function InformacionIdentificacion( {datosSolicitante, setViewPag const [isEditing, setIsEditing] = useState(false); // Estado para controlar si está en modo edición const [modifiedFields, setModifiedFields] = useState({}); // Campos que han sido modificados const [showModalConfirmation, setShowModalConfirmation] = useState(false); // Control para mostrar el modal de confirmación - const [urlFileIne, setUrlFileIne] = useState(null); // Hook para manejar formularios const { register, // Registra los inputs en el formulario @@ -39,9 +38,6 @@ export default function InformacionIdentificacion( {datosSolicitante, setViewPag // useEffect que se ejecuta cada vez que los datos del solicitante cambian useEffect(() => { resetData(); // Resetea los datos cuando cambia datosSolicitante - if(datosSolicitante.INE){//Si el usuario ya tiene un archivo ine subido - setUrlFileIne(apiUrl + datosSolicitante.INE);//Declaramos la ruta con el archivo subido - } }, [datosSolicitante, reset]); // Función que maneja el envío del formulario @@ -102,9 +98,6 @@ export default function InformacionIdentificacion( {datosSolicitante, setViewPag setIsEditing(false); // Deshabilitar el modo edición resetData(); // Restablecer los datos originales setModifiedFields({}); // Limpiar los campos modificados - if(datosSolicitante.INE){//Si el usuario ya tiene un archivo ine subido - setUrlFileIne(apiUrl + datosSolicitante.INE);//Declaramos la ruta con el archivo subido - } }; // Función para manejar el clic de "Guardar" con validación @@ -148,7 +141,7 @@ export default function InformacionIdentificacion( {datosSolicitante, setViewPag
{ const file = e.target.files[0]; // Capturamos el archivo seleccionado // Si no hay archivo seleccionado, restablecemos el estado @@ -52,8 +53,8 @@ export default function Label_InputFile({urlFile, label, id, name, type, placeho {urlFile && ( )} diff --git a/cosiap_frontend/src/components/users/RecepcionApoyo/Convenio.jsx b/cosiap_frontend/src/components/users/RecepcionApoyo/Convenio.jsx new file mode 100644 index 0000000..0bf2a62 --- /dev/null +++ b/cosiap_frontend/src/components/users/RecepcionApoyo/Convenio.jsx @@ -0,0 +1,39 @@ +import SectionContainer from "@/components/common/ui/SectionContainers/SectionContainer"; +import Label_InputFile from "../Perfil/Label_InputFile"; + +export default function Convenio( {setViewPageLoader, setShowAlertSuccesful} ){ + return ( + <> + +
+
+ + Descarga el documento prellenado, fírmalo y súbelo en su respectivo campo + +
+ +
+
+
+
+ + +
Sube tu archivo
+
+
+
+
+
+
+ + ); +} \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/RecepcionApoyo/DatosBancarios.jsx b/cosiap_frontend/src/components/users/RecepcionApoyo/DatosBancarios.jsx new file mode 100644 index 0000000..23795b8 --- /dev/null +++ b/cosiap_frontend/src/components/users/RecepcionApoyo/DatosBancarios.jsx @@ -0,0 +1,315 @@ +import ModalConfirmation from "@/components/common/ui/Modals/ModalConfirmation"; +import SectionContainer from "@/components/common/ui/SectionContainers/SectionContainer"; +import { useEffect, useState } from "react"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { useForm } from "react-hook-form"; +import Label_InputForm from "../Perfil/Label_InputForm"; +import Label_SelectForm from "../Perfil/Label_SelectForm"; +import api from "@/api"; +import Label_InputFile from "../Perfil/Label_InputFile"; +import { DatosBancariosValidationSchema } from "@/components/FormsValidations"; + +export default function DatosBancarios({obtenerInformacionSolicitante, datosSolicitante, setViewPageLoader, setShowAlertSuccesful }){ + // Estados + const [isEditing, setIsEditing] = useState(false); // Estado para controlar si está en modo edición + const [modifiedFields, setModifiedFields] = useState({}); // Campos que han sido modificados + const [showModalConfirmation, setShowModalConfirmation] = useState(false); + const [datosBancarios, setDatosBancarios] = useState([]); // Campos que han sido modificados + + //Opciones de regimen + const regimen_choices = [ + {value: "1", label:"Régimen Simplificado de Confianza"}, + {value: "2", label:"Sueldos y salarios e ingresos asimilados a salarios"}, + {value: "3", label:"Régimen de Actividades Empresariales y Profesionales"}, + {value: "4", label:"Régimen de Incorporación Fiscal"}, + {value: "5", label:"Enajenación de bienes"}, + {value: '6', label:'Régimen de Actividades Empresariales con ingresos a través de Plataformas Tecnológicas'}, + {value: '7', label:'Régimen de Arrendamiento'}, + {value: '8', label:'Intereses'}, + {value: '9', label:'Obtención de premios'}, + {value: '10', label: 'Dividendos'}, + {value: '11', label: 'Demás Ingresos'}, + {value: '12', label: 'Sin obligaciones fiscales'} + + ] + + // Hook para manejar el formulario + const { + register, // Registra los inputs en el formulario + handleSubmit, // Maneja el envío del formulario + reset, // Método para resetear los valores del formulario + formState: { errors, isSubmitting } // Estado del formulario: errores y si se está enviando + } = useForm({ + resolver: yupResolver(DatosBancariosValidationSchema), // Resolver para la validación con Yup + }); + + // Función para resetear los datos del formulario con los valores actuales del solicitante + const resetData = () => reset({ + nombre_banco: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.nombre_banco || "") : "", + cuenta_bancaria: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.cuenta_bancaria || "") : "", + clabe_bancaria: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.clabe_bancaria || "") : "", + doc_estado_cuenta: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.doc_estado_cuenta || "") : "", + doc_constancia_sat: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.doc_constancia_sat || "") : "", + codigo_postal_fiscal: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.codigo_postal_fiscal || "") : "", + regimen: datosSolicitante.datos_bancarios ? (datosSolicitante.datos_bancarios.regimen || "") : "", + }); + // useEffect que se ejecuta cada vez que los datos del solicitante cambian + useEffect(() => { + if(datosSolicitante.datos_bancarios){ + setDatosBancarios(datosSolicitante.datos_bancarios); + } + resetData(); // Resetea los datos cuando cambia datosSolicitante + }, [datosSolicitante, reset]); + + // Función que maneja el envío del formulario + const handleFormSubmission = async (data) => { + if (!isEditing) { + return; // Si no está en modo edición, no se envía el formulario + } + + setViewPageLoader(true); // Muestra un loader mientras se realiza la petición + try { + console.log(data) + // Crear una instancia de FormData + const formData = new FormData(); + + // Agregar campos al objeto 'datos_bancarios' usando notación de corchetes + formData.append('nombre_banco', data.nombre_banco) + formData.append('cuenta_bancaria', data.cuenta_bancaria) + formData.append('clabe_bancaria', data.clabe_bancaria) + formData.append('doc_estado_cuenta', data.doc_estado_cuenta[0]) + formData.append('doc_constancia_sat', data.doc_constancia_sat[0]) + formData.append('codigo_postal_fiscal', data.codigo_postal_fiscal) + formData.append('regimen', data.regimen) + + if(datosSolicitante.datos_bancarios){//Si existen los datos bancarios, significan que se van a actualizar + const response = await api.usuarios.datos_bancarios.update(datosSolicitante.datos_bancarios.id, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + console.log('Actualizacion realizada: ', response) + }else{ //Se van a crear + const response = await api.usuarios.datos_bancarios.post(formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + console.log('Creación realizada: ', response) + } + + //Obtenemos de nuevo los datos del solicitante + obtenerInformacionSolicitante(); + // Cerrar modal de confirmación y deshabilitar la edición + setShowModalConfirmation(false); + setIsEditing(false); + setModifiedFields({}); // Vaciar los campos modificados + setShowAlertSuccesful(true) + } catch (error) { + console.error(error); // Manejar errores + } finally { + setViewPageLoader(false); // Quitar el loader al finalizar + } + }; + + // Función para manejar los cambios en los inputs + const handleInputChange = (e) => { + const { name, value } = e.target; + // Actualizar el estado de campos modificados + setModifiedFields((InputsPrev) => ({ + ...InputsPrev, + [name]: value !== datosBancarios[name] // Solo marcar como modificado si el valor es diferente al original + })); + }; + // Función para cancelar la edición + const handleCancelEdition = () => { + setIsEditing(false); // Deshabilitar el modo edición + resetData(); // Restablecer los datos originales + setModifiedFields({}); // Limpiar los campos modificados + }; + + // Función para manejar el clic de "Guardar" con validación + const handleSaveClick = (data) => { + if (Object.values(modifiedFields).some((isModified) => isModified)) { + setShowModalConfirmation(true); // Mostrar el modal de confirmación si no hay errores + } + }; + + return ( + <> + +
+
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +
+ ({ value: item.value, label: item.label }) + ) + ]} + errors={ + errors.regimen ? errors.regimen.message : undefined + } + onChange={handleInputChange} + /> +
+ +
+ +
+
+ +
+ +
+
+ { + isEditing && ( +
+ +
+ ) + } +
+ {/* Botón para habilitar edición o guardar */} + +
+
+ { + showModalConfirmation && ( + + + + + + ) + } +
+
+ + ); +} \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/RecepcionApoyo/RecepcionApoyo.jsx b/cosiap_frontend/src/components/users/RecepcionApoyo/RecepcionApoyo.jsx new file mode 100644 index 0000000..423da42 --- /dev/null +++ b/cosiap_frontend/src/components/users/RecepcionApoyo/RecepcionApoyo.jsx @@ -0,0 +1,77 @@ +import Alert from "@/components/common/ui/Alert"; +import DatosBancarios from "./DatosBancarios"; +import { useEffect, useState } from "react"; +import { useAutenticacion } from "@/components/common/utility/Autenticador"; +import api from "@/api"; +import Convenio from "./Convenio"; + +export default function RecepcionApoyo( {setViewPageLoader} ){ + const [datosSolicitante, setDatosSolicitante] = useState([]); + const [showAlertSuccesful, setShowAlertSuccesful] = useState(false); // Control de mostrar la alerta + const { uid } = useAutenticacion(); + + const crearSolicitante = async () => { + try { + const response = await api.usuarios.solicitantes.post(); + console.log("Solicitante creado: ", response.data); + //Tratamos de obtener el solicitante con el id + const responseObtain = await api.usuarios.solicitantes.getById(uid); + //Declaramos los datos del solicitante + setDatosSolicitante(responseObtain.data); + } catch (error) { + console.log(error) + } + }; + + const obtenerInformacionSolicitante = async () =>{ + if (uid){ + setViewPageLoader(true); + try { + //Tratamos de obtener el solicitante con el id + const responseObtain = await api.usuarios.solicitantes.getById(uid); + //Declaramos los datos del solicitante + setDatosSolicitante(responseObtain.data); + console.log(responseObtain.data) + } catch (error) { // Si da error significa que es su primera vez ingresando + crearSolicitante() + }finally{ + setViewPageLoader(false); + } + } + }; + useEffect(() => { + obtenerInformacionSolicitante(); + },[uid]); + + return( + <> + +
+
+
+ +
+
+
+
+ +
+
+
+ + ); +} \ No newline at end of file -- GitLab From 0631355ea244435ce98b906b8f4e380840c4c4fb Mon Sep 17 00:00:00 2001 From: Elliot Axel Noriega Date: Thu, 10 Oct 2024 13:18:29 -0600 Subject: [PATCH 2/2] =?UTF-8?q?Soluci=C3=B3n=20de=20redirecciones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cosiap_frontend/src/App.jsx | 22 ++++++++++--------- .../SolicitudesAdmin/Solicitudes.jsx | 12 ++++++++++ .../common/utility/LoginRequiredRoute.jsx | 12 ++++++++++ .../users/Register/RegisterForm.jsx | 2 +- 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 cosiap_frontend/src/components/SolicitudesAdmin/Solicitudes.jsx diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx index 02cb15d..7a199b1 100644 --- a/cosiap_frontend/src/App.jsx +++ b/cosiap_frontend/src/App.jsx @@ -7,6 +7,7 @@ import { LoginRequiredRoute, IsAdminRequiredRoute, IsLogged, + IsSolicitanteRequiredRoute, } from "@/components/common/utility/LoginRequiredRoute"; import "./App.css"; import PageLoader from "@/components/common/ui/PageLoader"; @@ -32,6 +33,7 @@ import ListaSolicitudes from "./components/solicitudes/HistorialSolicitudes"; import EditarSolicitud from "./components/solicitudes/EditarSolicitud"; import VisualizarSolicitud from "./components/solicitudes/VerSolicitud"; import RecepcionApoyo from "./components/users/RecepcionApoyo/RecepcionApoyo"; +import Solicitudes from "./components/SolicitudesAdmin/Solicitudes"; function App() { const [viewPageLoader, setViewPageLoader] = useState(false); @@ -101,23 +103,23 @@ function RoutesApp({ setViewPageLoader }) { path="/modalidades" element={} /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* Solo administradores pueden acceder a estas url */} } > - - } /> - } /> + } /> } /> } /> diff --git a/cosiap_frontend/src/components/SolicitudesAdmin/Solicitudes.jsx b/cosiap_frontend/src/components/SolicitudesAdmin/Solicitudes.jsx new file mode 100644 index 0000000..065d4b6 --- /dev/null +++ b/cosiap_frontend/src/components/SolicitudesAdmin/Solicitudes.jsx @@ -0,0 +1,12 @@ +import SectionContainer from "../common/ui/SectionContainers/SectionContainer"; + +export default function Solicitudes( {} ){ + return ( + <> + + + + + + ); +} \ No newline at end of file diff --git a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx index 0a61304..49dca2b 100644 --- a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx +++ b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx @@ -26,6 +26,18 @@ export const IsAdminRequiredRoute = ({ setViewPageLoader }) => { } }; +export const IsSolicitanteRequiredRoute = ({ setViewPageLoader }) => { + const {isAdmin} = useAutenticacion(); + + if (isAdmin === true) { + return ; + } else if (isAdmin === undefined) { + return null; + }else { + return ; + } +}; + export const IsLogged = () => { const { token } = useAutenticacion(); if (token === null) { diff --git a/cosiap_frontend/src/components/users/Register/RegisterForm.jsx b/cosiap_frontend/src/components/users/Register/RegisterForm.jsx index 9c51347..e7035e7 100644 --- a/cosiap_frontend/src/components/users/Register/RegisterForm.jsx +++ b/cosiap_frontend/src/components/users/Register/RegisterForm.jsx @@ -40,7 +40,7 @@ export default function RegisterForm( {setSentEmail, setViewPageLoader} ) { id="nombre" name="nombre" type="text" - placeholder="NOMBRE COMPLETO" + placeholder="NOMBRE" register={register} errors={ -- GitLab