diff --git a/cosiap_api/cosiap_api/settings.py b/cosiap_api/cosiap_api/settings.py index 8b3015dc827cbb070efe3d5dc500301ecfbe3358..68e6856919d39872737e0f22c40c40256587c004 100644 --- a/cosiap_api/cosiap_api/settings.py +++ b/cosiap_api/cosiap_api/settings.py @@ -184,8 +184,8 @@ SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SIMPLE_JWT = { - 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), - 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=0.5), + 'REFRESH_TOKEN_LIFETIME': timedelta(minutes=1), 'ROTATE_REFRESH_TOKENS': False, 'BLACKLIST_AFTER_ROTATION': False, 'ALGORITHM': 'HS256', diff --git a/cosiap_api/users/views.py b/cosiap_api/users/views.py index 8e869c8d8e2f2ed8ef730d1050bde2ae28535dc3..4252bbe4b43918cf91741e87a9495bb7148e1e5b 100644 --- a/cosiap_api/users/views.py +++ b/cosiap_api/users/views.py @@ -45,10 +45,8 @@ class CustomTokenObtainPairView(TokenObtainPairView): # Eliminar el refresh token de la respuesta JSON del response.data['refresh'] # Agregar mensaje de éxito - response.data['message'] = {'succes':'Login successful'} - else: - response.data['errors'] = response.data # Mover errores a la clave 'errors' - response.data['message'] = {'error':'Login failed'} + response.data['message'] = {'succes':'Login exitoso'} + return response class CustomTokenRefreshView(TokenRefreshView): diff --git a/cosiap_frontend/src/components/common/ui/ErrorDisplay.css b/cosiap_frontend/src/components/common/ui/ErrorDisplay.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/cosiap_frontend/src/components/common/ui/ErrorDisplay.jsx b/cosiap_frontend/src/components/common/ui/ErrorDisplay.jsx new file mode 100644 index 0000000000000000000000000000000000000000..ebcada877ddd525791e2a761db42e2090d3e0a50 --- /dev/null +++ b/cosiap_frontend/src/components/common/ui/ErrorDisplay.jsx @@ -0,0 +1,36 @@ +import PropTypes from 'prop-types'; + + +export const ErrorDisplay = ({ errors }) => { + if (!errors) return null; + + return ( +
+ {renderErrors(errors)} +
+ ); +}; + +const renderErrors = (errors) => { + if (typeof errors === 'string') { + return

{errors}

; + } else if (Array.isArray(errors)) { + return errors.map((error, index) =>

{error}

); + } else if (typeof errors === 'object' && !Array.isArray(errors)) { + return Object.values(errors).map((errorList, index) => ( +
+ {errorList.map((error, idx) =>

{error}

)} +
+ )); + } +}; + +ErrorDisplay.propTypes = { + errors: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.array, + PropTypes.string + ]) +}; + +export default ErrorDisplay; \ No newline at end of file diff --git a/cosiap_frontend/src/components/common/ui/ErrorDisplay.utils.js b/cosiap_frontend/src/components/common/ui/ErrorDisplay.utils.js new file mode 100644 index 0000000000000000000000000000000000000000..2b9ed51c6f6ecba0cfd0ac6bf391239a99ab8b66 --- /dev/null +++ b/cosiap_frontend/src/components/common/ui/ErrorDisplay.utils.js @@ -0,0 +1,14 @@ +export function mergeErrors(errorList) { + // Filtrar y eliminar elementos undefined del array + const filteredErrors = errorList.filter(error => error !== undefined); + + return filteredErrors.reduce((acc, error) => { + Object.keys(error).forEach(key => { + if (!acc[key]) { + acc[key] = []; + } + acc[key] = acc[key].concat(error[key]); + }); + return acc; + }, {}); +} \ No newline at end of file diff --git a/cosiap_frontend/src/components/common/ui/FormInput.jsx b/cosiap_frontend/src/components/common/ui/FormInput.jsx index 7d397a5875d0a370476d23ec518cffc32d97c901..3ac82c14b9fbfc2c4cd73a52d3387ec9f7c0d601 100644 --- a/cosiap_frontend/src/components/common/ui/FormInput.jsx +++ b/cosiap_frontend/src/components/common/ui/FormInput.jsx @@ -1,6 +1,7 @@ import PropTypes from 'prop-types'; +import {ErrorDisplay} from '@/components/common/ui/ErrorDisplay' -export function FormInput({ id, name, type, placeholder, className, register, error }) { +export function FormInput({ id, name, type, placeholder, className, register, errors }) { return (
- {error &&
{error.message}
} +
); } @@ -20,8 +21,12 @@ FormInput.propTypes = { id: PropTypes.string.isRequired, name: PropTypes.string.isRequired, type: PropTypes.string.isRequired, - placeholder: PropTypes.string.isRequired, + placeholder: PropTypes.string, className: PropTypes.string, register: PropTypes.func.isRequired, - error: PropTypes.object, + errors: PropTypes.oneOfType([ + PropTypes.object, + PropTypes.array, + PropTypes.string + ]) }; \ No newline at end of file diff --git a/cosiap_frontend/src/components/users/LoginPage/LoginContainer.jsx b/cosiap_frontend/src/components/users/LoginPage/LoginContainer.jsx index 039145b98149f690eceb6119137f518e6483a75c..c15abd263ed6150a265c98787371ec089265cc7d 100644 --- a/cosiap_frontend/src/components/users/LoginPage/LoginContainer.jsx +++ b/cosiap_frontend/src/components/users/LoginPage/LoginContainer.jsx @@ -5,7 +5,7 @@ export function LoginContainer() {
= 640 ? "absolute" : "relative", diff --git a/cosiap_frontend/src/components/users/LoginPage/LoginForm.jsx b/cosiap_frontend/src/components/users/LoginPage/LoginForm.jsx index 93ec6440bdd0d92008ec8b16bab4e3b668c0888a..a9ab1fe01e8e38b124af8a36164d9eb157d7cad5 100644 --- a/cosiap_frontend/src/components/users/LoginPage/LoginForm.jsx +++ b/cosiap_frontend/src/components/users/LoginPage/LoginForm.jsx @@ -3,17 +3,20 @@ import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import api from "@/api"; // Asegúrate de importar tu instancia de API import {LoginInput} from '@/components/users/LoginPage/loginInput' +import { useState } from "react"; +import {ErrorDisplay} from '@/components/common/ui/ErrorDisplay' 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() - .matches(CURP_REGEX, "CURP inválida") + .matches(CURP_REGEX, "CURP inválido") .required("Requerido"), password: Yup.string().required("Requerido"), }); export function LoginForm() { + const [loginError, setLoginError] = useState('') const { register, handleSubmit, @@ -21,7 +24,7 @@ export function LoginForm() { } = useForm({ resolver: yupResolver(validationSchema), }); - + const onSubmit = async (data) => { try { const response = await api.usuarios.token.login(data); @@ -29,26 +32,27 @@ export function LoginForm() { // Manejar la respuesta exitosa aquí } catch (error) { console.error("Login failed:", error); - // Manejar el error aquí + setLoginError(error.response.data.detail) } }; return (
-
+ +