Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9f2d957
Implement user update functionality with validation and API integration
toscanodiaz Jun 1, 2025
6fa845b
Refactor user update forms to improve validation handling and integra…
toscanodiaz Jun 2, 2025
0996f12
Add ModalActualizarUsuario component to encapsulate user update funct…
toscanodiaz Jun 3, 2025
c009908
Add password confirmation validation and normalize user data in updat…
toscanodiaz Jun 3, 2025
bdef5a9
Merge branch 'develop' of https://github.com/CodeAnd-Co/Frontend-Text…
toscanodiaz Jun 4, 2025
c69f686
feat: add estatus field to user update form and handle undefined values
toscanodiaz Jun 4, 2025
6b983ff
Co-authored-by: Nicolas Hood Figueroa <NicoH00d@users.noreply.github.…
toscanodiaz Jun 6, 2025
f19a1c4
fix: remove unnecessary line breaks in user update form
toscanodiaz Jun 6, 2025
397e281
fix: improve user update handling and validation messages
toscanodiaz Jun 6, 2025
4e1361e
Merge branch 'develop' of https://github.com/CodeAnd-Co/Frontend-Text…
toscanodiaz Jun 6, 2025
73bff76
excluir cambiar usuario a superadmin
toscanodiaz Jun 6, 2025
4d12542
fix: update role filtering to exclude additional role ID
toscanodiaz Jun 6, 2025
fa75642
fix: adjust confirmation delay and update success alert message in us…
toscanodiaz Jun 6, 2025
7c56d5b
pull mbi
toscanodiaz Jun 6, 2025
8471589
fix: enhance user update confirmation with customizable alert message…
toscanodiaz Jun 6, 2025
bf13f34
fix: update password fields labels for clarity in user update form
toscanodiaz Jun 7, 2025
7e049b7
errores de lint
toscanodiaz Jun 7, 2025
5f3ff70
fix: improve email validation and enhance date of birth checks in use…
toscanodiaz Jun 7, 2025
6c62a8f
fix: enhance gender handling in user data initialization for better i…
toscanodiaz Jun 7, 2025
c6a6245
fix: improve validation and sanitization in user update form fields
toscanodiaz Jun 7, 2025
fc45422
Merge branch 'MBI-1' into feature/MT_RF4_ActualizaUsuario
toscanodiaz Jun 7, 2025
2f10010
Merge branch 'feature/MT_RF4_ActualizaUsuario' of https://github.com/…
NicoH00d Jun 7, 2025
47e44dd
Merge branch 'MBI-1' of https://github.com/CodeAnd-Co/Frontend-Text-L…
NicoH00d Jun 7, 2025
00e66f1
fix: corregir despliegue de super Admin
NicoH00d Jun 7, 2025
301ae5a
Merge branch 'MBI-1' into feature/MT_RF4_ActualizaUsuario
toscanodiaz Jun 7, 2025
576fd9e
fix: Despliegue de rol de usuario y eliminacion de carpeta
NicoH00d Jun 7, 2025
5e2104d
fix despliegue de roles
NicoH00d Jun 7, 2025
011ffdc
fix: posicion de alerta al eliminar
NicoH00d Jun 13, 2025
e5de6c6
Mensaje de correo duplicado
NicoH00d Jun 13, 2025
31a5afe
Manejo de la alerta
NicoH00d Jun 13, 2025
fe3cc77
Merge branch 'MBI-1' into feature/MT_RF4_ActualizaUsuario
NicoH00d Jun 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/Dominio/Modelos/Usuarios/ActualizarUsuario.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Modelo para actualizar un usuario

export const validarDatosActualizarUsuario = (datos, usuariosExistentes = []) => {
const errores = {};
const emailValido = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const telefonoValido = /^\d{10}$/;
const tieneCaracterEspecial = /[!@#$%^&*(),.?":{}|<>]/;
const tieneMayuscula = /[A-Z]/;

if (!datos.idUsuario) {
errores.idUsuario = true;
} else if (datos.idUsuario.toString().length > 10) {
errores.idUsuario = 'El idUsuario no debe tener más de 10 caracteres';
}

if (!datos.nombreCompleto || datos.nombreCompleto.trim().length === 0) {
errores.nombreCompleto = 'El nombre completo es obligatorio';
}

if (!datos.correoElectronico) {
errores.correoElectronico = 'El correo electrónico es obligatorio';
} else if (!emailValido.test(datos.correoElectronico)) {
errores.correoElectronico = 'Correo electrónico inválido';
} else if (
usuariosExistentes.some(
(usuario) =>
usuario.correoElectronico === datos.correoElectronico &&
usuario.idUsuario !== datos.idUsuario
)
) {
errores.correoElectronico = 'Este correo ya está registrado';
}

if (datos.contrasenia && /\s/.test(datos.contrasenia)) {
errores.contrasenia = 'La contraseña no debe contener espacios en blanco';
} else if (datos.contrasenia && datos.contrasenia.length < 8) {
errores.contrasenia = 'La contraseña debe tener al menos 8 caracteres';
} else if (datos.contrasenia && !tieneCaracterEspecial.test(datos.contrasenia)) {
errores.contrasenia =
'Debe contener al menos uno de estos caracteres: ! @ # $ % ^ & * ( ) , . ? " : { } | < >';
} else if (datos.contrasenia && !tieneMayuscula.test(datos.contrasenia)) {
errores.contrasenia = 'Debe contener al menos una letra mayúscula';
}

if (datos.contrasenia && !datos.confirmarContrasenia) {
errores.confirmarContrasenia = true;
} else if (datos.contrasenia && datos.contrasenia !== datos.confirmarContrasenia) {
errores.confirmarContrasenia = 'Las contraseñas no coinciden';
}

if (!datos.numeroTelefono) {
errores.numeroTelefono = 'El número de teléfono es obligatorio';
} else if (!telefonoValido.test(datos.numeroTelefono)) {
errores.numeroTelefono = 'El número de teléfono debe tener exactamente 10 dígitos';
}

if (!datos.direccion || datos.direccion.trim().length === 0) {
errores.direccion = 'La dirección es obligatoria';
} else if (datos.direccion.trim().length < 3) {
errores.direccion = 'La dirección debe tener al menos 3 caracteres';
} else if (datos.direccion.length > 100) {
errores.direccion = 'La dirección no debe exceder 100 caracteres';
}

/*if (!datos.fechaNacimiento) {
errores.fechaNacimiento = 'La fecha de nacimiento es obligatoria';
} else {
const hoy = new Date();
const fecha = new Date(datos.fechaNacimiento);

if (fecha > hoy) {
errores.fechaNacimiento = 'La fecha no puede ser futura';
}
}*/

if (!datos.fechaNacimiento) {
errores.fechaNacimiento = true;
} else {
const hoy = new Date();
const limiteInferiorFecha = new Date('1900-01-01');
const fecha = new Date(datos.fechaNacimiento);

if (fecha > hoy) {
errores.fechaNacimiento = 'La fecha no puede ser futura';
} else if (fecha < limiteInferiorFecha) {
errores.fechaNacimiento = 'Ingresa una fecha válida';
}
}

if (!datos.genero) {
errores.genero = 'El género es obligatorio';
}

if (typeof datos.estatus === 'undefined' || datos.estatus === null) {
errores.estatus = 'El estatus es obligatorio';
}

if (!datos.idRol) {
errores.idRol = 'El rol es obligatorio';
}

return errores;
};
25 changes: 25 additions & 0 deletions src/Dominio/Repositorios/Usuarios/RepositorioActualizarUsuario.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import axios from 'axios';
import { RUTAS_API } from '@Constantes/rutasAPI';

const API_KEY = import.meta.env.VITE_API_KEY;

export class RepositorioActualizarUsuario {
static async actualizar(cambios) {
try {
const respuesta = await axios.put(
RUTAS_API.USUARIOS.ACTUALIZAR_USUARIO,
{ cambios },
{
withCredentials: true,
headers: {
'x-api-key': API_KEY,
},
}
);
return respuesta.data;
} catch (error) {
const mensaje = error?.response?.data?.mensaje || 'Error al actualizar';
throw new Error(mensaje);
}
}
}
1 change: 1 addition & 0 deletions src/Utilidades/Constantes/rutas.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const RUTAS = {
BASE: '/usuarios',
CONSULTAR_ROLES: '/consultar-roles',
CONFIRMAR_CREACION: '/confirmar-creacion',
ACTUALIZAR_USUARIO: '/actualizar-usuario',
},
},
SISTEMA_TIENDA: {
Expand Down
1 change: 1 addition & 0 deletions src/Utilidades/Constantes/rutasAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const RUTAS_API = {
CONSULTAR_LISTA: `${BASE_USUARIOS}/consultar-lista-usuarios`,
CONSULTAR_USUARIO: `${BASE_USUARIOS}/consultar-usuario`,
ELIMINAR_USUARIOS: `${BASE_USUARIOS}/eliminar-usuarios`,
ACTUALIZAR_USUARIO: `${BASE_USUARIOS}/actualizar-usuario`,
},

AUTENTICACION: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import { useEffect, useRef } from 'react';
import { Box, Grid } from '@mui/material';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { DateField } from '@mui/x-date-pickers/DateField';
import CampoTexto from '@Atomos/CampoTexto';
import CampoSelect from '@Atomos/CampoSelect';
import CampoSelectMultiple from '@Atomos/CampoSelectMultiple';

const FormularioActualizarUsuario = ({
datosUsuario,
erroresValidacion,
manejarCambio,
manejarFechaNacimiento,
roles = [],
clientes = [],
cargandoRoles = false,
CAMPO_OBLIGATORIO = 'Este campo es obligatorio',
}) => {
const estiloCuadricula = {
display: 'flex',
justifyContent: 'center',
};

const rolAnterior = useRef(null);
useEffect(() => {
if (rolAnterior.current === 1 && datosUsuario.rol !== 1) {
manejarCambio({
target: {
name: 'cliente',
value: [],
},
});
}
if (datosUsuario.rol === 1 && clientes.length > 0) {
manejarCambio({
target: {
name: 'cliente',
value: clientes.map((cliente) => cliente.idCliente),
},
});
}
rolAnterior.current = datosUsuario.rol;
}, [datosUsuario.rol, clientes, manejarCambio]);

return (
<Grid container columns={12}>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Nombre'
name='nombreCompleto'
value={datosUsuario.nombreCompleto}
onChange={(letra) => {
const soloLetras = letra.target.value.replace(/[^a-zA-ZáéíóúÁÉÍÓÚñÑ\s]/g, '');
manejarCambio({ target: { name: 'nombreCompleto', value: soloLetras } });
}}
required
size='medium'
error={!!erroresValidacion.nombreCompleto}
helperText={erroresValidacion.nombreCompleto && CAMPO_OBLIGATORIO}
inputProps={{
maxLength: 50,
}}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale='es'>
<DateField
required
label='Fecha de nacimiento'
value={datosUsuario.fechaNacimiento}
onChange={manejarFechaNacimiento}
format='DD/MM/YYYY'
sx={{ width: '30ch' }}
slotProps={{
textField: {
error: !!erroresValidacion.fechaNacimiento,
helperText:
erroresValidacion.fechaNacimiento === true
? CAMPO_OBLIGATORIO
: erroresValidacion.fechaNacimiento || '',
},
}}
/>
</LocalizationProvider>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoSelect
required
label='Género'
name='genero'
value={datosUsuario.genero}
onChange={manejarCambio}
size='medium'
error={!!erroresValidacion.genero}
helperText={erroresValidacion.genero && CAMPO_OBLIGATORIO}
options={[
{ value: 'Hombre', label: 'Hombre' },
{ value: 'Mujer', label: 'Mujer' },
{ value: 'Otro', label: 'Otro' },
]}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Correo Electrónico'
name='correoElectronico'
value={datosUsuario.correoElectronico}
onChange={manejarCambio}
required
size='medium'
error={!!erroresValidacion.correoElectronico}
helperText={
erroresValidacion.correoElectronico === true
? CAMPO_OBLIGATORIO
: erroresValidacion.correoElectronico || ''
}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Número de Teléfono'
name='numeroTelefono'
value={datosUsuario.numeroTelefono}
onChange={(num) => {
const soloNumeros = num.target.value.replace(/\D/g, '');
manejarCambio({ target: { name: 'numeroTelefono', value: soloNumeros } });
}}
required
size='medium'
error={!!erroresValidacion.numeroTelefono}
helperText={
erroresValidacion.numeroTelefono === true
? CAMPO_OBLIGATORIO
: erroresValidacion.numeroTelefono || ''
}
inputProps={{
maxLength: 10,
}}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Dirección'
name='direccion'
value={datosUsuario.direccion}
onChange={manejarCambio}
required
size='medium'
error={!!erroresValidacion.direccion}
helperText={
erroresValidacion.direccion === true
? CAMPO_OBLIGATORIO
: erroresValidacion.direccion || ''
}
inputProps={{
maxLength: 100,
}}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoSelect
label='Rol'
name='idRol'
value={datosUsuario.idRol !== undefined ? datosUsuario.idRol : ''}
onChange={manejarCambio}
required
size='medium'
error={!!erroresValidacion.idRol}
helperText={erroresValidacion.idRol && CAMPO_OBLIGATORIO}
options={roles
.filter((rol) => rol.idRol !== 3)
.map((rol) => ({
value: rol.idRol,
label: rol.nombre,
}))}
disabled={cargandoRoles}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoSelect
label='Estatus'
name='estatus'
value={datosUsuario.estatus}
onChange={manejarCambio}
required
size='medium'
error={!!erroresValidacion.estatus}
helperText={erroresValidacion.estatus && CAMPO_OBLIGATORIO}
options={[
{ value: 1, label: 'Activo' },
{ value: 0, label: 'Inactivo' },
]}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoSelectMultiple
label='Cliente'
name='cliente'
value={datosUsuario.cliente[0] == null ? [] : datosUsuario.cliente} //datosUsuario.cliente || ['']}
onChange={manejarCambio}
required
size='medium'
error={!!erroresValidacion.cliente}
helperText={erroresValidacion.cliente && CAMPO_OBLIGATORIO}
options={clientes.map((cliente) => ({
value: cliente.idCliente,
label: cliente.nombreComercial,
}))}
//disabled={esSuperAdmin}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Cambiar contraseña'
name='contrasenia'
type='password'
value={datosUsuario.contrasenia}
onChange={manejarCambio}
size='medium'
error={!!erroresValidacion.contrasenia}
autoComplete='new-password'
helperText={
erroresValidacion.contrasenia === true
? CAMPO_OBLIGATORIO
: erroresValidacion.contrasenia || ''
}
/>
</Grid>
<Grid size={6} sx={estiloCuadricula}>
<CampoTexto
label='Confirmar cambio de contraseña'
name='confirmarContrasenia'
type='password'
value={datosUsuario.confirmarContrasenia || ''}
onChange={manejarCambio}
size='medium'
error={!!erroresValidacion.confirmarContrasenia}
autoComplete='new-password'
helperText={
erroresValidacion.confirmarContrasenia === true
? CAMPO_OBLIGATORIO
: erroresValidacion.confirmarContrasenia || ''
}
/>
</Grid>
</Grid>
);
};

export default FormularioActualizarUsuario;
Loading