diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx index 0ad6f47c9010c80a35a1978efab3673b302d6e7d..aca4efd5c5f2ee4e2fa625b92940f68173646f75 100644 --- a/cosiap_frontend/src/App.jsx +++ b/cosiap_frontend/src/App.jsx @@ -4,8 +4,8 @@ import {Autenticador} from "@/components/common/utility/Autenticador"; import {LoginRequiredRoutes} from "@/components/common/utility/LoginRequiredRoutes" import "./App.css"; import PageLoader from '@/components/common/ui/PageLoader'; - -import { AuthPage } from "./components/users/AuthPage"; +import { Login } from './components/users/Login/Login'; +import Register from './components/users/Register/Register'; import Inicio from "./components/users/Inicio"; import { useState } from 'react'; @@ -28,8 +28,9 @@ function App() { {/* Rutas publicas */} - } /> - } /> + } /> + } /> + } /> {/* Rutas protegidas */} }> diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js index f324376fb8a68ce887f310b95cfce3cfc2cb9ceb..7cd0b33c8eb737675e7f593f58d0f96227ad8d7d 100644 --- a/cosiap_frontend/src/api.js +++ b/cosiap_frontend/src/api.js @@ -50,12 +50,66 @@ const api = { getById: (id) => ax.get(`api/usuarios/${id}`), update: (id, data) => ax.put(`api/usuarios/${id}`, data), delete: (id) => ax.delete(`api/usuarios/${id}`), + + verificarCorreo: (uidb64, token) => ax.get(`api/usuarios/verificar-correo/${uidb64}/${token}`), + restablecerPassoword: (data) => ax.post(`api/usuarios/restablecer-password`, data), + nuevaPassword: (uidb64, token, data) => ax.get(`api/usuarios/nueva-password/${uidb64}/${token}`, data), + + administradores: { + get: () => ax.get('api/usuarios/administradores'), + post: (data) => ax.post('api/usuarios/administradores/', data), + }, + + // Endpoints del submodulo token token: { login: (data) => ax.post('api/usuarios/token/',data), refresh: () => ax.post('api/usuarios/token/refresh/'), }, + + //Endpoints del submodulo solicitantes + solicitantes: { + get: () => ax.get('api/usuarios/solicitantes'), + post: (data) => ax.post('api/usuarios/solicitantes/', data), + getById: (id) => ax.get(`api/usuarios/solicitantes/${id}`), + update: (id, data) => ax.put(`api/usuarios/solicitantes/${id}`, data), + }, + }, + administracion: { + //Aun por declarar }, - + modalidades: { + get: () => ax.get('api/modalidades'), + post: (data) => ax.post('api/modalidades/', data), + getById: (id) => ax.get(`api/modalidades/${id}`), + update: (id, data) => ax.put(`api/modalidades/${id}`, data), + delete: (id) => ax.delete(`api/modalidades/${id}`), + monto: { + post: (data) => ax.post('api/modalidades', data), + } + }, + notificaciones: { + // Aun por declarar + }, + solicitudes: { + get: () => ax.get('api/solicitudes'), + post: (data) => ax.post('api/solicitudes/', data), + getById: (id) => ax.get(`api/solicitudes/${id}`), + update: (id, data) => ax.put(`api/solicitudes/${id}`, data), + historial: { + get: () => ax.get('api/solicitudes/historial'), + getById: (id) => ax.get(`api/solicitudes/historial/${id}`), + }, + reportes: { + get: () => ax.get('api/solicitudes/reportes'), + getById: (id) => ax.get(`api/solicitudes/reportes/${id}`), + } + }, + dynamicTables: { + get: () => ax.get('api/dynamic-tables'), + post: (data) => ax.post('api/dynamic-tables/', data), + getById: (id) => ax.get(`api/dynamic-tables/${id}`), + update: (id, data) => ax.put(`api/dynamic-tables/${id}`, data), + } }; // Exportar los endpoints de la API diff --git a/cosiap_frontend/src/components/FormsValidations.jsx b/cosiap_frontend/src/components/FormsValidations.jsx new file mode 100644 index 0000000000000000000000000000000000000000..785a51a176fffe522f23a3f05044563863d95af8 --- /dev/null +++ b/cosiap_frontend/src/components/FormsValidations.jsx @@ -0,0 +1,46 @@ +import * as Yup from "yup"; + + +// Variable de formato para validación de formato de CURP +const CURP_REGEX = /^[A-Z]{1}[AEIOU]{1}[A-Z]{2}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|1[0-9]|2[0-9]|3[0-1])[HM]{1}(AS|BC|BS|CC|CH|CL|CM|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[0-9A-Z]{1}[0-9]{1}$/; + +// Validación de un campo CURP +const CURP_VALIDATION = Yup.string() + .required("La CURP es requerida") + .matches(CURP_REGEX, "Formato de CURP inválido"); + + +const PASSWORD_CREATION_VALIDATION = Yup.string() + .required("La contraseña es requerida") + .min(8, 'La contraseña debe tener al menos 8 caracteres') + .max(20, 'La contraseña no puede tener más de 20 caracteres') + .matches(/\d/, 'Debe contener al menos un número') + .matches(/[a-z]/, 'Debe contener al menos una letra minúscula') + .matches(/[A-Z]/, 'Debe contener al menos una letra mayúscula') + .matches(/[@$#¡!%*¿?&]/, 'Debe contener al menos un carácter especial') + .matches(/^\S*$/, 'La contraseña no puede contener espacios'); + +const CONFIRM_PASSWORD_VALIDATION = Yup.string() + .required("La confirmacion de la contraseña es requerida") + .oneOf([Yup.ref('password'), null], 'Las contraseñas no coinciden'); + + + + +export const LoginValidationSchema = Yup.object().shape({ + curp: CURP_VALIDATION, + password: Yup.string() + .required("La contraseña es requerida") +}); + +export const RegisterValidationSchema = Yup.object({ + nombre: Yup.string() + .required('El nombre es requerido') + .min(3, 'El nombre debe tener al menos 3 caracteres'), + curp: CURP_VALIDATION, + email: Yup.string() + .required("El correo electronico es requerido") + .email("El correo electronico no es válido"), + password: PASSWORD_CREATION_VALIDATION, + confirmar_password: CONFIRM_PASSWORD_VALIDATION +}); diff --git a/cosiap_frontend/src/components/users/AuthPage.jsx b/cosiap_frontend/src/components/users/AuthPage.jsx deleted file mode 100644 index 5a1913d622f0c667152524dd85b3549eb88605aa..0000000000000000000000000000000000000000 --- a/cosiap_frontend/src/components/users/AuthPage.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import LayoutBaseAuthenticator from '@/components/common/layouts/LayoutBaseAuthenticator'; -import { Login } from "./Login/Login"; -import Register from './Register/Register'; -import { useState } from 'react'; - -export function AuthPage( {setViewPageLoader} ) { - const [component, setComponent] = useState('Login') - - const renderPage = () => { - switch (component) { - case 'Login': - return ; - case 'Register': - return ; - // case 'resetPassword': - // return ; - default: - return ; - } - } - - return ( - <> - - - {renderPage()} - - - ); -} \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/Inicio.jsx b/cosiap_frontend/src/components/users/Inicio.jsx index a6d7e7b249788f4f0156613eb3d0f1b01cbb1e97..e004bcfbd5d8ec0b75c0e706bf61980b3b6a26b7 100644 --- a/cosiap_frontend/src/components/users/Inicio.jsx +++ b/cosiap_frontend/src/components/users/Inicio.jsx @@ -1,7 +1,67 @@ export default function Inicio() { return ( -
-

Inicio

-
- ); + <> + + + ) } diff --git a/cosiap_frontend/src/components/users/Login/Login.jsx b/cosiap_frontend/src/components/users/Login/Login.jsx index b59eea06df739fe53260d9f3af5f604053d3a716..a14210637f5862cce8923276573bb9183ff44a61 100644 --- a/cosiap_frontend/src/components/users/Login/Login.jsx +++ b/cosiap_frontend/src/components/users/Login/Login.jsx @@ -1,9 +1,18 @@ +import LayoutBaseAuthenticator from '@/components/common/layouts/LayoutBaseAuthenticator'; +import { useNavigate } from "react-router-dom"; import { LoginForm } from './LoginForm'; -export function Login( {setComponent, setViewPageLoader} ) { + +export function Login( {setViewPageLoader} ) { + const navigate = useNavigate(); + + function navigateRegister(){ + navigate('register'); + } + return ( - <> +

¿Haz olvidado tu contraseña? @@ -11,9 +20,9 @@ export function Login( {setComponent, setViewPageLoader} ) {

¿Aun no tienes cuenta?

-

setComponent('Register')}> +

Registrate

- +
); } \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/Login/LoginForm.jsx b/cosiap_frontend/src/components/users/Login/LoginForm.jsx index d81f83c3e41be3c07df0f465ed58fd4ee96dd210..b5b820cb2408f19fe6993b916df45889f53ca4c7 100644 --- a/cosiap_frontend/src/components/users/Login/LoginForm.jsx +++ b/cosiap_frontend/src/components/users/Login/LoginForm.jsx @@ -1,6 +1,8 @@ import { useForm } from "react-hook-form"; -import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; +//Importa el objeto de validacion del formulario +import { LoginValidationSchema } from "@/components/FormsValidations"; + import api from "@/api"; // Asegúrate de importar tu instancia de API import {LoginInputCURP} from '@/components/users/Login/LoginInputCURP' import { LoginInputPassword } from "@/components/users/Login/LoginInputPassword"; @@ -9,14 +11,7 @@ import {ErrorDisplay} from '@/components/common/ui/ErrorDisplay' import { useNavigate } from "react-router-dom"; import {useAutenticacion} from "@/components/common/utility/Autenticador" -const CURP_REGEX = /^[A-Z]{1}[AEIOU]{1}[A-Z]{2}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|1[0-9]|2[0-9]|3[0-1])[HM]{1}(AS|BC|BS|CC|CH|CL|CM|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[0-9A-Z]{1}[0-9]{1}$/; -const validationSchema = Yup.object({ - curp: Yup.string() - .required("Requerido") - .matches(CURP_REGEX, "CURP inválido"), - password: Yup.string().required("Requerido"), -}); export function LoginForm( {setViewPageLoader} ) { const [loginError, setLoginError] = useState('') @@ -27,10 +22,9 @@ export function LoginForm( {setViewPageLoader} ) { handleSubmit, formState: { errors, isSubmitting }, } = useForm({ - resolver: yupResolver(validationSchema), + resolver: yupResolver(LoginValidationSchema), }); - const onSubmit = async (data) => { setViewPageLoader(true) try { diff --git a/cosiap_frontend/src/components/users/Login/LoginInputCURP.jsx b/cosiap_frontend/src/components/users/Login/LoginInputCURP.jsx index c5ffafa97ce5fbf4f0611213c4dd346b05f8aeff..fa3dbe85ba47822bf54718979c01732bf6151533 100644 --- a/cosiap_frontend/src/components/users/Login/LoginInputCURP.jsx +++ b/cosiap_frontend/src/components/users/Login/LoginInputCURP.jsx @@ -9,7 +9,7 @@ export const LoginInputCURP = ({ name, type, placeholder, className, register, e name={name} type={type} placeholder={placeholder} - className={`customInputsButtons block w-full rounded-tr-2xl bg-[#F6F2F2] border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-[var(--principal-f)] placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-[var(--principal-mf)] sm:text-sm sm:leading-6 ${className}`} + className={`customInputsButtons block w-full rounded-tr-2xl bg-[var(--pagina-fondo)] border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-[var(--principal-f)] placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-[var(--principal-mf)] sm:text-sm sm:leading-6 ${className}`} register={register} errors={errors} /> diff --git a/cosiap_frontend/src/components/users/Login/LoginInputPassword.jsx b/cosiap_frontend/src/components/users/Login/LoginInputPassword.jsx index 1daaa4a099d22b100b2bd1023f7d818cb95d739a..5254f6630f2bd9b4a434b80b24a1354cd7288a54 100644 --- a/cosiap_frontend/src/components/users/Login/LoginInputPassword.jsx +++ b/cosiap_frontend/src/components/users/Login/LoginInputPassword.jsx @@ -9,7 +9,7 @@ export const LoginInputPassword = ({ name, placeholder, className, register, er id={name} name={name} placeholder={placeholder} - className={`customInputsButtons block w-full rounded-tr-2xl bg-[#F6F2F2] border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-[var(--principal-f)] placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-[var(--principal-mf)] sm:text-sm sm:leading-6 ${className}`} + className={`customInputsButtons block w-full rounded-tr-2xl bg-[var(--pagina-fondo)] border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-[var(--principal-f)] placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-[var(--principal-mf)] sm:text-sm sm:leading-6 ${className}`} register={register} errors={errors} /> diff --git a/cosiap_frontend/src/components/users/Register/Register.jsx b/cosiap_frontend/src/components/users/Register/Register.jsx index e343e9a37e68dfc61ceed895420000422f7448df..14e6c93907b210b95dfdd793b44c4228335f84da 100644 --- a/cosiap_frontend/src/components/users/Register/Register.jsx +++ b/cosiap_frontend/src/components/users/Register/Register.jsx @@ -1,15 +1,25 @@ +import LayoutBaseAuthenticator from '@/components/common/layouts/LayoutBaseAuthenticator'; +import { useNavigate } from "react-router-dom"; + + import { useState } from "react"; import RegisterForm from "./RegisterForm"; -export default function Register( {setComponent, setViewPageLoader} ) { +export default function Register({setViewPageLoader} ) { const [sentEmail, setSentEmail] = useState(false); + const navigate = useNavigate(); + + function navigateLogin(){ + navigate('/authentication'); + } + return ( - <> + { !sentEmail ? ( <> -

setComponent('Login')}> +

Iniciar sesión

@@ -24,13 +34,13 @@ export default function Register( {setComponent, setViewPageLoader} ) {
-
) } - +
); } \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/Register/RegisterForm.jsx b/cosiap_frontend/src/components/users/Register/RegisterForm.jsx index 017fddff712d599be51ac66fab5fdb674addbfba..bd51aa8e38ce7fef743fe2d79fb8fceaa9ec1cd0 100644 --- a/cosiap_frontend/src/components/users/Register/RegisterForm.jsx +++ b/cosiap_frontend/src/components/users/Register/RegisterForm.jsx @@ -3,27 +3,10 @@ import { RegisterInputPassword } from "./RegisterInputPassword"; import { useState } from "react"; import { ErrorDisplay } from '@/components/common/ui/ErrorDisplay'; import { useForm } from "react-hook-form"; -import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; +import { RegisterValidationSchema } from "@/components/FormsValidations"; import api from "@/api"; // Asegúrate de importar tu instancia de API -const CURP_REGEX = /^[A-Z]{1}[AEIOU]{1}[A-Z]{2}[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|1[0-9]|2[0-9]|3[0-1])[HM]{1}(AS|BC|BS|CC|CH|CL|CM|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[0-9A-Z]{1}[0-9]{1}$/; - -const validationSchema = Yup.object({ - nombre: Yup.string() - .required('Campo requerido'), - curp: Yup.string() - .required("Campo requerido") - .matches(CURP_REGEX, "Campo requerido"), - email: Yup.string() - .email("El email no es válido") - .required("Campo requerido"), - password: Yup.string() - .required("Campo requerido"), - confirmar_password: Yup.string() - .required("Campo requerido") - .oneOf([Yup.ref('password'), null], 'Las contraseñas no coinciden') -}); export default function RegisterForm( {setSentEmail, setViewPageLoader} ) { const [registerErrorMessage, setRegisterErrorMessage] = useState(''); @@ -32,7 +15,7 @@ export default function RegisterForm( {setSentEmail, setViewPageLoader} ) { handleSubmit, formState: { errors, isSubmitting }, } = useForm({ - resolver: yupResolver(validationSchema), + resolver: yupResolver(RegisterValidationSchema), }); const handleFormSubmission = async (data) => {