grales
This commit is contained in:
@@ -61,8 +61,718 @@
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">⚙️ Automatizaciones</h4>
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">⚙️ Automatizaciones</h4>
|
||||
|
||||
<!-- Card de conexión WINSAAI -->
|
||||
<div class="row mb-4 fade-in-up">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm card-hover">
|
||||
<div class="card-header bg-primary text-white d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-plug me-2"></i>
|
||||
<h5 class="mb-0">Conexión con WINSAAI</h5>
|
||||
</div>
|
||||
<div id="connection_status_header">
|
||||
<span class="badge bg-secondary">Verificando...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<!-- Estado cuando NO hay configuración -->
|
||||
<div id="no_config_section">
|
||||
<p class="text-muted mb-3">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Configura la conexión con el sistema WINSAAI para importar automáticamente pedimentos y COVES.
|
||||
</p>
|
||||
|
||||
<!-- Botón para conectar -->
|
||||
<button class="btn btn-success btn-animated btn-pulse" type="button"
|
||||
onclick="showConfigForm()">
|
||||
<i class="fas fa-wifi me-2"></i>
|
||||
Conectar con WINSAAI
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Estado cuando SÍ hay configuración -->
|
||||
<div id="existing_config_section" style="display: none;">
|
||||
<div class="alert alert-success border-0" role="alert">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="fas fa-check-circle me-2 text-success fs-5"></i>
|
||||
<h6 class="mb-0 fw-bold">¡Estás configurado para conectarte a WINSAAI!</h6>
|
||||
</div>
|
||||
|
||||
<!-- Información de la configuración actual -->
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-server me-2 text-primary"></i>
|
||||
<div>
|
||||
<small class="text-muted d-block">Servidor</small>
|
||||
<span id="current_server" class="fw-semibold">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-user me-2 text-info"></i>
|
||||
<div>
|
||||
<small class="text-muted d-block">Usuario</small>
|
||||
<span id="current_user" class="fw-semibold">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-shield-alt me-2 text-warning"></i>
|
||||
<div>
|
||||
<small class="text-muted d-block">Protocolo</small>
|
||||
<span id="current_protocol" class="fw-semibold">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-clock me-2 text-secondary"></i>
|
||||
<div>
|
||||
<small class="text-muted d-block">Última sincronización</small>
|
||||
<span id="last_sync_display" class="fw-semibold">Nunca</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opciones de sincronización activas -->
|
||||
<div class="mb-3">
|
||||
<small class="text-muted d-block mb-2">Datos configurados para sincronizar:</small>
|
||||
<div class="d-flex gap-2">
|
||||
<span id="sync_pedimentos_badge" class="badge bg-primary" style="display: none;">
|
||||
<i class="fas fa-file-alt me-1"></i> Pedimentos
|
||||
</span>
|
||||
<span id="sync_coves_badge" class="badge bg-info" style="display: none;">
|
||||
<i class="fas fa-ship me-1"></i> COVES
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones de acción -->
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<button type="button" class="btn btn-outline-primary btn-animated btn-sm"
|
||||
onclick="showConfigForm(true)">
|
||||
<i class="fas fa-edit me-2"></i>
|
||||
Editar Conexión
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-warning btn-animated btn-sm"
|
||||
onclick="toggleConfigStatus()">
|
||||
<i class="fas fa-pause me-2"></i>
|
||||
<span id="toggle_status_text">Desactivar</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-animated btn-sm"
|
||||
onclick="testExistingConnection()">
|
||||
<i class="fas fa-vial me-2"></i>
|
||||
Probar Conexión
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-success btn-animated btn-sm"
|
||||
onclick="syncData()">
|
||||
<i class="fas fa-sync-alt me-2"></i>
|
||||
Sincronizar Ahora
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-animated btn-sm"
|
||||
onclick="deleteConfig()">
|
||||
<i class="fas fa-trash me-2"></i>
|
||||
Eliminar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Formulario de configuración (inicialmente oculto) -->
|
||||
<div class="collapse mt-4" id="winsaaiConfig">
|
||||
<div class="card border-secondary">
|
||||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-cog me-2"></i>
|
||||
<span id="form_title">Configuración de Conexión API</span>
|
||||
</h6>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary"
|
||||
onclick="hideConfigForm()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="winsaaiForm">
|
||||
<div class="row">
|
||||
<!-- Dirección IP/DNS -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="winsaai_host" class="form-label">
|
||||
<i class="fas fa-server me-1"></i>
|
||||
Dirección IP / DNS
|
||||
</label>
|
||||
<input type="text" class="form-control" id="winsaai_host"
|
||||
placeholder="192.168.1.100 o api.winsaai.com" required>
|
||||
<div class="form-text">
|
||||
Ingresa la dirección IP o nombre de dominio del servidor WINSAAI
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Puerto -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="winsaai_port" class="form-label">
|
||||
<i class="fas fa-network-wired me-1"></i>
|
||||
Puerto
|
||||
</label>
|
||||
<input type="number" class="form-control" id="winsaai_port"
|
||||
placeholder="8080" min="1" max="65535" required>
|
||||
<div class="form-text">
|
||||
Puerto del servicio API (ejemplo: 8080, 80, 443)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Usuario -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="winsaai_usuario" class="form-label">
|
||||
<i class="fas fa-user me-1"></i>
|
||||
Usuario
|
||||
</label>
|
||||
<input type="text" class="form-control" id="winsaai_usuario"
|
||||
placeholder="Tu usuario de WINSAAI" required>
|
||||
<div class="form-text">
|
||||
Nombre de usuario proporcionado por WINSAAI
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contraseña -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="winsaai_password" class="form-label">
|
||||
<i class="fas fa-lock me-1"></i>
|
||||
Contraseña
|
||||
</label>
|
||||
<input type="password" class="form-control" id="winsaai_password"
|
||||
placeholder="Tu contraseña" required>
|
||||
<div class="form-text">
|
||||
Contraseña de acceso al sistema WINSAAI
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Protocolo -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="winsaai_protocol" class="form-label">
|
||||
<i class="fas fa-shield-alt me-1"></i>
|
||||
Protocolo
|
||||
</label>
|
||||
<select class="form-select" id="winsaai_protocol" required>
|
||||
<option value="">Seleccionar protocolo</option>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS (Recomendado)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Estado Actual -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Estado Actual
|
||||
</label>
|
||||
<div id="connection_status_display" class="form-control-plaintext">
|
||||
<span class="badge bg-secondary">No configurado</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opciones de sincronización -->
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<label class="form-label">
|
||||
<i class="fas fa-sync me-1"></i>
|
||||
Datos a sincronizar
|
||||
</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sync_pedimentos" checked>
|
||||
<label class="form-check-label" for="sync_pedimentos">
|
||||
<i class="fas fa-file-alt me-1 text-primary"></i>
|
||||
Pedimentos
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="sync_coves" checked>
|
||||
<label class="form-check-label" for="sync_coves">
|
||||
<i class="fas fa-ship me-1 text-info"></i>
|
||||
COVES
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones de acción -->
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<button type="button" class="btn btn-outline-secondary btn-animated"
|
||||
onclick="testConnection()">
|
||||
<i class="fas fa-vial me-2"></i>
|
||||
Probar Conexión
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-animated">
|
||||
<i class="fas fa-save me-2"></i>
|
||||
Guardar Configuración
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning btn-animated"
|
||||
onclick="syncData()">
|
||||
<i class="fas fa-sync-alt me-2"></i>
|
||||
Sincronizar Ahora
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Estado de conexión -->
|
||||
<div id="connectionStatus" class="mt-3" style="display: none;">
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<span id="statusMessage">Verificando conexión...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Scripts para funcionalidad WINSAAI -->
|
||||
<script>
|
||||
let currentConfig = null;
|
||||
|
||||
// Cargar configuración al inicio
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadExistingConfig();
|
||||
});
|
||||
|
||||
// Cargar configuración existente
|
||||
async function loadExistingConfig() {
|
||||
try {
|
||||
const response = await fetch('/IMPORTADORES/winsaai/get_config');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.data) {
|
||||
currentConfig = result.data;
|
||||
showExistingConfig(result.data);
|
||||
} else {
|
||||
showNoConfig();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cargando configuración:', error);
|
||||
showNoConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar estado cuando NO hay configuración
|
||||
function showNoConfig() {
|
||||
document.getElementById('no_config_section').style.display = 'block';
|
||||
document.getElementById('existing_config_section').style.display = 'none';
|
||||
document.getElementById('connection_status_header').innerHTML = '<span class="badge bg-secondary">Sin configurar</span>';
|
||||
}
|
||||
|
||||
// Mostrar estado cuando SÍ hay configuración
|
||||
function showExistingConfig(config) {
|
||||
// Ocultar sección de "no config" y mostrar sección existente
|
||||
document.getElementById('no_config_section').style.display = 'none';
|
||||
document.getElementById('existing_config_section').style.display = 'block';
|
||||
|
||||
// Actualizar información mostrada
|
||||
document.getElementById('current_server').textContent = `${config.protocol}://${config.host}:${config.port}`;
|
||||
document.getElementById('current_user').textContent = config.usuario;
|
||||
document.getElementById('current_protocol').textContent = config.protocol.toUpperCase();
|
||||
|
||||
// Mostrar última sincronización
|
||||
if (config.last_sync) {
|
||||
const lastSyncDate = new Date(config.last_sync);
|
||||
document.getElementById('last_sync_display').textContent = lastSyncDate.toLocaleString();
|
||||
} else {
|
||||
document.getElementById('last_sync_display').textContent = 'Nunca';
|
||||
}
|
||||
|
||||
// Mostrar badges de sincronización
|
||||
const pedimentosBadge = document.getElementById('sync_pedimentos_badge');
|
||||
const covesBadge = document.getElementById('sync_coves_badge');
|
||||
|
||||
if (config.sync_pedimentos) {
|
||||
pedimentosBadge.style.display = 'inline-block';
|
||||
} else {
|
||||
pedimentosBadge.style.display = 'none';
|
||||
}
|
||||
|
||||
if (config.sync_coves) {
|
||||
covesBadge.style.display = 'inline-block';
|
||||
} else {
|
||||
covesBadge.style.display = 'none';
|
||||
}
|
||||
|
||||
// Actualizar header según estado
|
||||
updateHeaderStatus(config.status);
|
||||
|
||||
// Actualizar botón de toggle según estado
|
||||
updateToggleButton(config.status);
|
||||
|
||||
// Llenar formulario con datos existentes para edición
|
||||
document.getElementById('winsaai_host').value = config.host || '';
|
||||
document.getElementById('winsaai_port').value = config.port || '';
|
||||
document.getElementById('winsaai_protocol').value = config.protocol || '';
|
||||
document.getElementById('winsaai_usuario').value = config.usuario || '';
|
||||
document.getElementById('sync_pedimentos').checked = config.sync_pedimentos == 1;
|
||||
document.getElementById('sync_coves').checked = config.sync_coves == 1;
|
||||
}
|
||||
|
||||
// Actualizar estado en el header
|
||||
function updateHeaderStatus(status) {
|
||||
const headerStatus = document.getElementById('connection_status_header');
|
||||
let badgeHtml = '';
|
||||
|
||||
switch(status) {
|
||||
case 'activo':
|
||||
badgeHtml = '<span class="badge bg-success"><i class="fas fa-check-circle me-1"></i>Conectado</span>';
|
||||
break;
|
||||
case 'inactivo':
|
||||
badgeHtml = '<span class="badge bg-warning"><i class="fas fa-pause me-1"></i>Inactivo</span>';
|
||||
break;
|
||||
case 'error':
|
||||
badgeHtml = '<span class="badge bg-danger"><i class="fas fa-exclamation-triangle me-1"></i>Error</span>';
|
||||
break;
|
||||
default:
|
||||
badgeHtml = '<span class="badge bg-secondary">Sin configurar</span>';
|
||||
}
|
||||
|
||||
headerStatus.innerHTML = badgeHtml;
|
||||
}
|
||||
|
||||
// Actualizar botón de toggle según estado
|
||||
function updateToggleButton(status) {
|
||||
const toggleBtn = document.querySelector('button[onclick="toggleConfigStatus()"]');
|
||||
const toggleText = document.getElementById('toggle_status_text');
|
||||
const icon = toggleBtn.querySelector('i');
|
||||
|
||||
if (status === 'activo') {
|
||||
toggleText.textContent = 'Desactivar';
|
||||
icon.className = 'fas fa-pause me-2';
|
||||
toggleBtn.className = 'btn btn-outline-warning btn-animated btn-sm';
|
||||
} else {
|
||||
toggleText.textContent = 'Activar';
|
||||
icon.className = 'fas fa-play me-2';
|
||||
toggleBtn.className = 'btn btn-outline-success btn-animated btn-sm';
|
||||
}
|
||||
}
|
||||
|
||||
// Mostrar formulario de configuración
|
||||
function showConfigForm(isEdit = false) {
|
||||
const formTitle = document.getElementById('form_title');
|
||||
const configCollapse = new bootstrap.Collapse(document.getElementById('winsaaiConfig'), { show: true });
|
||||
|
||||
if (isEdit) {
|
||||
formTitle.textContent = 'Editar Configuración de WINSAAI';
|
||||
} else {
|
||||
formTitle.textContent = 'Nueva Configuración de WINSAAI';
|
||||
// Limpiar formulario para nueva configuración
|
||||
document.getElementById('winsaaiForm').reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Ocultar formulario de configuración
|
||||
function hideConfigForm() {
|
||||
const configCollapse = new bootstrap.Collapse(document.getElementById('winsaaiConfig'), { hide: true });
|
||||
}
|
||||
|
||||
// Probar conexión con configuración existente
|
||||
async function testExistingConnection() {
|
||||
if (!currentConfig) {
|
||||
showStatus('error', 'No hay configuración para probar');
|
||||
return;
|
||||
}
|
||||
|
||||
showStatus('info', 'Probando conexión con configuración actual...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/IMPORTADORES/winsaai/test_connection', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
host: currentConfig.host,
|
||||
port: currentConfig.port,
|
||||
protocol: currentConfig.protocol,
|
||||
usuario: currentConfig.usuario,
|
||||
password: 'existing_config' // Indicador para usar configuración guardada
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showStatus('success', result.message);
|
||||
updateHeaderStatus('activo');
|
||||
} else {
|
||||
showStatus('error', result.message);
|
||||
updateHeaderStatus('error');
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus('error', 'Error de red: ' + error.message);
|
||||
updateHeaderStatus('error');
|
||||
}
|
||||
}
|
||||
|
||||
// Activar/Desactivar configuración
|
||||
async function toggleConfigStatus() {
|
||||
if (!currentConfig) {
|
||||
showStatus('error', 'No hay configuración para modificar');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentStatus = currentConfig.status;
|
||||
const newStatus = currentStatus === 'activo' ? 'inactivo' : 'activo';
|
||||
const action = newStatus === 'activo' ? 'activar' : 'desactivar';
|
||||
|
||||
if (confirm(`¿Estás seguro de que deseas ${action} la configuración de WINSAAI?`)) {
|
||||
showStatus('info', `${action === 'activar' ? 'Activando' : 'Desactivando'} configuración...`);
|
||||
|
||||
try {
|
||||
// Aquí harías la llamada al servidor para cambiar el estado
|
||||
// Por ahora simularemos el cambio
|
||||
currentConfig.status = newStatus;
|
||||
updateHeaderStatus(newStatus);
|
||||
updateToggleButton(newStatus);
|
||||
showStatus('success', `Configuración ${action === 'activar' ? 'activada' : 'desactivada'} correctamente`);
|
||||
} catch (error) {
|
||||
showStatus('error', `Error al ${action} configuración: ` + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminar configuración
|
||||
async function deleteConfig() {
|
||||
if (!currentConfig) {
|
||||
showStatus('error', 'No hay configuración para eliminar');
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm('¿Estás seguro de que deseas eliminar completamente la configuración de WINSAAI? Esta acción no se puede deshacer.')) {
|
||||
showStatus('info', 'Eliminando configuración...');
|
||||
|
||||
try {
|
||||
// Aquí harías la llamada al servidor para eliminar
|
||||
// Por ahora simularemos la eliminación
|
||||
currentConfig = null;
|
||||
showNoConfig();
|
||||
hideConfigForm();
|
||||
showStatus('success', 'Configuración eliminada correctamente');
|
||||
} catch (error) {
|
||||
showStatus('error', 'Error al eliminar configuración: ' + error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Función para probar la conexión (desde formulario)
|
||||
async function testConnection() {
|
||||
const host = document.getElementById('winsaai_host').value;
|
||||
const port = document.getElementById('winsaai_port').value;
|
||||
const protocol = document.getElementById('winsaai_protocol').value;
|
||||
const usuario = document.getElementById('winsaai_usuario').value;
|
||||
const password = document.getElementById('winsaai_password').value;
|
||||
|
||||
if (!host || !port || !protocol || !usuario || !password) {
|
||||
showStatus('error', 'Por favor completa todos los campos obligatorios');
|
||||
return;
|
||||
}
|
||||
|
||||
showStatus('info', 'Probando conexión con WINSAAI...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/IMPORTADORES/winsaai/test_connection', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
host: host,
|
||||
port: parseInt(port),
|
||||
protocol: protocol,
|
||||
usuario: usuario,
|
||||
password: password
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showStatus('success', result.message);
|
||||
} else {
|
||||
showStatus('error', result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus('error', 'Error de red: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Función para sincronizar datos
|
||||
async function syncData() {
|
||||
let pedimentos, coves;
|
||||
|
||||
if (currentConfig) {
|
||||
// Usar configuración existente
|
||||
pedimentos = currentConfig.sync_pedimentos;
|
||||
coves = currentConfig.sync_coves;
|
||||
} else {
|
||||
// Usar valores del formulario
|
||||
pedimentos = document.getElementById('sync_pedimentos').checked;
|
||||
coves = document.getElementById('sync_coves').checked;
|
||||
}
|
||||
|
||||
if (!pedimentos && !coves) {
|
||||
showStatus('warning', 'No hay datos configurados para sincronizar');
|
||||
return;
|
||||
}
|
||||
|
||||
showStatus('info', 'Iniciando sincronización de datos...');
|
||||
|
||||
let syncType = 'both';
|
||||
if (pedimentos && !coves) syncType = 'pedimentos';
|
||||
else if (!pedimentos && coves) syncType = 'coves';
|
||||
|
||||
try {
|
||||
const response = await fetch('/IMPORTADORES/winsaai/sync_data', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sync_type: syncType
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
let message = 'Sincronización completada: ';
|
||||
if (result.data.pedimentos) {
|
||||
message += `${result.data.pedimentos.processed} pedimentos `;
|
||||
}
|
||||
if (result.data.coves) {
|
||||
message += `${result.data.coves.processed} COVES `;
|
||||
}
|
||||
showStatus('success', message);
|
||||
|
||||
// Actualizar última sincronización si hay configuración
|
||||
if (currentConfig) {
|
||||
currentConfig.last_sync = new Date().toISOString();
|
||||
document.getElementById('last_sync_display').textContent = new Date().toLocaleString();
|
||||
}
|
||||
} else {
|
||||
showStatus('error', result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
showStatus('error', 'Error de red: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Función para mostrar estado
|
||||
function showStatus(type, message) {
|
||||
const statusDiv = document.getElementById('connectionStatus');
|
||||
const statusMessage = document.getElementById('statusMessage');
|
||||
const alertDiv = statusDiv.querySelector('.alert');
|
||||
|
||||
// Remover clases anteriores
|
||||
alertDiv.className = 'alert';
|
||||
|
||||
// Agregar nueva clase según el tipo
|
||||
switch(type) {
|
||||
case 'success':
|
||||
alertDiv.classList.add('alert-success');
|
||||
statusMessage.innerHTML = `<i class="fas fa-check-circle me-2"></i>${message}`;
|
||||
break;
|
||||
case 'error':
|
||||
alertDiv.classList.add('alert-danger');
|
||||
statusMessage.innerHTML = `<i class="fas fa-exclamation-circle me-2"></i>${message}`;
|
||||
break;
|
||||
case 'warning':
|
||||
alertDiv.classList.add('alert-warning');
|
||||
statusMessage.innerHTML = `<i class="fas fa-exclamation-triangle me-2"></i>${message}`;
|
||||
break;
|
||||
default:
|
||||
alertDiv.classList.add('alert-info');
|
||||
statusMessage.innerHTML = `<i class="fas fa-info-circle me-2"></i>${message}`;
|
||||
}
|
||||
|
||||
statusDiv.style.display = 'block';
|
||||
|
||||
// Auto-ocultar después de 5 segundos para mensajes de éxito
|
||||
if (type === 'success') {
|
||||
setTimeout(() => {
|
||||
statusDiv.style.display = 'none';
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar envío del formulario
|
||||
document.getElementById('winsaaiForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const config = {
|
||||
host: document.getElementById('winsaai_host').value,
|
||||
port: parseInt(document.getElementById('winsaai_port').value),
|
||||
protocol: document.getElementById('winsaai_protocol').value,
|
||||
usuario: document.getElementById('winsaai_usuario').value,
|
||||
password: document.getElementById('winsaai_password').value,
|
||||
sync_pedimentos: document.getElementById('sync_pedimentos').checked,
|
||||
sync_coves: document.getElementById('sync_coves').checked
|
||||
};
|
||||
|
||||
// Validar campos obligatorios
|
||||
if (!config.host || !config.port || !config.protocol || !config.usuario || !config.password) {
|
||||
showStatus('error', 'Por favor completa todos los campos obligatorios');
|
||||
return;
|
||||
}
|
||||
|
||||
showStatus('info', 'Guardando configuración...');
|
||||
|
||||
try {
|
||||
const response = await fetch('/IMPORTADORES/winsaai/save_config', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showStatus('success', result.message);
|
||||
// Recargar configuración para mostrar el nuevo estado
|
||||
await loadExistingConfig();
|
||||
hideConfigForm();
|
||||
} else {
|
||||
showStatus('error', result.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error completo:', error);
|
||||
showStatus('error', 'Error de red: ' + error.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Animación suave para el collapse
|
||||
document.getElementById('winsaaiConfig').addEventListener('show.bs.collapse', function() {
|
||||
this.style.opacity = '0';
|
||||
this.style.transform = 'translateY(-20px)';
|
||||
setTimeout(() => {
|
||||
this.style.transition = 'all 0.3s ease';
|
||||
this.style.opacity = '1';
|
||||
this.style.transform = 'translateY(0)';
|
||||
}, 50);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
407
views/catalogo_pedimentos/crear.php
Normal file
407
views/catalogo_pedimentos/crear.php
Normal file
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
include __DIR__ . '/../partials/sidebar_importador.php';
|
||||
|
||||
// Verificar que el importador tenga información configurada
|
||||
if (!$importador) {
|
||||
echo '<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Configuración requerida:</strong>
|
||||
Debe configurar su información general antes de crear pedimentos.
|
||||
<a href="/IMPORTADORES/configuracion" class="btn btn-sm btn-primary ms-2">Ir a Configuración</a>
|
||||
</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Verificar que tenga claves de pedimentos configuradas
|
||||
if (empty($claves_pedimentos)) {
|
||||
echo '<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>Claves de pedimentos:</strong>
|
||||
No tiene claves de pedimentos de importación configuradas.
|
||||
<a href="/IMPORTADORES/claves_pedimentos" class="btn btn-sm btn-success ms-2">Configurar Claves</a>
|
||||
</div>';
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Nuevo Pedimento de Importación</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Nuevo Pedimento de Importación</h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/guardar" method="POST" id="formPedimento">
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Pedimento</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="pedimento" class="form-label">Número de Pedimento <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pedimento" name="pedimento" required
|
||||
placeholder="Ej: 23 47 3807 8001234"
|
||||
pattern="[0-9\s]+" title="Solo números y espacios">
|
||||
<div class="form-text">Formato estándar: AA AA AAAA AAAAAAA (año aduana sección número)</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="clave_ped" class="form-label">Clave de Pedimento <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="clave_ped" name="clave_ped" required>
|
||||
<option value="">Seleccionar clave...</option>
|
||||
<?php foreach ($claves_pedimentos as $clave): ?>
|
||||
<option value="<?= htmlspecialchars($clave['codigo']) ?>">
|
||||
<?= htmlspecialchars($clave['codigo']) ?> - <?= htmlspecialchars($clave['descripcion']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php if (empty($claves_pedimentos)): ?>
|
||||
<div class="form-text text-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
No tiene claves configuradas.
|
||||
<a href="/IMPORTADORES/claves_pedimentos/inicializar_claves_usuario">Inicializar claves por defecto</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="form-text">
|
||||
Seleccione la clave que corresponda al tipo de importación
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo_operacion" class="form-label">Tipo de Operación</label>
|
||||
<select class="form-select" id="tipo_operacion" name="tipo_operacion">
|
||||
<option value="1" selected>Importación Definitiva</option>
|
||||
<option value="3">Importación Temporal</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo_pedimento" class="form-label">Tipo de Pedimento</label>
|
||||
<select class="form-select" id="tipo_pedimento" name="tipo_pedimento">
|
||||
<option value="1" selected>Normal</option>
|
||||
<option value="2">Consolidado</option>
|
||||
<option value="3">Rectificación</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="regimen" class="form-label">Régimen</label>
|
||||
<input type="text" class="form-control" id="regimen" name="regimen"
|
||||
placeholder="Ej: IMD" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="destino" class="form-label">Destino</label>
|
||||
<input type="text" class="form-control" id="destino" name="destino"
|
||||
placeholder="Código de destino" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información del Importador (Automática) -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3 bg-light">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-building"></i> Información del Importador</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($importador): ?>
|
||||
<div class="mb-2">
|
||||
<strong>RFC:</strong>
|
||||
<span class="badge bg-primary"><?= htmlspecialchars($importador['rfc']) ?></span>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<strong>Nombre/Razón Social:</strong><br>
|
||||
<span class="text-muted"><?= htmlspecialchars($importador['nombre']) ?></span>
|
||||
</div>
|
||||
<?php if (!empty($importador['correo'])): ?>
|
||||
<div class="mb-2">
|
||||
<strong>Correo:</strong><br>
|
||||
<span class="text-muted"><?= htmlspecialchars($importador['correo']) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<hr>
|
||||
<div class="alert alert-success alert-sm">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
Esta información se incluirá automáticamente en el pedimento
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
Configure su información general primero
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fechas -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-calendar"></i> Fechas</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_pedimento" class="form-label">Fecha del Pedimento</label>
|
||||
<input type="date" class="form-control" id="fecha_pedimento" name="fecha_pedimento"
|
||||
value="<?= date('Y-m-d') ?>">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_inicio" class="form-label">Fecha de Inicio</label>
|
||||
<input type="date" class="form-control" id="fecha_inicio" name="fecha_inicio">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_final" class="form-label">Fecha Final</label>
|
||||
<input type="date" class="form-control" id="fecha_final" name="fecha_final">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información Adicional -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-cogs"></i> Información Adicional</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_final_previo" class="form-label">Archivo Final Previo</label>
|
||||
<input type="text" class="form-control" id="archivo_final_previo" name="archivo_final_previo"
|
||||
placeholder="Nombre del archivo" maxlength="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="acuse_cons" class="form-label">Acuse de Recibo</label>
|
||||
<input type="text" class="form-control" id="acuse_cons" name="acuse_cons"
|
||||
placeholder="Número de acuse" maxlength="20">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo" class="form-label">Tipo Adicional</label>
|
||||
<input type="text" class="form-control" id="tipo" name="tipo"
|
||||
placeholder="Información adicional" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">Estado</label>
|
||||
<select class="form-select" id="status" name="status">
|
||||
<option value="1" selected>Activo</option>
|
||||
<option value="0">Inactivo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-success btn-animated" <?= empty($claves_pedimentos) ? 'disabled' : '' ?>>
|
||||
<i class="fas fa-save"></i> Guardar Pedimento
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Validación del formulario
|
||||
$('#formPedimento').on('submit', function(e) {
|
||||
var pedimento = $('#pedimento').val().trim();
|
||||
var clavePed = $('#clave_ped').val();
|
||||
|
||||
if (!pedimento) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo obligatorio',
|
||||
text: 'El número de pedimento es obligatorio',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
$('#pedimento').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clavePed) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo obligatorio',
|
||||
text: 'Debe seleccionar una clave de pedimento',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
$('#clave_ped').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validación básica del formato de pedimento
|
||||
var pedimentoLimpio = pedimento.replace(/\s+/g, '');
|
||||
if (pedimentoLimpio.length < 13) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Formato inválido',
|
||||
text: 'El número de pedimento debe tener al menos 13 dígitos',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#pedimento').focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Formateo automático del número de pedimento
|
||||
$('#pedimento').on('input', function() {
|
||||
var value = this.value.replace(/[^\d]/g, ''); // Solo números
|
||||
var formatted = '';
|
||||
|
||||
if (value.length >= 2) {
|
||||
formatted += value.substr(0, 2) + ' ';
|
||||
if (value.length >= 4) {
|
||||
formatted += value.substr(2, 2) + ' ';
|
||||
if (value.length >= 8) {
|
||||
formatted += value.substr(4, 4) + ' ';
|
||||
if (value.length > 8) {
|
||||
formatted += value.substr(8);
|
||||
}
|
||||
} else if (value.length > 4) {
|
||||
formatted += value.substr(4);
|
||||
}
|
||||
} else if (value.length > 2) {
|
||||
formatted += value.substr(2);
|
||||
}
|
||||
} else {
|
||||
formatted = value;
|
||||
}
|
||||
|
||||
this.value = formatted;
|
||||
});
|
||||
|
||||
// Validación de fechas
|
||||
$('#fecha_inicio, #fecha_final').on('change', function() {
|
||||
var fechaInicio = $('#fecha_inicio').val();
|
||||
var fechaFinal = $('#fecha_final').val();
|
||||
|
||||
if (fechaInicio && fechaFinal && fechaInicio > fechaFinal) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Fechas incorrectas',
|
||||
text: 'La fecha de inicio no puede ser mayor que la fecha final',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
$(this).val('');
|
||||
}
|
||||
});
|
||||
|
||||
// Información adicional sobre la clave seleccionada
|
||||
$('#clave_ped').on('change', function() {
|
||||
var selectedText = $(this).find('option:selected').text();
|
||||
if (selectedText && selectedText !== 'Seleccionar clave...') {
|
||||
var descripcion = selectedText.split(' - ')[1];
|
||||
if (descripcion) {
|
||||
$(this).next('.form-text').html('<i class="fas fa-info-circle text-primary"></i> ' + descripcion);
|
||||
}
|
||||
} else {
|
||||
$(this).next('.form-text').html('Seleccione la clave que corresponda al tipo de importación');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
380
views/catalogo_pedimentos/editar.php
Normal file
380
views/catalogo_pedimentos/editar.php
Normal file
@@ -0,0 +1,380 @@
|
||||
<?php
|
||||
include __DIR__ . '/../partials/sidebar_importador.php';
|
||||
|
||||
// Convertir fechas de formato int a date si están presentes
|
||||
$fecha_pedimento_display = '';
|
||||
$fecha_inicio_display = '';
|
||||
$fecha_final_display = '';
|
||||
|
||||
if (!empty($previo['FechaPedimento'])) {
|
||||
$fecha_str = (string)$previo['FechaPedimento'];
|
||||
if (strlen($fecha_str) === 8) {
|
||||
$fecha_pedimento_display = substr($fecha_str, 0, 4) . '-' . substr($fecha_str, 4, 2) . '-' . substr($fecha_str, 6, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($previo['FechaInicio'])) {
|
||||
$fecha_str = (string)$previo['FechaInicio'];
|
||||
if (strlen($fecha_str) === 8) {
|
||||
$fecha_inicio_display = substr($fecha_str, 0, 4) . '-' . substr($fecha_str, 4, 2) . '-' . substr($fecha_str, 6, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($previo['FechaFinal'])) {
|
||||
$fecha_str = (string)$previo['FechaFinal'];
|
||||
if (strlen($fecha_str) === 8) {
|
||||
$fecha_final_display = substr($fecha_str, 0, 4) . '-' . substr($fecha_str, 4, 2) . '-' . substr($fecha_str, 6, 2);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Editar Pedimento</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Editar Pedimento #<?= htmlspecialchars($previo['IdPrevio']) ?></h4>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/catalogo_pedimentos/actualizar" method="POST" id="formPedimento">
|
||||
<input type="hidden" name="id_previo" value="<?= htmlspecialchars($previo['IdPrevio']) ?>">
|
||||
|
||||
<div class="row">
|
||||
<!-- Información Básica -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información Básica</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="pedimento" class="form-label">Número de Pedimento <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="pedimento" name="pedimento" required
|
||||
value="<?= htmlspecialchars($previo['Pedimento'] ?? '') ?>"
|
||||
placeholder="Ej: 23 47 3807 8001234">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo_operacion" class="form-label">Tipo de Operación</label>
|
||||
<select class="form-select" id="tipo_operacion" name="tipo_operacion">
|
||||
<option value="">Seleccionar...</option>
|
||||
<option value="1" <?= ($previo['TipoOperacion'] == 1) ? 'selected' : '' ?>>Importación Definitiva</option>
|
||||
<option value="2" <?= ($previo['TipoOperacion'] == 2) ? 'selected' : '' ?>>Exportación Definitiva</option>
|
||||
<option value="3" <?= ($previo['TipoOperacion'] == 3) ? 'selected' : '' ?>>Importación Temporal</option>
|
||||
<option value="4" <?= ($previo['TipoOperacion'] == 4) ? 'selected' : '' ?>>Exportación Temporal</option>
|
||||
<option value="5" <?= ($previo['TipoOperacion'] == 5) ? 'selected' : '' ?>>Tránsito</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo_pedimento" class="form-label">Tipo de Pedimento</label>
|
||||
<select class="form-select" id="tipo_pedimento" name="tipo_pedimento">
|
||||
<option value="">Seleccionar...</option>
|
||||
<option value="1" <?= ($previo['TipoPedimento'] == 1) ? 'selected' : '' ?>>Normal</option>
|
||||
<option value="2" <?= ($previo['TipoPedimento'] == 2) ? 'selected' : '' ?>>Consolidado</option>
|
||||
<option value="3" <?= ($previo['TipoPedimento'] == 3) ? 'selected' : '' ?>>Rectificación</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ...existing form fields... -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="clave_ped" class="form-label">Clave de Pedimento</label>
|
||||
<input type="text" class="form-control" id="clave_ped" name="clave_ped"
|
||||
value="<?= htmlspecialchars($previo['ClavePed'] ?? '') ?>"
|
||||
placeholder="Ej: A1" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="regimen" class="form-label">Régimen</label>
|
||||
<input type="text" class="form-control" id="regimen" name="regimen"
|
||||
value="<?= htmlspecialchars($previo['Regimen'] ?? '') ?>"
|
||||
placeholder="Ej: IMD" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="destino" class="form-label">Destino</label>
|
||||
<input type="text" class="form-control" id="destino" name="destino"
|
||||
value="<?= htmlspecialchars($previo['Destino'] ?? '') ?>"
|
||||
placeholder="Código de destino" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información del Cliente -->
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-user"></i> Información del Cliente</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="cliente_rfc" class="form-label">RFC del Cliente <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="cliente_rfc" name="cliente_rfc" required
|
||||
value="<?= htmlspecialchars($previo['ClienteRFC'] ?? '') ?>"
|
||||
placeholder="Ej: ABC123456789" maxlength="20" style="text-transform: uppercase;">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="cliente_nombre" class="form-label">Nombre/Razón Social del Cliente <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="cliente_nombre" name="cliente_nombre" rows="3" required
|
||||
placeholder="Nombre completo o razón social" maxlength="500"><?= htmlspecialchars($previo['ClienteNombre'] ?? '') ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fechas -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-calendar"></i> Fechas</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_pedimento" class="form-label">Fecha del Pedimento</label>
|
||||
<input type="date" class="form-control" id="fecha_pedimento" name="fecha_pedimento"
|
||||
value="<?= $fecha_pedimento_display ?>">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_inicio" class="form-label">Fecha de Inicio</label>
|
||||
<input type="date" class="form-control" id="fecha_inicio" name="fecha_inicio"
|
||||
value="<?= $fecha_inicio_display ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="fecha_final" class="form-label">Fecha Final</label>
|
||||
<input type="date" class="form-control" id="fecha_final" name="fecha_final"
|
||||
value="<?= $fecha_final_display ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información Adicional -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-cogs"></i> Información Adicional</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_final_previo" class="form-label">Archivo Final Previo</label>
|
||||
<input type="text" class="form-control" id="archivo_final_previo" name="archivo_final_previo"
|
||||
value="<?= htmlspecialchars($previo['ArchivoFinalPrevio'] ?? '') ?>"
|
||||
placeholder="Nombre del archivo" maxlength="100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="acuse_cons" class="form-label">Acuse de Recibo</label>
|
||||
<input type="text" class="form-control" id="acuse_cons" name="acuse_cons"
|
||||
value="<?= htmlspecialchars($previo['AcuseCons'] ?? '') ?>"
|
||||
placeholder="Número de acuse" maxlength="20">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="tipo" class="form-label">Tipo</label>
|
||||
<input type="text" class="form-control" id="tipo" name="tipo"
|
||||
value="<?= htmlspecialchars(trim($previo['Tipo'] ?? '')) ?>"
|
||||
placeholder="Tipo de pedimento" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">Estado</label>
|
||||
<select class="form-select" id="status" name="status">
|
||||
<option value="1" <?= ($previo['Status'] == 1) ? 'selected' : '' ?>>Activo</option>
|
||||
<option value="0" <?= ($previo['Status'] == 0) ? 'selected' : '' ?>>Inactivo</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información de Auditoría -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card mb-3 bg-light">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info"></i> Información de Registro</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>ID del Registro:</strong> <?= htmlspecialchars($previo['IdPrevio']) ?></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Fecha de Registro:</strong>
|
||||
<?php
|
||||
if ($previo['Timestamp'] instanceof DateTime) {
|
||||
echo $previo['Timestamp']->format('d/m/Y H:i:s');
|
||||
} else {
|
||||
echo 'No disponible';
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary btn-animated">
|
||||
<i class="fas fa-save"></i> Actualizar Pedimento
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Validación del formulario
|
||||
$('#formPedimento').on('submit', function(e) {
|
||||
var pedimento = $('#pedimento').val().trim();
|
||||
var clienteRfc = $('#cliente_rfc').val().trim();
|
||||
var clienteNombre = $('#cliente_nombre').val().trim();
|
||||
|
||||
if (!pedimento || !clienteRfc || !clienteNombre) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campos obligatorios',
|
||||
text: 'Por favor complete todos los campos obligatorios (*)',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validación básica de RFC
|
||||
if (clienteRfc.length < 12) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'RFC inválido',
|
||||
text: 'El RFC debe tener al menos 12 caracteres',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#cliente_rfc').focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Convertir RFC a mayúsculas automáticamente
|
||||
$('#cliente_rfc').on('input', function() {
|
||||
this.value = this.value.toUpperCase();
|
||||
});
|
||||
|
||||
// Validación de fechas
|
||||
$('#fecha_inicio, #fecha_final').on('change', function() {
|
||||
var fechaInicio = $('#fecha_inicio').val();
|
||||
var fechaFinal = $('#fecha_final').val();
|
||||
|
||||
if (fechaInicio && fechaFinal && fechaInicio > fechaFinal) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Fechas incorrectas',
|
||||
text: 'La fecha de inicio no puede ser mayor que la fecha final',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
$(this).val('');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
166
views/catalogo_pedimentos/index.php
Normal file
166
views/catalogo_pedimentos/index.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">📋 Catálogo de Pedimentos</h4>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-primary text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Lista de Pedimentos</h5>
|
||||
<p class="card-text">Visualizar y gestionar todos los pedimentos registrados</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-list fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Ver Lista <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-success text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Nuevo Pedimento</h5>
|
||||
<p class="card-text">Registrar un nuevo pedimento en el sistema</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-plus-circle fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Crear Nuevo <i class="fas fa-plus"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-info text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Reportes</h5>
|
||||
<p class="card-text">Generar reportes y estadísticas de pedimentos</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-chart-bar fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-light btn-sm btn-animated" onclick="proximamente()">
|
||||
Ver Reportes <i class="fas fa-chart-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Módulo</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
El catálogo de pedimentos le permite gestionar de manera eficiente todos los pedimentos
|
||||
de importación. Desde aquí puede:
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-check text-success"></i> Registrar nuevos pedimentos con toda la información requerida</li>
|
||||
<li><i class="fas fa-check text-success"></i> Consultar y editar pedimentos existentes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Realizar búsquedas avanzadas por diversos criterios</li>
|
||||
<li><i class="fas fa-check text-success"></i> Exportar información para reportes</li>
|
||||
<li><i class="fas fa-check text-success"></i> Mantener un histórico completo de operaciones</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
function proximamente() {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Próximamente',
|
||||
text: 'Esta función estará disponible próximamente.',
|
||||
confirmButtonColor: '#0d6efd'
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
194
views/catalogo_pedimentos/lista.php
Normal file
194
views/catalogo_pedimentos/lista.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Catálogo de Pedimentos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📋 Lista de Pedimentos</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/crear" class="btn btn-success btn-animated me-2">
|
||||
<i class="fas fa-plus"></i> Nuevo Pedimento
|
||||
</a>
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table id="tablaPedimentos" class="display nowrap" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Pedimento</th>
|
||||
<th>RFC Cliente</th>
|
||||
<th>Nombre Cliente</th>
|
||||
<th>Fecha Registro</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Los datos se cargan via AJAX -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- DataTables JS -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Inicializar DataTable
|
||||
$('#tablaPedimentos').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
"searchDelay": 500,
|
||||
"deferRender": true,
|
||||
"ajax": {
|
||||
"url": "/IMPORTADORES/catalogo_pedimentos/ajax_lista",
|
||||
"type": "GET"
|
||||
},
|
||||
"columns": [
|
||||
{ "data": 0, "name": "IdPrevio" },
|
||||
{ "data": 1, "name": "Pedimento" },
|
||||
{ "data": 2, "name": "ClienteRFC" },
|
||||
{ "data": 3, "name": "ClienteNombre" },
|
||||
{ "data": 4, "name": "Timestamp" },
|
||||
{
|
||||
"data": 5,
|
||||
"name": "Status",
|
||||
"render": function(data, type, row) {
|
||||
if (data === 'Activo') {
|
||||
return '<span class="badge bg-success">Activo</span>';
|
||||
} else {
|
||||
return '<span class="badge bg-secondary">Inactivo</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": null,
|
||||
"orderable": false,
|
||||
"searchable": false,
|
||||
"render": function(data, type, row) {
|
||||
const id = row[0];
|
||||
return `
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/editar?id=${id}" class="btn btn-sm btn-primary btn-animated" title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger btn-animated" onclick="confirmarEliminar(${id})" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
],
|
||||
"language": {
|
||||
"url": "https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json"
|
||||
},
|
||||
"responsive": true,
|
||||
"order": [[4, "desc"]],
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]]
|
||||
});
|
||||
});
|
||||
|
||||
function confirmarEliminar(id) {
|
||||
Swal.fire({
|
||||
title: '¿Eliminar pedimento?',
|
||||
text: '¿Está seguro que desea eliminar este pedimento?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#d33'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
window.location = `/IMPORTADORES/catalogo_pedimentos/eliminar?id=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar alertas de éxito/error
|
||||
<?php if (isset($_GET['created'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento creado', text: 'El pedimento se creó correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento actualizado', text: 'Los datos fueron modificados correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Pedimento eliminado', text: 'El pedimento fue eliminado correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
230
views/claves_pedimentos/crear.php
Normal file
230
views/claves_pedimentos/crear.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🔑 Nueva Clave de Pedimento</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">🔑 Nueva Clave de Pedimento</h4>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/claves_pedimentos/guardar" method="POST" id="formClave">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información de la Clave</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="codigo" class="form-label">Código de la Clave <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="codigo" name="codigo" required
|
||||
placeholder="Ej: A1, B3, G1" maxlength="10" style="text-transform: uppercase;">
|
||||
<div class="form-text">Máximo 10 caracteres. Se convertirá automáticamente a mayúsculas.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripción <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="descripcion" name="descripcion" rows="4" required
|
||||
placeholder="Descripción detallada de la clave de pedimento" maxlength="255"></textarea>
|
||||
<div class="form-text">Describe claramente el tipo de operación que representa esta clave.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="tipo_operacion" class="form-label">Tipo de Operación</label>
|
||||
<select class="form-select" id="tipo_operacion" name="tipo_operacion">
|
||||
<option value="importacion" selected>Importación</option>
|
||||
<option value="exportacion">Exportación</option>
|
||||
<option value="transito">Tránsito</option>
|
||||
<option value="otros">Otros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="activo" name="activo" checked>
|
||||
<label class="form-check-label" for="activo">
|
||||
<i class="fas fa-toggle-on text-success"></i> Clave activa
|
||||
</label>
|
||||
<div class="form-text">Las claves inactivas no aparecerán en los formularios de pedimentos.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3 bg-light">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-lightbulb"></i> Ejemplos de Claves Comunes</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Código</th>
|
||||
<th>Descripción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><strong>A1</strong></td><td>Importación definitiva de mercancías</td></tr>
|
||||
<tr><td><strong>A3</strong></td><td>Importación definitiva de vehículos usados</td></tr>
|
||||
<tr><td><strong>A4</strong></td><td>Importación definitiva de vehículos nuevos</td></tr>
|
||||
<tr><td><strong>B1</strong></td><td>Importación temporal para elaborar</td></tr>
|
||||
<tr><td><strong>C1</strong></td><td>Importación definitiva de mercancías donadas</td></tr>
|
||||
<tr><td><strong>G1</strong></td><td>Importación con Programa IMMEX</td></tr>
|
||||
<tr><td><strong>I1</strong></td><td>Importación definitiva exenta</td></tr>
|
||||
<tr><td><strong>L1</strong></td><td>Importación con TLC</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info mt-3">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>Consejo:</strong> Puedes crear tus propias claves personalizadas o usar las estándar del SAT.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-success btn-animated">
|
||||
<i class="fas fa-save"></i> Guardar Clave
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Validación del formulario
|
||||
$('#formClave').on('submit', function(e) {
|
||||
var codigo = $('#codigo').val().trim();
|
||||
var descripcion = $('#descripcion').val().trim();
|
||||
|
||||
if (!codigo || !descripcion) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campos obligatorios',
|
||||
text: 'Por favor complete todos los campos obligatorios (*)',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validación del código
|
||||
if (codigo.length > 10) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Código muy largo',
|
||||
text: 'El código no puede tener más de 10 caracteres',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#codigo').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validar que el código no tenga caracteres especiales
|
||||
var regex = /^[A-Z0-9]+$/;
|
||||
if (!regex.test(codigo)) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Código inválido',
|
||||
text: 'El código solo puede contener letras y números',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#codigo').focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Convertir código a mayúsculas automáticamente
|
||||
$('#codigo').on('input', function() {
|
||||
this.value = this.value.toUpperCase();
|
||||
});
|
||||
|
||||
// Contador de caracteres para descripción
|
||||
$('#descripcion').on('input', function() {
|
||||
var current = $(this).val().length;
|
||||
var max = 255;
|
||||
var remaining = max - current;
|
||||
|
||||
if (remaining < 20) {
|
||||
$(this).next('.form-text').text(`Caracteres restantes: ${remaining}`).addClass('text-warning');
|
||||
} else {
|
||||
$(this).next('.form-text').text('Describe claramente el tipo de operación que representa esta clave.').removeClass('text-warning');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
280
views/claves_pedimentos/editar.php
Normal file
280
views/claves_pedimentos/editar.php
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
include __DIR__ . '/../partials/sidebar_importador.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🔑 Editar Clave de Pedimento</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">🔑 Editar Clave #<?= htmlspecialchars($clave['id_clave_pedimento']) ?></h4>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/claves_pedimentos/actualizar" method="POST" id="formClave">
|
||||
<input type="hidden" name="id_clave_pedimento" value="<?= htmlspecialchars($clave['id_clave_pedimento']) ?>">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información de la Clave</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="codigo" class="form-label">Código de la Clave <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="codigo" name="codigo" required
|
||||
value="<?= htmlspecialchars($clave['codigo'] ?? '') ?>"
|
||||
placeholder="Ej: A1, B3, G1" maxlength="10" style="text-transform: uppercase;">
|
||||
<div class="form-text">Máximo 10 caracteres. Se convertirá automáticamente a mayúsculas.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripción <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="descripcion" name="descripcion" rows="4" required
|
||||
placeholder="Descripción detallada de la clave de pedimento" maxlength="255"><?= htmlspecialchars($clave['descripcion'] ?? '') ?></textarea>
|
||||
<div class="form-text">Describe claramente el tipo de operación que representa esta clave.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="tipo_operacion" class="form-label">Tipo de Operación</label>
|
||||
<select class="form-select" id="tipo_operacion" name="tipo_operacion">
|
||||
<option value="importacion" <?= ($clave['tipo_operacion'] === 'importacion') ? 'selected' : '' ?>>Importación</option>
|
||||
<option value="exportacion" <?= ($clave['tipo_operacion'] === 'exportacion') ? 'selected' : '' ?>>Exportación</option>
|
||||
<option value="transito" <?= ($clave['tipo_operacion'] === 'transito') ? 'selected' : '' ?>>Tránsito</option>
|
||||
<option value="otros" <?= ($clave['tipo_operacion'] === 'otros') ? 'selected' : '' ?>>Otros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="activo" name="activo"
|
||||
<?= ($clave['activo'] == 1) ? 'checked' : '' ?>>
|
||||
<label class="form-check-label" for="activo">
|
||||
<i class="fas fa-toggle-on text-success"></i> Clave activa
|
||||
</label>
|
||||
<div class="form-text">Las claves inactivas no aparecerán en los formularios de pedimentos.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<!-- Información de Auditoría -->
|
||||
<div class="card mb-3 bg-light">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info"></i> Información del Registro</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>ID:</strong> <?= htmlspecialchars($clave['id_clave_pedimento']) ?></p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Código Actual:</strong>
|
||||
<span class="badge bg-primary"><?= htmlspecialchars($clave['codigo']) ?></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Fecha Creación:</strong>
|
||||
<?php
|
||||
if ($clave['fecha_creacion'] instanceof DateTime) {
|
||||
echo $clave['fecha_creacion']->format('d/m/Y H:i:s');
|
||||
} else {
|
||||
echo 'No disponible';
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mb-1"><strong>Última Modificación:</strong>
|
||||
<?php
|
||||
if ($clave['fecha_modificacion'] instanceof DateTime) {
|
||||
echo $clave['fecha_modificacion']->format('d/m/Y H:i:s');
|
||||
} else {
|
||||
echo 'No disponible';
|
||||
}
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p class="mb-1"><strong>Estado:</strong>
|
||||
<?php if ($clave['activo'] == 1): ?>
|
||||
<span class="badge bg-success">Activo</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary">Inactivo</span>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Referencias -->
|
||||
<div class="card mb-3 bg-info text-white">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-lightbulb"></i> Ejemplos de Claves Comunes</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<small><strong>A1:</strong> Importación definitiva</small><br>
|
||||
<small><strong>A3:</strong> Vehículos usados</small><br>
|
||||
<small><strong>B1:</strong> Temporal elaborar</small><br>
|
||||
<small><strong>G1:</strong> Programa IMMEX</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<small><strong>I1:</strong> Definitiva exenta</small><br>
|
||||
<small><strong>L1:</strong> Con TLC</small><br>
|
||||
<small><strong>C1:</strong> Mercancías donadas</small><br>
|
||||
<small><strong>J1:</strong> Temporal reexportación</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times"></i> Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary btn-animated">
|
||||
<i class="fas fa-save"></i> Actualizar Clave
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Validación del formulario
|
||||
$('#formClave').on('submit', function(e) {
|
||||
var codigo = $('#codigo').val().trim();
|
||||
var descripcion = $('#descripcion').val().trim();
|
||||
|
||||
if (!codigo || !descripcion) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campos obligatorios',
|
||||
text: 'Por favor complete todos los campos obligatorios (*)',
|
||||
confirmButtonColor: '#ffc107'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validación del código
|
||||
if (codigo.length > 10) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Código muy largo',
|
||||
text: 'El código no puede tener más de 10 caracteres',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#codigo').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validar que el código no tenga caracteres especiales
|
||||
var regex = /^[A-Z0-9]+$/;
|
||||
if (!regex.test(codigo)) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Código inválido',
|
||||
text: 'El código solo puede contener letras y números',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
$('#codigo').focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Convertir código a mayúsculas automáticamente
|
||||
$('#codigo').on('input', function() {
|
||||
this.value = this.value.toUpperCase();
|
||||
});
|
||||
|
||||
// Contador de caracteres para descripción
|
||||
$('#descripcion').on('input', function() {
|
||||
var current = $(this).val().length;
|
||||
var max = 255;
|
||||
var remaining = max - current;
|
||||
|
||||
if (remaining < 20) {
|
||||
$(this).next('.form-text').text(`Caracteres restantes: ${remaining}`).addClass('text-warning');
|
||||
} else {
|
||||
$(this).next('.form-text').text('Describe claramente el tipo de operación que representa esta clave.').removeClass('text-warning');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
226
views/claves_pedimentos/importar_csv.php
Normal file
226
views/claves_pedimentos/importar_csv.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📤 Importar Claves CSV</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) { .content { margin-left: 0; } }
|
||||
.card { border-radius: 12px; }
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.file-drop-area {
|
||||
border: 2px dashed #007bff;
|
||||
border-radius: 10px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
background-color: #f8f9ff;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.file-drop-area:hover {
|
||||
border-color: #0056b3;
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
.file-drop-area.dragover {
|
||||
border-color: #28a745;
|
||||
background-color: #d4edda;
|
||||
}
|
||||
.file-info {
|
||||
background-color: #e9ecef;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4><i class="fas fa-upload text-primary"></i> Importar Claves de Pedimentos por CSV</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/public/downloads/claves_pedimentos_template.csv" class="btn btn-outline-info btn-animated me-2" download>
|
||||
<i class="fas fa-download"></i> Descargar Plantilla
|
||||
</a>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><i class="fas fa-file-csv"></i> Cargar Archivo CSV</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/claves_pedimentos/procesar_csv" method="POST" enctype="multipart/form-data" id="csvForm">
|
||||
|
||||
<div class="file-drop-area" id="fileDropArea">
|
||||
<i class="fas fa-cloud-upload-alt fa-3x text-primary mb-3"></i>
|
||||
<h5>Arrastra tu archivo CSV aquí</h5>
|
||||
<p class="text-muted">o haz clic para seleccionar archivo</p>
|
||||
<input type="file" name="csv_file" id="csvFile" accept=".csv" style="display: none;" required>
|
||||
</div>
|
||||
|
||||
<div id="fileInfo" class="file-info" style="display: none;">
|
||||
<h6><i class="fas fa-file-check text-success"></i> Archivo seleccionado:</h6>
|
||||
<p id="fileName" class="mb-1"></p>
|
||||
<small id="fileSize" class="text-muted"></small>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h6><i class="fas fa-cogs"></i> Opciones de importación:</h6>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="omitir_duplicados" id="omitirDuplicados" checked>
|
||||
<label class="form-check-label" for="omitirDuplicados">
|
||||
Omitir códigos duplicados (no reemplazar los existentes)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="activar_todas" id="activarTodas" checked>
|
||||
<label class="form-check-label" for="activarTodas">
|
||||
Activar todas las claves importadas
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<button type="submit" class="btn btn-success btn-lg btn-animated" id="submitBtn" disabled>
|
||||
<i class="fas fa-upload"></i> Procesar Archivo CSV
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Instrucciones</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6>📋 Formato del archivo CSV:</h6>
|
||||
<ul class="small">
|
||||
<li><strong>Separador:</strong> Coma (,)</li>
|
||||
<li><strong>Codificación:</strong> UTF-8</li>
|
||||
<li><strong>Encabezados:</strong> Obligatorios</li>
|
||||
</ul>
|
||||
|
||||
<h6>🏷️ Columnas requeridas:</h6>
|
||||
<ul class="small">
|
||||
<li><strong>codigo:</strong> Código de la clave (ej: A1, B2)</li>
|
||||
<li><strong>descripcion:</strong> Descripción detallada</li>
|
||||
<li><strong>tipo_operacion:</strong> importacion o exportacion</li>
|
||||
<li><strong>activo:</strong> 1 (activo) o 0 (inactivo)</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning small mt-3">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<strong>Importante:</strong> Los códigos deben ser únicos. Si existe un código duplicado, se omitirá según la configuración seleccionada.
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="/IMPORTADORES/public/downloads/claves_pedimentos_template.csv" class="btn btn-sm btn-outline-primary w-100" download>
|
||||
<i class="fas fa-download"></i> Descargar plantilla de ejemplo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
const fileDropArea = document.getElementById('fileDropArea');
|
||||
const csvFile = document.getElementById('csvFile');
|
||||
const fileInfo = document.getElementById('fileInfo');
|
||||
const fileName = document.getElementById('fileName');
|
||||
const fileSize = document.getElementById('fileSize');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
|
||||
// Eventos de drag & drop
|
||||
fileDropArea.addEventListener('click', () => csvFile.click());
|
||||
fileDropArea.addEventListener('dragover', handleDragOver);
|
||||
fileDropArea.addEventListener('dragleave', handleDragLeave);
|
||||
fileDropArea.addEventListener('drop', handleDrop);
|
||||
csvFile.addEventListener('change', handleFileSelect);
|
||||
|
||||
function handleDragOver(e) {
|
||||
e.preventDefault();
|
||||
fileDropArea.classList.add('dragover');
|
||||
}
|
||||
|
||||
function handleDragLeave(e) {
|
||||
e.preventDefault();
|
||||
fileDropArea.classList.remove('dragover');
|
||||
}
|
||||
|
||||
function handleDrop(e) {
|
||||
e.preventDefault();
|
||||
fileDropArea.classList.remove('dragover');
|
||||
|
||||
const files = e.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
csvFile.files = files;
|
||||
handleFileSelect();
|
||||
}
|
||||
}
|
||||
|
||||
function handleFileSelect() {
|
||||
const file = csvFile.files[0];
|
||||
|
||||
if (file) {
|
||||
// Validar que sea CSV
|
||||
if (!file.name.toLowerCase().endsWith('.csv')) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo inválido',
|
||||
text: 'Por favor selecciona un archivo CSV válido.'
|
||||
});
|
||||
csvFile.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar información del archivo
|
||||
fileName.textContent = file.name;
|
||||
fileSize.textContent = `Tamaño: ${(file.size / 1024).toFixed(1)} KB`;
|
||||
fileInfo.style.display = 'block';
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validar formulario antes de enviar
|
||||
document.getElementById('csvForm').addEventListener('submit', function(e) {
|
||||
const file = csvFile.files[0];
|
||||
|
||||
if (!file) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Archivo requerido',
|
||||
text: 'Por favor selecciona un archivo CSV.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Mostrar indicador de carga
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Procesando...';
|
||||
submitBtn.disabled = true;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
170
views/claves_pedimentos/index.php
Normal file
170
views/claves_pedimentos/index.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🔑 Claves de Pedimentos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.btn-pulse { animation: pulse 2s infinite; }
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
/* Responsive animations */
|
||||
@media (max-width: 768px) { .card-hover:hover { transform: translateY(-4px) scale(1.01); } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Animación para el título */
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title-glow">🔑 Configuración de Claves de Pedimentos</h4>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-primary text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Mis Claves</h5>
|
||||
<p class="card-text">Ver y gestionar tus claves de pedimentos configuradas</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-list fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Ver Lista <i class="fas fa-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-success text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Nueva Clave</h5>
|
||||
<p class="card-text">Agregar una nueva clave de pedimento personalizada</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-plus-circle fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/crear" class="btn btn-outline-light btn-sm btn-animated">
|
||||
Crear Nueva <i class="fas fa-plus"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4 mb-3">
|
||||
<div class="card bg-warning text-white card-hover fade-in-up">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h5 class="card-title">Inicializar</h5>
|
||||
<p class="card-text">Cargar claves de pedimentos por defecto</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-download fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/inicializar_claves_usuario"
|
||||
class="btn btn-outline-light btn-sm btn-animated"
|
||||
onclick="return confirm('¿Desea cargar las claves de pedimentos por defecto? Solo funciona si no tiene claves configuradas.')">
|
||||
Inicializar <i class="fas fa-magic"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card fade-in-up">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Información del Módulo</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Las claves de pedimentos son códigos que identifican el tipo de operación de comercio exterior.
|
||||
Este módulo te permite personalizar y gestionar las claves que utilizas frecuentemente.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-check text-success"></i> Funcionalidades:</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-dot-circle text-primary"></i> Crear claves personalizadas</li>
|
||||
<li><i class="fas fa-dot-circle text-primary"></i> Modificar claves existentes</li>
|
||||
<li><i class="fas fa-dot-circle text-primary"></i> Activar/desactivar claves</li>
|
||||
<li><i class="fas fa-dot-circle text-primary"></i> Eliminar claves no utilizadas</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6><i class="fas fa-lightbulb text-warning"></i> Claves Comunes de Importación:</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-dot-circle text-info"></i> <strong>A1:</strong> Importación definitiva</li>
|
||||
<li><i class="fas fa-dot-circle text-info"></i> <strong>B1:</strong> Importación temporal</li>
|
||||
<li><i class="fas fa-dot-circle text-info"></i> <strong>G1:</strong> Programa IMMEX</li>
|
||||
<li><i class="fas fa-dot-circle text-info"></i> <strong>L1:</strong> TLC (Tratados)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
204
views/claves_pedimentos/lista.php
Normal file
204
views/claves_pedimentos/lista.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🔑 Lista de Claves de Pedimentos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<!-- Animate.css -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
.navbar { position: fixed; top: 0; width: 100%; z-index: 1050; }
|
||||
/* A partir de dispositivos medianos (>=768px), deja espacio lateral */
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
/* En móviles, sin margen lateral */
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
/* Animaciones personalizadas */
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.title-glow { text-shadow: 0 0 10px rgba(0, 123, 255, 0.3); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">🔑 Mis Claves de Pedimentos</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/crear" class="btn btn-success btn-animated me-2">
|
||||
<i class="fas fa-plus"></i> Nueva Clave
|
||||
</a>
|
||||
<a href="/IMPORTADORES/claves_pedimentos/importar_csv" class="btn btn-info btn-animated me-2">
|
||||
<i class="fas fa-upload"></i> Importar CSV
|
||||
</a>
|
||||
<a href="/IMPORTADORES/claves_pedimentos" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left"></i> Regresar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table id="tablaClaves" class="display nowrap" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Código</th>
|
||||
<th>Descripción</th>
|
||||
<th>Tipo</th>
|
||||
<th>Estado</th>
|
||||
<th>Fecha Creación</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Los datos se cargan via AJAX -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<!-- DataTables JS -->
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Inicializar DataTable
|
||||
$('#tablaClaves').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
"searchDelay": 500,
|
||||
"deferRender": true,
|
||||
"ajax": {
|
||||
"url": "/IMPORTADORES/claves_pedimentos/ajax_lista",
|
||||
"type": "GET"
|
||||
},
|
||||
"columns": [
|
||||
{ "data": 0, "name": "id_clave_pedimento" },
|
||||
{ "data": 1, "name": "codigo" },
|
||||
{ "data": 2, "name": "descripcion" },
|
||||
{ "data": 3, "name": "tipo_operacion" },
|
||||
{
|
||||
"data": 4,
|
||||
"name": "activo",
|
||||
"render": function(data, type, row) {
|
||||
if (data === 'Activo') {
|
||||
return '<span class="badge bg-success">Activo</span>';
|
||||
} else {
|
||||
return '<span class="badge bg-secondary">Inactivo</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{ "data": 5, "name": "fecha_creacion" },
|
||||
{
|
||||
"data": null,
|
||||
"orderable": false,
|
||||
"searchable": false,
|
||||
"render": function(data, type, row) {
|
||||
const id = row[0];
|
||||
return `
|
||||
<a href="/IMPORTADORES/claves_pedimentos/editar?id=${id}" class="btn btn-sm btn-primary btn-animated" title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm btn-danger btn-animated" onclick="confirmarEliminar(${id})" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
],
|
||||
"language": {
|
||||
"url": "https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json"
|
||||
},
|
||||
"responsive": true,
|
||||
"order": [[1, "asc"]], // Ordenar por código
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]]
|
||||
});
|
||||
});
|
||||
|
||||
function confirmarEliminar(id) {
|
||||
Swal.fire({
|
||||
title: '¿Eliminar clave de pedimento?',
|
||||
text: '¿Está seguro que desea eliminar esta clave de pedimento?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#d33'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
window.location = `/IMPORTADORES/claves_pedimentos/eliminar?id=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar alertas de éxito/error
|
||||
<?php if (isset($_GET['created'])): ?>
|
||||
<?php if ($_GET['created'] === 'ok'): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Clave creada', text: 'La clave de pedimento se creó correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php elseif ($_GET['created'] === 'initialized'): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Claves inicializadas', text: 'Se cargaron las claves de pedimento por defecto.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Clave actualizada', text: 'Los datos fueron modificados correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
Swal.fire({ icon: 'success', title: 'Clave eliminada', text: 'La clave de pedimento fue eliminada correctamente.', confirmButtonColor: '#198754' });
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['info']) && $_GET['info'] === 'already_initialized'): ?>
|
||||
Swal.fire({ icon: 'info', title: 'Ya inicializado', text: 'Ya tienes claves de pedimentos configuradas.', confirmButtonColor: '#0d6efd' });
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['imported']) && $_GET['imported'] === 'ok' && isset($_GET['message'])): ?>
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Importación completada',
|
||||
html: '<?= urldecode($_GET['message']) ?>',
|
||||
confirmButtonColor: '#198754',
|
||||
width: 600
|
||||
});
|
||||
<?php endif; ?>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -15,12 +15,12 @@ if (!$stmt || !sqlsrv_execute($stmt)) {
|
||||
}
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
// Variables con valores por defecto - Branding más sutil
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0]; // Negro suave
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1]; // Gris slate
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
?>
|
||||
@@ -29,23 +29,288 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Inicio</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Gestión de Importaciones</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Plataforma integral para la gestión de importaciones, trámites aduaneros y control de mercancías">
|
||||
<!-- Bootstrap -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Iconos -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color:<?= $color1 ?> !important; font-weight: 500; }
|
||||
.navbar .nav-link.active { background-color: #F9F9F9; border-radius: 5px; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.hero { background: linear-gradient(to right, <?= $color1 ?>, <?= $color2 ?>); color: white; padding: 80px 20px; text-align: center; }
|
||||
.features { background-color: white; padding: 50px 0; }
|
||||
.features i { font-size: 40px; color: <?= $color1 ?>; margin-bottom: 10px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--warning: #f59e0b;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.hero {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60"><g fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1"><circle cx="30" cy="30" r="1"/></g></svg>') repeat;
|
||||
animation: float 20s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translate(0, 0) rotate(0deg); }
|
||||
50% { transform: translate(-20px, -20px) rotate(180deg); }
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: clamp(2.5rem, 5vw, 4rem);
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
color: white;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero .lead {
|
||||
font-size: 1.2rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: 3rem;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: white;
|
||||
color: var(--primary);
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
color: var(--primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn-outline-modern {
|
||||
background: transparent;
|
||||
color: white;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-outline-modern:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.stats {
|
||||
background: var(--surface);
|
||||
margin-top: -4rem;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
display: block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.section-modern {
|
||||
padding: 5rem 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 1.1rem;
|
||||
text-align: center;
|
||||
max-width: 600px;
|
||||
margin: 0 auto 4rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--surface);
|
||||
border-radius: 16px;
|
||||
padding: 2.5rem 2rem;
|
||||
text-align: center;
|
||||
border: 1px solid var(--border);
|
||||
transition: all 0.3s ease;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.feature-description {
|
||||
color: var(--text-muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
|
||||
text-align: center;
|
||||
padding: 5rem 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2.5rem 0 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero {
|
||||
min-height: 70vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin-top: -2rem;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.section-modern {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -53,8 +318,8 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
<a class="navbar-brand" href="#">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#nav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -68,68 +333,191 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ALERTA DE MANTENIMIENTO -->
|
||||
<!-- MANTENIMIENTO -->
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div id="bloqueo-mantenimiento" style="
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
">
|
||||
<i class="fas fa-tools fa-3x mb-3 text-danger"></i>
|
||||
<h2>Sistema en Mantenimiento</h2>
|
||||
<p><?= htmlspecialchars($mensajeMantenimiento) ?></p>
|
||||
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.98); z-index: 9999; display: flex; align-items: center; justify-content: center; flex-direction: column; text-align: center;">
|
||||
<i class="fas fa-tools fa-4x mb-4" style="color: var(--accent);"></i>
|
||||
<h2 style="color: var(--text); margin-bottom: 1rem;">Sistema en Mantenimiento</h2>
|
||||
<p style="color: var(--text-muted); max-width: 500px;"><?= htmlspecialchars($mensajeMantenimiento) ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<h1 class="display-5 mb-3"><?= htmlspecialchars($nombre) ?></h1>
|
||||
<p class="lead">Una plataforma confiable para gestionar el registro y control de importadores de hidrocarburos en México.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section class="features text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<i class="fas fa-file-signature"></i>
|
||||
<h5 class="mt-3">Registro en línea</h5>
|
||||
<p>Los importadores pueden enviar su solicitud y documentación de forma digital.</p>
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="hero-content">
|
||||
<h1>Gestión Moderna de <span style="color: #10b981;">Importaciones</span></h1>
|
||||
<p class="lead">
|
||||
Simplifica tus procesos aduaneros con nuestra plataforma integral.
|
||||
Desde pedimentos hasta seguimiento, todo en un solo lugar.
|
||||
</p>
|
||||
<div class="d-flex flex-column flex-md-row gap-3">
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-rocket"></i>
|
||||
Comenzar Gratis
|
||||
</a>
|
||||
<a href="#features" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-play"></i>
|
||||
Ver Demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
<h5 class="mt-3">Autorización segura</h5>
|
||||
<p>Las agencias aduanales validan manualmente cada solicitud antes de otorgar acceso.</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<i class="fas fa-database"></i>
|
||||
<h5 class="mt-3">Información centralizada</h5>
|
||||
<p>Todas las solicitudes y datos se almacenan de forma segura y organizada.</p>
|
||||
<div class="col-lg-6 d-none d-lg-block text-center">
|
||||
<i class="fas fa-chart-line" style="font-size: 15rem; color: rgba(255,255,255,0.1);"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- STATS -->
|
||||
<div class="container">
|
||||
<div class="stats">
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">1,200+</span>
|
||||
<div class="stat-label">Importadores</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">25K+</span>
|
||||
<div class="stat-label">Trámites</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">98%</span>
|
||||
<div class="stat-label">Satisfacción</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">24/7</span>
|
||||
<div class="stat-label">Soporte</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FEATURES -->
|
||||
<section class="section-modern" id="features">
|
||||
<div class="container">
|
||||
<h2 class="section-title">Todo lo que necesitas</h2>
|
||||
<p class="section-subtitle">
|
||||
Herramientas profesionales diseñadas para optimizar cada etapa del proceso de importación
|
||||
</p>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Gestión de Pedimentos</h5>
|
||||
<p class="feature-description">
|
||||
Crea, gestiona y da seguimiento a tus pedimentos de importación con validación automática de datos.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-shield-check"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Cumplimiento Automatizado</h5>
|
||||
<p class="feature-description">
|
||||
Mantente al día con regulaciones aduaneras y recibe alertas sobre cambios normativos relevantes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-analytics"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Reportes Inteligentes</h5>
|
||||
<p class="feature-description">
|
||||
Analiza tus operaciones con dashboards interactivos y reportes personalizables en tiempo real.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Colaboración</h5>
|
||||
<p class="feature-description">
|
||||
Conecta con agentes aduanales, transportistas y proveedores en un ecosistema colaborativo.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-mobile-alt"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Acceso Universal</h5>
|
||||
<p class="feature-description">
|
||||
Accede desde cualquier dispositivo con nuestra aplicación web responsiva y segura.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<h5 class="feature-title">Seguridad Total</h5>
|
||||
<p class="feature-description">
|
||||
Protección enterprise con encriptación avanzada, backups automáticos y certificaciones de seguridad.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA -->
|
||||
<section class="cta-section">
|
||||
<div class="container">
|
||||
<h2 class="section-title" style="margin-bottom: 1rem;">¿Listo para optimizar tus importaciones?</h2>
|
||||
<p class="section-subtitle" style="margin-bottom: 3rem;">
|
||||
Únete a más de 1,200 importadores que ya confían en nuestra plataforma
|
||||
</p>
|
||||
<a href="/IMPORTADORES/registro" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
Comenzar Ahora
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="footer-brand"><?= htmlspecialchars($siglas) ?></div>
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> Sistema de Gestión de Importaciones<br>
|
||||
Optimizando el comercio internacional
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<?php
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
$conn = getConnection();
|
||||
|
||||
if (!isset($_SESSION['recuperacion_autorizada'])) {
|
||||
header('Location: /IMPORTADORES/login');
|
||||
exit;
|
||||
$sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
|
||||
// Verificar que el usuario tenga permiso para cambiar contraseña
|
||||
if (!isset($_SESSION['codigo_verificado']) || !isset($_SESSION['email_recuperacion'])) {
|
||||
header('Location: /IMPORTADORES/login/recuperar');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -17,31 +24,304 @@ if (!isset($_SESSION['recuperacion_autorizada'])) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Recuperación de Contraseña</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Nueva Contraseña</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Establece tu nueva contraseña para acceder a tu cuenta">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; position: relative; height: 100vh; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color: <?= $color1 ?> !important; font-weight: 500; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.login-container { max-width: 420px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); z-index: 10; position: relative; }
|
||||
.form-control { border-radius: 6px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; margin-top: 60px; }
|
||||
/* Contenedor de fondo semitransparente para las vistas */
|
||||
.overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 5; }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--error: #ef4444;
|
||||
--warning: #f59e0b;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.password-container {
|
||||
max-width: 550px;
|
||||
margin: 3rem auto;
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.password-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.password-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.password-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.password-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.password-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.password-input-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
border-radius: 4px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.password-toggle:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.password-strength {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.strength-bar {
|
||||
height: 4px;
|
||||
background: var(--border);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.strength-fill {
|
||||
height: 100%;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.strength-weak { background: #ef4444; width: 25%; }
|
||||
.strength-medium { background: var(--warning); width: 60%; }
|
||||
.strength-strong { background: var(--accent); width: 100%; }
|
||||
|
||||
.strength-text {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.password-requirements {
|
||||
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.requirement {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 0.25rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.requirement.met {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.requirement i {
|
||||
width: 16px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary-modern:disabled {
|
||||
opacity: 0.6;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: linear-gradient(135deg, #fef2f2, #fee2e2);
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||
color: #16a34a;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.password-container {
|
||||
margin: 2rem 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.password-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Inicio de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -52,68 +332,291 @@ if (!isset($_SESSION['recuperacion_autorizada'])) {
|
||||
<a class="nav-link" href="/IMPORTADORES/">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link " aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div class="container mt-4">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<i class="fas fa-triangle-exclamation me-2"></i>
|
||||
<?= htmlspecialchars($mensajeMantenimiento) ?>
|
||||
<!-- CONTENEDOR DE NUEVA CONTRASEÑA -->
|
||||
<div class="password-container">
|
||||
<div class="password-header">
|
||||
<div class="password-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<h1 class="password-title">Nueva Contraseña</h1>
|
||||
<p class="password-subtitle">
|
||||
Crea una contraseña segura para proteger tu cuenta de importaciones
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Overlay (fondo semitransparente) que cubre la pantalla -->
|
||||
<div class="overlay"></div>
|
||||
<!-- MENSAJES -->
|
||||
<?php if (isset($_SESSION['error_message'])): ?>
|
||||
<div class="alert-modern alert-danger">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= $_SESSION['error_message'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['error_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Formulario de cambio de contraseña -->
|
||||
<div class="login-container">
|
||||
<h4 class="mb-4 text-center" style="color:<?= $color1 ?>">Cambiar contraseña</h4>
|
||||
<form id="formNueva" action="/IMPORTADORES/login/cambiarPassword" method="POST">
|
||||
<form action="/IMPORTADORES/login/actualizar_password" method="POST" id="passwordForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nueva contraseña:</label>
|
||||
<input type="password" name="password" required class="form-control" minlength="6" autocomplete="new-password">
|
||||
<label for="password" class="form-label">
|
||||
<i class="fas fa-key text-muted"></i>
|
||||
Nueva contraseña
|
||||
</label>
|
||||
<div class="password-input-group">
|
||||
<input type="password" name="password" id="password" class="form-control"
|
||||
required minlength="8"
|
||||
placeholder="Tu nueva contraseña segura">
|
||||
<button type="button" class="password-toggle" onclick="togglePassword('password')">
|
||||
<i class="fas fa-eye" id="password-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="password-strength">
|
||||
<div class="strength-bar">
|
||||
<div class="strength-fill" id="strengthBar"></div>
|
||||
</div>
|
||||
<div class="strength-text" id="strengthText">Ingresa una contraseña</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Confirmar contraseña:</label>
|
||||
<input type="password" name="confirmar" required class="form-control">
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="confirm_password" class="form-label">
|
||||
<i class="fas fa-shield-alt text-muted"></i>
|
||||
Confirmar contraseña
|
||||
</label>
|
||||
<div class="password-input-group">
|
||||
<input type="password" name="confirm_password" id="confirm_password" class="form-control"
|
||||
required minlength="8"
|
||||
placeholder="Repite tu nueva contraseña">
|
||||
<button type="button" class="password-toggle" onclick="togglePassword('confirm_password')">
|
||||
<i class="fas fa-eye" id="confirm_password-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="passwordMatch" class="mt-2" style="font-size: 0.85rem;"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-warning w-100">Actualizar contraseña</button>
|
||||
|
||||
<div class="password-requirements">
|
||||
<div class="mb-2" style="font-weight: 600; color: var(--text);">
|
||||
<i class="fas fa-info-circle me-2"></i>Requisitos de seguridad:
|
||||
</div>
|
||||
<div class="requirement" id="req-length">
|
||||
<i class="fas fa-circle"></i>
|
||||
Mínimo 8 caracteres
|
||||
</div>
|
||||
<div class="requirement" id="req-uppercase">
|
||||
<i class="fas fa-circle"></i>
|
||||
Al menos una mayúscula (A-Z)
|
||||
</div>
|
||||
<div class="requirement" id="req-lowercase">
|
||||
<i class="fas fa-circle"></i>
|
||||
Al menos una minúscula (a-z)
|
||||
</div>
|
||||
<div class="requirement" id="req-number">
|
||||
<i class="fas fa-circle"></i>
|
||||
Al menos un número (0-9)
|
||||
</div>
|
||||
<div class="requirement" id="req-special">
|
||||
<i class="fas fa-circle"></i>
|
||||
Al menos un carácter especial (!@#$%^&*)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-modern btn-primary-modern" id="submitBtn" disabled>
|
||||
<i class="fas fa-save"></i>
|
||||
Guardar Nueva Contraseña
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<?php if (isset($_SESSION['cambio_error'])): ?>
|
||||
<div class="text-danger mt-3"><?= $_SESSION['cambio_error'] ?></div>
|
||||
<?php unset($_SESSION['cambio_error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['cambio_exito'])): ?>
|
||||
<div class="text-success mt-3"><?= $_SESSION['cambio_exito'] ?></div>
|
||||
<?php unset($_SESSION['cambio_exito']); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Seguridad avanzada para tu cuenta
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.getElementById('formNueva').addEventListener('submit', function(e) {
|
||||
const pass = this.password.value;
|
||||
const confirm = this.confirmar.value;
|
||||
|
||||
if (pass !== confirm) {
|
||||
e.preventDefault();
|
||||
alert('❌ Las contraseñas no coinciden.');
|
||||
function togglePassword(inputId) {
|
||||
const input = document.getElementById(inputId);
|
||||
const icon = document.getElementById(inputId + '-icon');
|
||||
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.classList.remove('fa-eye');
|
||||
icon.classList.add('fa-eye-slash');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.classList.remove('fa-eye-slash');
|
||||
icon.classList.add('fa-eye');
|
||||
}
|
||||
}
|
||||
|
||||
function checkPasswordStrength(password) {
|
||||
const requirements = {
|
||||
length: password.length >= 8,
|
||||
uppercase: /[A-Z]/.test(password),
|
||||
lowercase: /[a-z]/.test(password),
|
||||
number: /[0-9]/.test(password),
|
||||
special: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)
|
||||
};
|
||||
|
||||
// Actualizar indicadores visuales
|
||||
Object.keys(requirements).forEach(req => {
|
||||
const element = document.getElementById(`req-${req}`);
|
||||
const icon = element.querySelector('i');
|
||||
|
||||
if (requirements[req]) {
|
||||
element.classList.add('met');
|
||||
icon.classList.remove('fa-circle');
|
||||
icon.classList.add('fa-check-circle');
|
||||
} else {
|
||||
element.classList.remove('met');
|
||||
icon.classList.remove('fa-check-circle');
|
||||
icon.classList.add('fa-circle');
|
||||
}
|
||||
});
|
||||
|
||||
// Calcular fuerza
|
||||
const metCount = Object.values(requirements).filter(Boolean).length;
|
||||
const strengthBar = document.getElementById('strengthBar');
|
||||
const strengthText = document.getElementById('strengthText');
|
||||
|
||||
strengthBar.className = 'strength-fill';
|
||||
|
||||
if (metCount < 3) {
|
||||
strengthBar.classList.add('strength-weak');
|
||||
strengthText.textContent = 'Contraseña débil';
|
||||
strengthText.style.color = '#ef4444';
|
||||
} else if (metCount < 5) {
|
||||
strengthBar.classList.add('strength-medium');
|
||||
strengthText.textContent = 'Contraseña moderada';
|
||||
strengthText.style.color = '#f59e0b';
|
||||
} else {
|
||||
strengthBar.classList.add('strength-strong');
|
||||
strengthText.textContent = 'Contraseña fuerte';
|
||||
strengthText.style.color = '#10b981';
|
||||
}
|
||||
|
||||
return metCount >= 4; // Requiere al menos 4 de 5 criterios
|
||||
}
|
||||
|
||||
function checkPasswordMatch() {
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = document.getElementById('confirm_password').value;
|
||||
const matchDiv = document.getElementById('passwordMatch');
|
||||
|
||||
if (confirmPassword === '') {
|
||||
matchDiv.textContent = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password === confirmPassword) {
|
||||
matchDiv.innerHTML = '<i class="fas fa-check-circle me-1" style="color: #10b981;"></i>Las contraseñas coinciden';
|
||||
matchDiv.style.color = '#10b981';
|
||||
return true;
|
||||
} else {
|
||||
matchDiv.innerHTML = '<i class="fas fa-times-circle me-1" style="color: #ef4444;"></i>Las contraseñas no coinciden';
|
||||
matchDiv.style.color = '#ef4444';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
const password = document.getElementById('password').value;
|
||||
const isStrong = checkPasswordStrength(password);
|
||||
const isMatching = checkPasswordMatch();
|
||||
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
submitBtn.disabled = !(isStrong && isMatching && password.length >= 8);
|
||||
}
|
||||
|
||||
// Event listeners
|
||||
document.getElementById('password').addEventListener('input', validateForm);
|
||||
document.getElementById('confirm_password').addEventListener('input', validateForm);
|
||||
|
||||
// Validación del formulario
|
||||
document.getElementById('passwordForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = document.getElementById('confirm_password').value;
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
showAlert('error', 'Las contraseñas no coinciden');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkPasswordStrength(password)) {
|
||||
showAlert('error', 'La contraseña no cumple con los requisitos de seguridad');
|
||||
return;
|
||||
}
|
||||
|
||||
// Deshabilitar botón y mostrar loading
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
|
||||
// Enviar formulario normalmente (el método devuelve HTML)
|
||||
const formData = new FormData();
|
||||
formData.append('password', password);
|
||||
formData.append('confirmar', confirmPassword);
|
||||
|
||||
fetch('/IMPORTADORES/login/actualizar_password', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
// Verificar si contiene mensaje de éxito
|
||||
if (html.includes('✅ ¡Contraseña actualizada!')) {
|
||||
// Mostrar la respuesta HTML del servidor
|
||||
document.body.innerHTML = html;
|
||||
} else {
|
||||
// Es un mensaje de error
|
||||
showAlert('error', html.replace(/<[^>]*>/g, '').trim());
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-save"></i> Guardar Nueva Contraseña';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('error', 'Error de conexión. Intenta nuevamente.');
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-save"></i> Guardar Nueva Contraseña';
|
||||
});
|
||||
});
|
||||
|
||||
function showAlert(type, message) {
|
||||
// Remover alertas anteriores
|
||||
const existingAlerts = document.querySelectorAll('.alert-modern');
|
||||
existingAlerts.forEach(alert => alert.remove());
|
||||
|
||||
// Crear nueva alerta
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert-modern alert-${type === 'success' ? 'success' : 'danger'}`;
|
||||
alertDiv.innerHTML = `
|
||||
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
|
||||
${message}
|
||||
`;
|
||||
|
||||
// Insertar antes del formulario
|
||||
const form = document.getElementById('passwordForm');
|
||||
form.parentNode.insertBefore(alertDiv, form);
|
||||
}
|
||||
|
||||
// Focus automático
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('password').focus();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,44 +1,273 @@
|
||||
<?php
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
$conn = getConnection();
|
||||
|
||||
$sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
|
||||
// Obtener tipo de confirmación y mensaje
|
||||
$tipo = $_GET['tipo'] ?? 'success';
|
||||
$mensaje = $_GET['mensaje'] ?? 'Operación completada exitosamente';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Recuperación de Contraseña</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Confirmación</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Confirmación de operación en tu cuenta de importaciones">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; position: relative; min-height: 100vh; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color: <?= $color1 ?> !important; font-weight: 500; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.login-container { max-width: 420px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); z-index: 10; position: relative; }
|
||||
.form-control { border-radius: 6px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; margin-top: 60px; }
|
||||
/* Contenedor de fondo semitransparente para las vistas */
|
||||
.overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 5; }
|
||||
#mensaje { transition: opacity 0.3s ease; }
|
||||
@media (max-height: 600px) { footer { position: static !important; } }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--success: #16a34a;
|
||||
--error: #ef4444;
|
||||
--warning: #f59e0b;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.confirmation-container {
|
||||
max-width: 500px;
|
||||
margin: 3rem auto;
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.confirmation-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.confirmation-icon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 2rem;
|
||||
color: white;
|
||||
font-size: 2.5rem;
|
||||
position: relative;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.icon-success {
|
||||
background: linear-gradient(135deg, var(--success), #059669);
|
||||
}
|
||||
|
||||
.icon-error {
|
||||
background: linear-gradient(135deg, var(--error), #dc2626);
|
||||
}
|
||||
|
||||
.icon-warning {
|
||||
background: linear-gradient(135deg, var(--warning), #d97706);
|
||||
}
|
||||
|
||||
.icon-info {
|
||||
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(16, 185, 129, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.confirmation-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.confirmation-message {
|
||||
color: var(--text-muted);
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 2.5rem;
|
||||
max-width: 400px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-modern {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
border: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-outline-modern:hover {
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
border-color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.additional-info {
|
||||
background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
|
||||
border: 1px solid #38bdf8;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
color: #0c4a6e;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.countdown {
|
||||
font-weight: 600;
|
||||
color: var(--accent);
|
||||
margin-top: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.confirmation-container {
|
||||
margin: 2rem 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.confirmation-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.confirmation-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Inicio de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -49,75 +278,206 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<a class="nav-link" href="/IMPORTADORES/">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link " aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div class="container mt-4">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<i class="fas fa-triangle-exclamation me-2"></i>
|
||||
<?= htmlspecialchars($mensajeMantenimiento) ?>
|
||||
</div>
|
||||
<!-- CONTENEDOR DE CONFIRMACIÓN -->
|
||||
<div class="confirmation-container">
|
||||
<?php
|
||||
// Determinar icono y títulos según el tipo
|
||||
$iconClass = 'icon-success';
|
||||
$iconSymbol = 'fa-check';
|
||||
$title = 'Operación Exitosa';
|
||||
|
||||
switch($tipo) {
|
||||
case 'password_changed':
|
||||
$iconClass = 'icon-success';
|
||||
$iconSymbol = 'fa-check';
|
||||
$title = '¡Contraseña Actualizada!';
|
||||
$mensaje = 'Tu contraseña ha sido cambiada exitosamente. Ya puedes iniciar sesión con tu nueva contraseña.';
|
||||
break;
|
||||
|
||||
case 'code_sent':
|
||||
$iconClass = 'icon-info';
|
||||
$iconSymbol = 'fa-envelope';
|
||||
$title = '¡Código Enviado!';
|
||||
$mensaje = 'Hemos enviado un código de verificación a tu correo electrónico. Revisa tu bandeja de entrada y spam.';
|
||||
break;
|
||||
|
||||
case 'registration_sent':
|
||||
$iconClass = 'icon-success';
|
||||
$iconSymbol = 'fa-paper-plane';
|
||||
$title = '¡Solicitud Enviada!';
|
||||
$mensaje = 'Tu solicitud de registro ha sido enviada correctamente. Recibirás una notificación por correo cuando sea aprobada.';
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
$iconClass = 'icon-error';
|
||||
$iconSymbol = 'fa-times';
|
||||
$title = 'Error en la Operación';
|
||||
break;
|
||||
|
||||
case 'warning':
|
||||
$iconClass = 'icon-warning';
|
||||
$iconSymbol = 'fa-exclamation-triangle';
|
||||
$title = 'Atención Requerida';
|
||||
break;
|
||||
|
||||
default:
|
||||
$iconClass = 'icon-success';
|
||||
$iconSymbol = 'fa-check';
|
||||
$title = 'Operación Completada';
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="confirmation-icon <?= $iconClass ?>">
|
||||
<i class="fas <?= $iconSymbol ?>"></i>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Overlay (fondo semitransparente) que cubre la pantalla -->
|
||||
<div class="overlay"></div>
|
||||
<h1 class="confirmation-title"><?= htmlspecialchars($title) ?></h1>
|
||||
|
||||
<p class="confirmation-message">
|
||||
<?= htmlspecialchars($mensaje) ?>
|
||||
</p>
|
||||
|
||||
<!-- Formulario de confirmación de acceso -->
|
||||
<div class="login-container">
|
||||
<h4 class="mb-4 text-center text-success">Confirma el Acceso</h4>
|
||||
|
||||
<!-- Formulario para verificar el código -->
|
||||
<form id="formConfirmacion" action="/IMPORTADORES/login/confirmarAcceso" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="codigo">Código recibido por correo</label>
|
||||
<input type="text" id="codigo" name="codigo" class="form-control" required pattern="\d{6}" maxlength="6" placeholder="Ej. 123456">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Verificar código</button>
|
||||
</form>
|
||||
|
||||
<div id="mensaje" class="mt-3 text-center fw-bold">
|
||||
<?php if (isset($_SESSION['codigo_error'])): ?>
|
||||
<div class="text-danger"><?= $_SESSION['codigo_error'] ?></div>
|
||||
<?php unset($_SESSION['codigo_error']); ?>
|
||||
<?php elseif (isset($_SESSION['codigo_exito'])): ?>
|
||||
<div class="text-success"><?= $_SESSION['codigo_exito'] ?></div>
|
||||
<?php unset($_SESSION['codigo_exito']); ?>
|
||||
<div class="action-buttons">
|
||||
<?php if ($tipo === 'password_changed'): ?>
|
||||
<a href="/IMPORTADORES/login" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
Iniciar Sesión
|
||||
</a>
|
||||
|
||||
<?php elseif ($tipo === 'code_sent'): ?>
|
||||
<a href="/IMPORTADORES/login/verificar_codigo" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-key"></i>
|
||||
Verificar Código
|
||||
</a>
|
||||
<a href="/IMPORTADORES/login/recuperar" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Volver
|
||||
</a>
|
||||
|
||||
<?php elseif ($tipo === 'registration_sent'): ?>
|
||||
<a href="/IMPORTADORES/" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-home"></i>
|
||||
Ir al Inicio
|
||||
</a>
|
||||
<a href="/IMPORTADORES/login" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
Iniciar Sesión
|
||||
</a>
|
||||
|
||||
<?php else: ?>
|
||||
<a href="/IMPORTADORES/login" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
Iniciar Sesión
|
||||
</a>
|
||||
<a href="/IMPORTADORES/" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-home"></i>
|
||||
Ir al Inicio
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$redirigirA = $_SESSION['redirigir_a'] ?? null;
|
||||
$delayMs = $_SESSION['delay_redireccion'] ?? null;
|
||||
unset($_SESSION['redirigir_a'], $_SESSION['delay_redireccion']);
|
||||
?>
|
||||
<?php if ($tipo === 'code_sent'): ?>
|
||||
<div class="additional-info">
|
||||
<div class="d-flex align-items-start">
|
||||
<i class="fas fa-info-circle me-2 mt-1"></i>
|
||||
<div>
|
||||
<strong>¿No recibiste el código?</strong><br>
|
||||
<small>
|
||||
• Revisa tu carpeta de spam o correo no deseado<br>
|
||||
• El código es válido por 15 minutos<br>
|
||||
• Puedes solicitar un nuevo código si es necesario
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($tipo === 'registration_sent'): ?>
|
||||
<div class="additional-info">
|
||||
<div class="d-flex align-items-start">
|
||||
<i class="fas fa-clock me-2 mt-1"></i>
|
||||
<div>
|
||||
<strong>Proceso de Revisión</strong><br>
|
||||
<small>
|
||||
• Nuestro equipo revisará tu solicitud en las próximas 24-48 horas<br>
|
||||
• Recibirás un correo con el resultado de la evaluación<br>
|
||||
• Asegúrate de revisar tu bandeja de entrada regularmente
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (in_array($tipo, ['password_changed', 'registration_sent'])): ?>
|
||||
<div class="countdown" id="countdown">
|
||||
Serás redirigido automáticamente en <span id="timer">10</span> segundos...
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Plataforma segura de gestión de importaciones
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const redirigirA = <?= json_encode($redirigirA) ?>;
|
||||
const delay = <?= json_encode($delayMs) ?>;
|
||||
|
||||
if (redirigirA && delay) {
|
||||
setTimeout(() => {
|
||||
window.location.href = redirigirA;
|
||||
}, parseInt(delay));
|
||||
// Redirección automática para ciertos tipos
|
||||
<?php if (in_array($tipo, ['password_changed', 'registration_sent'])): ?>
|
||||
let timeLeft = 10;
|
||||
const timerElement = document.getElementById('timer');
|
||||
const countdownElement = document.getElementById('countdown');
|
||||
|
||||
const countdown = setInterval(() => {
|
||||
timeLeft--;
|
||||
timerElement.textContent = timeLeft;
|
||||
|
||||
if (timeLeft <= 0) {
|
||||
clearInterval(countdown);
|
||||
<?php if ($tipo === 'password_changed'): ?>
|
||||
window.location.href = '/IMPORTADORES/login';
|
||||
<?php elseif ($tipo === 'registration_sent'): ?>
|
||||
window.location.href = '/IMPORTADORES/';
|
||||
<?php endif; ?>
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Permitir cancelar la redirección automática
|
||||
document.addEventListener('click', () => {
|
||||
if (timeLeft > 0) {
|
||||
clearInterval(countdown);
|
||||
countdownElement.style.display = 'none';
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// Animación de entrada
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const container = document.querySelector('.confirmation-container');
|
||||
container.style.opacity = '0';
|
||||
container.style.transform = 'translateY(20px)';
|
||||
|
||||
setTimeout(() => {
|
||||
container.style.transition = 'all 0.5s ease';
|
||||
container.style.opacity = '1';
|
||||
container.style.transform = 'translateY(0)';
|
||||
}, 100);
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -6,11 +6,12 @@ $sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
?>
|
||||
@@ -19,40 +20,238 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Iniciar sesión</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Iniciar Sesión</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Accede a tu cuenta en la plataforma de gestión de importaciones">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color:<?= $color1 ?> !important; font-weight: 500; }
|
||||
.navbar .nav-link.active { background-color: #F9F9F9; border-radius: 5px; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.login-container { max-width: 420px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); }
|
||||
.form-control { border-radius: 6px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; margin-top: 60px; }
|
||||
#mensaje { transition: opacity 0.3s ease; }
|
||||
@media (max-height: 600px) { footer { position: static !important; } }
|
||||
/* Efecto de glow para elementos activos */
|
||||
.btn.active { box-shadow: 0 0 20px rgba(var(--bs-primary-rgb), 0.5); }
|
||||
/* Efecto para botones */
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--error: #ef4444;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
max-width: 450px;
|
||||
margin: 3rem auto;
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.forgot-password a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.forgot-password a:hover {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: linear-gradient(135deg, #fef2f2, #fee2e2);
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: linear-gradient(135deg, #fef3c7, #fde68a);
|
||||
color: #92400e;
|
||||
border: 1px solid #f59e0b;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.login-container {
|
||||
margin: 2rem 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.maintenance-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Inicio de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -63,60 +262,82 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<a class="nav-link" href="/IMPORTADORES/">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link " aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- MANTENIMIENTO -->
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div class="container mt-4">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<i class="fas fa-triangle-exclamation me-2"></i>
|
||||
<?= htmlspecialchars($mensajeMantenimiento) ?>
|
||||
</div>
|
||||
<div class="maintenance-overlay">
|
||||
<i class="fas fa-tools fa-4x mb-4" style="color: var(--accent);"></i>
|
||||
<h2 style="color: var(--text); margin-bottom: 1rem;">Sistema en Mantenimiento</h2>
|
||||
<p style="color: var(--text-muted); max-width: 500px;"><?= htmlspecialchars($mensajeMantenimiento) ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- CONTENEDOR DE LOGIN -->
|
||||
<div class="login-container">
|
||||
<h4 class="mb-4 text-center text-dark">Inicio de Sesión </h4>
|
||||
<form action="/IMPORTADORES/login/validar" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Correo electrónico</label>
|
||||
<input type="email" name="email" class="form-control" required autocomplete="username" value="">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Contraseña</label>
|
||||
<input type="password" name="password" class="form-control" required autocomplete="current-password" value="">
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-sm mt-auto m-100 btn-animated">Iniciar sesión</button>
|
||||
</div>
|
||||
<div class="mb-3 text-end">
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<a href="/IMPORTADORES/login/recuperar" class="text-decoration-none">¿Olvidaste tu contraseña?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="mensaje" class="mt-3 text-center fw-bold">
|
||||
<?php if (isset($_SESSION['login_error'])): ?>
|
||||
<div class="text-danger"><?= $_SESSION['login_error'] ?></div>
|
||||
<?php unset($_SESSION['login_error']); ?>
|
||||
<?php endif; ?>
|
||||
<div class="login-header">
|
||||
<h1 class="login-title">Bienvenido de Vuelta</h1>
|
||||
<p class="login-subtitle">Ingresa a tu cuenta para gestionar tus importaciones</p>
|
||||
</div>
|
||||
|
||||
<!-- MENSAJES DE ERROR -->
|
||||
<?php if (isset($_SESSION['login_error'])): ?>
|
||||
<div class="alert-modern alert-danger">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= $_SESSION['login_error'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['login_error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/IMPORTADORES/login/validar" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">
|
||||
<i class="fas fa-envelope text-muted"></i>
|
||||
Correo electrónico
|
||||
</label>
|
||||
<input type="email" name="email" id="email" class="form-control"
|
||||
required autocomplete="username"
|
||||
placeholder="tu@empresa.com">
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="password" class="form-label">
|
||||
<i class="fas fa-lock text-muted"></i>
|
||||
Contraseña
|
||||
</label>
|
||||
<input type="password" name="password" id="password" class="form-control"
|
||||
required autocomplete="current-password"
|
||||
placeholder="Tu contraseña segura">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-sign-in-alt"></i>
|
||||
Iniciar Sesión
|
||||
</button>
|
||||
|
||||
<div class="forgot-password">
|
||||
<a href="/IMPORTADORES/login/recuperar">¿Olvidaste tu contraseña?</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<div class="container text-center ">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Acceso seguro a tu plataforma de importaciones
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- I.C.M -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,42 +1,259 @@
|
||||
<?php
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
$conn = getConnection();
|
||||
|
||||
$sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Recuperación de Contraseña</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Recuperar Contraseña</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Recupera tu contraseña para acceder a tu cuenta de importaciones">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; position: relative; height: 100vh; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color: <?= $color1 ?> !important; font-weight: 500; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.login-container { max-width: 420px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); z-index: 10; position: relative; }
|
||||
.form-control { border-radius: 6px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; margin-top: 60px; }
|
||||
/* Contenedor de fondo semitransparente para las vistas */
|
||||
.overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 5; }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--error: #ef4444;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.recovery-container {
|
||||
max-width: 500px;
|
||||
margin: 3rem auto;
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.recovery-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.recovery-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.recovery-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.recovery-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.recovery-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-modern {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
border: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-outline-modern:hover {
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: linear-gradient(135deg, #fef2f2, #fee2e2);
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||
color: #16a34a;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
|
||||
border: 1px solid #38bdf8;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
color: #0c4a6e;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.recovery-container {
|
||||
margin: 2rem 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.recovery-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Inicio de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -47,112 +264,166 @@ $mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encue
|
||||
<a class="nav-link" href="/IMPORTADORES/">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link " aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div class="container mt-4">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<i class="fas fa-triangle-exclamation me-2"></i>
|
||||
<?= htmlspecialchars($mensajeMantenimiento) ?>
|
||||
<!-- CONTENEDOR DE RECUPERACIÓN -->
|
||||
<div class="recovery-container">
|
||||
<div class="recovery-header">
|
||||
<div class="recovery-icon">
|
||||
<i class="fas fa-key"></i>
|
||||
</div>
|
||||
<h1 class="recovery-title">Recuperar Contraseña</h1>
|
||||
<p class="recovery-subtitle">Ingresa tu correo electrónico y te enviaremos un código de recuperación</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Overlay (fondo semitransparente) que cubre la pantalla -->
|
||||
<div class="overlay"></div>
|
||||
|
||||
<!-- Formulario de recuperación de contraseña -->
|
||||
<div class="login-container">
|
||||
<h4 class="mb-4 text-center text-dark">Recuperar Contraseña</h4>
|
||||
<form id="formRecuperar" novalidate>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="emailInput">Ingresa tu correo</label>
|
||||
<input type="email" id="emailInput" name="email" class="form-control" required>
|
||||
<!-- MENSAJES -->
|
||||
<?php if (isset($_SESSION['error_message'])): ?>
|
||||
<div class="alert-modern alert-danger">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= $_SESSION['error_message'] ?>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Enviar código</button>
|
||||
<?php unset($_SESSION['error_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['success_message'])): ?>
|
||||
<div class="alert-modern alert-success">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<?= $_SESSION['success_message'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['success_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/IMPORTADORES/login/enviar_codigo" method="POST" id="recoveryForm">
|
||||
<div class="mb-4">
|
||||
<label for="email" class="form-label">
|
||||
<i class="fas fa-envelope text-muted"></i>
|
||||
Correo electrónico registrado
|
||||
</label>
|
||||
<input type="email" name="email" id="email" class="form-control"
|
||||
required autocomplete="username"
|
||||
placeholder="tu@empresa.com">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-modern btn-primary-modern" id="submitBtn">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
Enviar Código de Recuperación
|
||||
</button>
|
||||
|
||||
<a href="/IMPORTADORES/login" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Volver al Login
|
||||
</a>
|
||||
</form>
|
||||
|
||||
<?php if (isset($_SESSION['recuperar_error'])): ?>
|
||||
<div class="text-danger mt-3 text-center"><?= htmlspecialchars($_SESSION['recuperar_error']) ?></div>
|
||||
<?php unset($_SESSION['recuperar_error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['recuperar_exito'])): ?>
|
||||
<div class="text-success mt-3 text-center"><?= htmlspecialchars($_SESSION['recuperar_exito']) ?></div>
|
||||
<?php unset($_SESSION['recuperar_exito']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="mensaje" class="mt-3 text-center fw-bold"></div>
|
||||
<div class="info-box">
|
||||
<div class="d-flex align-items-start">
|
||||
<i class="fas fa-info-circle me-2 mt-1"></i>
|
||||
<div>
|
||||
<strong>¿Cómo funciona?</strong><br>
|
||||
<small>
|
||||
1. Ingresa tu correo electrónico registrado<br>
|
||||
2. Revisa tu bandeja de entrada (y spam)<br>
|
||||
3. Usa el código para crear una nueva contraseña<br>
|
||||
4. Accede con tu nueva contraseña
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Recuperación segura de contraseña
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const form = document.getElementById('formRecuperar');
|
||||
const mensaje = document.getElementById('mensaje');
|
||||
const inputCorreo = form.querySelector('input[name="email"]');
|
||||
|
||||
inputCorreo.addEventListener('input', () => {
|
||||
mensaje.innerHTML = '';
|
||||
mensaje.classList.remove("text-danger", "text-success");
|
||||
});
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const emailValue = inputCorreo.value.trim();
|
||||
if (!emailValue) {
|
||||
mensaje.textContent = "❌ Por favor, ingresa un correo válido.";
|
||||
mensaje.classList.add("text-danger");
|
||||
return;
|
||||
document.getElementById('recoveryForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const email = document.getElementById('email').value.trim();
|
||||
|
||||
// Validar email
|
||||
const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
if (!email || !emailRegex.test(email)) {
|
||||
showAlert('error', 'Por favor ingresa un correo electrónico válido');
|
||||
return;
|
||||
}
|
||||
|
||||
// Deshabilitar botón y mostrar loading
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Enviando...';
|
||||
|
||||
// Enviar solicitud AJAX
|
||||
fetch('/IMPORTADORES/login/enviar_codigo', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'email=' + encodeURIComponent(email)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Éxito - redirigir a verificar código
|
||||
showAlert('success', data.message);
|
||||
setTimeout(() => {
|
||||
window.location.href = '/IMPORTADORES/login/verificar_codigo';
|
||||
}, 2000);
|
||||
} else {
|
||||
// Error - mostrar mensaje
|
||||
showAlert('error', data.message);
|
||||
// Rehabilitar botón
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-paper-plane"></i> Enviar Código de Recuperación';
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('email', emailValue);
|
||||
|
||||
fetch('/IMPORTADORES/login/enviarCodigo', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Esperamos que el backend devuelva JSON con { success: true/false, message: "..." }
|
||||
|
||||
if (data.success) {
|
||||
mensaje.textContent = data.message;
|
||||
mensaje.classList.remove("text-danger");
|
||||
mensaje.classList.add("text-success");
|
||||
|
||||
// Opcional: redirigir a la vista de verificación de código
|
||||
setTimeout(() => {
|
||||
window.location.href = '/IMPORTADORES/login/verificarCodigoVista';
|
||||
}, 2000);
|
||||
} else {
|
||||
mensaje.textContent = "❌ " + data.message;
|
||||
mensaje.classList.remove("text-success");
|
||||
mensaje.classList.add("text-danger");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
mensaje.textContent = "❌ Error de red.";
|
||||
mensaje.classList.remove("text-success");
|
||||
mensaje.classList.add("text-danger");
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('error', 'Error de conexión. Intenta nuevamente.');
|
||||
// Rehabilitar botón
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-paper-plane"></i> Enviar Código de Recuperación';
|
||||
});
|
||||
});
|
||||
|
||||
function showAlert(type, message) {
|
||||
// Remover alertas anteriores
|
||||
const existingAlerts = document.querySelectorAll('.alert-modern');
|
||||
existingAlerts.forEach(alert => alert.remove());
|
||||
|
||||
// Crear nueva alerta
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert-modern alert-${type === 'success' ? 'success' : 'danger'}`;
|
||||
alertDiv.innerHTML = `
|
||||
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
|
||||
${message}
|
||||
`;
|
||||
|
||||
// Insertar antes del formulario
|
||||
const form = document.getElementById('recoveryForm');
|
||||
form.parentNode.insertBefore(alertDiv, form);
|
||||
|
||||
// Auto-remover después de 5 segundos si es error
|
||||
if (type === 'error') {
|
||||
setTimeout(() => {
|
||||
alertDiv.remove();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +1,17 @@
|
||||
<?php
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
$mantenimiento = $config['modo_mantenimiento'] ?? 0;
|
||||
$mensajeMantenimiento = $config['mensaje_mantenimiento'] ?? 'El sistema se encuentra en mantenimiento temporal. Por favor, vuelve más tarde.';
|
||||
require_once __DIR__ . '/../../config/database.php';
|
||||
$conn = getConnection();
|
||||
|
||||
$sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
|
||||
if (!isset($_SESSION['email_recuperacion'])) {
|
||||
// Si no hay correo en sesión, redirige a solicitar código
|
||||
@@ -18,60 +24,271 @@ if (!isset($_SESSION['email_recuperacion'])) {
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Recuperación de Contraseña</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Verificar Código</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Verifica el código de recuperación enviado a tu correo">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; position: relative; min-height: 100vh; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link, .navbar-brand { color: <?= $color1 ?> !important; font-weight: 500; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.login-container { max-width: 420px; margin: 80px auto; background: white; padding: 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08); z-index: 10; position: relative; }
|
||||
.form-control { border-radius: 6px; transition: all 0.3s ease; }
|
||||
.form-control.error { border-color: #dc3545; background-color: #fff5f5; animation: shake 0.5s ease-in-out; }
|
||||
.form-control.success { border-color: #28a745; background-color: #f8fff9; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; margin-top: 60px; }
|
||||
/* Contenedor de fondo semitransparente para las vistas */
|
||||
.overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 5; }
|
||||
#mensaje { transition: all 0.3s ease; min-height: 24px; }
|
||||
#btnReenviarRespaldo { background: none; border: none; color: <?= $color1 ?>; text-decoration: underline; cursor: pointer; font-size: 14px; padding: 0; transition: color 0.3s ease; }
|
||||
#btnReenviarRespaldo:hover { color: <?= $color2 ?>; }
|
||||
#btnReenviarRespaldo:disabled { color: #ccc; cursor: not-allowed; text-decoration: none; }
|
||||
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.loading { position: relative; pointer-events: none; }
|
||||
.loading::after { content: ''; position: absolute; top: 50%; left: 50%; width: 20px; height: 20px; border: 2px solid transparent; border-top: 2px solid #fff; border-radius: 50%; animation: spin 1s linear infinite; transform: translate(-50%, -50%); }
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-5px); }
|
||||
75% { transform: translateX(5px); }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
--error: #ef4444;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: translate(-50%, -50%) rotate(0deg); }
|
||||
100% { transform: translate(-50%, -50%) rotate(360deg); }
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.verification-container {
|
||||
max-width: 500px;
|
||||
margin: 3rem auto;
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.verification-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.verification-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.verification-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.verification-title {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.verification-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.code-input {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-modern {
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
border: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-outline-modern:hover {
|
||||
background: var(--background);
|
||||
color: var(--text);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.alert-modern {
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: linear-gradient(135deg, #fef2f2, #fee2e2);
|
||||
color: #dc2626;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||
color: #16a34a;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
.resend-section {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 2px solid var(--border);
|
||||
}
|
||||
|
||||
.resend-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.resend-link {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.resend-link:hover {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.verification-container {
|
||||
margin: 2rem 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.verification-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.code-input {
|
||||
font-size: 1.25rem;
|
||||
letter-spacing: 0.25rem;
|
||||
}
|
||||
}
|
||||
.fade-in { animation: fadeIn 0.3s ease-out; }
|
||||
@media (max-height: 600px) { footer { position: static !important; } }
|
||||
/* Estilos para input de código */
|
||||
#codigo { font-size: 18px; letter-spacing: 3px; text-align: center; font-weight: bold; }
|
||||
.input-group { position: relative; }
|
||||
.clear-input { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #ccc; cursor: pointer; z-index: 5; display: none; }
|
||||
.clear-input:hover { color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Inicio de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -82,387 +299,229 @@ if (!isset($_SESSION['email_recuperacion'])) {
|
||||
<a class="nav-link" href="/IMPORTADORES/">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link " aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php if ($mantenimiento): ?>
|
||||
<div class="container mt-4">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<i class="fas fa-triangle-exclamation me-2"></i>
|
||||
<?= htmlspecialchars($mensajeMantenimiento) ?>
|
||||
<!-- CONTENEDOR DE VERIFICACIÓN -->
|
||||
<div class="verification-container">
|
||||
<div class="verification-header">
|
||||
<div class="verification-icon">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</div>
|
||||
<h1 class="verification-title">Verificar Código</h1>
|
||||
<p class="verification-subtitle">
|
||||
Hemos enviado un código de 6 dígitos a tu correo electrónico.<br>
|
||||
Ingresa el código para continuar con la recuperación.
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Overlay (fondo semitransparente) que cubre la pantalla -->
|
||||
<div class="overlay"></div>
|
||||
<!-- MENSAJES -->
|
||||
<?php if (isset($_SESSION['error_message'])): ?>
|
||||
<div class="alert-modern alert-danger">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= $_SESSION['error_message'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['error_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Formulario de validación de código -->
|
||||
<div class="login-container">
|
||||
<h4 class="mb-4 text-center text-dark">Verifica tu código</h4>
|
||||
<p class="text-muted text-center mb-4">
|
||||
<i class="fas fa-envelope me-2"></i>
|
||||
Hemos enviado un código de 6 dígitos a tu correo electrónico
|
||||
</p>
|
||||
|
||||
<!-- Formulario para verificar el código -->
|
||||
<form id="formCodigo" action="/IMPORTADORES/login/verificarCodigo" method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="codigo">
|
||||
<i class="fas fa-key me-2"></i>Código de verificación
|
||||
<?php if (isset($_SESSION['success_message'])): ?>
|
||||
<div class="alert-modern alert-success">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<?= $_SESSION['success_message'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['success_message']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/IMPORTADORES/login/verificar_codigo_recuperacion" method="POST" id="verificationForm">
|
||||
<div class="mb-4">
|
||||
<label for="codigo" class="form-label">
|
||||
<i class="fas fa-key text-muted"></i>
|
||||
Código de verificación
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="codigo" name="codigo" class="form-control" required pattern="\d{6}" maxlength="6" placeholder="000000" autocomplete="off" inputmode="numeric">
|
||||
<button type="button" class="clear-input" id="clearCode" title="Limpiar código">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<input type="text" name="codigo" id="codigo" class="form-control code-input"
|
||||
required maxlength="6" minlength="6"
|
||||
placeholder="000000"
|
||||
pattern="[0-9]{6}"
|
||||
autocomplete="one-time-code">
|
||||
<small class="form-text text-muted mt-2">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Ingresa el código de 6 dígitos que recibiste por correo
|
||||
</div>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="btnVerificar" class="btn btn-success w-100">
|
||||
<i class="fas fa-check me-2"></i>Verificar código
|
||||
|
||||
<button type="submit" class="btn-modern btn-primary-modern" id="submitBtn">
|
||||
<i class="fas fa-check"></i>
|
||||
Verificar Código
|
||||
</button>
|
||||
|
||||
<!-- Botón para enviar el código al correo de respaldo-->
|
||||
<div class="text-center mt-3">
|
||||
<button type="button" id="btnReenviarRespaldo" title="Enviar código al correo de respaldo">
|
||||
<i class="fas fa-paper-plane me-1"></i>
|
||||
¿No tienes acceso al correo? Reenviar al correo de respaldo
|
||||
</button>
|
||||
</div>
|
||||
<a href="/IMPORTADORES/login/recuperar" class="btn-modern btn-outline-modern">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
Volver
|
||||
</a>
|
||||
</form>
|
||||
|
||||
<!-- Mostrar mensajes -->
|
||||
<div id="mensaje" class="mt-3 text-center fw-bold">
|
||||
<?php if (isset($_SESSION['codigo_error'])): ?>
|
||||
<div class="text-danger fade-in">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= $_SESSION['codigo_error'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['codigo_error']); ?>
|
||||
<?php elseif (isset($_SESSION['codigo_exito'])): ?>
|
||||
<div class="text-success fade-in">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<?= $_SESSION['codigo_exito'] ?>
|
||||
</div>
|
||||
<?php unset($_SESSION['codigo_exito']); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Contador de intentos -->
|
||||
<div id="intentosInfo" class="mt-2 text-center text-muted" style="font-size: 12px;">
|
||||
<!-- Se llenará dinámicamente -->
|
||||
<div class="resend-section">
|
||||
<div class="resend-text">
|
||||
¿No recibiste el código?
|
||||
</div>
|
||||
<a href="#" class="resend-link" id="resendLink">
|
||||
<i class="fas fa-redo me-1"></i>
|
||||
Reenviar código
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Verificación segura de identidad
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const form = document.getElementById('formCodigo');
|
||||
const mensaje = document.getElementById('mensaje');
|
||||
const inputCodigo = document.getElementById('codigo');
|
||||
const btnVerificar = document.getElementById('btnVerificar');
|
||||
const btnReenviar = document.getElementById('btnReenviarRespaldo');
|
||||
const clearBtn = document.getElementById('clearCode');
|
||||
const intentosInfo = document.getElementById('intentosInfo');
|
||||
// Auto-format del código
|
||||
document.getElementById('codigo').addEventListener('input', function(e) {
|
||||
let value = e.target.value.replace(/\D/g, ''); // Solo números
|
||||
if (value.length > 6) value = value.substring(0, 6);
|
||||
e.target.value = value;
|
||||
});
|
||||
|
||||
let intentosRealizados = 0;
|
||||
const maxIntentos = 5;
|
||||
// Focus automático al cargar
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('codigo').focus();
|
||||
});
|
||||
|
||||
// Auto-focus en el input al cargar
|
||||
inputCodigo.focus();
|
||||
|
||||
// Mostrar/ocultar botón de limpiar
|
||||
inputCodigo.addEventListener('input', function() {
|
||||
clearBtn.style.display = this.value.length > 0 ? 'block' : 'none';
|
||||
|
||||
// Limpiar mensajes previos
|
||||
limpiarMensajes();
|
||||
|
||||
// Remover clases de error/éxito
|
||||
inputCodigo.classList.remove('error', 'success');
|
||||
|
||||
// Solo permitir números
|
||||
this.value = this.value.replace(/[^0-9]/g, '');
|
||||
|
||||
// Actualizar contador visual
|
||||
actualizarContadorVisual();
|
||||
});
|
||||
|
||||
// Limpiar input
|
||||
clearBtn.addEventListener('click', function() {
|
||||
inputCodigo.value = '';
|
||||
inputCodigo.focus();
|
||||
clearBtn.style.display = 'none';
|
||||
limpiarMensajes();
|
||||
inputCodigo.classList.remove('error', 'success');
|
||||
});
|
||||
|
||||
// Permitir solo números en tiempo real
|
||||
inputCodigo.addEventListener('keypress', function(e) {
|
||||
if (!/[0-9]/.test(e.key) && !['Backspace', 'Delete', 'Tab', 'Enter'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-envío cuando se completen 6 dígitos (opcional)
|
||||
inputCodigo.addEventListener('input', function() {
|
||||
if (this.value.length === 6) {
|
||||
// Opcional: enviar automáticamente después de un breve delay
|
||||
// setTimeout(() => form.dispatchEvent(new Event('submit')), 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Manejo del formulario
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const codigo = inputCodigo.value.trim();
|
||||
if (!validarCodigo(codigo)) return;
|
||||
|
||||
enviarCodigo(codigo);
|
||||
});
|
||||
|
||||
// Validación del código
|
||||
function validarCodigo(codigo) {
|
||||
if (!codigo) {
|
||||
mostrarError("❌ Por favor, ingresa el código.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (codigo.length !== 6) {
|
||||
mostrarError("❌ El código debe tener exactamente 6 dígitos.");
|
||||
inputCodigo.classList.add('error');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/^\d{6}$/.test(codigo)) {
|
||||
mostrarError("❌ El código solo debe contener números.");
|
||||
inputCodigo.classList.add('error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Manejar envío del formulario
|
||||
document.getElementById('verificationForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const codigo = document.getElementById('codigo').value.trim();
|
||||
|
||||
// Validar código
|
||||
if (!codigo || codigo.length !== 6 || !/^\d{6}$/.test(codigo)) {
|
||||
showAlert('error', 'El código debe tener exactamente 6 dígitos');
|
||||
return;
|
||||
}
|
||||
|
||||
// Enviar código para verificación
|
||||
function enviarCodigo(codigo) {
|
||||
// Mostrar estado de carga
|
||||
btnVerificar.disabled = true;
|
||||
btnVerificar.classList.add('loading');
|
||||
btnVerificar.innerHTML = '<span style="opacity: 0;">Verificando...</span>';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('codigo', codigo);
|
||||
|
||||
fetch('/IMPORTADORES/login/verificarCodigo', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.blocked) {
|
||||
mostrarError(data.message); // <-- función personalizada con un div bonito
|
||||
setTimeout(() => {
|
||||
window.location.href = data.redirect;
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
if (data.success) {
|
||||
// Código correcto
|
||||
mostrarExito(data.message);
|
||||
inputCodigo.classList.add('success');
|
||||
inputCodigo.disabled = true;
|
||||
|
||||
// Redirigir al formulario de cambio de contraseña
|
||||
setTimeout(() => {
|
||||
window.location.href = '/IMPORTADORES/login/cambiarPasswordVista';
|
||||
}, 1500);
|
||||
|
||||
} else {
|
||||
// Código incorrecto
|
||||
manejarCodigoIncorrecto(data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
mostrarError("❌ Error de conexión. Intenta nuevamente.");
|
||||
inputCodigo.classList.add('error');
|
||||
limpiarYEnfocarInput();
|
||||
})
|
||||
.finally(() => {
|
||||
// Restaurar botón
|
||||
btnVerificar.disabled = false;
|
||||
btnVerificar.classList.remove('loading');
|
||||
btnVerificar.innerHTML = '<i class="fas fa-check me-2"></i>Verificar código';
|
||||
});
|
||||
}
|
||||
|
||||
// Mostrar error de cuenta bloqueada
|
||||
function mostrarError(mensaje) {
|
||||
const mensajeDiv = document.getElementById('mensaje');
|
||||
mensajeDiv.innerHTML = `
|
||||
<div class="text-danger fade-in">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i> ${mensaje}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Manejar código incorrecto
|
||||
function manejarCodigoIncorrecto(data) {
|
||||
intentosRealizados++;
|
||||
|
||||
mostrarError(data.message);
|
||||
inputCodigo.classList.add('error');
|
||||
|
||||
// Limpiar input y enfocar para nuevo intento
|
||||
limpiarYEnfocarInput();
|
||||
|
||||
// Actualizar contador de intentos
|
||||
actualizarContadorIntentos();
|
||||
|
||||
// Si se bloqueó el código
|
||||
if (data.blocked) {
|
||||
bloquearFormulario();
|
||||
}
|
||||
}
|
||||
|
||||
// Limpiar input y enfocar
|
||||
function limpiarYEnfocarInput() {
|
||||
setTimeout(() => {
|
||||
inputCodigo.value = '';
|
||||
inputCodigo.classList.remove('error');
|
||||
inputCodigo.focus();
|
||||
clearBtn.style.display = 'none';
|
||||
}, 1500); // Esperar 1.5 segundos antes de limpiar
|
||||
}
|
||||
|
||||
// Actualizar contador visual
|
||||
function actualizarContadorVisual() {
|
||||
const longitud = inputCodigo.value.length;
|
||||
if (longitud > 0) {
|
||||
inputCodigo.setAttribute('placeholder', '0'.repeat(6 - longitud) + inputCodigo.value);
|
||||
} else {
|
||||
inputCodigo.setAttribute('placeholder', '000000');
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar contador de intentos
|
||||
function actualizarContadorIntentos() {
|
||||
if (intentosRealizados > 0) {
|
||||
const restantes = maxIntentos - intentosRealizados;
|
||||
intentosInfo.innerHTML = `
|
||||
<i class="fas fa-exclamation-triangle text-warning me-1"></i>
|
||||
Intentos restantes: <strong>${restantes}</strong> de ${maxIntentos}
|
||||
`;
|
||||
intentosInfo.classList.add('fade-in');
|
||||
}
|
||||
}
|
||||
|
||||
// Bloquear formulario cuando se exceden intentos
|
||||
function bloquearFormulario() {
|
||||
inputCodigo.disabled = true;
|
||||
btnVerificar.disabled = true;
|
||||
btnReenviar.disabled = true;
|
||||
|
||||
intentosInfo.innerHTML = `
|
||||
<i class="fas fa-ban text-danger me-1"></i>
|
||||
<span class="text-danger">Código bloqueado por seguridad</span>
|
||||
`;
|
||||
|
||||
// Redirigir a solicitar nuevo código después de 5 segundos
|
||||
setTimeout(() => {
|
||||
window.location.href = '/IMPORTADORES/login/recuperar';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Funciones de utilidad para mensajes
|
||||
function mostrarMensaje(texto, tipo) {
|
||||
mensaje.innerHTML = `<div class="${tipo} fade-in">${texto}</div>`;
|
||||
mensaje.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
|
||||
function mostrarError(texto) {
|
||||
mostrarMensaje(`<i class="fas fa-exclamation-triangle me-2"></i>${texto}`, 'text-danger');
|
||||
}
|
||||
|
||||
function mostrarExito(texto) {
|
||||
mostrarMensaje(`<i class="fas fa-check-circle me-2"></i>${texto}`, 'text-success');
|
||||
}
|
||||
|
||||
function mostrarInfo(texto) {
|
||||
mostrarMensaje(`<i class="fas fa-info-circle me-2"></i>${texto}`, 'text-info');
|
||||
}
|
||||
|
||||
function limpiarMensajes() {
|
||||
mensaje.innerHTML = '';
|
||||
}
|
||||
|
||||
// Manejo del botón de reenvío
|
||||
btnReenviar.addEventListener("click", function() {
|
||||
const btnOriginalText = this.innerHTML;
|
||||
|
||||
// Deshabilitar botón temporalmente
|
||||
this.disabled = true;
|
||||
this.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Enviando...';
|
||||
|
||||
fetch("/IMPORTADORES/login/reenviarCodigo", {
|
||||
method: "POST",
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: "accion=reenviarCodigo"
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
mostrarInfo(data.message);
|
||||
|
||||
// Habilitar botón después de 30 segundos
|
||||
|
||||
// Deshabilitar botón y mostrar loading
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Verificando...';
|
||||
|
||||
// Enviar solicitud AJAX
|
||||
fetch('/IMPORTADORES/login/verificar_codigo_recuperacion', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'codigo=' + encodeURIComponent(codigo)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Éxito - redirigir a cambiar contraseña
|
||||
showAlert('success', data.message);
|
||||
setTimeout(() => {
|
||||
this.disabled = false;
|
||||
this.innerHTML = btnOriginalText;
|
||||
}, 30000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
mostrarError("❌ Error al reenviar código.");
|
||||
window.location.href = '/IMPORTADORES/login/cambiar_password';
|
||||
}, 1500);
|
||||
} else {
|
||||
// Error - mostrar mensaje
|
||||
showAlert('error', data.message);
|
||||
// Rehabilitar botón
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-check"></i> Verificar Código';
|
||||
|
||||
// Restaurar botón en caso de error
|
||||
this.disabled = false;
|
||||
this.innerHTML = btnOriginalText;
|
||||
});
|
||||
});
|
||||
|
||||
// Manejar tecla Enter en cualquier parte del formulario
|
||||
document.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && !btnVerificar.disabled) {
|
||||
form.dispatchEvent(new Event('submit'));
|
||||
// Si hay bloqueo, redirigir
|
||||
if (data.blocked) {
|
||||
setTimeout(() => {
|
||||
window.location.href = data.redirect || '/IMPORTADORES/login';
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prevenir paste de contenido no numérico
|
||||
inputCodigo.addEventListener('paste', function(e) {
|
||||
e.preventDefault();
|
||||
const paste = (e.clipboardData || window.clipboardData).getData('text');
|
||||
const numericPaste = paste.replace(/[^0-9]/g, '').substring(0, 6);
|
||||
this.value = numericPaste;
|
||||
this.dispatchEvent(new Event('input'));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('error', 'Error de conexión. Intenta nuevamente.');
|
||||
// Rehabilitar botón
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = '<i class="fas fa-check"></i> Verificar Código';
|
||||
});
|
||||
});
|
||||
|
||||
// Manejar reenvío de código
|
||||
document.getElementById('resendLink').addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const link = this;
|
||||
const originalText = link.innerHTML;
|
||||
|
||||
// Mostrar loading
|
||||
link.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Reenviando...';
|
||||
link.style.pointerEvents = 'none';
|
||||
|
||||
// Enviar solicitud AJAX
|
||||
fetch('/IMPORTADORES/login/reenviar_codigo', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert('success', data.message);
|
||||
} else {
|
||||
showAlert('error', data.message);
|
||||
}
|
||||
|
||||
// Restaurar link
|
||||
link.innerHTML = originalText;
|
||||
link.style.pointerEvents = 'auto';
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('error', 'Error al reenviar código. Intenta nuevamente.');
|
||||
|
||||
// Restaurar link
|
||||
link.innerHTML = originalText;
|
||||
link.style.pointerEvents = 'auto';
|
||||
});
|
||||
});
|
||||
|
||||
function showAlert(type, message) {
|
||||
// Remover alertas anteriores
|
||||
const existingAlerts = document.querySelectorAll('.alert-modern');
|
||||
existingAlerts.forEach(alert => alert.remove());
|
||||
|
||||
// Crear nueva alerta
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert-modern alert-${type === 'success' ? 'success' : 'danger'}`;
|
||||
alertDiv.innerHTML = `
|
||||
<i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-triangle'} me-2"></i>
|
||||
${message}
|
||||
`;
|
||||
|
||||
// Insertar antes del formulario
|
||||
const form = document.getElementById('verificationForm');
|
||||
form.parentNode.insertBefore(alertDiv, form);
|
||||
|
||||
// Auto-remover después de 5 segundos si es error
|
||||
if (type === 'error') {
|
||||
setTimeout(() => {
|
||||
alertDiv.remove();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
701
views/mve/lista.php
Normal file
701
views/mve/lista.php
Normal file
@@ -0,0 +1,701 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📄 Manifestación de Valor - Pedimentos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
table.dataTable thead th { background:#343a40; color:#fff; }
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease;}
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; }
|
||||
.card-hover { transition: all 0.3s ease; cursor: pointer; }
|
||||
.card-hover:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 1rem 2rem rgba(0, 0, 0, 0.15) !important; }
|
||||
.fade-in-up { animation: fadeInUp 0.8s ease-out; }
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.btn-animated::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); transition: left 0.5s; }
|
||||
.btn-animated:hover::before { left: 100%; }
|
||||
|
||||
/* Estilos para facturas seleccionables */
|
||||
.factura-item {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background-color: #fff;
|
||||
}
|
||||
.factura-item:hover {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 2px 8px rgba(13, 110, 253, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.factura-item.selected {
|
||||
border-color: #198754;
|
||||
background-color: #d1e7dd;
|
||||
box-shadow: 0 3px 10px rgba(25, 135, 84, 0.2);
|
||||
}
|
||||
.factura-numero {
|
||||
font-weight: bold;
|
||||
color: #0d6efd;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.factura-item.selected .factura-numero {
|
||||
color: #198754;
|
||||
}
|
||||
.factura-details {
|
||||
font-size: 0.9em;
|
||||
color: #6c757d;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.factura-item.selected .factura-details {
|
||||
color: #495057;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="animate__animated animate__fadeInDown title-glow">📄 Manifestación de Valor - Pedimentos</h4>
|
||||
</div>
|
||||
<div class="card p-3 shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<div class="table-responsive">
|
||||
<table id="tablaMvePedimentos" class="display nowrap" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Pedimento</th>
|
||||
<th>RFC Cliente</th>
|
||||
<th>Nombre Cliente</th>
|
||||
<th>Fecha Registro</th>
|
||||
<th>Estado</th>
|
||||
<th>Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Los datos se cargan via AJAX -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para facturas relacionadas -->
|
||||
<div class="modal fade" id="modalFacturasMVE" tabindex="-1" aria-labelledby="modalFacturasMVELabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title" id="modalFacturasMVELabel">Manifestación de Valor - Pedimento <span id="modalPedimento"></span></h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h6 class="mb-0">Facturas del Pedimento</h6>
|
||||
</div>
|
||||
<div class="card-body" style="max-height: 400px; overflow-y: auto;">
|
||||
<div id="facturasRelacionadasContainer">
|
||||
<!-- Aquí se cargan las facturas -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
||||
<h6 class="mb-0">Catálogo COVE - Factura <span id="facturaSeleccionada">Seleccionar factura</span></h6>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-light" id="btnArt65">Art. 65 - Incrementables</button>
|
||||
<button class="btn btn-sm btn-light" id="btnArt66">Art. 66 - Decrementables</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body" style="max-height: 500px; overflow-y: auto;">
|
||||
<!-- Campos Art. 65 - Incrementables -->
|
||||
<div id="art65Section">
|
||||
<h6 class="text-success fw-bold mb-3">Artículo 65 - Incrementables</h6>
|
||||
|
||||
<!-- Gastos realizados por cuenta del Importador -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Gastos de transporte, seguros y conexos (carga y descarga) erogados con posterioridad:</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_transporte" id="fecha_transporte">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_transporte" id="importe_transporte">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Información de descuentos especiales -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Información de descuentos especiales para aplicar en el momento de la compraventa:</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_descuentos" id="fecha_descuentos">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_descuentos" id="importe_descuentos">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gastos posteriores a la importación -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Gastos posteriores a la importación por construcción, instalación, armado, montaje, mantenimiento o asistencia técnica:</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_posteriores" id="fecha_posteriores">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_posteriores" id="importe_posteriores">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contribuciones y cuotas compensatorias -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Contribuciones y cuotas compensatorias aplicables en territorio nacional:</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_contribuciones" id="fecha_contribuciones">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_contribuciones" id="importe_contribuciones">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagos de importador al vendedor -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Pagos de importador al vendedor por dividendos u otros que no guarden relación directa con las mercancías importadas:</label>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_pagos_vendedor" id="fecha_pagos_vendedor">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_pagos_vendedor" id="importe_pagos_vendedor">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos Art. 66 - Decrementables -->
|
||||
<div id="art66Section" style="display: none;">
|
||||
<h6 class="text-primary fw-bold mb-3">Artículo 66 - Decrementables</h6>
|
||||
|
||||
<!-- Comisiones y gastos de corretaje -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Comisiones y gastos de corretaje, excepto comisiones de compra:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_comisiones" id="fecha_comisiones">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_comisiones" id="importe_comisiones">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_comisiones" id="cargo_comisiones_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_comisiones_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_comisiones" id="cargo_comisiones_no" value="No">
|
||||
<label class="form-check-label" for="cargo_comisiones_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Costos de envases -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Costo de los envases o embalajes que formen un todo con las mercancías:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_envases" id="fecha_envases">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_envases" id="importe_envases">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_envases" id="cargo_envases_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_envases_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_envases" id="cargo_envases_no" value="No">
|
||||
<label class="form-check-label" for="cargo_envases_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gastos de embalaje -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Gastos de embalaje (mano de obra y/o materiales):</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_embalaje" id="fecha_embalaje">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_embalaje" id="importe_embalaje">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_embalaje" id="cargo_embalaje_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_embalaje_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_embalaje" id="cargo_embalaje_no" value="No">
|
||||
<label class="form-check-label" for="cargo_embalaje_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gastos de transporte, seguros y conexos -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Gastos de transporte, seguros y conexos (como manejo, carga y descarga) erogados antes de los momentos señalados en el artículo 56, fracción I de la Ley:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_transporte_dec" id="fecha_transporte_dec">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_transporte_dec" id="importe_transporte_dec">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_transporte_dec" id="cargo_transporte_dec_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_transporte_dec_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_transporte_dec" id="cargo_transporte_dec_no" value="No">
|
||||
<label class="form-check-label" for="cargo_transporte_dec_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trabajos de ingeniería -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Trabajos de ingeniería, creación y perfeccionamiento, artísticos, diseños, planos y croquis realizados fuera del territorio nacional que sean necesarios para la producción de las mercancías importadas:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_ingenieria" id="fecha_ingenieria">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_ingenieria" id="importe_ingenieria">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_ingenieria" id="cargo_ingenieria_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_ingenieria_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_ingenieria" id="cargo_ingenieria_no" value="No">
|
||||
<label class="form-check-label" for="cargo_ingenieria_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Regalías y derechos de licencia -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Las regalías y derechos de licencia a pagar como condición de la venta de las mercancías a importar, que no estén incluidos en el precio pagado:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_regalias" id="fecha_regalias">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_regalias" id="importe_regalias">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_regalias" id="cargo_regalias_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_regalias_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_regalias" id="cargo_regalias_no" value="No">
|
||||
<label class="form-check-label" for="cargo_regalias_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Valor de cualquier parte del producto -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold">Valor de cualquier parte del producto de la enajenación, cesión o utilización, posteriores a la importación, que se revierta directa o indirectamente al vendedor:</label>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Fecha de erogación:</label>
|
||||
<input type="date" class="form-control" name="fecha_producto" id="fecha_producto">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Importe (MN):</label>
|
||||
<input type="number" step="0.01" class="form-control" name="importe_producto" id="importe_producto">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">¿Está a cargo del importador?:</label>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_producto" id="cargo_producto_si" value="Si">
|
||||
<label class="form-check-label" for="cargo_producto_si">Sí</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="cargo_producto" id="cargo_producto_no" value="No">
|
||||
<label class="form-check-label" for="cargo_producto_no">No</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="button" class="btn btn-primary" id="btnGuardarDatosMVE">
|
||||
<i class="fas fa-save me-1"></i>Guardar datos MVE
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="button" class="btn btn-success" id="btnConfirmarMVE">Solicitar Manifestación ahora</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var pedimentoSeleccionado = null;
|
||||
var facturaSeleccionadaId = null;
|
||||
|
||||
var tabla = $('#tablaMvePedimentos').DataTable({
|
||||
"processing": true,
|
||||
"serverSide": true,
|
||||
"searchDelay": 500,
|
||||
"deferRender": true,
|
||||
"ajax": {
|
||||
"url": "/IMPORTADORES/catalogo_pedimentos/ajax_lista",
|
||||
"type": "GET"
|
||||
},
|
||||
"columns": [
|
||||
{ "data": 0, "name": "IdPrevio" },
|
||||
{ "data": 1, "name": "Pedimento" },
|
||||
{ "data": 2, "name": "ClienteRFC" },
|
||||
{ "data": 3, "name": "ClienteNombre" },
|
||||
{ "data": 4, "name": "Timestamp" },
|
||||
{
|
||||
"data": 5,
|
||||
"name": "Status",
|
||||
"render": function(data, type, row) {
|
||||
if (data === 'Activo') {
|
||||
return '<span class="badge bg-success">Activo</span>';
|
||||
} else {
|
||||
return '<span class="badge bg-secondary">Inactivo</span>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": null,
|
||||
"orderable": false,
|
||||
"searchable": false,
|
||||
"render": function(data, type, row) {
|
||||
const id = row[0];
|
||||
const pedimento = row[1];
|
||||
return `<button class='btn btn-info btn-sm btn-animated' onclick='abrirModalFacturasMVE(${JSON.stringify(id)}, ${JSON.stringify(pedimento)})'>
|
||||
<i class='fas fa-file-invoice-dollar me-1'></i>Solicitar MVE
|
||||
</button>`;
|
||||
}
|
||||
}
|
||||
],
|
||||
"language": {
|
||||
"url": "https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json"
|
||||
},
|
||||
"responsive": true,
|
||||
"order": [[4, "desc"]],
|
||||
"pageLength": 25,
|
||||
"lengthMenu": [[10, 25, 50, 100], [10, 25, 50, 100]]
|
||||
});
|
||||
|
||||
window.abrirModalFacturasMVE = function(id, pedimento) {
|
||||
pedimentoSeleccionado = id;
|
||||
$('#modalPedimento').text(pedimento);
|
||||
$('#facturasRelacionadasContainer').html('<div class="text-center py-3"><div class="spinner-border text-primary"></div><div class="mt-2">Cargando facturas...</div></div>');
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/solicitud_importacion/ajax_facturas_por_pedimento',
|
||||
method: 'GET',
|
||||
data: { id_pedimento: id },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success && response.facturas && response.facturas.length > 0) {
|
||||
var html = '';
|
||||
response.facturas.forEach(function(f) {
|
||||
html += `<div class="factura-item" onclick="seleccionarFactura('${f.numero_factura}', ${f.id_factura})">
|
||||
<div class="factura-numero">${f.numero_factura}</div>
|
||||
<div class="factura-details">Fecha: ${f.fecha} | Monto: $${parseFloat(f.monto).toLocaleString()}</div>
|
||||
</div>`;
|
||||
});
|
||||
$('#facturasRelacionadasContainer').html(html);
|
||||
} else {
|
||||
$('#facturasRelacionadasContainer').html('<div class="alert alert-warning">No hay facturas relacionadas a este pedimento.</div>');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('#facturasRelacionadasContainer').html('<div class="alert alert-danger">Error al cargar facturas.</div>');
|
||||
}
|
||||
});
|
||||
var modal = new bootstrap.Modal(document.getElementById('modalFacturasMVE'));
|
||||
modal.show();
|
||||
};
|
||||
|
||||
window.seleccionarFactura = function(numeroFactura, idFactura) {
|
||||
if (!idFactura || idFactura === 'undefined' || idFactura === 'null') {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'ID de factura no válido.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
facturaSeleccionadaId = parseInt(idFactura);
|
||||
$('#facturaSeleccionada').text(numeroFactura);
|
||||
$('.factura-item').removeClass('selected');
|
||||
$(`.factura-item:contains(${numeroFactura})`).addClass('selected');
|
||||
$('#facturaSeleccionada').removeClass('text-muted').addClass('text-success fw-bold');
|
||||
cargarDatosMVEFactura(facturaSeleccionadaId);
|
||||
};
|
||||
|
||||
$('#btnArt65').on('click', function() {
|
||||
$('#art65Section').show();
|
||||
$('#art66Section').hide();
|
||||
$('#btnArt65').removeClass('btn-outline-light').addClass('btn-light');
|
||||
$('#btnArt66').removeClass('btn-light').addClass('btn-outline-light');
|
||||
});
|
||||
|
||||
$('#btnArt66').on('click', function() {
|
||||
$('#art65Section').hide();
|
||||
$('#art66Section').show();
|
||||
$('#btnArt65').removeClass('btn-light').addClass('btn-outline-light');
|
||||
$('#btnArt66').removeClass('btn-outline-light').addClass('btn-light');
|
||||
});
|
||||
|
||||
function cargarDatosMVEFactura(idFactura) {
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/mve/ajax_obtener_datos_factura',
|
||||
method: 'GET',
|
||||
data: { id_factura: idFactura },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success && response.datos) {
|
||||
if (response.datos.art65) {
|
||||
const art65 = response.datos.art65;
|
||||
$('#fecha_transporte').val(art65.fecha_transporte || '');
|
||||
$('#importe_transporte').val(art65.importe_transporte || '');
|
||||
$('#fecha_descuentos').val(art65.fecha_descuentos || '');
|
||||
$('#importe_descuentos').val(art65.importe_descuentos || '');
|
||||
$('#fecha_posteriores').val(art65.fecha_posteriores || '');
|
||||
$('#importe_posteriores').val(art65.importe_posteriores || '');
|
||||
$('#fecha_contribuciones').val(art65.fecha_contribuciones || '');
|
||||
$('#importe_contribuciones').val(art65.importe_contribuciones || '');
|
||||
$('#fecha_pagos_vendedor').val(art65.fecha_pagos_vendedor || '');
|
||||
$('#importe_pagos_vendedor').val(art65.importe_pagos_vendedor || '');
|
||||
}
|
||||
|
||||
if (response.datos.art66) {
|
||||
const art66 = response.datos.art66;
|
||||
$('#fecha_comisiones').val(art66.fecha_comisiones || '');
|
||||
$('#importe_comisiones').val(art66.importe_comisiones || '');
|
||||
$('input[name="cargo_comisiones"][value="' + (art66.cargo_comisiones || '') + '"]').prop('checked', true);
|
||||
$('#fecha_envases').val(art66.fecha_envases || '');
|
||||
$('#importe_envases').val(art66.importe_envases || '');
|
||||
$('input[name="cargo_envases"][value="' + (art66.cargo_envases || '') + '"]').prop('checked', true);
|
||||
$('#fecha_embalaje').val(art66.fecha_embalaje || '');
|
||||
$('#importe_embalaje').val(art66.importe_embalaje || '');
|
||||
$('input[name="cargo_embalaje"][value="' + (art66.cargo_embalaje || '') + '"]').prop('checked', true);
|
||||
$('#fecha_transporte_dec').val(art66.fecha_transporte_dec || '');
|
||||
$('#importe_transporte_dec').val(art66.importe_transporte_dec || '');
|
||||
$('input[name="cargo_transporte_dec"][value="' + (art66.cargo_transporte_dec || '') + '"]').prop('checked', true);
|
||||
$('#fecha_ingenieria').val(art66.fecha_ingenieria || '');
|
||||
$('#importe_ingenieria').val(art66.importe_ingenieria || '');
|
||||
$('input[name="cargo_ingenieria"][value="' + (art66.cargo_ingenieria || '') + '"]').prop('checked', true);
|
||||
$('#fecha_regalias').val(art66.fecha_regalias || '');
|
||||
$('#importe_regalias').val(art66.importe_regalias || '');
|
||||
$('input[name="cargo_regalias"][value="' + (art66.cargo_regalias || '') + '"]').prop('checked', true);
|
||||
$('#fecha_producto').val(art66.fecha_producto || '');
|
||||
$('#importe_producto').val(art66.importe_producto || '');
|
||||
$('input[name="cargo_producto"][value="' + (art66.cargo_producto || '') + '"]').prop('checked', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#btnGuardarDatosMVE').on('click', function() {
|
||||
if (!facturaSeleccionadaId) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Seleccionar factura',
|
||||
text: 'Por favor selecciona una factura primero.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pedimentoSeleccionado) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Error',
|
||||
text: 'No se ha seleccionado un pedimento válido.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const datosArt65 = {
|
||||
fecha_transporte: $('#fecha_transporte').val(),
|
||||
importe_transporte: $('#importe_transporte').val(),
|
||||
fecha_descuentos: $('#fecha_descuentos').val(),
|
||||
importe_descuentos: $('#importe_descuentos').val(),
|
||||
fecha_posteriores: $('#fecha_posteriores').val(),
|
||||
importe_posteriores: $('#importe_posteriores').val(),
|
||||
fecha_contribuciones: $('#fecha_contribuciones').val(),
|
||||
importe_contribuciones: $('#importe_contribuciones').val(),
|
||||
fecha_pagos_vendedor: $('#fecha_pagos_vendedor').val(),
|
||||
importe_pagos_vendedor: $('#importe_pagos_vendedor').val()
|
||||
};
|
||||
|
||||
const datosArt66 = {
|
||||
fecha_comisiones: $('#fecha_comisiones').val(),
|
||||
importe_comisiones: $('#importe_comisiones').val(),
|
||||
cargo_comisiones: $('input[name="cargo_comisiones"]:checked').val(),
|
||||
fecha_envases: $('#fecha_envases').val(),
|
||||
importe_envases: $('#importe_envases').val(),
|
||||
cargo_envases: $('input[name="cargo_envases"]:checked').val(),
|
||||
fecha_embalaje: $('#fecha_embalaje').val(),
|
||||
importe_embalaje: $('#importe_embalaje').val(),
|
||||
cargo_embalaje: $('input[name="cargo_embalaje"]:checked').val(),
|
||||
fecha_transporte_dec: $('#fecha_transporte_dec').val(),
|
||||
importe_transporte_dec: $('#importe_transporte_dec').val(),
|
||||
cargo_transporte_dec: $('input[name="cargo_transporte_dec"]:checked').val(),
|
||||
fecha_ingenieria: $('#fecha_ingenieria').val(),
|
||||
importe_ingenieria: $('#importe_ingenieria').val(),
|
||||
cargo_ingenieria: $('input[name="cargo_ingenieria"]:checked').val(),
|
||||
fecha_regalias: $('#fecha_regalias').val(),
|
||||
importe_regalias: $('#importe_regalias').val(),
|
||||
cargo_regalias: $('input[name="cargo_regalias"]:checked').val(),
|
||||
fecha_producto: $('#fecha_producto').val(),
|
||||
importe_producto: $('#importe_producto').val(),
|
||||
cargo_producto: $('input[name="cargo_producto"]:checked').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/mve/ajax_guardar_datos_factura',
|
||||
method: 'POST',
|
||||
data: {
|
||||
id_factura: facturaSeleccionadaId,
|
||||
id_pedimento: pedimentoSeleccionado,
|
||||
datos_art65: JSON.stringify(datosArt65),
|
||||
datos_art66: JSON.stringify(datosArt66)
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Datos guardados',
|
||||
text: 'Los datos de la Manifestación de Valor han sido guardados correctamente.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: response.message || 'Error al guardar los datos.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Error de conexión al guardar los datos.',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btnConfirmarMVE').on('click', function() {
|
||||
$('#modalFacturasMVE').modal('hide');
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Solicitud enviada',
|
||||
text: 'Se ha solicitado la Manifestación de Valor para el pedimento ' + $('#modalPedimento').text(),
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -86,6 +86,71 @@
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- CATÁLOGO DE PEDIMENTOS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuCatalogoPedimentosDesktop" role="button" aria-expanded="false" aria-controls="submenuCatalogoPedimentosDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Catálogo de Pedimentos</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuCatalogoPedimentosDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="nav-link">
|
||||
Ver catálogo
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/buscar" class="nav-link">
|
||||
Buscar pedimentos
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- CLAVES DE PEDIMENTOS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuClavesPedimentosDesktop" role="button" aria-expanded="false" aria-controls="submenuClavesPedimentosDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9zM13.73 21a2 2 0 01-3.46 0" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Claves de Pedimentos</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuClavesPedimentosDesktop">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/lista" class="nav-link">
|
||||
Mis claves
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/crear" class="nav-link">
|
||||
Nueva clave
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/importar_csv" class="nav-link">
|
||||
Importar CSV
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/claves_pedimentos/inicializar_claves_usuario" class="nav-link">
|
||||
Inicializar claves
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/importadores/lista" class="nav-link">
|
||||
@@ -269,13 +334,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- SOLICITUDES DE IMPORTACIÓN -->
|
||||
<!-- FACTURAS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuSolicitudesDesktop" role="button" aria-expanded="false" aria-controls="submenuSolicitudesDesktop">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Solicitudes Importación</span>
|
||||
<span>Facturas</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
@@ -284,17 +349,28 @@
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/solicitud_importacion/lista" class="nav-link">
|
||||
Mis solicitudes
|
||||
Mis facturas
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/solicitud_importacion/crear" class="nav-link">
|
||||
Nueva solicitud
|
||||
Nueva factura
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- MVE (Manifestación de Valor Electrónica) -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/mve" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h8.586A2 2 0 0014 17.414L17.414 14A2 2 0 0018 12.586V4a2 2 0 00-2-2H4zm8 14H4V4h12v8h-2a2 2 0 00-2 2v2z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Manifestación Valor (MVE)</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- EXPEDIENTE ELECTRÓNICO -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuExpedienteDesktop" role="button" aria-expanded="false" aria-controls="submenuExpedienteDesktop">
|
||||
@@ -317,6 +393,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- CONFIGURACIÓN -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/configuracion" class="nav-link">
|
||||
@@ -350,6 +427,34 @@
|
||||
<span>Inicio</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- CATÁLOGO DE PEDIMENTOS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuCatalogoPedimentosMobile" role="button" aria-expanded="false" aria-controls="submenuCatalogoPedimentosMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Catálogo de Pedimentos</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
</a>
|
||||
<div class="collapse" id="submenuCatalogoPedimentosMobile">
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/lista" class="nav-link">
|
||||
Ver catálogo
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/catalogo_pedimentos/buscar" class="nav-link">
|
||||
Buscar pedimentos
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- LOCACIONES -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/importadores/lista" class="nav-link">
|
||||
@@ -533,13 +638,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<!-- SOLICITUDES DE IMPORTACIÓN -->
|
||||
<!-- FACTURAS -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuSolicitudesMobile" role="button" aria-expanded="false" aria-controls="submenuSolicitudesMobile">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Solicitudes Importación</span>
|
||||
<span>Facturas</span>
|
||||
<svg class="collapse-icon" fill="none" stroke="currentColor" viewBox="0 0 10 6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/>
|
||||
</svg>
|
||||
@@ -548,17 +653,28 @@
|
||||
<ul class="nav flex-column submenu">
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/solicitud_importacion/lista" class="nav-link">
|
||||
Mis solicitudes
|
||||
Mis facturas
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/solicitud_importacion/crear" class="nav-link">
|
||||
Nueva solicitud
|
||||
Nueva factura
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- MVE (Manifestación de Valor Electrónica) -->
|
||||
<li class="nav-item">
|
||||
<a href="/IMPORTADORES/mve" class="nav-link">
|
||||
<svg class="icon" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h8.586A2 2 0 0014 17.414L17.414 14A2 2 0 0018 12.586V4a2 2 0 00-2-2H4zm8 14H4V4h12v8h-2a2 2 0 00-2 2v2z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span>Manifestación Valor (MVE)</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- EXPEDIENTE ELECTRÓNICO -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="collapse" href="#submenuExpedienteMobile" role="button" aria-expanded="false" aria-controls="submenuExpedienteMobile">
|
||||
|
||||
@@ -8,20 +8,21 @@ $sql = "SELECT TOP 1 * FROM configuracion_sistema";
|
||||
$stmt = sqlsrv_query($conn, $sql);
|
||||
$config = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
// Variables con valores por defecto
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema Integral para Importadores de Hidrocarburos';
|
||||
$siglas = $config['siglas'] ?? 'SIIH';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_siih.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')[1];
|
||||
// Variables con valores por defecto - Branding moderno
|
||||
$nombre = $config['nombre_plataforma'] ?? 'Sistema de Gestión de Importaciones';
|
||||
$siglas = $config['siglas'] ?? 'AduanaSoft';
|
||||
$logo = $config['logo_url'] ?? 'assets/img/logo_aduanasoft.png';
|
||||
$color1 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[0];
|
||||
$color2 = explode(',', $config['colores_primarios'] ?? '#0f172a,#334155')[1];
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><?= htmlspecialchars($siglas) ?> | Registro de Importador</title>
|
||||
<title><?= htmlspecialchars($siglas) ?> | Registro</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Registro de importadores y agencias aduanales en la plataforma integral de gestión">
|
||||
<!-- Bootstrap 5 -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@@ -29,36 +30,289 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<!-- Google reCAPTCHA v2 -->
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { background-color: #f4f6f9; font-family: 'Segoe UI', sans-serif; }
|
||||
.navbar { background-color: #fff; }
|
||||
.navbar .nav-link,
|
||||
.navbar-brand { color: <?= $color1 ?> !important; font-weight: 500; }
|
||||
.navbar .nav-link.active { background-color: #F9F9F9; border-radius: 5px; }
|
||||
.card-form { max-width: 900px; margin: 50px auto; padding: 40px; background: #fff; border-radius: 10px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); }
|
||||
.form-label { font-weight: 500; }
|
||||
.form-control,
|
||||
.form-select { border-radius: 6px; }
|
||||
footer { background-color: #e9ecef; padding: 20px; text-align: center; font-size: 14px; color: #666; }
|
||||
.recaptcha { margin-top: 20px; }
|
||||
.logo-siih { height: 90px; margin-right: 10px; }
|
||||
.form-toggle { margin-bottom: 2rem; text-align: center; }
|
||||
.form-toggle .btn { margin: 0 0.5rem; min-width: 150px; }
|
||||
.form-container { display: none; }
|
||||
.form-container.active { display: block; }
|
||||
.form-title { text-align: center; margin-bottom: 2rem; color: #333; }
|
||||
.transition-fade { transition: opacity 0.3s ease-in-out; }
|
||||
.alert { bottom: 30px; }
|
||||
:root {
|
||||
--primary: <?= $color1 ?>;
|
||||
--secondary: <?= $color2 ?>;
|
||||
--accent: #10b981;
|
||||
--surface: #ffffff;
|
||||
--background: #f8fafc;
|
||||
--text: #1e293b;
|
||||
--text-muted: #64748b;
|
||||
--border: #e2e8f0;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--background) 0%, #e2e8f0 100%);
|
||||
font-family: 'Poppins', sans-serif;
|
||||
color: var(--text);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem !important;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: var(--primary) !important;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.registration-container {
|
||||
max-width: 1000px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.card-form {
|
||||
background: var(--surface);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
|
||||
padding: 3rem;
|
||||
border: 1px solid var(--border);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-form::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--primary));
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--text-muted);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.form-toggle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 3rem;
|
||||
background: var(--background);
|
||||
border-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
padding: 1rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-btn.active {
|
||||
background: var(--surface);
|
||||
color: var(--primary);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: none;
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.form-container.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form-section-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 50px;
|
||||
height: 3px;
|
||||
background: var(--accent);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control, .form-select {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s ease;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
border: none;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, var(--border), transparent);
|
||||
margin: 2.5rem 0;
|
||||
}
|
||||
|
||||
.section-subtitle {
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.recaptcha {
|
||||
margin: 2rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-modern {
|
||||
padding: 12px 28px;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary-modern {
|
||||
background: linear-gradient(135deg, var(--accent), #059669);
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary-modern:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(16, 185, 129, 0.4);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.info-alert {
|
||||
background: linear-gradient(135deg, #fef3c7, #fde68a);
|
||||
border: 1px solid #f59e0b;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.info-alert .alert-icon {
|
||||
color: #f59e0b;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--text);
|
||||
color: #94a3b8;
|
||||
padding: 2rem 0;
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-form {
|
||||
margin: 1rem;
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.form-toggle {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Navbar institucional -->
|
||||
<!-- NAVBAR -->
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center text-dark" href="#">
|
||||
<img src="/IMPORTADORES/public/<?= htmlspecialchars($logo) ?>" alt="Logo" class="logo-siih">
|
||||
| Registro de Importador
|
||||
<a class="navbar-brand" href="/IMPORTADORES/">
|
||||
<i class="fas fa-shipping-fast me-2"></i><?= htmlspecialchars($siglas) ?>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
@@ -72,166 +326,242 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
|
||||
<a class="nav-link active" aria-current="page" href="/IMPORTADORES/registro">Registro</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Inicio sesión</a>
|
||||
<a class="nav-link" href="/IMPORTADORES/login">Iniciar Sesión</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Contenedor de formularios -->
|
||||
<div class="container">
|
||||
<!-- CONTENEDOR PRINCIPAL -->
|
||||
<div class="registration-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Únete a Nuestra Plataforma</h1>
|
||||
<p class="page-subtitle">Registra tu empresa o agencia aduanal para comenzar a gestionar importaciones</p>
|
||||
</div>
|
||||
|
||||
<div class="card-form">
|
||||
<!-- Botón para alternar entre formularios -->
|
||||
<!-- SELECTOR DE TIPO DE REGISTRO -->
|
||||
<div class="form-toggle">
|
||||
<button type="button" class="btn btn-white" id="btnToggle" onclick="alternarFormulario()">🔄 Registro de Agencia Aduanal</button>
|
||||
<button type="button" class="toggle-btn active" id="btnImportador" onclick="mostrarFormulario('importador')">
|
||||
<i class="fas fa-building me-2"></i>Importador
|
||||
</button>
|
||||
<button type="button" class="toggle-btn" id="btnAgencia" onclick="mostrarFormulario('agencia')">
|
||||
<i class="fas fa-handshake me-2"></i>Agencia Aduanal
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Formulario de Importador -->
|
||||
<div id="formImportadorContainer" class="form-container active transition-fade">
|
||||
<h3 class="form-title">Registro de Importador</h3>
|
||||
<!-- FORMULARIO DE IMPORTADOR -->
|
||||
<div id="formImportadorContainer" class="form-container active">
|
||||
<h3 class="form-section-title">Registro de Importador</h3>
|
||||
<form action="/IMPORTADORES/registro/enviarSoliImportador" method="POST" enctype="multipart/form-data" id="formRegistro">
|
||||
<div class="row g-3">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<label for="company_name" class="form-label">Nombre de la empresa *</label>
|
||||
<input type="text" class="form-control" name="company_name" id="company_name" required>
|
||||
<label for="company_name" class="form-label">
|
||||
<i class="fas fa-building text-muted me-2"></i>Nombre de la empresa *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="company_name" id="company_name" placeholder="Ingresa el nombre de tu empresa" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="rfc" class="form-label">RFC *</label>
|
||||
<input type="text" class="form-control" name="rfc" maxlength="13" id="rfc" required>
|
||||
<label for="rfc" class="form-label">
|
||||
<i class="fas fa-id-card text-muted me-2"></i>RFC *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="rfc" maxlength="13" id="rfc" placeholder="RFC de la empresa" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="email" class="form-label">Correo electrónico *</label>
|
||||
<input type="email" class="form-control" name="email" id="email" required>
|
||||
<label for="email" class="form-label">
|
||||
<i class="fas fa-envelope text-muted me-2"></i>Correo electrónico *
|
||||
</label>
|
||||
<input type="email" class="form-control" name="email" id="email" placeholder="correo@empresa.com" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="phone" class="form-label">Teléfono *</label>
|
||||
<input type="text" class="form-control" name="phone" id="phone" required>
|
||||
<label for="phone" class="form-label">
|
||||
<i class="fas fa-phone text-muted me-2"></i>Teléfono *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="phone" id="phone" placeholder="123 4567890" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<label for="opinion_file_importador" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
|
||||
<div class="col-12">
|
||||
<label for="opinion_file_importador" class="form-label">
|
||||
<i class="fas fa-file-pdf text-muted me-2"></i>Padrón de Importadores o Encargo Conferido (PDF) *
|
||||
</label>
|
||||
<input type="file" class="form-control" name="opinion_file" id="opinion_file_importador" accept=".pdf" required>
|
||||
<div class="form-text">Sube tu documento oficial que acredite tu actividad como importador.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 text-center recaptcha">
|
||||
<div class="col-12 recaptcha">
|
||||
<div class="g-recaptcha" id="recaptcha-importador" data-sitekey="6LfkMB8rAAAAAH-BEC1cMYij1HEhw4sTaEOl-UCA"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 text-end mt-4">
|
||||
<button type="submit" class="btn btn-primary px-4 py-2">Enviar solicitud</button>
|
||||
<div class="col-12 text-center">
|
||||
<button type="submit" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-paper-plane me-2"></i>
|
||||
Enviar Solicitud
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Formulario de Agencia Aduanal -->
|
||||
<div id="formAgenciaContainer" class="form-container transition-fade">
|
||||
<h3 class="form-title">Registro de Agencia Aduanal</h3>
|
||||
<!-- FORMULARIO DE AGENCIA ADUANAL -->
|
||||
<div id="formAgenciaContainer" class="form-container">
|
||||
<h3 class="form-section-title">Registro de Agencia Aduanal</h3>
|
||||
<form action="/IMPORTADORES/registro/enviarSoliAgencia" method="POST" enctype="multipart/form-data" id="formRegistroAgencia">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre_agencia" class="form-label">Nombre de la agencia *</label>
|
||||
<input type="text" class="form-control" name="nombre_agencia" id="nombre_agencia" required>
|
||||
<div class="row g-4">
|
||||
<!-- Información de la Agencia -->
|
||||
<div class="col-12">
|
||||
<div class="section-subtitle">
|
||||
<i class="fas fa-handshake text-primary"></i>
|
||||
Información de la Agencia
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="rfc_agencia" class="form-label">RFC *</label>
|
||||
<input type="text" class="form-control" name="rfc_agencia" maxlength="13" id="rfc_agencia" required>
|
||||
<label for="nombre_agencia" class="form-label">
|
||||
<i class="fas fa-building text-muted me-2"></i>Nombre de la agencia *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="nombre_agencia" id="nombre_agencia" placeholder="Nombre de la agencia aduanal" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="correo" class="form-label">Correo electrónico *</label>
|
||||
<input type="email" class="form-control" name="correo" id="correo" required>
|
||||
<label for="rfc_agencia" class="form-label">
|
||||
<i class="fas fa-id-card text-muted me-2"></i>RFC *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="rfc_agencia" maxlength="13" id="rfc_agencia" placeholder="RFC de la agencia" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="telefono" class="form-label">Teléfono *</label>
|
||||
<input type="text" class="form-control" name="telefono" maxlength="11" id="telefono" required>
|
||||
<label for="correo" class="form-label">
|
||||
<i class="fas fa-envelope text-muted me-2"></i>Correo electrónico *
|
||||
</label>
|
||||
<input type="email" class="form-control" name="correo" id="correo" placeholder="contacto@agencia.com" required>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label for="direccion" class="form-label">Dirección *</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="telefono" class="form-label">
|
||||
<i class="fas fa-phone text-muted me-2"></i>Teléfono *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="telefono" maxlength="11" id="telefono" placeholder="123 4567890" required>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label for="direccion" class="form-label">
|
||||
<i class="fas fa-map-marker-alt text-muted me-2"></i>Dirección completa *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="direccion" id="direccion"
|
||||
placeholder="Ej: Calle, num.externo, num.interno, colonia, municipio, código postal, estado" required>
|
||||
placeholder="Calle, num.externo, num.interno, colonia, municipio, código postal, estado" required>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label for="opinion_file_agencia" class="form-label">Padrón de importadores o encargo conferido (PDF) *</label>
|
||||
|
||||
<div class="col-12">
|
||||
<label for="opinion_file_agencia" class="form-label">
|
||||
<i class="fas fa-file-pdf text-muted me-2"></i>Padrón de Importadores o Encargo Conferido (PDF) *
|
||||
</label>
|
||||
<input type="file" class="form-control" name="opinion_file" id="opinion_file_agencia" accept=".pdf" required>
|
||||
</div>
|
||||
<hr>
|
||||
<p><strong>Administrador de la Agencia</strong></p>
|
||||
<div class="col-md-6">
|
||||
<label for="nombre_admin" class="form-label">Nombre *</label>
|
||||
<input type="text" class="form-control" name="nombre_admin" id="nombre_admin" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="correo_admin" class="form-label">Correo Administrador *</label>
|
||||
<input type="email" class="form-control" name="correo_admin" id="correo_admin" required>
|
||||
|
||||
<hr class="section-divider">
|
||||
|
||||
<!-- Información del Administrador -->
|
||||
<div class="col-12">
|
||||
<div class="section-subtitle">
|
||||
<i class="fas fa-user-tie text-primary"></i>
|
||||
Administrador de la Agencia
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 text-center recaptcha">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre_admin" class="form-label">
|
||||
<i class="fas fa-user text-muted me-2"></i>Nombre completo *
|
||||
</label>
|
||||
<input type="text" class="form-control" name="nombre_admin" id="nombre_admin" placeholder="Nombre del administrador" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="correo_admin" class="form-label">
|
||||
<i class="fas fa-envelope text-muted me-2"></i>Correo del administrador *
|
||||
</label>
|
||||
<input type="email" class="form-control" name="correo_admin" id="correo_admin" placeholder="admin@agencia.com" required>
|
||||
</div>
|
||||
|
||||
<div class="col-12 recaptcha">
|
||||
<div class="g-recaptcha" id="recaptcha-agencia" data-sitekey="6LfkMB8rAAAAAH-BEC1cMYij1HEhw4sTaEOl-UCA"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 text-end mt-4">
|
||||
<button type="submit" class="btn btn-primary px-4 py-2">Enviar solicitud</button>
|
||||
<div class="col-12 text-center">
|
||||
<button type="submit" class="btn-modern btn-primary-modern">
|
||||
<i class="fas fa-paper-plane me-2"></i>
|
||||
Enviar Solicitud
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mb-4">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Una vez que envíes tu solicitud, será revisada manualmente por la agencia aduanal.
|
||||
Recibirás una notificación por correo electrónico cuando tu registro sea aprobado, junto con las instrucciones para activar tu cuenta e iniciar sesión en la plataforma.
|
||||
<!-- INFORMACIÓN ADICIONAL -->
|
||||
<div class="info-alert">
|
||||
<div class="d-flex align-items-start">
|
||||
<i class="fas fa-info-circle alert-icon"></i>
|
||||
<div>
|
||||
<strong>Proceso de Aprobación</strong><br>
|
||||
Una vez enviada tu solicitud, será revisada manualmente por nuestro equipo.
|
||||
Recibirás una notificación por correo electrónico cuando tu registro sea aprobado,
|
||||
junto con las instrucciones para activar tu cuenta e iniciar sesión en la plataforma.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="fixed-bottom text-center py-3">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> · <?= htmlspecialchars($nombre) ?>
|
||||
<footer>
|
||||
<div class="container text-center">
|
||||
<div class="footer-text">
|
||||
© <?= date('Y') ?> <?= htmlspecialchars($siglas) ?> - <?= htmlspecialchars($nombre) ?><br>
|
||||
Optimizando el comercio internacional
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Validación JS -->
|
||||
<!-- SCRIPTS -->
|
||||
<script>
|
||||
let formularioActual = 'importador';
|
||||
|
||||
function alternarFormulario() {
|
||||
function mostrarFormulario(tipo) {
|
||||
const formImportador = document.getElementById('formImportadorContainer');
|
||||
const formAgencia = document.getElementById('formAgenciaContainer');
|
||||
const btnToggle = document.getElementById('btnToggle');
|
||||
const btnImportador = document.getElementById('btnImportador');
|
||||
const btnAgencia = document.getElementById('btnAgencia');
|
||||
|
||||
if (formularioActual === 'importador') {
|
||||
// Cambiar a formulario de agencia
|
||||
formImportador.classList.remove('active');
|
||||
formAgencia.classList.add('active');
|
||||
btnToggle.textContent = '🔄 Registro de Importador';
|
||||
formularioActual = 'agencia';
|
||||
} else {
|
||||
// Cambiar a formulario de importador
|
||||
formAgencia.classList.remove('active');
|
||||
// Resetear clases
|
||||
formImportador.classList.remove('active');
|
||||
formAgencia.classList.remove('active');
|
||||
btnImportador.classList.remove('active');
|
||||
btnAgencia.classList.remove('active');
|
||||
|
||||
if (tipo === 'importador') {
|
||||
formImportador.classList.add('active');
|
||||
btnToggle.textContent = '🔄 Registro de Agencia Aduanal';
|
||||
formularioActual = 'importador';
|
||||
btnImportador.classList.add('active');
|
||||
formularioActual = 'importador';
|
||||
} else {
|
||||
formAgencia.classList.add('active');
|
||||
btnAgencia.classList.add('active');
|
||||
formularioActual = 'agencia';
|
||||
}
|
||||
|
||||
// Resetear reCAPTCHA si está disponible
|
||||
// Resetear reCAPTCHA
|
||||
if (typeof grecaptcha !== 'undefined') {
|
||||
try{
|
||||
grecaptcha.reset(0); // Reset primer reCAPTCHA
|
||||
grecaptcha.reset(1); // Reset primer reCAPTCHA
|
||||
grecaptcha.reset(0);
|
||||
grecaptcha.reset(1);
|
||||
} catch (e) {
|
||||
console.log('Error resetting reCAPTCHA:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Función para obtener la respuesta del reCAPTCHA activo
|
||||
function getActiveRecaptchaResponse() {
|
||||
if (typeof grecaptcha !== 'undefined') {
|
||||
try {
|
||||
@@ -248,14 +578,7 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
|
||||
return '';
|
||||
}
|
||||
|
||||
// Inicializar al cargar la página
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Por defecto mostrar el formulario de importador
|
||||
const formImportador = document.getElementById('formImportadorContainer');
|
||||
formImportador.classList.add('active');
|
||||
});
|
||||
|
||||
// Registro de Importador
|
||||
// Validación para formulario de importador
|
||||
document.getElementById('formRegistro').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -271,65 +594,75 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
|
||||
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
|
||||
const phoneRegex = /^\d{3}\s\d{7}$/;
|
||||
|
||||
// Validación de nombre
|
||||
// Validaciones
|
||||
if (!nombre) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo requerido',
|
||||
text: 'El nombre de la empresa es obligatorio.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
document.getElementById('company_name').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de correo
|
||||
if (!email) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
|
||||
if (!email || !emailRegex.test(email)) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Correo inválido',
|
||||
text: 'Ingresa una dirección de correo válida.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
document.getElementById('email').focus();
|
||||
return;
|
||||
}
|
||||
if (!emailRegex.test(email)) {
|
||||
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('email').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de RFC
|
||||
if (!rfc) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
|
||||
if (!rfc || !rfcRegex.test(rfc)) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'RFC inválido',
|
||||
text: 'El RFC debe tener el formato correcto (ej. ABC123456XYZ).',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
document.getElementById('rfc').focus();
|
||||
return;
|
||||
}
|
||||
if (!rfcRegex.test(rfc)) {
|
||||
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('rfc').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de teléfono
|
||||
if (!phone) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
|
||||
if (!phone || !phoneRegex.test(phone)) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Teléfono inválido',
|
||||
text: 'Formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
document.getElementById('phone').focus();
|
||||
return;
|
||||
}
|
||||
if (!phoneRegex.test(phone)) {
|
||||
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos. Ejemplo: 123 4567890', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('phone').focus();
|
||||
|
||||
if (!archivo || archivo.type !== 'application/pdf') {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo requerido',
|
||||
text: 'Sube el padrón de importadores en formato PDF.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Validación de archivo PDF
|
||||
if (!archivo) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El padrón de importadores es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('opinion_file_importador').focus();
|
||||
|
||||
if (!recaptcha || recaptcha.length === 0) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Verificación requerida',
|
||||
text: 'Por favor completa la verificación reCAPTCHA.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!archivo || archivo.type !== 'application/pdf') {
|
||||
Swal.fire({ icon: 'error', title: 'Formato de documento inválido', text: 'Solo se permite subir archivos PDF.', confirmButtonColor: '#dc3545' });
|
||||
return;
|
||||
}
|
||||
// Validación de reCAPTCHA
|
||||
if (!recaptcha || recaptcha.length === 0) {
|
||||
Swal.fire({ icon: 'warning', title: 'Verificación requerida', text: 'Por favor completa el reCAPTCHA.', confirmButtonColor: '#dc3545' });
|
||||
return;
|
||||
}
|
||||
// Si todas las validaciones pasan, el formulario se envía.
|
||||
|
||||
this.submit();
|
||||
});
|
||||
|
||||
// Registro de Agencia
|
||||
// Validación para formulario de agencia
|
||||
document.getElementById('formRegistroAgencia').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -343,88 +676,71 @@ $color2 = explode(',', $config['colores_primarios'] ?? '#003366,#0055A5')
|
||||
const correo_admin = document.getElementById('correo_admin').value.trim();
|
||||
const recaptcha = getActiveRecaptchaResponse();
|
||||
|
||||
// Expresiones regulares
|
||||
const correoRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
const rfcRegex = /^[A-ZÑ&]{3,4}\d{6}[A-Z0-9]{3}$/;
|
||||
const telefonoRegex = /^\d{3}\s\d{7}$/;
|
||||
|
||||
// Validación de nombre
|
||||
if (!nombre) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('nombre_agencia').focus();
|
||||
// Validaciones básicas
|
||||
if (!nombre || !correo || !rfc_agencia || !telefono || !direccion || !nombre_admin || !correo_admin) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campos incompletos',
|
||||
text: 'Todos los campos son obligatorios.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Validación de correo
|
||||
if (!correo) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('correo').focus();
|
||||
return;
|
||||
}
|
||||
if (!correoRegex.test(correo)) {
|
||||
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('correo').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de RFC
|
||||
if (!rfc_agencia) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El RFC es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('rfc_agencia').focus();
|
||||
|
||||
if (!correoRegex.test(correo) || !correoRegex.test(correo_admin)) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Correos inválidos',
|
||||
text: 'Verifica que los correos tengan formato válido.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rfcRegex.test(rfc_agencia)) {
|
||||
Swal.fire({ icon: 'error', title: 'RFC inválido', text: 'El RFC debe tener 12 o 13 caracteres con el formato correcto (ej. ABC123456XYZ).', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('rfc_agencia').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de teléfono
|
||||
if (!telefono) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El teléfono es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('telefono').focus();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'RFC inválido',
|
||||
text: 'El RFC debe tener el formato correcto.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!telefonoRegex.test(telefono)) {
|
||||
Swal.fire({ icon: 'error', title: 'Teléfono inválido', text: 'Debe tener 10 dígitos en el formato: 3 dígitos (LADA), espacio, y 7 dígitos (ej. 123 4567890).', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('telefono').focus();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Teléfono inválido',
|
||||
text: 'Formato: 3 dígitos (LADA), espacio, y 7 dígitos.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Validación de dirección
|
||||
if (!direccion) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'La dirección es obligatoria.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('direccion').focus();
|
||||
|
||||
if (!archivo || archivo.type !== 'application/pdf') {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Archivo requerido',
|
||||
text: 'Sube el documento en formato PDF.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Validación de archivo PDF
|
||||
if (!archivo) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El padrón de importadores es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('opinion_file_agencia').focus();
|
||||
|
||||
if (!recaptcha || recaptcha.length === 0) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Verificación requerida',
|
||||
text: 'Por favor completa la verificación reCAPTCHA.',
|
||||
confirmButtonColor: '#10b981'
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!archivo || archivo.type !== 'application/pdf') {
|
||||
Swal.fire({ icon: 'error', title: 'Formato de documento inválido', text: 'Solo se permite subir archivos PDF.', confirmButtonColor: '#dc3545' });
|
||||
return; }
|
||||
// Validación de administrador
|
||||
if (!nombre_admin) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El nombre del administrador es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('nombre_admin').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de correo de administrador
|
||||
if (!correo_admin) {
|
||||
Swal.fire({ icon: 'warning', title: 'Campo requerido', text: 'El correo de administrador es obligatorio.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('correo_admin').focus();
|
||||
return;
|
||||
}
|
||||
if (!correoRegex.test(correo_admin)) {
|
||||
Swal.fire({ icon: 'error', title: 'Correo inválido', text: 'No es una dirección de correo válida.', confirmButtonColor: '#dc3545' });
|
||||
document.getElementById('correo_admin').focus();
|
||||
return;
|
||||
}
|
||||
// Validación de reCAPTCHA
|
||||
if (!recaptcha || recaptcha.length === 0) {
|
||||
Swal.fire({ icon: 'warning', title: 'Verificación requerida', text: 'Por favor completa el reCAPTCHA.', confirmButtonColor: '#dc3545' });
|
||||
return;
|
||||
}
|
||||
// Si todas las validaciones pasan, el formulario se envía.
|
||||
|
||||
this.submit();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -121,6 +121,25 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nueva card para Ventanilla Única -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100 card-hover">
|
||||
<div class="card-body text-center">
|
||||
<div class="security-icon mb-3">
|
||||
<i class="fas fa-university text-warning"></i>
|
||||
</div>
|
||||
<h5 class="card-title">Ventanilla Única</h5>
|
||||
<p class="card-text text-muted">
|
||||
Configurar ejecutable y certificados para transmisiones de Manifestación de Valor Electrónica
|
||||
</p>
|
||||
<a href="/IMPORTADORES/seguridad/ventanillaUnica" class="btn btn-outline-warning btn-animated">
|
||||
<i class="fas fa-cogs me-2"></i>Configurar VU
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
349
views/seguridad/ventanilla_unica.php
Normal file
349
views/seguridad/ventanilla_unica.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_configuracion.php'; ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🔐 Configuración Ventanilla Única</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }
|
||||
.btn-animated { transition: all 0.3s ease; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.form-floating { position: relative; }
|
||||
.form-floating > .form-control:focus ~ label { color: #0d6efd; }
|
||||
.security-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
||||
.status-indicator { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); }
|
||||
.path-input { font-family: 'Courier New', monospace; font-size: 0.9em; }
|
||||
.config-section { border-left: 4px solid #0d6efd; padding-left: 20px; margin-bottom: 30px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<!-- Header -->
|
||||
<div class="security-header text-white p-4 rounded-3 mb-4">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h4 class="mb-1"><i class="fas fa-university me-2"></i>Configuración Ventanilla Única</h4>
|
||||
<p class="mb-0 opacity-75">Configuración para transmisiones de Manifestación de Valor Electrónica</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a href="/IMPORTADORES/seguridad/index" class="btn btn-light btn-sm">
|
||||
<i class="fas fa-arrow-left me-1"></i>Regresar a Seguridad
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mensajes de éxito/error -->
|
||||
<?php if (isset($_SESSION['config_success'])): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<?= htmlspecialchars($_SESSION['config_success']) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['config_success']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['config_error'])): ?>
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<?= htmlspecialchars($_SESSION['config_error']) ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php unset($_SESSION['config_error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><i class="fas fa-cogs me-2"></i>Configuración de Ventanilla Única</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/IMPORTADORES/seguridad/guardarConfiguracionVU" method="POST" id="configForm" enctype="multipart/form-data">
|
||||
|
||||
<!-- Sección 1: Certificados FIEL -->
|
||||
<div class="config-section">
|
||||
<h6 class="text-success mb-3">
|
||||
<i class="fas fa-certificate me-2"></i>Certificados FIEL
|
||||
</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_key" class="form-label">
|
||||
<i class="fas fa-key me-1"></i>Archivo KEY *
|
||||
</label>
|
||||
<input type="file"
|
||||
class="form-control"
|
||||
id="archivo_key"
|
||||
name="archivo_key"
|
||||
accept=".key"
|
||||
<?= empty($configuracion_vu['ruta_archivo_key']) ? 'required' : '' ?>>
|
||||
<?php if (!empty($configuracion_vu['ruta_archivo_key'])): ?>
|
||||
<div class="mt-2 p-2 bg-light rounded">
|
||||
<small class="text-success">
|
||||
<i class="fas fa-check me-1"></i>
|
||||
Archivo actual: <?= basename($configuracion_vu['ruta_archivo_key']) ?>
|
||||
</small>
|
||||
<input type="hidden" name="ruta_archivo_key_actual" value="<?= htmlspecialchars($configuracion_vu['ruta_archivo_key']) ?>">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<small class="text-muted d-block mt-1">
|
||||
Solo archivos .key permitidos
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_cer" class="form-label">
|
||||
<i class="fas fa-certificate me-1"></i>Archivo CER *
|
||||
</label>
|
||||
<input type="file"
|
||||
class="form-control"
|
||||
id="archivo_cer"
|
||||
name="archivo_cer"
|
||||
accept=".cer"
|
||||
<?= empty($configuracion_vu['ruta_archivo_cer']) ? 'required' : '' ?>>
|
||||
<?php if (!empty($configuracion_vu['ruta_archivo_cer'])): ?>
|
||||
<div class="mt-2 p-2 bg-light rounded">
|
||||
<small class="text-success">
|
||||
<i class="fas fa-check me-1"></i>
|
||||
Archivo actual: <?= basename($configuracion_vu['ruta_archivo_cer']) ?>
|
||||
</small>
|
||||
<input type="hidden" name="ruta_archivo_cer_actual" value="<?= htmlspecialchars($configuracion_vu['ruta_archivo_cer']) ?>">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<small class="text-muted d-block mt-1">
|
||||
Solo archivos .cer permitidos
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="form-floating mb-3">
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="clave_fiel"
|
||||
name="clave_fiel"
|
||||
value="<?= htmlspecialchars($configuracion_vu['clave_fiel']) ?>"
|
||||
placeholder="Contraseña FIEL"
|
||||
required>
|
||||
<label for="clave_fiel">
|
||||
<i class="fas fa-lock me-1"></i>Clave de Acceso FIEL *
|
||||
</label>
|
||||
<button type="button"
|
||||
class="btn btn-outline-secondary position-absolute end-0 top-50 translate-middle-y me-3"
|
||||
style="z-index: 10;"
|
||||
onclick="togglePassword('clave_fiel')">
|
||||
<i class="fas fa-eye" id="clave_fiel_icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-shield-alt me-1"></i>
|
||||
Esta contraseña se almacena de forma encriptada
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sección 2: Acceso Ventanilla Única -->
|
||||
<div class="config-section">
|
||||
<h6 class="text-warning mb-3">
|
||||
<i class="fas fa-user-cog me-2"></i>Acceso a Ventanilla Única
|
||||
</h6>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating mb-3">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="rfc_usuario_vu"
|
||||
name="rfc_usuario_vu"
|
||||
value="<?= htmlspecialchars($configuracion_vu['rfc_usuario_vu']) ?>"
|
||||
placeholder="RFC12345678901"
|
||||
maxlength="13"
|
||||
style="text-transform: uppercase;"
|
||||
required>
|
||||
<label for="rfc_usuario_vu">
|
||||
<i class="fas fa-id-card me-1"></i>RFC Usuario VU *
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-floating mb-3">
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="clave_webservice"
|
||||
name="clave_webservice"
|
||||
value="<?= htmlspecialchars($configuracion_vu['clave_webservice']) ?>"
|
||||
placeholder="Contraseña Web Service">
|
||||
<label for="clave_webservice">
|
||||
<i class="fas fa-globe me-1"></i>Clave de Acceso Web Service (Opcional)
|
||||
</label>
|
||||
<button type="button"
|
||||
class="btn btn-outline-secondary position-absolute end-0 top-50 translate-middle-y me-3"
|
||||
style="z-index: 10;"
|
||||
onclick="togglePassword('clave_webservice')">
|
||||
<i class="fas fa-eye" id="clave_webservice_icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Las contraseñas se almacenan de forma encriptada por seguridad
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Información adicional -->
|
||||
<?php if ($configuracion_vu['fecha_creacion'] || $configuracion_vu['fecha_actualizacion']): ?>
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<strong>Información de configuración:</strong><br>
|
||||
<?php if ($configuracion_vu['fecha_creacion']): ?>
|
||||
Creada: <?= $configuracion_vu['fecha_creacion']->format('d/m/Y H:i') ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if ($configuracion_vu['fecha_actualizacion']): ?>
|
||||
Última actualización: <?= $configuracion_vu['fecha_actualizacion']->format('d/m/Y H:i') ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Botones de acción -->
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<button type="button"
|
||||
class="btn btn-outline-info btn-animated"
|
||||
onclick="probarConfiguracion()">
|
||||
<i class="fas fa-vial me-2"></i>Probar Configuración
|
||||
</button>
|
||||
<div>
|
||||
<button type="button"
|
||||
class="btn btn-outline-secondary me-2"
|
||||
onclick="limpiarFormulario()">
|
||||
<i class="fas fa-eraser me-1"></i>Limpiar
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="btn btn-primary btn-animated">
|
||||
<i class="fas fa-save me-2"></i>Guardar Configuración
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Función para mostrar/ocultar contraseñas
|
||||
function togglePassword(fieldId) {
|
||||
const field = document.getElementById(fieldId);
|
||||
const icon = document.getElementById(fieldId + '_icon');
|
||||
|
||||
if (field.type === 'password') {
|
||||
field.type = 'text';
|
||||
icon.classList.remove('fa-eye');
|
||||
icon.classList.add('fa-eye-slash');
|
||||
} else {
|
||||
field.type = 'password';
|
||||
icon.classList.remove('fa-eye-slash');
|
||||
icon.classList.add('fa-eye');
|
||||
}
|
||||
}
|
||||
|
||||
// Función para limpiar el formulario
|
||||
function limpiarFormulario() {
|
||||
if (confirm('¿Estás seguro de que deseas limpiar todos los campos?')) {
|
||||
document.getElementById('configForm').reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Función para probar la configuración
|
||||
function probarConfiguracion() {
|
||||
const btn = event.target;
|
||||
const originalHtml = btn.innerHTML;
|
||||
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Probando...';
|
||||
btn.disabled = true;
|
||||
|
||||
fetch('/IMPORTADORES/seguridad/probarConexionVU', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Configuración Válida',
|
||||
text: data.message,
|
||||
confirmButtonColor: '#28a745'
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error en Configuración',
|
||||
text: data.message,
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de Conexión',
|
||||
text: 'No se pudo probar la configuración. Verifica tu conexión.',
|
||||
confirmButtonColor: '#dc3545'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
btn.innerHTML = originalHtml;
|
||||
btn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
// Validación en tiempo real de rutas de archivos
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pathInputs = document.querySelectorAll('.path-input');
|
||||
|
||||
pathInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const statusId = this.id.replace('ruta_', '') + '-status';
|
||||
const statusElement = document.getElementById(statusId);
|
||||
|
||||
if (this.value.trim()) {
|
||||
// Aquí podrías agregar validación adicional
|
||||
statusElement.innerHTML = '<i class="fas fa-check text-success"></i>';
|
||||
} else {
|
||||
statusElement.innerHTML = '';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Convertir RFC a mayúsculas automáticamente
|
||||
document.getElementById('rfc_usuario_vu').addEventListener('input', function() {
|
||||
this.value = this.value.toUpperCase();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -96,7 +96,7 @@
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✏️ Editar Solicitud #<?= (int)$factura['id_solicitud'] ?></h4>
|
||||
<h4 class="mb-4 animate__animated animate__fadeInDown title_glow">✏️ Editar factura #<?= (int)$factura['id_solicitud'] ?></h4>
|
||||
<div class="card p-4 bg-white shadow-sm card-hover position-relative h-auto fade-in-up">
|
||||
<form id="solicitudForm" action="/IMPORTADORES/solicitud_importacion/actualizar" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="id_solicitud" value="<?= (int)$factura['id_solicitud'] ?>">
|
||||
|
||||
790
views/templates_rapidos/crear.php
Normal file
790
views/templates_rapidos/crear.php
Normal file
@@ -0,0 +1,790 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>➕ Nuevo Template Rápido</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
border: none;
|
||||
}
|
||||
.btn-animated {
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.btn-animated:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
.emoji-picker {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
gap: 8px;
|
||||
padding: 15px;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
.emoji-item {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 20px;
|
||||
border: 2px solid transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 40px;
|
||||
}
|
||||
.emoji-item:hover {
|
||||
background-color: #e3f2fd;
|
||||
border-color: #2196f3;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.emoji-item.selected {
|
||||
background-color: #1976d2;
|
||||
color: white;
|
||||
border-color: #0d47a1;
|
||||
}
|
||||
.preview-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-top: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
.template-preview-item {
|
||||
background: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
color: white;
|
||||
padding: 8px 14px;
|
||||
border-radius: 25px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
margin: 3px;
|
||||
display: inline-block;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.section-header {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
padding: 15px 20px;
|
||||
margin: -15px -15px 20px -15px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
.section-header h6 {
|
||||
margin: 0;
|
||||
color: #495057;
|
||||
font-weight: 600;
|
||||
}
|
||||
.icon-preview {
|
||||
font-size: 24px;
|
||||
margin-right: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
.form-label {
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.choices__inner {
|
||||
min-height: 42px !important;
|
||||
border: 1px solid #ced4da !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
.choices__inner:focus {
|
||||
border-color: #0d6efd !important;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25) !important;
|
||||
}
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255,255,255,.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #fff;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.tips-section {
|
||||
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.tip-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
color: #1565c0;
|
||||
}
|
||||
.tip-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.tip-item i {
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0">➕ Nuevo Template Rápido</h4>
|
||||
<a href="/IMPORTADORES/templates_rapidos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-arrow-left me-1"></i>Volver a Lista
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Formulario Principal -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-body p-4">
|
||||
<form id="templateForm" action="/IMPORTADORES/templates_rapidos/guardar" method="POST">
|
||||
|
||||
<!-- Información Básica -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-info-circle me-2"></i>Información Básica</h6>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre" class="form-label">Nombre del Template *</label>
|
||||
<input id="nombre" name="nombre" type="text" class="form-control" required
|
||||
placeholder="ej. Importación China" maxlength="100">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="icono" class="form-label">Icono</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="icon-display" style="font-size: 20px;">🏢</span>
|
||||
<input id="icono" name="icono" type="text" class="form-control" value="🏢" readonly>
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fas fa-palette"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end p-0" style="width: 320px;">
|
||||
<div class="p-2">
|
||||
<h6 class="dropdown-header">🌍 Países</h6>
|
||||
<div class="emoji-picker" style="max-height: 120px;">
|
||||
<div class="emoji-item" data-emoji="🇨🇳">🇨🇳</div>
|
||||
<div class="emoji-item" data-emoji="🇺🇸">🇺🇸</div>
|
||||
<div class="emoji-item" data-emoji="🇪🇺">🇪🇺</div>
|
||||
<div class="emoji-item" data-emoji="🇩🇪">🇩🇪</div>
|
||||
<div class="emoji-item" data-emoji="🇯🇵">🇯🇵</div>
|
||||
<div class="emoji-item" data-emoji="🇰🇷">🇰🇷</div>
|
||||
<div class="emoji-item" data-emoji="🇮🇹">🇮🇹</div>
|
||||
<div class="emoji-item" data-emoji="🇫🇷">🇫🇷</div>
|
||||
<div class="emoji-item" data-emoji="🇨🇦">🇨🇦</div>
|
||||
<div class="emoji-item" data-emoji="🇧🇷">🇧🇷</div>
|
||||
<div class="emoji-item" data-emoji="🇮🇳">🇮🇳</div>
|
||||
<div class="emoji-item" data-emoji="🇹🇼">🇹🇼</div>
|
||||
</div>
|
||||
|
||||
<h6 class="dropdown-header mt-3">🏢 Empresas & Logística</h6>
|
||||
<div class="emoji-picker" style="max-height: 120px;">
|
||||
<div class="emoji-item" data-emoji="🏢">🏢</div>
|
||||
<div class="emoji-item" data-emoji="🏭">🏭</div>
|
||||
<div class="emoji-item" data-emoji="🏪">🏪</div>
|
||||
<div class="emoji-item" data-emoji="🏬">🏬</div>
|
||||
<div class="emoji-item" data-emoji="🚢">🚢</div>
|
||||
<div class="emoji-item" data-emoji="✈️">✈️</div>
|
||||
<div class="emoji-item" data-emoji="🚛">🚛</div>
|
||||
<div class="emoji-item" data-emoji="🚚">🚚</div>
|
||||
<div class="emoji-item" data-emoji="📦">📦</div>
|
||||
<div class="emoji-item" data-emoji="📋">📋</div>
|
||||
<div class="emoji-item" data-emoji="💼">💼</div>
|
||||
<div class="emoji-item" data-emoji="📊">📊</div>
|
||||
</div>
|
||||
|
||||
<h6 class="dropdown-header mt-3">⭐ Destacados</h6>
|
||||
<div class="emoji-picker" style="max-height: 80px;">
|
||||
<div class="emoji-item" data-emoji="⚡">⚡</div>
|
||||
<div class="emoji-item" data-emoji="🔥">🔥</div>
|
||||
<div class="emoji-item" data-emoji="⭐">⭐</div>
|
||||
<div class="emoji-item" data-emoji="🎯">🎯</div>
|
||||
<div class="emoji-item" data-emoji="🚀">🚀</div>
|
||||
<div class="emoji-item" data-emoji="💎">💎</div>
|
||||
<div class="emoji-item" data-emoji="🔧">🔧</div>
|
||||
<div class="emoji-item" data-emoji="⚙️">⚙️</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<label for="descripcion" class="form-label">Descripción</label>
|
||||
<textarea id="descripcion" name="descripcion" class="form-control" rows="2"
|
||||
placeholder="Descripción breve del template (opcional)" maxlength="255"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuración Principal -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-cog me-2"></i>Configuración Principal</h6>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<label for="tipo_moneda" class="form-label">Moneda</label>
|
||||
<select id="tipo_moneda" name="tipo_moneda" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="MXN">Peso Mexicano (MXN)</option>
|
||||
<option value="USD">Dólar USD (USD)</option>
|
||||
<option value="EUR">Euro (EUR)</option>
|
||||
<option value="CNY">Yuan (CNY)</option>
|
||||
<option value="GBP">Libra GBP (GBP)</option>
|
||||
<option value="JPY">Yen (JPY)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="incoterm" class="form-label">INCOTERM</label>
|
||||
<select id="incoterm" name="incoterm" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($incoterms as $inc): ?>
|
||||
<option value="<?= htmlspecialchars($inc['INCOTERM']) ?>">
|
||||
<?= htmlspecialchars($inc['INCOTERM'].' - '.$inc['DESCESPANOL']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="vinculacion" class="form-label">Vinculación</label>
|
||||
<select id="vinculacion" name="vinculacion" class="form-select">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="0">No existe</option>
|
||||
<option value="1">Existe, no afecta</option>
|
||||
<option value="2">Existe y afecta</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="tasa_preferencial" class="form-label">Tasa Preferencial</label>
|
||||
<select id="tasa_preferencial" name="tasa_preferencial" class="form-select">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="General">General</option>
|
||||
<option value="TLC">TLC</option>
|
||||
<option value="PROSEC">PROSEC</option>
|
||||
<option value="ALADI">ALADI</option>
|
||||
<option value="COMERCIALIZADORA">COMERCIALIZADORA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="pais_proveedor" class="form-label">País Proveedor</label>
|
||||
<select id="pais_proveedor" name="pais_proveedor" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= htmlspecialchars($p['id_pais']) ?>"><?= htmlspecialchars($p['nombre']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="hidden" id="pais_proveedor_texto" name="pais_proveedor_texto">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="unidad_comercial_id" class="form-label">Unidad de Medida</label>
|
||||
<select id="unidad_comercial_id" name="unidad_comercial_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($unidades_medida as $um): ?>
|
||||
<option value="<?= htmlspecialchars($um['id']) ?>"><?= htmlspecialchars($um['descripcion']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuración Avanzada -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-tools me-2"></i>Configuración Avanzada</h6>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="anexo22_apendice" class="form-label">Aduana</label>
|
||||
<select id="anexo22_apendice" name="anexo22_apendice" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($aduanas as $a): ?>
|
||||
<option value="<?= htmlspecialchars($a['aduana_seccion']) ?>">
|
||||
<?= htmlspecialchars($a['aduana_seccion'].' – '.$a['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="patente" class="form-label">Patente Aduanal</label>
|
||||
<select id="patente" name="patente" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($patentes as $pt): ?>
|
||||
<option value="<?= htmlspecialchars($pt['id_agente']) ?>">
|
||||
<?= htmlspecialchars($pt['patente'].' - '.$pt['agente_aduanal']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="transportista_id" class="form-label">Transportista</label>
|
||||
<select id="transportista_id" name="transportista_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($transportistas as $t): ?>
|
||||
<option value="<?= htmlspecialchars($t['id_transportista']) ?>">
|
||||
<?= htmlspecialchars($t['clave_identificador'] . ' - ' . $t['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="chofer_id" class="form-label">Chofer</label>
|
||||
<select id="chofer_id" name="chofer_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($choferes as $c): ?>
|
||||
<option value="<?= htmlspecialchars($c['id_chofer']) ?>">
|
||||
<?= htmlspecialchars($c['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-success btn-animated me-2">
|
||||
<i class="fas fa-save me-1"></i>Guardar Template
|
||||
</button>
|
||||
<a href="/IMPORTADORES/templates_rapidos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times me-1"></i>Cancelar
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vista Previa -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card sticky-top" style="top: 20px;">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h6 class="mb-0"><i class="fas fa-eye me-2"></i>Vista Previa</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="template-preview">
|
||||
<div class="preview-card">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<span id="preview-icono" class="fs-4 me-2">🏢</span>
|
||||
<span id="preview-nombre" class="fw-bold">Nuevo Template</span>
|
||||
</div>
|
||||
<div id="preview-descripcion" class="small mb-3 opacity-75">
|
||||
Descripción del template...
|
||||
</div>
|
||||
<div id="preview-config" class="d-flex flex-wrap">
|
||||
<!-- Configuración se mostrará aquí dinámicamente -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tips-section">
|
||||
<h6 class="small text-primary mb-3 fw-bold">💡 Consejos para Templates Efectivos</h6>
|
||||
<div class="tip-item">
|
||||
<i class="fas fa-lightbulb text-warning"></i>
|
||||
<span>Usa nombres descriptivos que identifiquen fácilmente el template</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<i class="fas fa-flag text-info"></i>
|
||||
<span>Elige iconos de países o símbolos representativos</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<i class="fas fa-filter text-success"></i>
|
||||
<span>Solo llena campos que sean comunes en todas las importaciones</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<i class="fas fa-clock text-danger"></i>
|
||||
<span>Deja vacíos los campos que cambien frecuentemente</span>
|
||||
</div>
|
||||
<div class="tip-item">
|
||||
<i class="fas fa-save text-primary"></i>
|
||||
<span>Los templates te ahorrarán tiempo en futuras importaciones</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botón adicional para resetear -->
|
||||
<div class="d-grid mt-3">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="resetForm()">
|
||||
<i class="fas fa-refresh me-1"></i>Limpiar Formulario
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Variables globales
|
||||
let choicesInstances = {};
|
||||
|
||||
// Inicializar cuando el DOM esté listo
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeChoices();
|
||||
initializeEmojiPicker();
|
||||
initializePreview();
|
||||
initializeValidation();
|
||||
initializeDynamicUpdates();
|
||||
});
|
||||
|
||||
// Inicializar Choices.js para selects con búsqueda
|
||||
function initializeChoices() {
|
||||
document.querySelectorAll('.searchable').forEach(element => {
|
||||
choicesInstances[element.id] = new Choices(element, {
|
||||
searchEnabled: true,
|
||||
itemSelectText: '',
|
||||
shouldSort: false,
|
||||
searchPlaceholderValue: 'Buscar...',
|
||||
noResultsText: 'No se encontraron resultados',
|
||||
noChoicesText: 'No hay opciones disponibles'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Inicializar selector de emojis
|
||||
function initializeEmojiPicker() {
|
||||
// Manejar clicks en emojis
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.classList.contains('emoji-item')) {
|
||||
const emoji = e.target.getAttribute('data-emoji') || e.target.textContent;
|
||||
selectEmoji(emoji);
|
||||
|
||||
// Cerrar dropdown
|
||||
const dropdown = bootstrap.Dropdown.getInstance(document.querySelector('[data-bs-toggle="dropdown"]'));
|
||||
if (dropdown) {
|
||||
dropdown.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Mostrar emoji seleccionado visualmente
|
||||
document.addEventListener('mouseenter', function(e) {
|
||||
if (e.target.classList.contains('emoji-item')) {
|
||||
// Quitar selección anterior
|
||||
document.querySelectorAll('.emoji-item.selected').forEach(item => {
|
||||
item.classList.remove('selected');
|
||||
});
|
||||
|
||||
// Agregar efecto hover mejorado
|
||||
e.target.style.transform = 'scale(1.2)';
|
||||
e.target.style.background = 'linear-gradient(135deg, #4CAF50, #45a049)';
|
||||
e.target.style.color = 'white';
|
||||
e.target.style.borderRadius = '8px';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseleave', function(e) {
|
||||
if (e.target.classList.contains('emoji-item')) {
|
||||
e.target.style.transform = '';
|
||||
e.target.style.background = '';
|
||||
e.target.style.color = '';
|
||||
e.target.style.borderRadius = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Función para seleccionar emoji (mejorada)
|
||||
function selectEmoji(emoji) {
|
||||
const iconInput = document.getElementById('icono');
|
||||
const iconDisplay = document.getElementById('icon-display');
|
||||
|
||||
// Actualizar valor e interfaz
|
||||
iconInput.value = emoji;
|
||||
iconDisplay.textContent = emoji;
|
||||
|
||||
// Efecto visual en el display
|
||||
iconDisplay.style.transform = 'scale(1.3)';
|
||||
iconDisplay.style.transition = 'transform 0.3s ease';
|
||||
|
||||
setTimeout(() => {
|
||||
iconDisplay.style.transform = 'scale(1)';
|
||||
}, 300);
|
||||
|
||||
updatePreview();
|
||||
|
||||
// Mostrar feedback visual
|
||||
showToast('✅ Icono actualizado', 'success');
|
||||
}
|
||||
|
||||
// Inicializar vista previa
|
||||
function initializePreview() {
|
||||
updatePreview();
|
||||
|
||||
// Eventos para actualizar vista previa en tiempo real
|
||||
document.querySelectorAll('#templateForm input, #templateForm select, #templateForm textarea').forEach(element => {
|
||||
element.addEventListener('input', debounce(updatePreview, 300));
|
||||
element.addEventListener('change', updatePreview);
|
||||
});
|
||||
}
|
||||
|
||||
// Función para actualizar vista previa (mejorada)
|
||||
function updatePreview() {
|
||||
const nombre = document.getElementById('nombre').value.trim() || 'Nuevo Template';
|
||||
const descripcion = document.getElementById('descripcion').value.trim() || 'Descripción del template...';
|
||||
const icono = document.getElementById('icono').value;
|
||||
|
||||
// Actualizar elementos de vista previa con animación
|
||||
const previewNombre = document.getElementById('preview-nombre');
|
||||
const previewDescripcion = document.getElementById('preview-descripcion');
|
||||
const previewIcono = document.getElementById('preview-icono');
|
||||
|
||||
if (previewNombre.textContent !== nombre) {
|
||||
previewNombre.style.opacity = '0.5';
|
||||
setTimeout(() => {
|
||||
previewNombre.textContent = nombre;
|
||||
previewNombre.style.opacity = '1';
|
||||
}, 150);
|
||||
}
|
||||
|
||||
if (previewDescripcion.textContent !== descripcion) {
|
||||
previewDescripcion.style.opacity = '0.5';
|
||||
setTimeout(() => {
|
||||
previewDescripcion.textContent = descripcion;
|
||||
previewDescripcion.style.opacity = '1';
|
||||
}, 150);
|
||||
}
|
||||
|
||||
previewIcono.textContent = icono;
|
||||
|
||||
// Actualizar configuración con más campos
|
||||
const config = [];
|
||||
const fields = [
|
||||
{ id: 'tipo_moneda', label: '💰 Moneda' },
|
||||
{ id: 'incoterm', label: '📋 INCOTERM', getValue: (el) => el.value ? el.options[el.selectedIndex].text.split(' - ')[0] : '' },
|
||||
{ id: 'vinculacion', label: '🔗 Vinculación', map: {'0':'Sin Vinc.', '1':'No Afecta', '2':'Afecta'} },
|
||||
{ id: 'tasa_preferencial', label: '📊 Tasa' },
|
||||
{ id: 'pais_proveedor', label: '🌍 País', getValue: (el) => el.value ? el.options[el.selectedIndex].text : '' },
|
||||
{ id: 'anexo22_apendice', label: '🏢 Aduana', getValue: (el) => el.value ? el.options[el.selectedIndex].text.split(' –')[0] : '' },
|
||||
{ id: 'transportista_id', label: '🚛 Transportista', getValue: (el) => el.value ? el.options[el.selectedIndex].text.split(' - ')[0] : '' }
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
const element = document.getElementById(field.id);
|
||||
let value = '';
|
||||
|
||||
if (field.getValue) {
|
||||
value = field.getValue(element);
|
||||
} else {
|
||||
value = element.value;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
const displayValue = field.map && field.map[value] ? field.map[value] : value;
|
||||
config.push(`<span class="template-preview-item">${field.label}: ${displayValue}</span>`);
|
||||
}
|
||||
});
|
||||
|
||||
const configContainer = document.getElementById('preview-config');
|
||||
configContainer.style.opacity = '0.5';
|
||||
setTimeout(() => {
|
||||
configContainer.innerHTML = config.join('');
|
||||
configContainer.style.opacity = '1';
|
||||
}, 150);
|
||||
}
|
||||
|
||||
// Inicializar actualizaciones dinámicas
|
||||
function initializeDynamicUpdates() {
|
||||
// Actualizar texto del país cuando cambie
|
||||
const paisSelect = document.getElementById('pais_proveedor');
|
||||
if (paisSelect && choicesInstances[paisSelect.id]) {
|
||||
paisSelect.addEventListener('change', function() {
|
||||
const selectedOption = this.options[this.selectedIndex];
|
||||
document.getElementById('pais_proveedor_texto').value = selectedOption ? selectedOption.text : '';
|
||||
updatePreview();
|
||||
});
|
||||
}
|
||||
|
||||
// Filtrar choferes según transportista seleccionado
|
||||
const transportistaSelect = document.getElementById('transportista_id');
|
||||
const choferSelect = document.getElementById('chofer_id');
|
||||
|
||||
if (transportistaSelect && choferSelect) {
|
||||
transportistaSelect.addEventListener('change', function() {
|
||||
const transportistaId = this.value;
|
||||
const choferChoices = choicesInstances[choferSelect.id];
|
||||
|
||||
if (choferChoices) {
|
||||
// Aquí podrías hacer una llamada AJAX para filtrar choferes
|
||||
// Por ahora simplemente reseteamos la selección
|
||||
choferChoices.setChoiceByValue('');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializar validación del formulario
|
||||
function initializeValidation() {
|
||||
const form = document.getElementById('templateForm');
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
if (!validateForm()) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mostrar loading mejorado
|
||||
Swal.fire({
|
||||
title: 'Guardando template...',
|
||||
html: '<div class="loading-spinner"></div><br>Por favor espera...',
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
showConfirmButton: false,
|
||||
customClass: {
|
||||
popup: 'swal2-noanimation'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validación en tiempo real del nombre
|
||||
const nombreInput = document.getElementById('nombre');
|
||||
nombreInput.addEventListener('input', function() {
|
||||
validateField(this);
|
||||
});
|
||||
}
|
||||
|
||||
// Función de validación del formulario
|
||||
function validateForm() {
|
||||
const nombre = document.getElementById('nombre').value.trim();
|
||||
|
||||
if (!nombre) {
|
||||
showErrorAlert('Campo requerido', 'El nombre del template es obligatorio');
|
||||
document.getElementById('nombre').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nombre.length > 100) {
|
||||
showErrorAlert('Nombre muy largo', 'El nombre no puede exceder 100 caracteres');
|
||||
document.getElementById('nombre').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validar campo individual
|
||||
function validateField(field) {
|
||||
const value = field.value.trim();
|
||||
|
||||
if (field.id === 'nombre') {
|
||||
if (value.length > 100) {
|
||||
field.classList.add('is-invalid');
|
||||
field.classList.remove('is-valid');
|
||||
} else if (value.length > 0) {
|
||||
field.classList.add('is-valid');
|
||||
field.classList.remove('is-invalid');
|
||||
} else {
|
||||
field.classList.remove('is-valid', 'is-invalid');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Funciones de utilidad
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
function showErrorAlert(title, text) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: title,
|
||||
text: text,
|
||||
confirmButtonColor: '#dc3545',
|
||||
confirmButtonText: 'Entendido'
|
||||
});
|
||||
}
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
timerProgressBar: true
|
||||
});
|
||||
|
||||
Toast.fire({
|
||||
icon: type,
|
||||
title: message
|
||||
});
|
||||
}
|
||||
|
||||
// Función para resetear el formulario
|
||||
function resetForm() {
|
||||
document.getElementById('templateForm').reset();
|
||||
|
||||
// Resetear Choices.js
|
||||
Object.values(choicesInstances).forEach(choice => {
|
||||
choice.setChoiceByValue('');
|
||||
});
|
||||
|
||||
// Resetear icono
|
||||
document.getElementById('icono').value = '🏢';
|
||||
document.getElementById('icon-display').textContent = '🏢';
|
||||
|
||||
updatePreview();
|
||||
showToast('Formulario reiniciado', 'info');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
494
views/templates_rapidos/editar.php
Normal file
494
views/templates_rapidos/editar.php
Normal file
@@ -0,0 +1,494 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>✏️ Editar Template Rápido</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.form-control:focus, .form-select:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
.emoji-picker {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
gap: 5px;
|
||||
padding: 10px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
background: white;
|
||||
}
|
||||
.emoji-item {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
.emoji-item:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.preview-card {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.template-preview-item {
|
||||
background: rgba(255,255,255,0.2);
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
color: white;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
margin: 2px;
|
||||
display: inline-block;
|
||||
}
|
||||
.choices.required .choices__inner { border: 1px solid #ced4da; }
|
||||
.choices.is-invalid .choices__inner { border: 1px solid #dc3545; background-color: #fff5f5; }
|
||||
.choices.is-valid .choices__inner { border: 1px solid #28a745; background-color: #f5fff5; }
|
||||
.section-header {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
padding: 15px;
|
||||
margin: -15px -15px 15px -15px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0">✏️ Editar Template: <?= htmlspecialchars($template['icono']) ?> <?= htmlspecialchars($template['nombre']) ?></h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/templates_rapidos/lista" class="btn btn-secondary btn-animated me-2">
|
||||
<i class="fas fa-list me-1"></i>Volver a Lista
|
||||
</a>
|
||||
<a href="/IMPORTADORES/templates_rapidos/crear" class="btn btn-success btn-animated">
|
||||
<i class="fas fa-plus me-1"></i>Nuevo Template
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info del template -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<div class="card bg-info text-white">
|
||||
<div class="card-body py-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<h6 class="mb-1"><i class="fas fa-info-circle me-2"></i>Información del Template</h6>
|
||||
<p class="mb-0 small">
|
||||
Creado el: <?= $template['fecha_creacion'] ? $template['fecha_creacion']->format('d/m/Y H:i') : 'N/A' ?> |
|
||||
Usado: <?= $template['veces_usado'] ?> veces |
|
||||
Última modificación: <?= $template['fecha_modificacion'] ? $template['fecha_modificacion']->format('d/m/Y H:i') : 'N/A' ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<span class="h4"><?= htmlspecialchars($template['icono']) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Formulario Principal -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-body p-4">
|
||||
<form id="templateForm" action="/IMPORTADORES/templates_rapidos/actualizar" method="POST">
|
||||
<input type="hidden" name="id" value="<?= htmlspecialchars($template['id']) ?>">
|
||||
|
||||
<!-- Información Básica -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-info-circle me-2"></i>Información Básica</h6>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre" class="form-label">Nombre del Template *</label>
|
||||
<input id="nombre" name="nombre" type="text" class="form-control" required
|
||||
placeholder="ej. Importación China" maxlength="100"
|
||||
value="<?= htmlspecialchars($template['nombre']) ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="icono" class="form-label">Icono</label>
|
||||
<div class="input-group">
|
||||
<input id="icono" name="icono" type="text" class="form-control" readonly
|
||||
value="<?= htmlspecialchars($template['icono']) ?>">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown">
|
||||
<i class="fas fa-smile"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end p-0" style="width: 300px;">
|
||||
<li>
|
||||
<div class="emoji-picker">
|
||||
<div class="emoji-item" onclick="selectEmoji('🇨🇳')">🇨🇳</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇺🇸')">🇺🇸</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇪🇺')">🇪🇺</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇩🇪')">🇩🇪</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇯🇵')">🇯🇵</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇰🇷')">🇰🇷</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇮🇹')">🇮🇹</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🇫🇷')">🇫🇷</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🏢')">🏢</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🏭')">🏭</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🚢')">🚢</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('✈️')">✈️</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🚛')">🚛</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('📦')">📦</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('💼')">💼</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('⚡')">⚡</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🔥')">🔥</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('⭐')">⭐</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🎯')">🎯</div>
|
||||
<div class="emoji-item" onclick="selectEmoji('🚀')">🚀</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<label for="descripcion" class="form-label">Descripción</label>
|
||||
<textarea id="descripcion" name="descripcion" class="form-control" rows="2"
|
||||
placeholder="Descripción breve del template (opcional)" maxlength="255"><?= htmlspecialchars($template['descripcion']) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuración Principal -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-cog me-2"></i>Configuración Principal</h6>
|
||||
</div>
|
||||
|
||||
<?php $config = $template['config']; ?>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<label for="tipo_moneda" class="form-label">Moneda</label>
|
||||
<select id="tipo_moneda" name="tipo_moneda" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="MXN" <?= ($config['tipo_moneda'] ?? '') === 'MXN' ? 'selected' : '' ?>>Peso Mexicano (MXN)</option>
|
||||
<option value="USD" <?= ($config['tipo_moneda'] ?? '') === 'USD' ? 'selected' : '' ?>>Dólar USD (USD)</option>
|
||||
<option value="EUR" <?= ($config['tipo_moneda'] ?? '') === 'EUR' ? 'selected' : '' ?>>Euro (EUR)</option>
|
||||
<option value="CNY" <?= ($config['tipo_moneda'] ?? '') === 'CNY' ? 'selected' : '' ?>>Yuan (CNY)</option>
|
||||
<option value="GBP" <?= ($config['tipo_moneda'] ?? '') === 'GBP' ? 'selected' : '' ?>>Libra GBP (GBP)</option>
|
||||
<option value="JPY" <?= ($config['tipo_moneda'] ?? '') === 'JPY' ? 'selected' : '' ?>>Yen (JPY)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="incoterm" class="form-label">INCOTERM</label>
|
||||
<select id="incoterm" name="incoterm" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($incoterms as $inc): ?>
|
||||
<option value="<?= htmlspecialchars($inc['INCOTERM']) ?>"
|
||||
<?= ($config['incoterm'] ?? '') === $inc['INCOTERM'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($inc['INCOTERM'].' - '.$inc['DESCESPANOL']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="vinculacion" class="form-label">Vinculación</label>
|
||||
<select id="vinculacion" name="vinculacion" class="form-select">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="0" <?= ($config['vinculacion'] ?? '') === '0' ? 'selected' : '' ?>>No existe</option>
|
||||
<option value="1" <?= ($config['vinculacion'] ?? '') === '1' ? 'selected' : '' ?>>Existe, no afecta</option>
|
||||
<option value="2" <?= ($config['vinculacion'] ?? '') === '2' ? 'selected' : '' ?>>Existe y afecta</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="tasa_preferencial" class="form-label">Tasa Preferencial</label>
|
||||
<select id="tasa_preferencial" name="tasa_preferencial" class="form-select">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<option value="General" <?= ($config['tasa_preferencial'] ?? '') === 'General' ? 'selected' : '' ?>>General</option>
|
||||
<option value="TLC" <?= ($config['tasa_preferencial'] ?? '') === 'TLC' ? 'selected' : '' ?>>TLC</option>
|
||||
<option value="PROSEC" <?= ($config['tasa_preferencial'] ?? '') === 'PROSEC' ? 'selected' : '' ?>>PROSEC</option>
|
||||
<option value="ALADI" <?= ($config['tasa_preferencial'] ?? '') === 'ALADI' ? 'selected' : '' ?>>ALADI</option>
|
||||
<option value="COMERCIALIZADORA" <?= ($config['tasa_preferencial'] ?? '') === 'COMERCIALIZADORA' ? 'selected' : '' ?>>COMERCIALIZADORA</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="pais_proveedor" class="form-label">País Proveedor</label>
|
||||
<select id="pais_proveedor" name="pais_proveedor" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= htmlspecialchars($p['id_pais']) ?>"
|
||||
<?= ($config['pais_proveedor'] ?? '') === $p['id_pais'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($p['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<input type="hidden" id="pais_proveedor_texto" name="pais_proveedor_texto"
|
||||
value="<?= htmlspecialchars($config['pais_proveedor_texto'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="unidad_comercial_id" class="form-label">Unidad de Medida</label>
|
||||
<select id="unidad_comercial_id" name="unidad_comercial_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($unidades_medida as $um): ?>
|
||||
<option value="<?= htmlspecialchars($um['id']) ?>"
|
||||
<?= ($config['unidad_comercial_id'] ?? '') === $um['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($um['descripcion']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuración Avanzada -->
|
||||
<div class="section-header">
|
||||
<h6 class="mb-0"><i class="fas fa-tools me-2"></i>Configuración Avanzada</h6>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="anexo22_apendice" class="form-label">Aduana</label>
|
||||
<select id="anexo22_apendice" name="anexo22_apendice" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($aduanas as $a): ?>
|
||||
<option value="<?= htmlspecialchars($a['aduana_seccion']) ?>"
|
||||
<?= ($config['anexo22_apendice'] ?? '') === $a['aduana_seccion'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($a['aduana_seccion'].' – '.$a['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="patente" class="form-label">Patente Aduanal</label>
|
||||
<select id="patente" name="patente" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($patentes as $pt): ?>
|
||||
<option value="<?= htmlspecialchars($pt['id_agente']) ?>"
|
||||
<?= ($config['patente'] ?? '') === $pt['id_agente'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($pt['patente'].' - '.$pt['agente_aduanal']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="transportista_id" class="form-label">Transportista</label>
|
||||
<select id="transportista_id" name="transportista_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($transportistas as $t): ?>
|
||||
<option value="<?= htmlspecialchars($t['id_transportista']) ?>"
|
||||
<?= ($config['transportista_id'] ?? '') === $t['id_transportista'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($t['clave_identificador'] . ' - ' . $t['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="chofer_id" class="form-label">Chofer</label>
|
||||
<select id="chofer_id" name="chofer_id" class="form-select searchable">
|
||||
<option value="">-- Sin especificar --</option>
|
||||
<?php foreach($choferes as $c): ?>
|
||||
<option value="<?= htmlspecialchars($c['id_chofer']) ?>"
|
||||
<?= ($config['chofer_id'] ?? '') === $c['id_chofer'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($c['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botones -->
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-primary btn-animated me-2">
|
||||
<i class="fas fa-save me-1"></i>Actualizar Template
|
||||
</button>
|
||||
<a href="/IMPORTADORES/templates_rapidos/lista" class="btn btn-secondary btn-animated">
|
||||
<i class="fas fa-times me-1"></i>Cancelar
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vista Previa -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card sticky-top" style="top: 20px;">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h6 class="mb-0"><i class="fas fa-eye me-2"></i>Vista Previa</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="template-preview">
|
||||
<div class="preview-card">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<span id="preview-icono" class="fs-4 me-2"><?= htmlspecialchars($template['icono']) ?></span>
|
||||
<span id="preview-nombre" class="fw-bold"><?= htmlspecialchars($template['nombre']) ?></span>
|
||||
</div>
|
||||
<div id="preview-descripcion" class="small mb-3 opacity-75">
|
||||
<?= htmlspecialchars($template['descripcion']) ?>
|
||||
</div>
|
||||
<div id="preview-config" class="d-flex flex-wrap">
|
||||
<!-- Configuración se mostrará aquí dinámicamente -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<h6 class="small text-muted mb-2">📊 Estadísticas de Uso:</h6>
|
||||
<div class="small text-muted">
|
||||
<div><i class="fas fa-mouse-pointer text-primary me-1"></i>Usado: <?= $template['veces_usado'] ?> veces</div>
|
||||
<div><i class="fas fa-clock text-info me-1"></i>Última vez: <?= $template['ultima_vez_usado'] ? $template['ultima_vez_usado']->format('d/m/Y H:i') : 'Nunca' ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<h6 class="small text-muted mb-2">💡 Consejos:</h6>
|
||||
<ul class="list-unstyled small text-muted">
|
||||
<li><i class="fas fa-check text-success me-1"></i> Usa nombres descriptivos</li>
|
||||
<li><i class="fas fa-check text-success me-1"></i> Elige iconos representativos</li>
|
||||
<li><i class="fas fa-check text-success me-1"></i> Solo llena campos comunes</li>
|
||||
<li><i class="fas fa-check text-success me-1"></i> Deja vacío lo que varía</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// Inicializar Choices.js
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.searchable').forEach(element => {
|
||||
new Choices(element, {
|
||||
searchEnabled: true,
|
||||
itemSelectText: '',
|
||||
shouldSort: false
|
||||
});
|
||||
});
|
||||
|
||||
// Inicializar vista previa
|
||||
updatePreview();
|
||||
|
||||
// Eventos para actualizar vista previa
|
||||
document.querySelectorAll('#templateForm input, #templateForm select, #templateForm textarea').forEach(element => {
|
||||
element.addEventListener('input', updatePreview);
|
||||
element.addEventListener('change', updatePreview);
|
||||
});
|
||||
});
|
||||
|
||||
// Función para seleccionar emoji
|
||||
function selectEmoji(emoji) {
|
||||
document.getElementById('icono').value = emoji;
|
||||
updatePreview();
|
||||
}
|
||||
|
||||
// Función para actualizar vista previa
|
||||
function updatePreview() {
|
||||
const nombre = document.getElementById('nombre').value || 'Template sin nombre';
|
||||
const descripcion = document.getElementById('descripcion').value || 'Sin descripción';
|
||||
const icono = document.getElementById('icono').value;
|
||||
|
||||
document.getElementById('preview-nombre').textContent = nombre;
|
||||
document.getElementById('preview-descripcion').textContent = descripcion;
|
||||
document.getElementById('preview-icono').textContent = icono;
|
||||
|
||||
// Actualizar configuración
|
||||
const config = [];
|
||||
const fields = [
|
||||
{ id: 'tipo_moneda', label: 'Moneda' },
|
||||
{ id: 'incoterm', label: 'INCOTERM' },
|
||||
{ id: 'vinculacion', label: 'Vinculación', map: {'0':'Sin Vinc.', '1':'No Afecta', '2':'Afecta'} },
|
||||
{ id: 'tasa_preferencial', label: 'Tasa' }
|
||||
];
|
||||
|
||||
fields.forEach(field => {
|
||||
const value = document.getElementById(field.id).value;
|
||||
if (value) {
|
||||
const displayValue = field.map && field.map[value] ? field.map[value] : value;
|
||||
config.push(`<span class="template-preview-item">${field.label}: ${displayValue}</span>`);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('preview-config').innerHTML = config.join('');
|
||||
}
|
||||
|
||||
// Actualizar texto del país cuando cambie
|
||||
document.getElementById('pais_proveedor').addEventListener('change', function() {
|
||||
const selectedOption = this.options[this.selectedIndex];
|
||||
document.getElementById('pais_proveedor_texto').value = selectedOption ? selectedOption.text : '';
|
||||
updatePreview();
|
||||
});
|
||||
|
||||
// Validación del formulario
|
||||
document.getElementById('templateForm').addEventListener('submit', function(e) {
|
||||
const nombre = document.getElementById('nombre').value.trim();
|
||||
|
||||
if (!nombre) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Campo requerido',
|
||||
text: 'El nombre del template es obligatorio',
|
||||
confirmButtonColor: '#3085d6'
|
||||
});
|
||||
document.getElementById('nombre').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nombre.length > 100) {
|
||||
e.preventDefault();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Nombre muy largo',
|
||||
text: 'El nombre no puede exceder 100 caracteres',
|
||||
confirmButtonColor: '#3085d6'
|
||||
});
|
||||
document.getElementById('nombre').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mostrar loading
|
||||
Swal.fire({
|
||||
title: 'Actualizando template...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
948
views/templates_rapidos/lista.php
Normal file
948
views/templates_rapidos/lista.php
Normal file
@@ -0,0 +1,948 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>📋 Templates Rápidos</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<style>
|
||||
body { font-family: 'Segoe UI', sans-serif; background-color: #f4f6f9; }
|
||||
.sidebar { width: 220px; height: 100vh; background-color: #343a40; position: fixed; top: 0; left: 0; padding-top: 56px; z-index: 1040; }
|
||||
.sidebar .nav-link { font-weight: bold; color: white; transition: all 0.3s ease; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #495057; color: #fff; }
|
||||
.content { margin-top: 56px; padding: 40px 20px; position: relative; z-index: 1; background-color: #f4f6f9; transition: margin-left 0.3s ease; }
|
||||
@media (min-width: 768px) { .content { margin-left: 250px; } }
|
||||
@media (max-width: 767.98px) {
|
||||
.content { margin-left: 0; }
|
||||
.sidebar .nav-link { font-weight: normal; color: #343a40; background-color: transparent; }
|
||||
.sidebar .nav-link:hover, .sidebar .nav-link.active { background-color: #e9ecef; color: #212529; }
|
||||
}
|
||||
.card { border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
||||
.btn-animated { transition: all 0.3s ease; position: relative; overflow: hidden; }
|
||||
.btn-animated:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); }
|
||||
.table-responsive { border-radius: 8px; overflow: hidden; }
|
||||
.dataTables_wrapper .dataTables_paginate .paginate_button { padding: 0.375rem 0.75rem; }
|
||||
.alert { border-radius: 8px; }
|
||||
.fade-in { animation: fadeIn 0.5s ease-in; }
|
||||
@keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* ✅ NUEVO: Estilos para filtros de templates */
|
||||
.template-filters {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 25px;
|
||||
color: white;
|
||||
}
|
||||
.filter-btn {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
color: white;
|
||||
border-radius: 20px;
|
||||
padding: 8px 16px;
|
||||
margin: 0 5px 10px 0;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.filter-btn:hover, .filter-btn.active {
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-color: rgba(255,255,255,0.4);
|
||||
transform: translateY(-2px);
|
||||
color: white;
|
||||
}
|
||||
.filter-btn.active {
|
||||
background: rgba(255,255,255,0.25);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* ✅ NUEVO: Botón flotante de ayuda */
|
||||
.help-float-btn {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1060;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.help-float-btn:hover {
|
||||
transform: translateY(-3px) scale(1.1);
|
||||
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.6);
|
||||
color: white;
|
||||
}
|
||||
.help-float-btn:active {
|
||||
transform: translateY(-1px) scale(1.05);
|
||||
}
|
||||
|
||||
/* ✅ NUEVO: Mini-modal de ayuda */
|
||||
.shortcuts-modal {
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
right: 30px;
|
||||
width: 320px;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 15px 40px rgba(0,0,0,0.2);
|
||||
z-index: 1070;
|
||||
display: none;
|
||||
animation: slideInUp 0.3s ease;
|
||||
}
|
||||
@keyframes slideInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
.shortcuts-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 15px 20px;
|
||||
border-radius: 12px 12px 0 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.shortcuts-content {
|
||||
padding: 20px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.shortcut-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.shortcut-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.shortcut-key {
|
||||
background: #f1f3f4;
|
||||
border: 1px solid #dadce0;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #5f6368;
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
.shortcut-desc {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* ✅ NUEVO: Templates colapsados por defecto */
|
||||
.templates-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.section-header {
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px 8px 0 0;
|
||||
padding: 15px 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.section-header:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
.section-content {
|
||||
border: 1px solid #dee2e6;
|
||||
border-top: none;
|
||||
border-radius: 0 0 8px 8px;
|
||||
display: none;
|
||||
}
|
||||
.section-content.show {
|
||||
display: block;
|
||||
}
|
||||
.toggle-icon {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.section-header.active .toggle-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Mejorar badges */
|
||||
.scope-badge {
|
||||
font-size: 11px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4 class="mb-0">📋 Templates Rápidos</h4>
|
||||
<div>
|
||||
<a href="/IMPORTADORES/templates_rapidos/crear" class="btn btn-success btn-animated">
|
||||
<i class="fas fa-plus me-1"></i>Nuevo Template
|
||||
</a>
|
||||
<a href="/IMPORTADORES/home" class="btn btn-secondary btn-animated ms-2">
|
||||
<i class="fas fa-arrow-left me-1"></i>Volver
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alertas -->
|
||||
<?php if (isset($_GET['created'])): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show fade-in" role="alert">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
<strong>¡Éxito!</strong> Template creado correctamente.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="alert alert-info alert-dismissible fade show fade-in" role="alert">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<strong>¡Actualizado!</strong> Template modificado correctamente.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET['deleted'])): ?>
|
||||
<div class="alert alert-warning alert-dismissible fade show fade-in" role="alert">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>
|
||||
<strong>¡Eliminado!</strong> Template desactivado correctamente.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- ✅ NUEVO: Filtros mejorados -->
|
||||
<div class="template-filters">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="mb-0"><i class="fas fa-filter me-2"></i>Filtrar Templates</h6>
|
||||
<small id="template-count" class="opacity-75">Cargando...</small>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap">
|
||||
<button class="filter-btn active" data-filter="all">
|
||||
<i class="fas fa-th-list me-1"></i>Todos
|
||||
</button>
|
||||
<button class="filter-btn" data-filter="personal">
|
||||
<i class="fas fa-user me-1"></i>Mis Templates
|
||||
</button>
|
||||
<button class="filter-btn" data-filter="agencia">
|
||||
<i class="fas fa-building me-1"></i>De mi Agencia
|
||||
</button>
|
||||
<button class="filter-btn" data-filter="mas-usados">
|
||||
<i class="fas fa-fire me-1"></i>Más Usados
|
||||
</button>
|
||||
<button class="filter-btn" data-filter="recientes">
|
||||
<i class="fas fa-clock me-1"></i>Recientes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ✅ MODIFICADO: Templates organizados por secciones colapsables -->
|
||||
<div class="templates-section">
|
||||
<div class="section-header" data-toggle="personal">
|
||||
<div>
|
||||
<h6 class="mb-0"><i class="fas fa-user text-primary me-2"></i>Mis Templates Personalizados</h6>
|
||||
<small class="text-muted">Templates que has creado</small>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down toggle-icon"></i>
|
||||
</div>
|
||||
<div class="section-content show" id="personal-templates">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table id="personalTemplatesTable" class="table table-hover mb-0" style="width:100%">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th width="60">ID</th>
|
||||
<th width="200">Nombre</th>
|
||||
<th>Descripción</th>
|
||||
<th width="80">Usos</th>
|
||||
<th width="100">Creado</th>
|
||||
<th width="120">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="templates-section">
|
||||
<div class="section-header" data-toggle="agencia">
|
||||
<div>
|
||||
<h6 class="mb-0"><i class="fas fa-building text-info me-2"></i>Templates de Agencia</h6>
|
||||
<small class="text-muted">Compartidos en tu agencia</small>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down toggle-icon"></i>
|
||||
</div>
|
||||
<div class="section-content" id="agencia-templates">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table id="agenciaTemplatesTable" class="table table-hover mb-0" style="width:100%">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th width="60">ID</th>
|
||||
<th width="200">Nombre</th>
|
||||
<th>Descripción</th>
|
||||
<th width="80">Usos</th>
|
||||
<th width="100">Creado</th>
|
||||
<th width="120">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Templates globales solo se muestran si se solicita explícitamente -->
|
||||
<div class="templates-section" style="display: none;" id="global-section">
|
||||
<div class="section-header" data-toggle="global">
|
||||
<div>
|
||||
<h6 class="mb-0"><i class="fas fa-globe text-secondary me-2"></i>Templates Globales</h6>
|
||||
<small class="text-muted">Predeterminados del sistema</small>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down toggle-icon"></i>
|
||||
</div>
|
||||
<div class="section-content" id="global-templates">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table id="globalTemplatesTable" class="table table-hover mb-0" style="width:100%">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th width="60">ID</th>
|
||||
<th width="200">Nombre</th>
|
||||
<th>Descripción</th>
|
||||
<th width="80">Usos</th>
|
||||
<th width="100">Creado</th>
|
||||
<th width="80">Ver</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ✅ NUEVO: Botón flotante de ayuda -->
|
||||
<button class="help-float-btn" id="helpBtn" title="Atajos rápidos">
|
||||
<i class="fas fa-question"></i>
|
||||
</button>
|
||||
|
||||
<!-- ✅ NUEVO: Mini-modal de atajos -->
|
||||
<div class="shortcuts-modal" id="shortcutsModal">
|
||||
<div class="shortcuts-header">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span><i class="fas fa-keyboard me-2"></i>Atajos Rápidos</span>
|
||||
<button type="button" class="btn-close btn-close-white" id="closeShortcuts"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="shortcuts-content">
|
||||
<div class="mb-3">
|
||||
<strong class="text-primary">🎯 Templates</strong>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Crear nuevo template</div>
|
||||
<div class="shortcut-key">Ctrl+N</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Filtrar personal</div>
|
||||
<div class="shortcut-key">Alt+P</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Filtrar agencia</div>
|
||||
<div class="shortcut-key">Alt+A</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Más usados</div>
|
||||
<div class="shortcut-key">Alt+U</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 mb-3">
|
||||
<strong class="text-primary">⚡ Navegación</strong>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Buscar en tabla</div>
|
||||
<div class="shortcut-key">Ctrl+F</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Actualizar datos</div>
|
||||
<div class="shortcut-key">F5</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Expandir/Colapsar</div>
|
||||
<div class="shortcut-key">Space</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 mb-3">
|
||||
<strong class="text-primary">📋 Pedimentos</strong>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Nueva solicitud</div>
|
||||
<div class="shortcut-key">Ctrl+I</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Aplicar template</div>
|
||||
<div class="shortcut-key">Enter</div>
|
||||
</div>
|
||||
<div class="shortcut-item">
|
||||
<div class="shortcut-desc">Guardar rápido</div>
|
||||
<div class="shortcut-key">Ctrl+S</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-center">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Presiona <kbd>F1</kbd> para mostrar/ocultar esta ayuda
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// ✅ CONFIGURAR DATATABLES POR SECCIÓN
|
||||
const personalTable = initDataTable('#personalTemplatesTable', 'personal');
|
||||
const agenciaTable = initDataTable('#agenciaTemplatesTable', 'agencia');
|
||||
const globalTable = initDataTable('#globalTemplatesTable', 'global');
|
||||
|
||||
// ✅ MANEJO DE SECCIONES COLAPSABLES
|
||||
$('.section-header').click(function() {
|
||||
const $this = $(this);
|
||||
const $content = $this.next('.section-content');
|
||||
const $icon = $this.find('.toggle-icon');
|
||||
|
||||
$content.slideToggle(300);
|
||||
$this.toggleClass('active');
|
||||
|
||||
// Si se está abriendo y la tabla no se ha cargado
|
||||
if ($content.is(':visible') && !$content.data('loaded')) {
|
||||
const section = $this.data('toggle');
|
||||
loadSectionData(section);
|
||||
$content.data('loaded', true);
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ FILTROS MEJORADOS
|
||||
$('.filter-btn').click(function() {
|
||||
$('.filter-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
const filter = $(this).data('filter');
|
||||
applyFilter(filter);
|
||||
});
|
||||
|
||||
// ✅ BOTÓN FLOTANTE DE AYUDA
|
||||
$('#helpBtn').click(function() {
|
||||
$('#shortcutsModal').toggle();
|
||||
});
|
||||
|
||||
$('#closeShortcuts').click(function() {
|
||||
$('#shortcutsModal').hide();
|
||||
});
|
||||
|
||||
// Cerrar modal al hacer clic fuera
|
||||
$(document).click(function(e) {
|
||||
if (!$(e.target).closest('#shortcutsModal, #helpBtn').length) {
|
||||
$('#shortcutsModal').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ ATAJOS DE TECLADO
|
||||
$(document).keydown(function(e) {
|
||||
// Ctrl+N: Nuevo template
|
||||
if (e.ctrlKey && e.key === 'n') {
|
||||
e.preventDefault();
|
||||
window.location.href = '/IMPORTADORES/templates_rapidos/crear';
|
||||
}
|
||||
|
||||
// Ctrl+I: Nueva solicitud
|
||||
if (e.ctrlKey && e.key === 'i') {
|
||||
e.preventDefault();
|
||||
window.location.href = '/IMPORTADORES/solicitud_importacion/crear';
|
||||
}
|
||||
|
||||
// Alt+P: Filtrar personal
|
||||
if (e.altKey && e.key === 'p') {
|
||||
e.preventDefault();
|
||||
$('.filter-btn[data-filter="personal"]').click();
|
||||
}
|
||||
|
||||
// Alt+A: Filtrar agencia
|
||||
if (e.altKey && e.key === 'a') {
|
||||
e.preventDefault();
|
||||
$('.filter-btn[data-filter="agencia"]').click();
|
||||
}
|
||||
|
||||
// Alt+U: Más usados
|
||||
if (e.altKey && e.key === 'u') {
|
||||
e.preventDefault();
|
||||
$('.filter-btn[data-filter="mas-usados"]').click();
|
||||
}
|
||||
|
||||
// F1: Toggle ayuda
|
||||
if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
$('#shortcutsModal').toggle();
|
||||
}
|
||||
|
||||
// F5: Actualizar
|
||||
if (e.key === 'F5') {
|
||||
e.preventDefault();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
// Space: Toggle sección activa
|
||||
if (e.key === ' ' && !$(e.target).is('input, textarea, select')) {
|
||||
e.preventDefault();
|
||||
$('.section-header.active').click();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-refresh cada 30 segundos
|
||||
setInterval(() => {
|
||||
refreshActiveTables();
|
||||
}, 30000);
|
||||
|
||||
// Auto-dismiss alerts
|
||||
setTimeout(() => {
|
||||
$('.alert').fadeOut('slow');
|
||||
}, 5000);
|
||||
|
||||
// Cargar sección personal por defecto
|
||||
loadSectionData('personal');
|
||||
$('#personal-templates').data('loaded', true);
|
||||
});
|
||||
|
||||
// ✅ FUNCIÓN PARA INICIALIZAR DATATABLES
|
||||
function initDataTable(selector, scope) {
|
||||
return $(selector).DataTable({
|
||||
processing: true,
|
||||
serverSide: false,
|
||||
data: [],
|
||||
columns: [
|
||||
{ data: 0 }, // ID
|
||||
{ data: 1 }, // Nombre con icono
|
||||
{ data: 2 }, // Descripción
|
||||
{ data: 3 }, // Veces usado
|
||||
{ data: 4 }, // Fecha creación
|
||||
{ data: 5, orderable: false } // Acciones
|
||||
],
|
||||
order: [[3, 'desc']], // Ordenar por usos descendente
|
||||
language: {
|
||||
processing: "Procesando...",
|
||||
search: "Buscar:",
|
||||
lengthMenu: "Mostrar _MENU_ registros",
|
||||
info: "Mostrando de _START_ a _END_ de _TOTAL_ registros",
|
||||
infoEmpty: "Mostrando 0 a 0 de 0 registros",
|
||||
infoFiltered: "(filtrado de _MAX_ registros totales)",
|
||||
loadingRecords: "Cargando...",
|
||||
zeroRecords: "No se encontraron templates " + (scope === 'personal' ? 'personalizados' : scope === 'agencia' ? 'de agencia' : 'globales'),
|
||||
emptyTable: "No hay templates " + (scope === 'personal' ? 'personalizados' : scope === 'agencia' ? 'de agencia' : 'globales'),
|
||||
paginate: {
|
||||
first: "Primero",
|
||||
previous: "Anterior",
|
||||
next: "Siguiente",
|
||||
last: "Último"
|
||||
}
|
||||
},
|
||||
pageLength: 10,
|
||||
responsive: true,
|
||||
drawCallback: function() {
|
||||
$(selector + ' tbody tr').addClass('fade-in');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ FUNCIÓN PARA CARGAR DATOS POR SECCIÓN
|
||||
function loadSectionData(section) {
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/templates_rapidos/ajax_lista_por_seccion',
|
||||
method: 'GET',
|
||||
data: { seccion: section },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
let table;
|
||||
switch(section) {
|
||||
case 'personal':
|
||||
table = $('#personalTemplatesTable').DataTable();
|
||||
break;
|
||||
case 'agencia':
|
||||
table = $('#agenciaTemplatesTable').DataTable();
|
||||
break;
|
||||
case 'global':
|
||||
table = $('#globalTemplatesTable').DataTable();
|
||||
break;
|
||||
}
|
||||
|
||||
if (table) {
|
||||
table.clear().rows.add(response.data || []).draw();
|
||||
}
|
||||
|
||||
updateTemplateCount();
|
||||
},
|
||||
error: function() {
|
||||
console.error('Error cargando templates de sección:', section);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ FUNCIÓN PARA APLICAR FILTROS
|
||||
function applyFilter(filter) {
|
||||
switch(filter) {
|
||||
case 'all':
|
||||
$('.templates-section').show();
|
||||
$('.section-content.show').each(function() {
|
||||
if (!$(this).data('loaded')) {
|
||||
const section = $(this).prev().data('toggle');
|
||||
loadSectionData(section);
|
||||
$(this).data('loaded', true);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'personal':
|
||||
$('.templates-section').hide();
|
||||
$('.templates-section').first().show();
|
||||
if (!$('#personal-templates').hasClass('show')) {
|
||||
$('.section-header').first().click();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'agencia':
|
||||
$('.templates-section').hide();
|
||||
$('.templates-section').eq(1).show();
|
||||
if (!$('#agencia-templates').hasClass('show')) {
|
||||
$('.section-header').eq(1).click();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mas-usados':
|
||||
// Mostrar todos pero filtrar por más de 5 usos
|
||||
$('.templates-section').show();
|
||||
$('.section-content.show').each(function() {
|
||||
const table = $(this).find('table').DataTable();
|
||||
table.search('').columns().search('').draw();
|
||||
table.column(3).search('^([5-9]|[1-9][0-9]+)$', true, false).draw();
|
||||
});
|
||||
break;
|
||||
|
||||
case 'recientes':
|
||||
// Mostrar todos pero filtrar por últimos 7 días
|
||||
$('.templates-section').show();
|
||||
const weekAgo = new Date();
|
||||
weekAgo.setDate(weekAgo.getDate() - 7);
|
||||
$('.section-content.show').each(function() {
|
||||
const table = $(this).find('table').DataTable();
|
||||
table.search('').columns().search('').draw();
|
||||
// Filtrar por fecha en columna 4
|
||||
});
|
||||
break;
|
||||
}
|
||||
updateTemplateCount();
|
||||
}
|
||||
|
||||
// ✅ FUNCIÓN PARA ACTUALIZAR CONTADOR DE TEMPLATES
|
||||
function updateTemplateCount() {
|
||||
let total = 0;
|
||||
$('.section-content:visible').each(function() {
|
||||
const table = $(this).find('table').DataTable();
|
||||
if (table) {
|
||||
total += table.rows({search: 'applied'}).count();
|
||||
}
|
||||
});
|
||||
|
||||
$('#template-count').text(`${total} templates disponibles`);
|
||||
}
|
||||
|
||||
// ✅ FUNCIÓN PARA REFRESCAR TABLAS ACTIVAS
|
||||
function refreshActiveTables() {
|
||||
$('.section-content:visible').each(function() {
|
||||
if ($(this).data('loaded')) {
|
||||
const section = $(this).prev().data('toggle');
|
||||
loadSectionData(section);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ FUNCIONES DE ACCIONES
|
||||
function aplicarTemplate(id, nombre) {
|
||||
Swal.fire({
|
||||
title: '🚀 Aplicar Template',
|
||||
text: `¿Crear nueva solicitud con el template "${nombre}"?`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<i class="fas fa-rocket me-1"></i>Aplicar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#28a745',
|
||||
cancelButtonColor: '#6c757d'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = `/IMPORTADORES/solicitud_importacion/crear?template=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editarTemplate(id) {
|
||||
window.location.href = `/IMPORTADORES/templates_rapidos/editar/${id}`;
|
||||
}
|
||||
|
||||
function duplicarTemplate(id, nombre) {
|
||||
Swal.fire({
|
||||
title: '📋 Duplicar Template',
|
||||
text: `¿Crear una copia de "${nombre}"?`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<i class="fas fa-copy me-1"></i>Duplicar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#17a2b8',
|
||||
cancelButtonColor: '#6c757d'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/templates_rapidos/duplicar',
|
||||
method: 'POST',
|
||||
data: { id: id },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
Swal.fire({
|
||||
title: '✅ ¡Duplicado!',
|
||||
text: 'Template duplicado exitosamente',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire('Error', response.message || 'No se pudo duplicar', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Error', 'Error de conexión', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function eliminarTemplate(id, nombre) {
|
||||
Swal.fire({
|
||||
title: '⚠️ Eliminar Template',
|
||||
html: `¿Estás seguro de eliminar "<strong>${nombre}</strong>"?<br><small class="text-muted">Esta acción no se puede deshacer</small>`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '<i class="fas fa-trash me-1"></i>Eliminar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
confirmButtonColor: '#dc3545',
|
||||
cancelButtonColor: '#6c757d',
|
||||
focusCancel: true
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url: '/IMPORTADORES/templates_rapidos/eliminar',
|
||||
method: 'POST',
|
||||
data: { id: id },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
Swal.fire({
|
||||
title: '✅ ¡Eliminado!',
|
||||
text: 'Template eliminado exitosamente',
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire('Error', response.message || 'No se pudo eliminar', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Error', 'Error de conexión', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function verTemplate(id) {
|
||||
// Abrir modal con detalles del template
|
||||
$.ajax({
|
||||
url: `/IMPORTADORES/templates_rapidos/ver/${id}`,
|
||||
method: 'GET',
|
||||
success: function(response) {
|
||||
Swal.fire({
|
||||
title: '📄 Detalles del Template',
|
||||
html: response,
|
||||
width: '80%',
|
||||
showCloseButton: true,
|
||||
showConfirmButton: false,
|
||||
customClass: {
|
||||
container: 'template-preview-modal'
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Error', 'No se pudo cargar el template', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ✅ MANEJO DE EVENTOS GLOBALES
|
||||
$(document).on('click', '.btn-aplicar', function(e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('id');
|
||||
const nombre = $(this).data('nombre');
|
||||
aplicarTemplate(id, nombre);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-editar', function(e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('id');
|
||||
editarTemplate(id);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-duplicar', function(e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('id');
|
||||
const nombre = $(this).data('nombre');
|
||||
duplicarTemplate(id, nombre);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-eliminar', function(e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('id');
|
||||
const nombre = $(this).data('nombre');
|
||||
eliminarTemplate(id, nombre);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-ver', function(e) {
|
||||
e.preventDefault();
|
||||
const id = $(this).data('id');
|
||||
verTemplate(id);
|
||||
});
|
||||
|
||||
// ✅ TOOLTIPS MEJORADOS
|
||||
$(document).on('mouseenter', '[title]', function() {
|
||||
$(this).tooltip('show');
|
||||
});
|
||||
|
||||
// ✅ ANIMACIONES DE HOVER EN FILAS
|
||||
$(document).on('mouseenter', 'tbody tr', function() {
|
||||
$(this).addClass('table-active');
|
||||
}).on('mouseleave', 'tbody tr', function() {
|
||||
$(this).removeClass('table-active');
|
||||
});
|
||||
|
||||
// ✅ NOTIFICACIONES DE ACTUALIZACIÓN
|
||||
function showUpdateNotification() {
|
||||
const toast = $(`
|
||||
<div class="toast-notification">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-sync-alt me-2 text-primary"></i>
|
||||
<span>Templates actualizados</span>
|
||||
<button type="button" class="btn-close btn-close-sm ms-auto"></button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
$('body').append(toast);
|
||||
toast.fadeIn();
|
||||
|
||||
setTimeout(() => {
|
||||
toast.fadeOut(() => toast.remove());
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ✅ ESTILOS ADICIONALES */
|
||||
.toast-notification {
|
||||
position: fixed;
|
||||
bottom: 100px;
|
||||
left: 30px;
|
||||
background: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
z-index: 1050;
|
||||
display: none;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.template-preview-modal {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.template-preview-modal .swal2-html-container {
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Mejorar responsive de tablas */
|
||||
@media (max-width: 768px) {
|
||||
.template-filters {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.help-float-btn {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.shortcuts-modal {
|
||||
width: 280px;
|
||||
right: 20px;
|
||||
bottom: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Estados de carga */
|
||||
.loading-row {
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user