import React, { useState, useEffect } from 'react'; import { Modal, Box, Typography, TextField, Button, CircularProgress, Alert, FormControl, InputLabel, Select, MenuItem, FormControlLabel, Checkbox } from '@mui/material'; import type { UsuarioDto } from '../../../models/dtos/Usuarios/UsuarioDto'; import type { CreateUsuarioRequestDto } from '../../../models/dtos/Usuarios/CreateUsuarioRequestDto'; import type { UpdateUsuarioRequestDto } from '../../../models/dtos/Usuarios/UpdateUsuarioRequestDto'; import type { PerfilDto } from '../../../models/dtos/Usuarios/PerfilDto'; // Para el dropdown de perfiles import perfilService from '../../../services/Usuarios/perfilService'; // Para obtener la lista de perfiles const modalStyle = { position: 'absolute' as 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: { xs: '90%', sm: 500 }, // Responsive width bgcolor: 'background.paper', border: '2px solid #000', boxShadow: 24, p: 4, maxHeight: '90vh', // Para permitir scroll si el contenido es mucho overflowY: 'auto' // Habilitar scroll vertical }; interface UsuarioFormModalProps { open: boolean; onClose: () => void; onSubmit: (data: CreateUsuarioRequestDto | UpdateUsuarioRequestDto, id?: number) => Promise; initialData?: UsuarioDto | null; errorMessage?: string | null; clearErrorMessage: () => void; } const UsuarioFormModal: React.FC = ({ open, onClose, onSubmit, initialData, errorMessage, clearErrorMessage }) => { const [user, setUser] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [nombre, setNombre] = useState(''); const [apellido, setApellido] = useState(''); const [idPerfil, setIdPerfil] = useState(''); // Puede ser string vacío inicialmente const [habilitada, setHabilitada] = useState(true); const [supAdmin, setSupAdmin] = useState(false); const [debeCambiarClave, setDebeCambiarClave] = useState(true); const [verLog, setVerLog] = useState('1.0.0.0'); const [perfiles, setPerfiles] = useState([]); const [loading, setLoading] = useState(false); const [loadingPerfiles, setLoadingPerfiles] = useState(false); const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({}); const isEditing = Boolean(initialData); useEffect(() => { const fetchPerfiles = async () => { setLoadingPerfiles(true); try { const data = await perfilService.getAllPerfiles(); setPerfiles(data); } catch (error) { console.error("Error al cargar perfiles", error); setLocalErrors(prev => ({...prev, perfiles: 'Error al cargar perfiles.'})); } finally { setLoadingPerfiles(false); } }; if (open) { fetchPerfiles(); setUser(initialData?.user || ''); // No pre-rellenar contraseña en edición setPassword(''); setConfirmPassword(''); setNombre(initialData?.nombre || ''); setApellido(initialData?.apellido || ''); setIdPerfil(initialData?.idPerfil || ''); setHabilitada(initialData ? initialData.habilitada : true); setSupAdmin(initialData ? initialData.supAdmin : false); setDebeCambiarClave(initialData ? initialData.debeCambiarClave : !isEditing); // true para creación setVerLog(initialData?.verLog || '1.0.0.0'); setLocalErrors({}); clearErrorMessage(); } }, [open, initialData, clearErrorMessage, isEditing]); const validate = (): boolean => { const errors: { [key: string]: string | null } = {}; if (!user.trim()) errors.user = 'El nombre de usuario es obligatorio.'; else if (user.length < 3) errors.user = 'El usuario debe tener al menos 3 caracteres.'; if (!isEditing || (isEditing && password)) { // Validar contraseña solo si se está creando o si se ingresó algo en edición if (!password) errors.password = 'La contraseña es obligatoria.'; else if (password.length < 6) errors.password = 'La contraseña debe tener al menos 6 caracteres.'; if (password !== confirmPassword) errors.confirmPassword = 'Las contraseñas no coinciden.'; if (user.trim().toLowerCase() === password.toLowerCase()) errors.password = 'La contraseña no puede ser igual al nombre de usuario.' } if (!nombre.trim()) errors.nombre = 'El nombre es obligatorio.'; if (!apellido.trim()) errors.apellido = 'El apellido es obligatorio.'; if (!idPerfil) errors.idPerfil = 'Debe seleccionar un perfil.'; setLocalErrors(errors); return Object.keys(errors).length === 0; }; const handleInputChange = (fieldName: string) => { if (localErrors[fieldName]) { setLocalErrors(prev => ({ ...prev, [fieldName]: null })); } if (errorMessage) clearErrorMessage(); }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); clearErrorMessage(); if (!validate()) return; setLoading(true); try { if (isEditing && initialData) { const dataToSubmit: UpdateUsuarioRequestDto = { nombre, apellido, idPerfil: Number(idPerfil), habilitada, supAdmin, debeCambiarClave, verLog }; await onSubmit(dataToSubmit, initialData.id); // Si se ingresó una nueva contraseña, llamar a un endpoint separado para cambiarla if (password) { // Esto requeriría un endpoint adicional en el backend para que un admin cambie la clave de otro user // o adaptar el flujo de `AuthService.ChangePasswordAsync` si es posible, // o un nuevo endpoint en UsuarioController para setear clave. // Por ahora, lo dejamos así, la clave se cambia por el propio usuario o por un reset del admin. console.warn("El cambio de contraseña en edición de usuario no está implementado en este modal directamente. Usar la opción de 'Resetear Contraseña'."); } } else { const dataToSubmit: CreateUsuarioRequestDto = { user, password, nombre, apellido, idPerfil: Number(idPerfil), habilitada, supAdmin, debeCambiarClave, verLog }; await onSubmit(dataToSubmit); } onClose(); } catch (error: any) { console.error("Error en submit de UsuarioFormModal:", error); } finally { setLoading(false); } }; return ( {isEditing ? 'Editar Usuario' : 'Agregar Nuevo Usuario'} {/* SECCIÓN DE CAMPOS CON BOX Y FLEXBOX */} {/* Contenedor principal de campos */} {/* Fila 1 */} {setUser(e.target.value); handleInputChange('user');}} margin="dense" error={!!localErrors.user} helperText={localErrors.user || ''} disabled={loading || isEditing} autoFocus={!isEditing} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} // 8px es la mitad del gap /> Perfil {localErrors.idPerfil && {localErrors.idPerfil}} {loadingPerfiles && } {!isEditing && ( {/* Fila 2 (Contraseñas) */} {setPassword(e.target.value); handleInputChange('password');}} margin="dense" error={!!localErrors.password} helperText={localErrors.password || ''} disabled={loading} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} /> {setConfirmPassword(e.target.value); handleInputChange('confirmPassword');}} margin="dense" error={!!localErrors.confirmPassword} helperText={localErrors.confirmPassword || ''} disabled={loading} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} /> )} {/* Fila 3 (Nombre y Apellido) */} {setNombre(e.target.value); handleInputChange('nombre');}} margin="dense" error={!!localErrors.nombre} helperText={localErrors.nombre || ''} disabled={loading} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} /> {setApellido(e.target.value); handleInputChange('apellido');}} margin="dense" error={!!localErrors.apellido} helperText={localErrors.apellido || ''} disabled={loading} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} /> {/* Fila 4 (VerLog y Habilitado) */} setVerLog(e.target.value)} margin="dense" disabled={loading} sx={{ flex: 1, minWidth: 'calc(50% - 8px)' }} /> setHabilitada(e.target.checked)} disabled={loading}/>} label="Habilitado" /> {/* Fila 5 (Checkboxes) */} setSupAdmin(e.target.checked)} disabled={loading}/>} label="Super Administrador" /> setDebeCambiarClave(e.target.checked)} disabled={loading}/>} label="Debe Cambiar Clave" /> {/* Fin contenedor principal de campos */} {errorMessage && {errorMessage}} ); }; export default UsuarioFormModal;