CRUD gestión de locaciones
This commit is contained in:
286
views/locaciones/alta_locaciones.php
Normal file
286
views/locaciones/alta_locaciones.php
Normal file
@@ -0,0 +1,286 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</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>
|
||||
<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; }
|
||||
.hide { display: none !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🗺️ Nuevas Locaciones</h4>
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- Nuevo Estado -->
|
||||
<div class="col-md">
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form id="estadoForm">
|
||||
<h4 class="mb-4 text-dark">➕ Nuevo Estado</h4><br>
|
||||
<!-- País -->
|
||||
<div class="col-md-12">
|
||||
<label for="paisEstado" class="form-label">País *</label>
|
||||
<select id="paisEstado" name="pais" class="form-select" required>
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>">
|
||||
<?= htmlspecialchars($p['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div><br><br><br>
|
||||
|
||||
<!-- Estado -->
|
||||
<div class="col-md-12">
|
||||
<label for="entidadEstado" class="form-label">Entidad / Provincia *</label>
|
||||
<input id="entidadEstado" name="entidad" class="form-control" required>
|
||||
</div><br><br>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-plus"></i> Registrar Estado
|
||||
</button>
|
||||
<a href="/IMPORTADORES/agentes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nueva Ciudad -->
|
||||
<div class="col-md">
|
||||
<div class="card p-4 bg-white shadow-sm">
|
||||
<form id="ciudadForm" action="/IMPORTADORES/agentes/guadarCiudad" method="POST" enctype="multipart/form-data">
|
||||
<h4 class="mb-4 text-dark">➕ Nueva Ciudad</h4>
|
||||
<!-- País -->
|
||||
<div class="col-md-12">
|
||||
<label for="paisCiudad" class="form-label">País *</label>
|
||||
<select id="paisCiudad" name="pais" class="form-select" required>
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>">
|
||||
<?= htmlspecialchars($p['nombre']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div><br>
|
||||
|
||||
<!-- Estado -->
|
||||
<div class="col-md-12">
|
||||
<label for="entidadCiudad" class="form-label">Entidad / Provincia *</label>
|
||||
<select id="estadoCiudad" name="entidad" class="form-select" required disabled>
|
||||
<option value="">Primero país…</option>
|
||||
</select>
|
||||
</div><br>
|
||||
|
||||
<!-- Ciudad -->
|
||||
<div class="col-md-12">
|
||||
<label for="nombreCiudad" class="form-label">Ciudad *</label>
|
||||
<input id="nombreCiudad" name="ciudad" class="form-control" required>
|
||||
</div><br>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-plus"></i> Registrar Ciudad
|
||||
</button>
|
||||
<a href="/IMPORTADORES/agentes/lista" class="btn btn-secondary ms-2">Cancelar</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Función para cargar estados
|
||||
function cargarEstados(paisId, selectElement) {
|
||||
selectElement.innerHTML = '<option>Cargando...</option>';
|
||||
selectElement.disabled = true;
|
||||
|
||||
fetch(`/IMPORTADORES/agentes/estados?pais=${paisId}`)
|
||||
.then(response => response.json())
|
||||
.then(estados => {
|
||||
selectElement.innerHTML = '<option value="">Selecciona estado</option>';
|
||||
estados.forEach(estado => {
|
||||
const option = new Option(estado.nombre, estado.id_estado);
|
||||
selectElement.add(option);
|
||||
});
|
||||
selectElement.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
selectElement.innerHTML = '<option value="">Error al cargar</option>';
|
||||
});
|
||||
}
|
||||
|
||||
// Evento para cargar estados cuando se selecciona país en formulario de ciudad
|
||||
document.getElementById('paisCiudad').addEventListener('change', function(e) {
|
||||
const paisId = e.target.value;
|
||||
const estadoSelect = document.getElementById('estadoCiudad');
|
||||
|
||||
if (paisId) {
|
||||
cargarEstados(paisId, estadoSelect);
|
||||
} else {
|
||||
estadoSelect.innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
estadoSelect.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Formulario de nuevo estado
|
||||
document.getElementById('estadoForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Obtener valores directamente
|
||||
const paisSelect = document.getElementById('paisEstado');
|
||||
const entidadInput = document.getElementById('entidadEstado');
|
||||
|
||||
const paisValue = paisSelect.value;
|
||||
const entidadValue = entidadInput.value.trim();
|
||||
|
||||
// Validación en frontend
|
||||
if (!paisValue) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo requerido',
|
||||
text: 'Debe seleccionar un país'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entidadValue) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Campo requerido',
|
||||
text: 'Debe ingresar el nombre del estado'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Crear FormData manualmente
|
||||
const formData = new FormData();
|
||||
formData.append('pais', paisValue);
|
||||
formData.append('entidad', entidadValue);
|
||||
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// Debug: mostrar lo que se va a enviar
|
||||
console.log('Enviando:', {
|
||||
pais: paisValue,
|
||||
entidad: entidadValue
|
||||
});
|
||||
|
||||
fetch('/IMPORTADORES/agentes/guardarEstado', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
this.reset();
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al procesar la solicitud'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
// Formulario de nueva ciudad
|
||||
document.getElementById('ciudadForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(this);
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
fetch('/IMPORTADORES/agentes/guardarCiudad', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
this.reset();
|
||||
// Resetear el select de estados
|
||||
document.getElementById('estadoCiudad').innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
document.getElementById('estadoCiudad').disabled = true;
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al procesar la solicitud'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
492
views/locaciones/gestion_locaciones.php
Normal file
492
views/locaciones/gestion_locaciones.php
Normal file
@@ -0,0 +1,492 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_agente.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dashboard | Agente Aduanal</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;}
|
||||
.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; /* Ancho del sidebar */ } }
|
||||
/* 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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🌍 Locaciones</h4>
|
||||
<a href="/IMPORTADORES/agentes/alta" class="btn btn-success mb-3">➕ Agregar Locación</a>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-locaciones">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>ID País</th>
|
||||
<th>País</th>
|
||||
<th>ISO3</th>
|
||||
<th>ID Estado</th>
|
||||
<th>Estado</th>
|
||||
<th>ID Ciudad</th>
|
||||
<th>Ciudad</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($locaciones as $loc): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['iso3']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
|
||||
<td>
|
||||
<!-- Botones de acción (editar/eliminar) -->
|
||||
<?php if (!empty($loc['id_ciudad'])): ?>
|
||||
<!-- Es una ciudad -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('ciudad', <?= $loc['id_ciudad'] ?>, '<?= htmlspecialchars($loc['nombre_ciudad'], ENT_QUOTES) ?>', '', '', '', <?= $loc['id_estado'] ?>)">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('ciudad', <?= $loc['id_ciudad'] ?>, '<?= htmlspecialchars($loc['nombre_ciudad'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php elseif (!empty($loc['id_estado'])): ?>
|
||||
<!-- Es un estado - CORREGIDO: Obtener abreviatura correctamente -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('estado', <?= $loc['id_estado'] ?>, '<?= htmlspecialchars($loc['nombre_estado'], ENT_QUOTES) ?>', '', '', '<?= htmlspecialchars($loc['abreviatura'] ?? '', ENT_QUOTES) ?>', <?= $loc['id_pais'] ?>)">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('estado', <?= $loc['id_estado'] ?>, '<?= htmlspecialchars($loc['nombre_estado'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<!-- Es un país -->
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editarItem('pais', <?= $loc['id_pais'] ?>, '<?= htmlspecialchars($loc['nombre_pais'], ENT_QUOTES) ?>', '<?= htmlspecialchars($loc['iso2'] ?? '', ENT_QUOTES) ?>', '<?= htmlspecialchars($loc['iso3'] ?? '', ENT_QUOTES) ?>')">
|
||||
✏️
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="eliminarItem('pais', <?= $loc['id_pais'] ?>, '<?= htmlspecialchars($loc['nombre_pais'], ENT_QUOTES) ?>')">
|
||||
🗑️
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal para Editar -->
|
||||
<div class="modal fade" id="editarModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">✏️ Editar <span id="tipoItem"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form id="editarForm">
|
||||
<div class="modal-body">
|
||||
<input name="editTipo" type="hidden" id="editTipo">
|
||||
<input name="editId" type="hidden" id="editId">
|
||||
|
||||
<!-- Campos para País -->
|
||||
<div id="camposPais" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editNombrePais" class="form-label">Nombre del País *</label>
|
||||
<input name="editNombrePais" type="text" class="form-control" id="editNombrePais">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="editIso2" class="form-label">ISO2</label>
|
||||
<input name="editIso2" type="text" class="form-control" id="editIso2" maxlength="2">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="editIso3" class="form-label">ISO3</label>
|
||||
<input name="editIso3" type="text" class="form-control" id="editIso3" maxlength="3">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos para Estado -->
|
||||
<div id="camposEstado" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editPaisEstado" class="form-label">País *</label>
|
||||
<select name="editPaisEstado" class="form-select" id="editPaisEstado">
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editNombreEstado" class="form-label">Nombre del Estado *</label>
|
||||
<input name="editNombreEstado" type="text" class="form-control" id="editNombreEstado">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editAbreviatura" class="form-label">Abreviatura</label>
|
||||
<input name="editAbreviatura" type="text" class="form-control" id="editAbreviatura" maxlength="10">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Campos para Ciudad -->
|
||||
<div id="camposCiudad" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="editPaisCiudad" class="form-label">País *</label>
|
||||
<select name="editPaisCiudad" class="form-select" id="editPaisCiudad">
|
||||
<option value="">Selecciona país</option>
|
||||
<?php foreach($paises as $p): ?>
|
||||
<option value="<?= $p['id_pais'] ?>"><?= htmlspecialchars($p['nombre']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editEstadoCiudad" class="form-label">Estado *</label>
|
||||
<select name="editEstadoCiudad" class="form-select" id="editEstadoCiudad">
|
||||
<option value="">Primero selecciona país</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editNombreCiudad" class="form-label">Nombre de la Ciudad *</label>
|
||||
<input name="editNombreCiudad" type="text" class="form-control" id="editNombreCiudad">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#tabla-locaciones').DataTable({
|
||||
order: [],
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Función para cargar estados
|
||||
function cargarEstados(paisId, selectElement, estadoSeleccionado = null) {
|
||||
selectElement.innerHTML = '<option>Cargando...</option>';
|
||||
selectElement.disabled = true;
|
||||
|
||||
fetch(`/IMPORTADORES/agentes/estados?pais=${paisId}`)
|
||||
.then(response => response.json())
|
||||
.then(estados => {
|
||||
selectElement.innerHTML = '<option value="">Selecciona estado</option>';
|
||||
estados.forEach(estado => {
|
||||
const option = new Option(estado.nombre, estado.id_estado);
|
||||
if (estadoSeleccionado && estado.id_estado == estadoSeleccionado) {
|
||||
option.selected = true;
|
||||
}
|
||||
selectElement.add(option);
|
||||
});
|
||||
selectElement.disabled = false;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
selectElement.innerHTML = '<option value="">Error al cargar</option>';
|
||||
});
|
||||
}
|
||||
|
||||
// Evento para cargar estados en modal de edición
|
||||
document.getElementById('editPaisCiudad').addEventListener('change', function(e) {
|
||||
const paisId = e.target.value;
|
||||
const estadoSelect = document.getElementById('editEstadoCiudad');
|
||||
|
||||
if (paisId) {
|
||||
cargarEstados(paisId, estadoSelect);
|
||||
} else {
|
||||
estadoSelect.innerHTML = '<option value="">Primero selecciona país</option>';
|
||||
estadoSelect.disabled = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Función para abrir modal de edición
|
||||
function editarItem(tipo, id, nombre, iso2 = '', iso3 = '', abreviatura = '', padreId = null) {
|
||||
document.getElementById('editTipo').value = tipo;
|
||||
document.getElementById('editId').value = id;
|
||||
document.getElementById('tipoItem').textContent = tipo.charAt(0).toUpperCase() + tipo.slice(1);
|
||||
|
||||
// Ocultar todos los campos
|
||||
document.getElementById('camposPais').style.display = 'none';
|
||||
document.getElementById('camposEstado').style.display = 'none';
|
||||
document.getElementById('camposCiudad').style.display = 'none';
|
||||
|
||||
if (tipo === 'pais') {
|
||||
document.getElementById('camposPais').style.display = 'block';
|
||||
document.getElementById('editNombrePais').value = nombre;
|
||||
document.getElementById('editIso2').value = iso2;
|
||||
document.getElementById('editIso3').value = iso3;
|
||||
|
||||
} else if (tipo === 'estado') {
|
||||
document.getElementById('camposEstado').style.display = 'block';
|
||||
document.getElementById('editNombreEstado').value = nombre;
|
||||
document.getElementById('editAbreviatura').value = abreviatura;
|
||||
document.getElementById('editPaisEstado').value = padreId;
|
||||
|
||||
} else if (tipo === 'ciudad') {
|
||||
document.getElementById('camposCiudad').style.display = 'block';
|
||||
document.getElementById('editNombreCiudad').value = nombre;
|
||||
|
||||
// Necesitamos obtener el país del estado para cargar los estados
|
||||
if (padreId) {
|
||||
fetch(`/IMPORTADORES/agentes/obtenerPaisPorEstado?estado=${padreId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('editPaisCiudad').value = data.pais_id;
|
||||
cargarEstados(data.pais_id, document.getElementById('editEstadoCiudad'), padreId);
|
||||
} else {
|
||||
console.error('Error al obtener país:', data.message);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'No se pudo cargar la información del país' + data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Error al cargar la información'
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editarModal')).show();
|
||||
}
|
||||
|
||||
// Función para eliminar item
|
||||
function eliminarItem(tipo, id, nombre) {
|
||||
Swal.fire({
|
||||
title: '¿Estás seguro?',
|
||||
text: `¿Deseas eliminar el ${tipo} "${nombre}"?`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonText: 'Sí, eliminar',
|
||||
cancelButtonText: 'Cancelar'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch('/IMPORTADORES/agentes/eliminar', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tipo: tipo,
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Eliminado!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Ocurrió un error al eliminar'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// REEMPLAZA tu función de submit del formulario con esta versión corregida
|
||||
document.getElementById('editarForm').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const tipo = document.getElementById('editTipo').value;
|
||||
const id = document.getElementById('editId').value;
|
||||
|
||||
// VALIDACIÓN BÁSICA
|
||||
if (!tipo || !id) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Faltan datos requeridos'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append('tipo', tipo);
|
||||
formData.append('id', id);
|
||||
|
||||
// Determinar endpoint y datos según el tipo
|
||||
let endpoint = '';
|
||||
let isValid = true;
|
||||
|
||||
if (tipo === 'pais') {
|
||||
const nombre = document.getElementById('editNombrePais').value.trim();
|
||||
if (!nombre) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre del país es obligatorio'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarPais';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('iso2', document.getElementById('editIso2').value.trim());
|
||||
formData.append('iso3', document.getElementById('editIso3').value.trim());
|
||||
|
||||
} else if (tipo === 'estado') {
|
||||
const nombre = document.getElementById('editNombreEstado').value.trim();
|
||||
const paisId = document.getElementById('editPaisEstado').value;
|
||||
|
||||
if (!nombre || !paisId) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre del estado y el país son obligatorios'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarEstado';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('abreviatura', document.getElementById('editAbreviatura').value.trim());
|
||||
formData.append('pais_id', paisId);
|
||||
|
||||
} else if (tipo === 'ciudad') {
|
||||
const nombre = document.getElementById('editNombreCiudad').value.trim();
|
||||
const estadoId = document.getElementById('editEstadoCiudad').value;
|
||||
|
||||
if (!nombre || !estadoId) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'El nombre de la ciudad y el estado son obligatorios'
|
||||
});
|
||||
return;
|
||||
}
|
||||
endpoint = '/IMPORTADORES/agentes/actualizarCiudad';
|
||||
formData.append('nombre', nombre);
|
||||
formData.append('estado_id', estadoId);
|
||||
}
|
||||
|
||||
const submitBtn = this.querySelector('button[type="submit"]');
|
||||
const originalText = submitBtn.innerHTML;
|
||||
|
||||
// INDICADOR DE CARGA
|
||||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Guardando...';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// ENVÍO AJAX al endpoint específico
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
// VERIFICAR QUE LA RESPUESTA SEA OK
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '¡Éxito!',
|
||||
text: data.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
}).then(() => {
|
||||
// CERRAR MODAL Y RECARGAR
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('editarModal'));
|
||||
if (modal) {
|
||||
modal.hide();
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: data.message || 'Error desconocido'
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error completo:', error);
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error de conexión',
|
||||
text: 'No se pudo procesar la solicitud. Verifique su conexión.'
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
// RESTAURAR BOTÓN
|
||||
submitBtn.innerHTML = originalText;
|
||||
submitBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
85
views/locaciones/lista.php
Normal file
85
views/locaciones/lista.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php include __DIR__ . '/../partials/sidebar_importador.php'; ?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>🌍 Locaciones</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;}
|
||||
.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; /* Ancho del sidebar */ } }
|
||||
/* 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; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="content">
|
||||
<h4 class="mb-4">🗺️ Locaciones</h4>
|
||||
<div class="card p-3 shadow-sm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped" id="tabla-locaciones">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>ID País</th>
|
||||
<th>País</th>
|
||||
<th>ISO3</th>
|
||||
<th>ID Estado</th>
|
||||
<th>Estado</th>
|
||||
<th>ID Ciudad</th>
|
||||
<th>Ciudad</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($locaciones as $loc): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($loc['id_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_pais']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['iso3']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_estado']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['id_ciudad']) ?></td>
|
||||
<td><?= htmlspecialchars($loc['nombre_ciudad']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('#tabla-locaciones').DataTable({
|
||||
order: [],
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user