Se agrego funcionalidad a procesos
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,8 @@ const initialForm = {
|
|||||||
first_name: '',
|
first_name: '',
|
||||||
last_name: '',
|
last_name: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
rfc: '', // Agregar campo RFC para importadores
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Users() {
|
export default function Users() {
|
||||||
@@ -44,6 +46,20 @@ export default function Users() {
|
|||||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||||
const { showMessage } = useNotification();
|
const { showMessage } = useNotification();
|
||||||
|
|
||||||
|
// Estados para validación de contraseña
|
||||||
|
const [passwordValidation, setPasswordValidation] = useState({
|
||||||
|
length: false,
|
||||||
|
uppercase: false,
|
||||||
|
lowercase: false,
|
||||||
|
number: false,
|
||||||
|
special: false,
|
||||||
|
});
|
||||||
|
const [showPasswordValidation, setShowPasswordValidation] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
const [passwordsMatch, setPasswordsMatch] = useState(true);
|
||||||
|
const [showPasswordMatchValidation, setShowPasswordMatchValidation] = useState(false);
|
||||||
|
|
||||||
const loadUsers = () => {
|
const loadUsers = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
fetchUsers()
|
fetchUsers()
|
||||||
@@ -107,7 +123,52 @@ export default function Users() {
|
|||||||
}, [searchTerm]);
|
}, [searchTerm]);
|
||||||
|
|
||||||
const handleChange = e => {
|
const handleChange = e => {
|
||||||
setForm({ ...form, [e.target.name]: e.target.value });
|
const { name, value } = e.target;
|
||||||
|
setForm({ ...form, [name]: value });
|
||||||
|
|
||||||
|
// Validar contraseña en tiempo real
|
||||||
|
if (name === 'password') {
|
||||||
|
validatePassword(value);
|
||||||
|
// Validar coincidencia si ya hay confirmación
|
||||||
|
if (form.confirmPassword) {
|
||||||
|
validatePasswordMatch(value, form.confirmPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validar coincidencia de contraseñas
|
||||||
|
if (name === 'confirmPassword') {
|
||||||
|
validatePasswordMatch(form.password, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Función para validar contraseña
|
||||||
|
const validatePassword = (password) => {
|
||||||
|
const validation = {
|
||||||
|
length: password.length >= 8,
|
||||||
|
uppercase: /[A-Z]/.test(password),
|
||||||
|
lowercase: /[a-z]/.test(password),
|
||||||
|
number: /\d/.test(password),
|
||||||
|
special: /[!@#$%^&*(),.?":{}|<>]/.test(password),
|
||||||
|
};
|
||||||
|
setPasswordValidation(validation);
|
||||||
|
setShowPasswordValidation(password.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Función para validar coincidencia de contraseñas
|
||||||
|
const validatePasswordMatch = (password, confirmPassword) => {
|
||||||
|
const match = password === confirmPassword;
|
||||||
|
setPasswordsMatch(match);
|
||||||
|
setShowPasswordMatchValidation(confirmPassword.length > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verificar si la contraseña es válida
|
||||||
|
const isPasswordValid = () => {
|
||||||
|
return Object.values(passwordValidation).every(valid => valid);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verificar si el formulario es válido para envío
|
||||||
|
const isFormValid = () => {
|
||||||
|
return isPasswordValid() && passwordsMatch && form.password.length > 0 && form.confirmPassword.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// (Eliminado duplicado de handleSubmit, ahora solo existe la versión unificada más abajo)
|
// (Eliminado duplicado de handleSubmit, ahora solo existe la versión unificada más abajo)
|
||||||
@@ -160,6 +221,19 @@ export default function Users() {
|
|||||||
setShowEditModal(false);
|
setShowEditModal(false);
|
||||||
setShowDeleteModal(false);
|
setShowDeleteModal(false);
|
||||||
setUserToDelete(null);
|
setUserToDelete(null);
|
||||||
|
// Resetear estados de validación de contraseña
|
||||||
|
setPasswordValidation({
|
||||||
|
length: false,
|
||||||
|
uppercase: false,
|
||||||
|
lowercase: false,
|
||||||
|
number: false,
|
||||||
|
special: false,
|
||||||
|
});
|
||||||
|
setShowPasswordValidation(false);
|
||||||
|
setShowPassword(false);
|
||||||
|
setShowConfirmPassword(false);
|
||||||
|
setPasswordsMatch(true);
|
||||||
|
setShowPasswordMatchValidation(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Función para normalizar texto (quitar acentos y espacios extra)
|
// Función para normalizar texto (quitar acentos y espacios extra)
|
||||||
@@ -981,27 +1055,64 @@ export default function Users() {
|
|||||||
{/* Modales */}
|
{/* Modales */}
|
||||||
{/* Modal Crear Usuario */}
|
{/* Modal Crear Usuario */}
|
||||||
{showCreateModal && (
|
{showCreateModal && (
|
||||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-60 backdrop-blur-sm overflow-y-auto h-full w-full z-50 flex items-center justify-center p-4">
|
||||||
<div className="relative top-20 mx-auto p-5 border w-11/12 md:w-3/4 lg:w-1/2 shadow-lg rounded-md bg-white">
|
<div className="relative mx-auto w-full max-w-2xl bg-white rounded-2xl shadow-2xl transform transition-all duration-300 animate-in slide-in-from-bottom-4">
|
||||||
<div className="mt-3">
|
{/* Header formal con gradiente */}
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className={`${createType === 'importador' ? 'bg-gradient-to-r from-green-700 to-green-900' : 'bg-gradient-to-r from-blue-700 to-blue-900'} rounded-t-2xl p-4 text-white border-b-2 ${createType === 'importador' ? 'border-green-500' : 'border-blue-500'}`}>
|
||||||
<h3 className="text-lg font-medium text-gray-900">Crear Nuevo Usuario</h3>
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className={`${createType === 'importador' ? 'bg-green-500' : 'bg-blue-500'} bg-opacity-30 rounded-xl p-2 border ${createType === 'importador' ? 'border-green-400' : 'border-blue-400'} border-opacity-30`}>
|
||||||
|
{createType === 'importador' ? (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold tracking-wide">
|
||||||
|
{createType === 'importador' ? 'Registro de Nuevo Importador' : 'Registro de Nuevo Agente'}
|
||||||
|
</h3>
|
||||||
|
<p className={`${createType === 'importador' ? 'text-green-200' : 'text-blue-200'} text-xs font-medium`}>
|
||||||
|
{createType === 'importador' ? 'Sistema de Gestión de Importadores' : 'Sistema de Gestión de Agentes Aduanales'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
className={`${createType === 'importador' ? 'text-green-100 hover:text-white hover:bg-green-600' : 'text-blue-100 hover:text-white hover:bg-blue-600'} transition-colors p-2 hover:bg-opacity-50 rounded-lg border ${createType === 'importador' ? 'border-green-500' : 'border-blue-500'} border-opacity-30`}
|
||||||
>
|
>
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Contenido del formulario */}
|
||||||
|
<div className="p-6">
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-5">
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
{/* Sección de Información Personal */}
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
<div className="bg-slate-50 rounded-xl p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className={`${createType === 'importador' ? 'bg-green-600' : 'bg-blue-600'} rounded-lg p-2 mr-3 shadow-sm`}>
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<h4 className="text-sm font-semibold text-slate-800">Información Personal</h4>
|
||||||
Nombre de usuario *
|
<p className="text-xs text-slate-600">Datos de identificación del usuario</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Nombre de usuario <span className="text-red-600">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -1009,14 +1120,13 @@ export default function Users() {
|
|||||||
value={form.username}
|
value={form.username}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm transition-colors"
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
placeholder="nombre_usuario"
|
placeholder="nombre_usuario"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
<div>
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
Correo electrónico <span className="text-red-600">*</span>
|
||||||
Email *
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
@@ -1024,35 +1134,12 @@ export default function Users() {
|
|||||||
value={form.email}
|
value={form.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm transition-colors"
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
placeholder="usuario@ejemplo.com"
|
placeholder="usuario@ejemplo.com"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
{/* Campo RFC solo para importador */}
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
{createType === 'importador' && (
|
|
||||||
<>
|
|
||||||
<div className="sm:col-span-2">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
RFC del Importador *
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="rfc"
|
|
||||||
value={form.rfc || ''}
|
|
||||||
onChange={handleChange}
|
|
||||||
required
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500 text-sm transition-colors"
|
|
||||||
placeholder="RFC del importador"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/* Campo oculto para is_importador */}
|
|
||||||
<input type="hidden" name="is_importador" value="true" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Nombre
|
Nombre
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -1060,13 +1147,12 @@ export default function Users() {
|
|||||||
name="first_name"
|
name="first_name"
|
||||||
value={form.first_name}
|
value={form.first_name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm transition-colors"
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
placeholder="Nombre"
|
placeholder="Nombre del usuario"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
<div>
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
Apellido
|
Apellido
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -1074,45 +1160,224 @@ export default function Users() {
|
|||||||
name="last_name"
|
name="last_name"
|
||||||
value={form.last_name}
|
value={form.last_name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm transition-colors"
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
placeholder="Apellido"
|
placeholder="Apellido del usuario"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="sm:col-span-2">
|
{/* Sección de RFC (solo para importador) */}
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
{createType === 'importador' && (
|
||||||
Contraseña *
|
<div className="bg-green-50 rounded-xl p-4 border border-green-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-green-300">
|
||||||
|
<div className="bg-green-700 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Información Fiscal</h4>
|
||||||
|
<p className="text-xs text-slate-600">Datos fiscales del importador</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
RFC del Importador <span className="text-red-600">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="text"
|
||||||
|
name="rfc"
|
||||||
|
value={form.rfc || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
maxLength="13"
|
||||||
|
className="w-full px-3 py-2 border border-green-300 rounded-md shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm font-mono uppercase"
|
||||||
|
placeholder="RFC13CARACTERES"
|
||||||
|
style={{ textTransform: 'uppercase' }}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-green-600 mt-1">Formato: 12-13 caracteres (ABCD123456ABC)</p>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="is_importador" value="true" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Sección de Seguridad */}
|
||||||
|
<div className="bg-slate-50 rounded-xl p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-red-600 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Credenciales de Acceso</h4>
|
||||||
|
<p className="text-xs text-slate-600">Configuración de seguridad de la cuenta</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Contraseña <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
name="password"
|
name="password"
|
||||||
value={form.password}
|
value={form.password}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm transition-colors"
|
className={`w-full px-3 py-2 pr-10 border rounded-md shadow-sm focus:ring-2 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm ${
|
||||||
placeholder="Contraseña del usuario"
|
showPasswordValidation && isPasswordValid()
|
||||||
|
? 'border-green-300 focus:ring-green-500 focus:border-green-500'
|
||||||
|
: showPasswordValidation
|
||||||
|
? 'border-red-300 focus:ring-red-500 focus:border-red-500'
|
||||||
|
: 'border-slate-300 focus:ring-red-500 focus:border-red-500'
|
||||||
|
}`}
|
||||||
|
placeholder="Contraseña segura del usuario"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute inset-y-0 right-0 flex items-center pr-3 text-slate-400 hover:text-slate-600"
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Indicadores de validación de contraseña */}
|
||||||
|
{showPasswordValidation && (
|
||||||
|
<div className="mt-3 p-3 bg-slate-100 rounded-lg border">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="text-xs font-semibold text-slate-700">Requisitos de contraseña:</span>
|
||||||
|
{isPasswordValid() && (
|
||||||
|
<div className="flex items-center text-green-600">
|
||||||
|
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span className="text-xs font-medium">Válida</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||||
|
<div className={`flex items-center text-xs ${passwordValidation.length ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordValidation.length ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
Mínimo 8 caracteres
|
||||||
|
</div>
|
||||||
|
<div className={`flex items-center text-xs ${passwordValidation.uppercase ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordValidation.uppercase ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
Una letra mayúscula
|
||||||
|
</div>
|
||||||
|
<div className={`flex items-center text-xs ${passwordValidation.lowercase ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordValidation.lowercase ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
Una letra minúscula
|
||||||
|
</div>
|
||||||
|
<div className={`flex items-center text-xs ${passwordValidation.number ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordValidation.number ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
Un número
|
||||||
|
</div>
|
||||||
|
<div className={`flex items-center text-xs sm:col-span-2 ${passwordValidation.special ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordValidation.special ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
Un carácter especial (!@#$%^&*(),.?":{}|<>)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Campo de confirmación de contraseña */}
|
||||||
|
<div className="space-y-1 mt-4">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Confirmar Contraseña <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type={showConfirmPassword ? "text" : "password"}
|
||||||
|
name="confirmPassword"
|
||||||
|
value={form.confirmPassword}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
className={`w-full px-3 py-2 pr-10 border rounded-md shadow-sm focus:ring-2 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm ${
|
||||||
|
showPasswordMatchValidation && passwordsMatch && form.confirmPassword.length > 0
|
||||||
|
? 'border-green-300 focus:ring-green-500 focus:border-green-500'
|
||||||
|
: showPasswordMatchValidation && !passwordsMatch
|
||||||
|
? 'border-red-300 focus:ring-red-500 focus:border-red-500'
|
||||||
|
: 'border-slate-300 focus:ring-red-500 focus:border-red-500'
|
||||||
|
}`}
|
||||||
|
placeholder="Confirme la contraseña"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute inset-y-0 right-0 flex items-center pr-3 text-slate-400 hover:text-slate-600"
|
||||||
|
>
|
||||||
|
{showConfirmPassword ? (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Indicador de coincidencia de contraseñas */}
|
||||||
|
{showPasswordMatchValidation && (
|
||||||
|
<div className={`mt-2 flex items-center text-xs ${passwordsMatch ? 'text-green-600' : 'text-red-500'}`}>
|
||||||
|
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={passwordsMatch ? "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" : "M6 18L18 6M6 6l12 12"} />
|
||||||
|
</svg>
|
||||||
|
<span className="font-medium">
|
||||||
|
{passwordsMatch ? 'Las contraseñas coinciden' : 'Las contraseñas no coinciden'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-end space-x-3 pt-4 border-t border-gray-200">
|
{/* Botones de acción */}
|
||||||
|
<div className="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-3 pt-4 border-t border-slate-200">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50"
|
className="w-full sm:w-auto px-6 py-2 border border-slate-300 rounded-md shadow-sm text-sm font-semibold text-slate-700 bg-white hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 transition-all duration-200 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting}
|
disabled={submitting || (showPasswordValidation && !isFormValid())}
|
||||||
className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50 flex items-center"
|
className={`w-full sm:w-auto px-6 py-2 border border-transparent rounded-md shadow-lg text-sm font-semibold text-white ${createType === 'importador' ? 'bg-gradient-to-r from-green-700 to-green-900 hover:from-green-800 hover:to-green-950 focus:ring-green-500' : 'bg-gradient-to-r from-blue-700 to-blue-900 hover:from-blue-800 hover:to-blue-950 focus:ring-blue-500'} focus:outline-none focus:ring-2 focus:ring-offset-2 transition-all duration-200 flex items-center justify-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||||
>
|
>
|
||||||
{submitting && (
|
{submitting && (
|
||||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
|
||||||
)}
|
)}
|
||||||
{submitting ? 'Creando...' : 'Crear Usuario'}
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
||||||
|
</svg>
|
||||||
|
<span>{submitting ? 'Creando...' : (createType === 'importador' ? 'Crear Importador' : 'Crear Agente')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ export default function Vucem() {
|
|||||||
const [showEditPassword, setShowEditPassword] = useState(false);
|
const [showEditPassword, setShowEditPassword] = useState(false);
|
||||||
const [showEditEfirma, setShowEditEfirma] = useState(false);
|
const [showEditEfirma, setShowEditEfirma] = useState(false);
|
||||||
|
|
||||||
|
// Estados específicos para modal de creación
|
||||||
|
const [showCreatePassword, setShowCreatePassword] = useState(false);
|
||||||
|
const [showCreateEfirma, setShowCreateEfirma] = useState(false);
|
||||||
|
|
||||||
// Funciones para alternar visibilidad
|
// Funciones para alternar visibilidad
|
||||||
const togglePasswordVisibility = (id) => {
|
const togglePasswordVisibility = (id) => {
|
||||||
setShowPassword(prev => ({
|
setShowPassword(prev => ({
|
||||||
@@ -137,6 +141,9 @@ export default function Vucem() {
|
|||||||
// Resetear estados de visibilidad del modal de edición
|
// Resetear estados de visibilidad del modal de edición
|
||||||
setShowEditPassword(false);
|
setShowEditPassword(false);
|
||||||
setShowEditEfirma(false);
|
setShowEditEfirma(false);
|
||||||
|
// Resetear estados de visibilidad del modal de creación
|
||||||
|
setShowCreatePassword(false);
|
||||||
|
setShowCreateEfirma(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch list
|
// Fetch list
|
||||||
@@ -642,8 +649,7 @@ export default function Vucem() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleVucemStatus(vucem.id, vucem.is_active)}
|
onClick={() => toggleVucemStatus(vucem.id, vucem.is_active)}
|
||||||
className={`inline-flex items-center p-2 border shadow-sm font-medium rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 transition-all duration-200 transform hover:scale-105 ${
|
className={`inline-flex items-center p-2 border shadow-sm font-medium rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 transition-all duration-200 transform hover:scale-105 ${vucem.is_active
|
||||||
vucem.is_active
|
|
||||||
? 'border-orange-300 text-orange-700 hover:bg-orange-50 focus:ring-orange-500'
|
? 'border-orange-300 text-orange-700 hover:bg-orange-50 focus:ring-orange-500'
|
||||||
: 'border-green-300 text-green-700 hover:bg-green-50 focus:ring-green-500'
|
: 'border-green-300 text-green-700 hover:bg-green-50 focus:ring-green-500'
|
||||||
}`}
|
}`}
|
||||||
@@ -741,8 +747,7 @@ export default function Vucem() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleVucemStatus(vucem.id, vucem.is_active)}
|
onClick={() => toggleVucemStatus(vucem.id, vucem.is_active)}
|
||||||
className={`inline-flex items-center justify-center p-2 border shadow-sm font-medium rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 transition-all duration-200 ${
|
className={`inline-flex items-center justify-center p-2 border shadow-sm font-medium rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 transition-all duration-200 ${vucem.is_active
|
||||||
vucem.is_active
|
|
||||||
? 'border-orange-300 text-orange-700 hover:bg-orange-50 focus:ring-orange-500'
|
? 'border-orange-300 text-orange-700 hover:bg-orange-50 focus:ring-orange-500'
|
||||||
: 'border-green-300 text-green-700 hover:bg-green-50 focus:ring-green-500'
|
: 'border-green-300 text-green-700 hover:bg-green-50 focus:ring-green-500'
|
||||||
}`}
|
}`}
|
||||||
@@ -966,8 +971,7 @@ export default function Vucem() {
|
|||||||
<button
|
<button
|
||||||
key={pageNum}
|
key={pageNum}
|
||||||
onClick={() => setPage(pageNum)}
|
onClick={() => setPage(pageNum)}
|
||||||
className={`px-3 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${
|
className={`px-3 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${page === pageNum
|
||||||
page === pageNum
|
|
||||||
? 'bg-blue-600 text-white shadow-lg transform scale-105'
|
? 'bg-blue-600 text-white shadow-lg transform scale-105'
|
||||||
: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 hover:transform hover:scale-105'
|
: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 hover:transform hover:scale-105'
|
||||||
}`}
|
}`}
|
||||||
@@ -993,15 +997,36 @@ export default function Vucem() {
|
|||||||
|
|
||||||
{/* Modal de creación */}
|
{/* Modal de creación */}
|
||||||
{showCreateModal && (
|
{showCreateModal && (
|
||||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-60 backdrop-blur-sm overflow-y-auto h-full w-full z-50 flex items-center justify-center p-2">
|
||||||
<div className="relative top-20 mx-auto p-5 border w-11/12 md:w-3/4 lg:w-1/2 shadow-lg rounded-md bg-white">
|
<div className="relative mx-auto w-full max-w-4xl bg-white rounded-lg shadow-2xl transform transition-all duration-300 animate-in slide-in-from-bottom-4">
|
||||||
<div className="flex items-center justify-between mb-4">
|
{/* Header formal en escala de azules */}
|
||||||
<h3 className="text-lg font-medium text-gray-900">Crear VUCEM</h3>
|
<div className="bg-gradient-to-r from-blue-700 to-blue-900 rounded-t-lg p-4 text-white border-b-2 border-blue-500">
|
||||||
<button onClick={closeModals} className="text-gray-400 hover:text-gray-600 transition-colors">
|
<div className="flex items-center justify-between">
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="bg-blue-500 bg-opacity-30 rounded-lg p-2 border border-blue-400 border-opacity-30">
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold tracking-wide">Registro de Credencial VUCEM</h3>
|
||||||
|
<p className="text-blue-200 text-xs font-medium">Sistema de Gestión de Credenciales Aduanales</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={closeModals}
|
||||||
|
className="text-blue-100 hover:text-white transition-colors p-2 hover:bg-blue-600 hover:bg-opacity-50 rounded-lg border border-blue-500 border-opacity-30"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form className="space-y-6 w-full" onSubmit={async (e) => {
|
</div>
|
||||||
|
|
||||||
|
{/* Contenido del formulario */}
|
||||||
|
<div className="p-4 max-h-[85vh] overflow-y-auto">
|
||||||
|
<form className="space-y-4" onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('usuario', form.usuario);
|
formData.append('usuario', form.usuario);
|
||||||
@@ -1023,31 +1048,86 @@ export default function Vucem() {
|
|||||||
alert('Error al crear VUCEM');
|
alert('Error al crear VUCEM');
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
||||||
<div>
|
{/* Sección de Información Básica */}
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Usuario *</label>
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
<input name="usuario" value={form.usuario} onChange={handleInputChange} required className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" placeholder="usuario" />
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-blue-600 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Password *</label>
|
<h4 className="text-sm font-semibold text-slate-800">Información Básica del Usuario</h4>
|
||||||
|
<p className="text-xs text-slate-600">Datos de identificación del usuario VUCEM</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Usuario VUCEM <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="usuario"
|
||||||
|
value={form.usuario}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
|
placeholder="Ingrese el nombre de usuario VUCEM"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Número de Patente <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="patente"
|
||||||
|
value={form.patente}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
|
placeholder="Ingrese el número de patente"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sección de Credenciales */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-slate-700 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Credenciales de Seguridad</h4>
|
||||||
|
<p className="text-xs text-slate-600">Información de autenticación del sistema</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-gray-700">
|
||||||
|
Password <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
name="password"
|
name="password"
|
||||||
type={showPassword ? "text" : "password"}
|
type={showCreatePassword ? "text" : "password"}
|
||||||
value={form.password}
|
value={form.password}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-sm"
|
||||||
placeholder="password"
|
placeholder="Ingrese el password"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center">
|
<div className="absolute inset-y-0 right-0 flex items-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowCreatePassword(!showCreatePassword)}
|
||||||
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title={showPassword ? "Ocultar password" : "Mostrar password"}
|
title={showCreatePassword ? "Ocultar password" : "Mostrar password"}
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showCreatePassword ? (
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -1061,7 +1141,7 @@ export default function Vucem() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard(form.password, 'Password')}
|
onClick={() => copyToClipboard(form.password, 'Password')}
|
||||||
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title="Copiar password"
|
title="Copiar password"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -1071,30 +1151,28 @@ export default function Vucem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Patente *</label>
|
<label className="block text-xs font-semibold text-gray-700">
|
||||||
<input name="patente" value={form.patente} onChange={handleInputChange} required className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" placeholder="patente" />
|
e.firma <span className="text-red-500">*</span>
|
||||||
</div>
|
</label>
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">e.firma *</label>
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
name="efirma"
|
name="efirma"
|
||||||
type={showEfirma ? "text" : "password"}
|
type={showCreateEfirma ? "text" : "password"}
|
||||||
value={form.efirma}
|
value={form.efirma}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-sm"
|
||||||
placeholder="e.firma"
|
placeholder="Ingrese la e.firma"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center">
|
<div className="absolute inset-y-0 right-0 flex items-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowEfirma(!showEfirma)}
|
onClick={() => setShowCreateEfirma(!showCreateEfirma)}
|
||||||
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title={showEfirma ? "Ocultar e.firma" : "Mostrar e.firma"}
|
title={showCreateEfirma ? "Ocultar e.firma" : "Mostrar e.firma"}
|
||||||
>
|
>
|
||||||
{showEfirma ? (
|
{showCreateEfirma ? (
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -1108,7 +1186,7 @@ export default function Vucem() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard(form.efirma, 'e.firma')}
|
onClick={() => copyToClipboard(form.efirma, 'e.firma')}
|
||||||
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title="Copiar e.firma"
|
title="Copiar e.firma"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -1118,53 +1196,181 @@ export default function Vucem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Key (.key)</label>
|
</div>
|
||||||
<input name="key" type="file" accept=".key" onChange={handleInputChange} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm" />
|
|
||||||
{form.key && <span className="text-xs text-gray-500">{form.key.name}</span>}
|
{/* Sección de Archivos de Certificado */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-green-700 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Cer (.cer)</label>
|
<h4 className="text-sm font-semibold text-slate-800">Certificados Digitales</h4>
|
||||||
<input name="cer" type="file" accept=".cer" onChange={handleInputChange} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm" />
|
<p className="text-xs text-slate-600">Archivos de certificado y clave privada</p>
|
||||||
{form.cer && <span className="text-xs text-gray-500">{form.cer.name}</span>}
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="is_importador" type="checkbox" checked={form.is_importador} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Importador</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="acusecove" type="checkbox" checked={form.acusecove} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Acuse COVE</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="acuseedocument" type="checkbox" checked={form.acuseedocument} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Acuse e-Document</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="is_active" type="checkbox" checked={form.is_active} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Activo</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end space-x-3 pt-4 border-t border-gray-200">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<button type="button" onClick={closeModals} className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50">Cancelar</button>
|
<div className="space-y-1">
|
||||||
<button type="submit" className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50 flex items-center">Crear</button>
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Archivo de Clave (.key)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="key"
|
||||||
|
type="file"
|
||||||
|
accept=".key"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all duration-200 bg-white text-slate-900 file:mr-3 file:py-1 file:px-3 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-green-100 file:text-green-800 hover:file:bg-green-200 text-sm"
|
||||||
|
/>
|
||||||
|
{form.key && (
|
||||||
|
<div className="flex items-center text-xs text-green-700 bg-green-100 px-2 py-1.5 rounded-md border border-green-200">
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
{form.key.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Archivo de Certificado (.cer)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="cer"
|
||||||
|
type="file"
|
||||||
|
accept=".cer"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all duration-200 bg-white text-slate-900 file:mr-3 file:py-1 file:px-3 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-green-100 file:text-green-800 hover:file:bg-green-200 text-sm"
|
||||||
|
/>
|
||||||
|
{form.cer && (
|
||||||
|
<div className="flex items-center text-xs text-green-700 bg-green-100 px-2 py-1.5 rounded-md border border-green-200">
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
{form.cer.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sección de Configuraciones */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-amber-600 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Configuraciones del Sistema</h4>
|
||||||
|
<p className="text-xs text-slate-600">Opciones y permisos de la credencial</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="is_importador"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.is_importador}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Importador</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="acusecove"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.acusecove}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Acuse COVE</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="acuseedocument"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.acuseedocument}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Acuse e-Document</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="is_active"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.is_active}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-green-600 border-slate-300 rounded focus:ring-green-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Credencial Activa</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Botones de acción */}
|
||||||
|
<div className="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-3 pt-4 border-t border-slate-200">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={closeModals}
|
||||||
|
className="w-full sm:w-auto px-6 py-2 border border-slate-300 rounded-md shadow-sm text-sm font-semibold text-slate-700 bg-white hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 transition-all duration-200"
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full sm:w-auto px-6 py-2 border border-transparent rounded-md shadow-lg text-sm font-semibold text-white bg-gradient-to-r from-blue-700 to-blue-900 hover:from-blue-800 hover:to-blue-950 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span>Registrar Credencial</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modal de edición */}
|
{/* Modal de edición */}
|
||||||
{showEditModal && editVucem && (
|
{showEditModal && editVucem && (
|
||||||
<div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-60 backdrop-blur-sm overflow-y-auto h-full w-full z-50 flex items-center justify-center p-2">
|
||||||
<div className="relative top-20 mx-auto p-5 border w-11/12 md:w-3/4 lg:w-1/2 shadow-lg rounded-md bg-white">
|
<div className="relative mx-auto w-full max-w-4xl bg-white rounded-lg shadow-2xl transform transition-all duration-300 animate-in slide-in-from-bottom-4">
|
||||||
<div className="flex items-center justify-between mb-4">
|
{/* Header formal en escala de azules */}
|
||||||
<h3 className="text-lg font-medium text-gray-900">Editar VUCEM</h3>
|
<div className="bg-gradient-to-r from-blue-700 to-blue-900 rounded-t-lg p-4 text-white border-b-2 border-blue-500">
|
||||||
<button onClick={closeModals} className="text-gray-400 hover:text-gray-600 transition-colors">
|
<div className="flex items-center justify-between">
|
||||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="bg-blue-500 bg-opacity-30 rounded-lg p-2 border border-blue-400 border-opacity-30">
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold tracking-wide">Editar Credencial VUCEM</h3>
|
||||||
|
<p className="text-blue-200 text-xs font-medium">Modificación de Credenciales Aduanales</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={closeModals}
|
||||||
|
className="text-blue-100 hover:text-white transition-colors p-2 hover:bg-blue-600 hover:bg-opacity-50 rounded-lg border border-blue-500 border-opacity-30"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form className="space-y-6 w-full" onSubmit={async (e) => {
|
</div>
|
||||||
|
|
||||||
|
{/* Contenido del formulario */}
|
||||||
|
<div className="p-4 max-h-[85vh] overflow-y-auto">
|
||||||
|
<form className="space-y-4" onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!editVucem) return;
|
if (!editVucem) return;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -1187,27 +1393,82 @@ export default function Vucem() {
|
|||||||
alert('Error al actualizar VUCEM');
|
alert('Error al actualizar VUCEM');
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
||||||
<div>
|
{/* Sección de Información Básica */}
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Usuario *</label>
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
<input name="usuario" value={form.usuario} onChange={handleInputChange} required className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" placeholder="usuario" />
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-blue-600 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Password *</label>
|
<h4 className="text-sm font-semibold text-slate-800">Información Básica del Usuario</h4>
|
||||||
|
<p className="text-xs text-slate-600">Datos de identificación del usuario VUCEM</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Usuario VUCEM <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="usuario"
|
||||||
|
value={form.usuario}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
|
placeholder="Ingrese el nombre de usuario VUCEM"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Número de Patente <span className="text-red-600">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="patente"
|
||||||
|
value={form.patente}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-slate-900 placeholder-slate-400 text-sm"
|
||||||
|
placeholder="Ingrese el número de patente"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sección de Credenciales */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-slate-700 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Credenciales de Seguridad</h4>
|
||||||
|
<p className="text-xs text-slate-600">Información de autenticación del sistema</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-gray-700">
|
||||||
|
Password <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
name="password"
|
name="password"
|
||||||
type={showEditPassword ? "text" : "password"}
|
type={showEditPassword ? "text" : "password"}
|
||||||
value={form.password}
|
value={form.password}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-sm"
|
||||||
placeholder="password"
|
placeholder="Ingrese el password"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center">
|
<div className="absolute inset-y-0 right-0 flex items-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleEditPasswordVisibility}
|
onClick={toggleEditPasswordVisibility}
|
||||||
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title={showEditPassword ? "Ocultar password" : "Mostrar password"}
|
title={showEditPassword ? "Ocultar password" : "Mostrar password"}
|
||||||
>
|
>
|
||||||
{showEditPassword ? (
|
{showEditPassword ? (
|
||||||
@@ -1224,7 +1485,7 @@ export default function Vucem() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard(form.password, 'Password')}
|
onClick={() => copyToClipboard(form.password, 'Password')}
|
||||||
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title="Copiar password"
|
title="Copiar password"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -1234,12 +1495,10 @@ export default function Vucem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="space-y-1">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Patente *</label>
|
<label className="block text-xs font-semibold text-gray-700">
|
||||||
<input name="patente" value={form.patente} onChange={handleInputChange} required className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" placeholder="patente" />
|
e.firma <span className="text-red-500">*</span>
|
||||||
</div>
|
</label>
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">e.firma *</label>
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
name="efirma"
|
name="efirma"
|
||||||
@@ -1247,17 +1506,17 @@ export default function Vucem() {
|
|||||||
value={form.efirma}
|
value={form.efirma}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
className="w-full px-3 py-2 pr-20 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-200 bg-white text-sm"
|
||||||
placeholder="e.firma"
|
placeholder="Ingrese la e.firma"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center">
|
<div className="absolute inset-y-0 right-0 flex items-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleEditEfirmaVisibility}
|
onClick={toggleEditEfirmaVisibility}
|
||||||
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title={showEditEfirma ? "Ocultar e.firma" : "Mostrar e.firma"}
|
title={showEditEfirma ? "Ocultar e.firma" : "Mostrar e.firma"}
|
||||||
>
|
>
|
||||||
{showEfirma ? (
|
{showEditEfirma ? (
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.878 9.878L8.464 8.464m1.414 1.414L21.536 21.536" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -1271,7 +1530,7 @@ export default function Vucem() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => copyToClipboard(form.efirma, 'e.firma')}
|
onClick={() => copyToClipboard(form.efirma, 'e.firma')}
|
||||||
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none"
|
className="px-2 py-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none transition-colors"
|
||||||
title="Copiar e.firma"
|
title="Copiar e.firma"
|
||||||
>
|
>
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -1281,40 +1540,148 @@ export default function Vucem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Key (.key)</label>
|
</div>
|
||||||
<input name="key" type="file" accept=".key" onChange={handleInputChange} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm" />
|
|
||||||
{form.key && <span className="text-xs text-gray-500">{form.key.name}</span>}
|
{/* Sección de Archivos de Certificado */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-green-700 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">Cer (.cer)</label>
|
<h4 className="text-sm font-semibold text-slate-800">Certificados Digitales</h4>
|
||||||
<input name="cer" type="file" accept=".cer" onChange={handleInputChange} className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm text-sm" />
|
<p className="text-xs text-slate-600">Archivos de certificado y clave privada</p>
|
||||||
{form.cer && <span className="text-xs text-gray-500">{form.cer.name}</span>}
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="is_importador" type="checkbox" checked={form.is_importador} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Importador</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="acusecove" type="checkbox" checked={form.acusecove} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Acuse COVE</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="acuseedocument" type="checkbox" checked={form.acuseedocument} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Acuse e-Document</label>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<input name="is_active" type="checkbox" checked={form.is_active} onChange={handleInputChange} />
|
|
||||||
<label className="text-sm">Activo</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end space-x-3 pt-4 border-t border-gray-200">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
<button type="button" onClick={closeModals} className="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50">Cancelar</button>
|
<div className="space-y-1">
|
||||||
<button type="submit" className="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors disabled:opacity-50 flex items-center">Actualizar</button>
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Archivo de Clave (.key)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="key"
|
||||||
|
type="file"
|
||||||
|
accept=".key"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all duration-200 bg-white text-slate-900 file:mr-3 file:py-1 file:px-3 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-green-100 file:text-green-800 hover:file:bg-green-200 text-sm"
|
||||||
|
/>
|
||||||
|
{form.key && (
|
||||||
|
<div className="flex items-center text-xs text-green-700 bg-green-100 px-2 py-1.5 rounded-md border border-green-200">
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
{form.key.name}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<label className="block text-xs font-semibold text-slate-700">
|
||||||
|
Archivo de Certificado (.cer)
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="cer"
|
||||||
|
type="file"
|
||||||
|
accept=".cer"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-full px-3 py-2 border border-slate-300 rounded-md shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-all duration-200 bg-white text-slate-900 file:mr-3 file:py-1 file:px-3 file:rounded-md file:border-0 file:text-xs file:font-semibold file:bg-green-100 file:text-green-800 hover:file:bg-green-200 text-sm"
|
||||||
|
/>
|
||||||
|
{form.cer && (
|
||||||
|
<div className="flex items-center text-xs text-green-700 bg-green-100 px-2 py-1.5 rounded-md border border-green-200">
|
||||||
|
<svg className="w-3 h-3 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
{form.cer.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sección de Configuraciones */}
|
||||||
|
<div className="bg-slate-50 rounded-lg p-4 border border-slate-200">
|
||||||
|
<div className="flex items-center mb-3 pb-2 border-b border-slate-300">
|
||||||
|
<div className="bg-amber-600 rounded-lg p-2 mr-3 shadow-sm">
|
||||||
|
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-slate-800">Configuraciones del Sistema</h4>
|
||||||
|
<p className="text-xs text-slate-600">Opciones y permisos de la credencial</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="is_importador"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.is_importador}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Importador</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="acusecove"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.acusecove}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Acuse COVE</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="acuseedocument"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.acuseedocument}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-blue-600 border-slate-300 rounded focus:ring-blue-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Acuse e-Document</label>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2 bg-white p-3 rounded-md border border-slate-200 shadow-sm">
|
||||||
|
<input
|
||||||
|
name="is_active"
|
||||||
|
type="checkbox"
|
||||||
|
checked={form.is_active}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="w-4 h-4 text-green-600 border-slate-300 rounded focus:ring-green-500 focus:ring-1"
|
||||||
|
/>
|
||||||
|
<label className="text-xs font-semibold text-slate-700">Credencial Activa</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Botones de acción */}
|
||||||
|
<div className="flex flex-col sm:flex-row justify-end space-y-2 sm:space-y-0 sm:space-x-3 pt-4 border-t border-slate-200">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={closeModals}
|
||||||
|
className="w-full sm:w-auto px-6 py-2 border border-slate-300 rounded-md shadow-sm text-sm font-semibold text-slate-700 bg-white hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-500 transition-all duration-200"
|
||||||
|
>
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full sm:w-auto px-6 py-2 border border-transparent rounded-md shadow-lg text-sm font-semibold text-white bg-gradient-to-r from-blue-700 to-blue-900 hover:from-blue-800 hover:to-blue-950 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||||
|
>
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||||
|
</svg>
|
||||||
|
<span>Actualizar Credencial</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Modal de eliminación */}
|
{/* Modal de eliminación */}
|
||||||
|
|||||||
Reference in New Issue
Block a user