first commit
This commit is contained in:
commit
5d534f9c24
|
@ -0,0 +1,476 @@
|
|||
<?php
|
||||
require_once 'database/database.php';
|
||||
|
||||
$query = "SELECT DISTINCT empresa, examen, certificacion FROM entrada";
|
||||
$result = $conexion->query($query);
|
||||
|
||||
$opciones = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$empresa = $row['empresa'];
|
||||
$examen = $row['examen'];
|
||||
$certificacion = $row['certificacion'];
|
||||
if (!isset($opciones[$empresa])) $opciones[$empresa] = [];
|
||||
if (!isset($opciones[$empresa][$examen])) $opciones[$empresa][$examen] = [];
|
||||
if (!in_array($certificacion, $opciones[$empresa][$examen])) {
|
||||
$opciones[$empresa][$examen][] = $certificacion;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Centro de Certificación LANIA</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
<link href="style/dashboard.css" rel="stylesheet">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
<div class="container py-5">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="display-5">Centro de Certificación LANIA</h1>
|
||||
<p class="lead">Formulario de registro para candidatos de exámenes PEARSON VUE</p>
|
||||
</div>
|
||||
|
||||
<div class="card shadow p-4">
|
||||
<form id="registroForm" action="model/login.php" method="POST">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre" class="form-label">Nombre completo</label>
|
||||
<input type="text" class="form-control" id="nombre" name="nombre" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 position-relative">
|
||||
<label for="correo" class="form-label">Correo electrónico</label>
|
||||
<input type="email" class="form-control" id="correo" name="correo" required autocomplete="off">
|
||||
<div id="sugerencias-correo" class="list-group position-absolute w-100" style="z-index: 1000;"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="telefono" class="form-label">Teléfono</label>
|
||||
<input type="text" class="form-control" id="telefono" name="telefono" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="genero" class="form-label">Género</label>
|
||||
<select class="form-select" id="genero" name="genero" required>
|
||||
<option value="">Selecciona una opción</option>
|
||||
<option value="Masculino">Masculino</option>
|
||||
<option value="Femenino">Femenino</option>
|
||||
<option value="Otro">Prefiero no especificar</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="edad" class="form-label">Edad</label>
|
||||
<select class="form-select" id="edad" name="edad" required>
|
||||
<option value="">Selecciona un rango de edad</option>
|
||||
<option value="menor">Menos de 17</option>
|
||||
<option value="18-21">18-21</option>
|
||||
<option value="22-25">22-25</option>
|
||||
<option value="26-30">26-30</option>
|
||||
<option value="31-35">31-35</option>
|
||||
<option value="36-40">36-40</option>
|
||||
<option value="41-45">41-45</option>
|
||||
<option value="46-50">46-50</option>
|
||||
<option value="51-55">51-55</option>
|
||||
<option value="56-60">56-60</option>
|
||||
<option value="mayor">Más de 60</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="estado_procedencia" class="form-label">Estado de procedencia</label>
|
||||
<input type="text" class="form-control" id="estado_procedencia" name="estado_procedencia" placeholder="Ingresa tu estado de procedencia" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label for="identificacion" class="form-label">Identificación</label>
|
||||
<select id="identificacion" name="identificacion" class="form-select" required>
|
||||
<option value="">Seleccione una identificación</option>
|
||||
<option value="CE">Credencial de Estudiante</option>
|
||||
<option value="INE">INE</option>
|
||||
<option value="C.M">Cartilla Militar</option>
|
||||
<option value="Pasaporte">Pasaporte</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label for="empresa" class="form-label">Proveedor</label>
|
||||
<select id="empresa" name="empresa" class="form-select" required onchange="mostrarInputOtro('empresa'); actualizarExamenes();">
|
||||
<option value="">Seleccione un proveedor</option>
|
||||
<?php
|
||||
$proveedoresPredefinidos = ['Microsoft', 'Oracle', 'Cisco'];
|
||||
foreach ($proveedoresPredefinidos as $proveedor) {
|
||||
echo "<option value='$proveedor'>$proveedor</option>";
|
||||
}
|
||||
|
||||
foreach ($opciones as $empresa => $examenes) {
|
||||
if (!in_array($empresa, $proveedoresPredefinidos)) {
|
||||
echo "<option value='$empresa'>$empresa</option>";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<option value="otro">Otro</option>
|
||||
</select>
|
||||
<input type="text" id="empresa_otro" name="empresa_otro" class="form-control mt-2 d-none" placeholder="Especifica otro proveedor">
|
||||
</div>
|
||||
<!-- Examen -->
|
||||
<div class="col-md-4">
|
||||
<label for="examen" class="form-label">Examen</label>
|
||||
<select id="examen" name="examen" class="form-select" required onchange="mostrarInputOtro('examen'); actualizarCertificaciones();">
|
||||
<option value="">Selecciona un examen</option>
|
||||
<option value="otro">Otro</option>
|
||||
</select>
|
||||
<input type="text" id="examen_otro" name="examen_otro" class="form-control mt-2 d-none" placeholder="Especifica otro examen">
|
||||
</div>
|
||||
|
||||
<!-- Certificación -->
|
||||
<div class="col-md-4">
|
||||
<label for="certificacion" class="form-label">Certificación</label>
|
||||
<select id="certificacion" name="certificacion" class="form-select" required onchange="mostrarInputOtro('certificacion')">
|
||||
<option value="">Selecciona una certificación</option>
|
||||
<option value="otro">Otro</option>
|
||||
</select>
|
||||
<input type="text" id="certificacion_otro" name="certificacion_otro" class="form-control mt-2 d-none" placeholder="Especifica otra certificación">
|
||||
</div>
|
||||
|
||||
|
||||
<input type="hidden" id="hora_local" name="h_entrada">
|
||||
|
||||
<div class="mt-4 text-end">
|
||||
<button type="submit" class="btn btn-primary">Enviar registro</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de confirmación -->
|
||||
<div class="modal fade" id="modalConfirmacion" tabindex="-1" aria-labelledby="modalConfirmacionLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success text-white">
|
||||
<h5 class="modal-title" id="modalConfirmacionLabel">¡Registro exitoso!</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p id="mensajeConfirmacion" class="fs-5 fw-bold">Registro exitoso. Bienvenido a LANIA 😊</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal">Aceptar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const correoInput = document.getElementById('correo');
|
||||
const sugerenciasDiv = document.getElementById('sugerencias-correo');
|
||||
const dominios = ['@gmail.com', '@outlook.com', '@hotmail.com', '@lania.edu.mx'];
|
||||
|
||||
correoInput.addEventListener('input', () => {
|
||||
const valor = correoInput.value.trim();
|
||||
sugerenciasDiv.innerHTML = '';
|
||||
|
||||
if (valor && !valor.includes('@')) {
|
||||
dominios.forEach(dominio => {
|
||||
const sugerencia = document.createElement('div');
|
||||
sugerencia.classList.add('list-group-item', 'list-group-item-action');
|
||||
sugerencia.textContent = valor + dominio;
|
||||
sugerencia.onclick = () => {
|
||||
correoInput.value = sugerencia.textContent;
|
||||
sugerenciasDiv.innerHTML = '';
|
||||
};
|
||||
sugerenciasDiv.appendChild(sugerencia);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!sugerenciasDiv.contains(e.target) && e.target !== correoInput) {
|
||||
sugerenciasDiv.innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
const datos = {
|
||||
Microsoft: {
|
||||
"AZ-900": ["Fundamentals", "Associate"],
|
||||
"AZ-104": ["Associate", "Expert"],
|
||||
"MS-900": ["Fundamentals"]
|
||||
},
|
||||
Oracle: {
|
||||
"Oracle Database SQL": ["Associate", "Professional"],
|
||||
"Java SE Programmer": ["Associate", "Professional", "Master"]
|
||||
},
|
||||
Cisco: {
|
||||
"CCNA": ["Associate"],
|
||||
"CCNP": ["Professional"],
|
||||
"CCIE": ["Expert"]
|
||||
}
|
||||
};
|
||||
|
||||
const datosBD = <?php echo json_encode($opciones); ?>;
|
||||
for (const proveedor in datosBD) {
|
||||
if (!datos[proveedor]) datos[proveedor] = {};
|
||||
for (const examen in datosBD[proveedor]) {
|
||||
if (!datos[proveedor][examen]) datos[proveedor][examen] = [];
|
||||
datosBD[proveedor][examen].forEach(cert => {
|
||||
if (!datos[proveedor][examen].includes(cert)) {
|
||||
datos[proveedor][examen].push(cert);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mostrarInputOtro(tipo) {
|
||||
const select = document.getElementById(tipo);
|
||||
const inputOtro = document.getElementById(tipo + '_otro');
|
||||
|
||||
if (select.value === 'otro') {
|
||||
inputOtro.classList.remove('d-none');
|
||||
inputOtro.required = true;
|
||||
} else {
|
||||
inputOtro.classList.add('d-none');
|
||||
inputOtro.value = '';
|
||||
inputOtro.required = false;
|
||||
}
|
||||
|
||||
// Si cambias proveedor, resetea examen y certificación
|
||||
if (tipo === 'empresa') {
|
||||
// Oculta y limpia examen_otro y certificacion_otro
|
||||
document.getElementById('examen_otro').classList.add('d-none');
|
||||
document.getElementById('examen_otro').value = '';
|
||||
document.getElementById('examen_otro').required = false;
|
||||
|
||||
document.getElementById('certificacion_otro').classList.add('d-none');
|
||||
document.getElementById('certificacion_otro').value = '';
|
||||
document.getElementById('certificacion_otro').required = false;
|
||||
}
|
||||
// Si cambias examen, resetea certificación
|
||||
if (tipo === 'examen') {
|
||||
document.getElementById('certificacion_otro').classList.add('d-none');
|
||||
document.getElementById('certificacion_otro').value = '';
|
||||
document.getElementById('certificacion_otro').required = false;
|
||||
}
|
||||
}
|
||||
|
||||
function actualizarExamenes() {
|
||||
const empresa = document.getElementById("empresa").value;
|
||||
const examenSelect = document.getElementById("examen");
|
||||
const certificacionSelect = document.getElementById("certificacion");
|
||||
|
||||
examenSelect.innerHTML = '<option value="">Selecciona un examen</option>';
|
||||
|
||||
if (empresa && datos[empresa]) {
|
||||
for (let examen in datos[empresa]) {
|
||||
const option = document.createElement("option");
|
||||
option.value = examen;
|
||||
option.textContent = examen;
|
||||
examenSelect.appendChild(option);
|
||||
}
|
||||
}
|
||||
if (!Array.from(examenSelect.options).some(opt => opt.value === "otro")) {
|
||||
const optionOtro = document.createElement("option");
|
||||
optionOtro.value = "otro";
|
||||
optionOtro.textContent = "Otro";
|
||||
examenSelect.appendChild(optionOtro);
|
||||
}
|
||||
|
||||
|
||||
// Reinicia certificaciones
|
||||
certificacionSelect.innerHTML = '<option value="">Selecciona una certificación</option>';
|
||||
if (!Array.from(certificacionSelect.options).some(opt => opt.value === "otro")) {
|
||||
const optionCertOtro = document.createElement("option");
|
||||
optionCertOtro.value = "otro";
|
||||
optionCertOtro.textContent = "Otro";
|
||||
certificacionSelect.appendChild(optionCertOtro);
|
||||
}
|
||||
}
|
||||
|
||||
function actualizarCertificaciones() {
|
||||
const empresa = document.getElementById("empresa").value;
|
||||
const examen = document.getElementById("examen").value;
|
||||
const certificacionSelect = document.getElementById("certificacion");
|
||||
|
||||
certificacionSelect.innerHTML = '<option value="">Selecciona una certificación</option>';
|
||||
|
||||
if (empresa && examen && datos[empresa] && datos[empresa][examen]) {
|
||||
datos[empresa][examen].forEach(cert => {
|
||||
const option = document.createElement("option");
|
||||
option.value = cert;
|
||||
option.textContent = cert;
|
||||
certificacionSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
// Solo agrega la opción "Otro" si no existe ya
|
||||
if (![...certificacionSelect.options].some(opt => opt.value === "otro")) {
|
||||
const optionOtro = document.createElement("option");
|
||||
optionOtro.value = "otro";
|
||||
optionOtro.textContent = "Otro";
|
||||
certificacionSelect.appendChild(optionOtro);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("registroForm").addEventListener("submit", function(e) {
|
||||
const ahora = new Date();
|
||||
const hora = ahora.toTimeString().slice(0,8); // "HH:MM:SS"
|
||||
document.getElementById("hora_local").value = hora;
|
||||
|
||||
e.preventDefault(); // Evitar el envío predeterminado del formulario
|
||||
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
const empresaSelect = document.getElementById('empresa');
|
||||
const empresaOtro = document.getElementById('empresa_otro');
|
||||
if (empresaSelect.value === 'otro' && empresaOtro.value.trim() !== '') {
|
||||
formData.set('empresa', empresaOtro.value.trim());
|
||||
}
|
||||
|
||||
const examenSelect = document.getElementById('examen');
|
||||
const examenOtro = document.getElementById('examen_otro');
|
||||
if (examenSelect.value === 'otro' && examenOtro.value.trim() !== '') {
|
||||
formData.set('examen', examenOtro.value.trim());
|
||||
}
|
||||
|
||||
const certSelect = document.getElementById('certificacion');
|
||||
const certOtro = document.getElementById('certificacion_otro');
|
||||
if (certSelect.value === 'otro' && certOtro.value.trim() !== '') {
|
||||
formData.set('certificacion', certOtro.value.trim());
|
||||
}
|
||||
|
||||
if (empresaSelect.value === empresaOtro.value.trim()) {
|
||||
if (!datos[empresaOtro.value.trim()]) {
|
||||
datos[empresaOtro.value.trim()] = {};
|
||||
}
|
||||
}
|
||||
if (examenSelect.value === examenOtro.value.trim()) {
|
||||
if (!datos[empresaSelect.value]) datos[empresaSelect.value] = {};
|
||||
if (!datos[empresaSelect.value][examenOtro.value.trim()]) {
|
||||
datos[empresaSelect.value][examenOtro.value.trim()] = [];
|
||||
}
|
||||
}
|
||||
if (certSelect.value === certOtro.value.trim()) {
|
||||
if (!datos[empresaSelect.value]) datos[empresaSelect.value] = {};
|
||||
if (!datos[empresaSelect.value][examenSelect.value]) datos[empresaSelect.value][examenSelect.value] = [];
|
||||
if (!datos[empresaSelect.value][examenSelect.value].includes(certOtro.value.trim())) {
|
||||
datos[empresaSelect.value][examenSelect.value].push(certOtro.value.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (empresaSelect.value === 'otro' && empresaOtro.value.trim() !== '') {
|
||||
const nuevoProveedor = empresaOtro.value.trim();
|
||||
if (!datos[nuevoProveedor]) {
|
||||
datos[nuevoProveedor] = {};
|
||||
const option = document.createElement("option");
|
||||
option.value = nuevoProveedor;
|
||||
option.textContent = nuevoProveedor;
|
||||
empresaSelect.insertBefore(option, empresaSelect.querySelector('option[value="otro"]'));
|
||||
}
|
||||
localStorage.setItem('datosLania', JSON.stringify(datos));
|
||||
}
|
||||
|
||||
if (examenSelect.value === 'otro' && examenOtro.value.trim() !== '') {
|
||||
const proveedor = empresaSelect.value === 'otro' ? empresaOtro.value.trim() : empresaSelect.value;
|
||||
const nuevoExamen = examenOtro.value.trim();
|
||||
if (!datos[proveedor]) datos[proveedor] = {};
|
||||
if (!datos[proveedor][nuevoExamen]) {
|
||||
datos[proveedor][nuevoExamen] = [];
|
||||
// Agregar la opción al select de examen
|
||||
const option = document.createElement("option");
|
||||
option.value = nuevoExamen;
|
||||
option.textContent = nuevoExamen;
|
||||
examenSelect.insertBefore(option, examenSelect.querySelector('option[value="otro"]'));
|
||||
}
|
||||
}
|
||||
|
||||
if (certSelect.value === 'otro' && certOtro.value.trim() !== '') {
|
||||
const proveedor = empresaSelect.value === 'otro' ? empresaOtro.value.trim() : empresaSelect.value;
|
||||
const examen = examenSelect.value === 'otro' ? examenOtro.value.trim() : examenSelect.value;
|
||||
const nuevaCert = certOtro.value.trim();
|
||||
if (!datos[proveedor]) datos[proveedor] = {};
|
||||
if (!datos[proveedor][examen]) datos[proveedor][examen] = [];
|
||||
if (!datos[proveedor][examen].includes(nuevaCert)) {
|
||||
datos[proveedor][examen].push(nuevaCert);
|
||||
// Agregar la opción al select de certificación
|
||||
const option = document.createElement("option");
|
||||
option.value = nuevaCert;
|
||||
option.textContent = nuevaCert;
|
||||
certSelect.insertBefore(option, certSelect.querySelector('option[value="otro"]'));
|
||||
}
|
||||
}
|
||||
|
||||
fetch("model/login.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(data => {
|
||||
// Mostrar el modal de confirmación
|
||||
const modalConfirmacion = new bootstrap.Modal(document.getElementById('modalConfirmacion'));
|
||||
modalConfirmacion.show();
|
||||
form.reset(); // Reiniciar el formulario
|
||||
|
||||
const examenSelect = document.getElementById("examen");
|
||||
const certificacionSelect = document.getElementById("certificacion");
|
||||
|
||||
examenSelect.innerHTML = '<option value="">Selecciona un examen</option><option value="otro">Otro</option>';
|
||||
|
||||
certificacionSelect.innerHTML = '<option value="">Selecciona una certificación</option><option value="otro">Otro</option>';
|
||||
|
||||
['empresa', 'examen', 'certificacion'].forEach(function(campo) {
|
||||
const inputOtro = document.getElementById(campo + '_otro');
|
||||
if (inputOtro) {
|
||||
inputOtro.classList.add('d-none');
|
||||
inputOtro.value = '';
|
||||
inputOtro.removeAttribute('required');
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error al enviar:", error);
|
||||
alert("Ocurrió un error al registrar la entrada.");
|
||||
});
|
||||
|
||||
|
||||
// Guardar datos en localStorage
|
||||
localStorage.setItem('datosLania', JSON.stringify(datos));
|
||||
});
|
||||
|
||||
const datosGuardados = localStorage.getItem('datosLania');
|
||||
if (datosGuardados) {
|
||||
const datosExtra = JSON.parse(datosGuardados);
|
||||
for (const proveedor in datosExtra) {
|
||||
if (!datos[proveedor]) datos[proveedor] = {};
|
||||
for (const examen in datosExtra[proveedor]) {
|
||||
if (!datos[proveedor][examen]) datos[proveedor][examen] = [];
|
||||
datosExtra[proveedor][examen].forEach(cert => {
|
||||
if (!datos[proveedor][examen].includes(cert)) {
|
||||
datos[proveedor][examen].push(cert);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.sidebar-menu li').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
const menuEntrada = document.getElementById('menu-entrada');
|
||||
if (menuEntrada) {
|
||||
menuEntrada.classList.add('active');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
require_once 'database/database.php';
|
||||
|
||||
$query = "SELECT id_usuario, nombre FROM entrada";
|
||||
$resultado = $conexion->query($query);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Centro de Certificación LANIA</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome para íconos -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
<link href="style/dashboard.css" rel="stylesheet">
|
||||
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
|
||||
<?php include 'sidebar.php'; ?>
|
||||
|
||||
|
||||
<div class="main-content">
|
||||
<div class="container py-5">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="display-5">Centro de Certificación LANIA</h1>
|
||||
<p class="lead">Formulario de salida para candidatos de exámenes PEARSON VUE</p>
|
||||
</div>
|
||||
|
||||
<div class="card shadow p-4">
|
||||
<form id="formSalida" method="POST">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="nombre" class="form-label">Nombre del Usuario</label>
|
||||
<select class="form-select" id="nombre" name="id_usuario" required>
|
||||
<option value="">Selecciona tu nombre</option>
|
||||
<?php while ($row = $resultado->fetch_assoc()): ?>
|
||||
<option value="<?= $row['id_usuario'] ?>"><?= htmlspecialchars($row['nombre']) ?></option>
|
||||
<?php endwhile; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-4 survey">
|
||||
<h5>Encuesta de satisfacción</h5>
|
||||
|
||||
<?php
|
||||
$preguntas = [
|
||||
'atencion_personal' => '¿Cómo calificaría usted la atención proporcionada por el personal de LANIA antes y durante la realización del examen?',
|
||||
'equipo_funcionando' => '¿ El equipo de cómputo proporcionado funcionó correctamente durante todo el examen?',
|
||||
'ambiente_aula' => '¿Cómo valoraría el ambiente del aula (nivel de ruido, iluminación, temperatura, mobiliario) durante su examen?',
|
||||
'calidad_internet' => '¿Qué tan satisfactoria fue la calidad de la conexión a internet mientras realizaba su examen?',
|
||||
'instrucciones_claras' => '¿Recibió instrucciones claras sobre el procedimiento del examen y las normas del centro?',
|
||||
'respuesta_personal' => 'En caso de surgir dudas o problemas, ¿el personal respondió con rapidez y eficacia? ',
|
||||
'recomendacion_lania' => '¿Qué tan probable es que usted recomiende el Centro de Exámenes LANIA a otras personas?'
|
||||
];
|
||||
foreach ($preguntas as $campo => $texto): ?>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= $texto ?></label>
|
||||
<div>
|
||||
<?php for ($i = 1; $i <= 5; $i++): ?>
|
||||
<input
|
||||
type="radio"
|
||||
name="<?= $campo ?>"
|
||||
id="<?= $campo . '_' . $i ?>"
|
||||
value="<?= $i ?>"
|
||||
style="display: none;"
|
||||
>
|
||||
<label for="<?= $campo . '_' . $i ?>">
|
||||
<img src="assets/face_<?= $i ?>.png" alt="<?= $i ?>" width="30">
|
||||
</label>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Consentimiento -->
|
||||
<input type="hidden" name="consentimiento" value="no">
|
||||
<div class="form-check mb-4">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="consentimiento"
|
||||
name="consentimiento"
|
||||
value="si"
|
||||
checked
|
||||
>
|
||||
<label class="form-check-label" for="consentimiento">
|
||||
Doy mi consentimiento para recibir publicidad
|
||||
</label>
|
||||
</div>
|
||||
<input type="hidden" id="hora_salida_local" name="hora_salida">
|
||||
|
||||
<!-- Botón enviar -->
|
||||
<div class="mt-4 text-end">
|
||||
<button type="submit" class="btn btn-primary">Enviar registro</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal de agradecimiento -->
|
||||
<div class="modal fade" id="modalGracias" tabindex="-1" aria-labelledby="modalGraciasLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-success text-white">
|
||||
<h5 class="modal-title" id="modalGraciasLabel">¡Salida registrada!</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
Gracias por tu asistencia.
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal">Aceptar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("formSalida").addEventListener("submit", function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const ahora = new Date();
|
||||
const hora = ahora.toTimeString().slice(0,8); // "HH:MM:SS"
|
||||
document.getElementById("hora_salida_local").value = hora;
|
||||
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch("model/logout.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const modalGracias = new bootstrap.Modal(document.getElementById('modalGracias'));
|
||||
modalGracias.show();
|
||||
form.reset();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error al enviar:", error);
|
||||
alert("Ocurrió un error al registrar la salida.");
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.sidebar-menu li').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
const menuSalida = document.getElementById('menu-salida');
|
||||
if (menuSalida) {
|
||||
menuSalida.classList.add('active');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
use lania;
|
||||
|
||||
drop table entrada;
|
||||
CREATE TABLE entrada(
|
||||
id_usuario INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nombre VARCHAR(255) NOT NULL,
|
||||
correo VARCHAR(255) NOT NULL,
|
||||
telefono VARCHAR(20) NOT NULL,
|
||||
genero VARCHAR(10) NOT NULL,
|
||||
edad VARCHAR (50) NOT NULL,
|
||||
estado_procedencia VARCHAR (50) NOT NULL,
|
||||
identificacion VARCHAR (50) NOT NULL,
|
||||
empresa VARCHAR(100) NOT NULL,
|
||||
examen VARCHAR(50) NOT NULL,
|
||||
certificacion VARCHAR(50) NOT NULL,
|
||||
h_entrada TIME NOT NULL,
|
||||
fecha DATE NOT NULL
|
||||
);
|
||||
|
||||
drop table salida;
|
||||
CREATE TABLE salida(
|
||||
id_salida INT AUTO_INCREMENT PRIMARY KEY,
|
||||
id_usuario INT NOT NULL,
|
||||
hora_salida TIME NOT NULL,
|
||||
consentimiento ENUM('si', 'no') DEFAULT 'si',
|
||||
FOREIGN KEY (id_usuario) REFERENCES entrada(id_usuario)
|
||||
);
|
||||
|
||||
CREATE TABLE encuesta_satisfaccion (
|
||||
id_encuesta INT AUTO_INCREMENT PRIMARY KEY,
|
||||
id_salida INT NOT NULL,
|
||||
atencion_personal TINYINT,
|
||||
equipo_funcionando TINYINT,
|
||||
ambiente_aula TINYINT,
|
||||
calidad_internet TINYINT,
|
||||
instrucciones_claras TINYINT,
|
||||
respuesta_personal TINYINT,
|
||||
recomendacion_lania TINYINT,
|
||||
FOREIGN KEY (id_salida) REFERENCES salida(id_salida)
|
||||
);
|
||||
|
||||
CREATE TABLE admin (
|
||||
id_usuario INT AUTO_INCREMENT PRIMARY KEY,
|
||||
password VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
select * from entrada;
|
||||
|
||||
ALTER TABLE salida ADD COLUMN consentimiento VARCHAR(10) DEFAULT 'no';
|
|
@ -0,0 +1,69 @@
|
|||
const http = require('http');
|
||||
const mysql = require('mysql2');
|
||||
|
||||
// Configuración de la conexión a MySQL
|
||||
const db = mysql.createConnection({
|
||||
host: 'localhost', // MySQL está en tu misma máquina
|
||||
user: 'root', // Usuario por defecto (cámbialo si es necesario)
|
||||
password: 'Starfox19.', // Asegúrate de que sea la contraseña correcta
|
||||
database: 'lania', // Nombre de tu base de datos
|
||||
});
|
||||
|
||||
// Conectar a MySQL
|
||||
db.connect((err) => {
|
||||
if (err) {
|
||||
console.error('❌ Error al conectar a MySQL:', err.message);
|
||||
return;
|
||||
}
|
||||
console.log('✅ Conectado a MySQL');
|
||||
});
|
||||
|
||||
// Crear servidor HTTP
|
||||
const server = http.createServer((req, res) => {
|
||||
// Solo aceptamos GET en la ruta principal
|
||||
if (req.method === 'GET' && req.url === '/') {
|
||||
const query = `
|
||||
SELECT
|
||||
e.id_usuario AS user_id,
|
||||
e.correo AS email,
|
||||
e.estado_procedencia AS procedencia,
|
||||
e.edad AS rango_edad,
|
||||
e.empresa AS proveedor,
|
||||
s.consentimiento AS consentimiento_publicidad
|
||||
FROM entrada e
|
||||
LEFT JOIN salida s ON e.id_usuario = s.id_usuario
|
||||
`;
|
||||
|
||||
// Ejecutar consulta
|
||||
db.query(query, (err, results) => {
|
||||
if (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: 'Error en la base de datos' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Formatear respuesta
|
||||
const data = results.map((user) => ({
|
||||
user_id: user.user_id.toString().padStart(3, '0'),
|
||||
email: user.email,
|
||||
procedencia: user.procedencia,
|
||||
rango_edad: user.rango_edad,
|
||||
proveedor: user.proveedor,
|
||||
consentimiento_publicidad: user.consentimiento_publicidad,
|
||||
}));
|
||||
|
||||
// Enviar respuesta
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(data, null, 2)); // Formato bonito en JSON
|
||||
});
|
||||
} else {
|
||||
// Ruta no encontrada
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Ruta no encontrada');
|
||||
}
|
||||
});
|
||||
|
||||
// Iniciar servidor en el puerto 3000
|
||||
server.listen(3001, () => {
|
||||
console.log('🚀 Servidor Node.js escuchando en http://localhost:3001');
|
||||
});
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'database/database.php';
|
||||
|
||||
$data = [];
|
||||
|
||||
// 1. Datos de Género
|
||||
$queryGenero = "SELECT genero, COUNT(*) AS count FROM entrada GROUP BY genero";
|
||||
$resultGenero = $conexion->query($queryGenero);
|
||||
$generoData = [];
|
||||
$totalGenero = 0;
|
||||
while ($row = $resultGenero->fetch_assoc()) {
|
||||
$generoData[$row['genero']] = (int)$row['count'];
|
||||
$totalGenero += (int)$row['count'];
|
||||
}
|
||||
|
||||
$generoPercentages = [];
|
||||
foreach ($generoData as $genero => $count) {
|
||||
$generoPercentages[$genero] = ($totalGenero > 0) ? round(($count / $totalGenero) * 100, 2) : 0;
|
||||
}
|
||||
$data['genero'] = $generoPercentages;
|
||||
|
||||
// 2. Datos de Edades
|
||||
$queryEdad = "SELECT edad, COUNT(*) AS count FROM entrada GROUP BY edad ORDER BY
|
||||
CASE
|
||||
WHEN edad = 'menor' THEN 1
|
||||
WHEN edad = '18-21' THEN 2
|
||||
WHEN edad = '22-25' THEN 3
|
||||
WHEN edad = '26-30' THEN 4
|
||||
WHEN edad = '31-35' THEN 5
|
||||
WHEN edad = '36-40' THEN 6
|
||||
WHEN edad = '41-45' THEN 7
|
||||
WHEN edad = '46-50' THEN 8
|
||||
WHEN edad = '51-55' THEN 9
|
||||
WHEN edad = '56-60' THEN 10
|
||||
WHEN edad = 'mayor' THEN 11
|
||||
ELSE 12
|
||||
END";
|
||||
$resultEdad = $conexion->query($queryEdad);
|
||||
$edadData = [];
|
||||
$totalEdad = 0;
|
||||
while ($row = $resultEdad->fetch_assoc()) {
|
||||
$edadData[$row['edad']] = (int)$row['count'];
|
||||
$totalEdad += (int)$row['count'];
|
||||
}
|
||||
|
||||
$edadPercentages = [];
|
||||
foreach ($edadData as $edad => $count) {
|
||||
$edadPercentages[$edad] = ($totalEdad > 0) ? round(($count / $totalEdad) * 100, 2) : 0;
|
||||
}
|
||||
$data['edad'] = $edadPercentages;
|
||||
|
||||
// Gráfica de barra de estados que más nos visitan
|
||||
$queryEstados = "SELECT estado_procedencia, COUNT(*) AS count FROM entrada GROUP BY estado_procedencia ORDER BY count DESC LIMIT 5";
|
||||
$resultEstados = $conexion->query($queryEstados);
|
||||
$estadosData = [];
|
||||
while ($row = $resultEstados->fetch_assoc()) {
|
||||
$estadosData[$row['estado_procedencia']] = (int)$row['count'];
|
||||
}
|
||||
$data['estados'] = $estadosData;
|
||||
|
||||
|
||||
// Gráfica de proveedores de exámenes que más hacen examen o colocan en el formulario
|
||||
$queryProveedores = "SELECT empresa, COUNT(*) AS count FROM entrada GROUP BY empresa ORDER BY count DESC LIMIT 5";
|
||||
$resultProveedores = $conexion->query($queryProveedores);
|
||||
$proveedoresData = [];
|
||||
while ($row = $resultProveedores->fetch_assoc()) {
|
||||
$proveedoresData[$row['empresa']] = (int)$row['count'];
|
||||
}
|
||||
$data['proveedores'] = $proveedoresData;
|
||||
|
||||
|
||||
// 5. Cuantas personas y datos del mes pasado (filtrado por fecha a fecha)
|
||||
$fechaActual = date('Y-m-d');
|
||||
$fechaMesPasadoInicio = date('Y-m-01', strtotime('last month'));
|
||||
$fechaMesPasadoFin = date('Y-m-t', strtotime('last month'));
|
||||
|
||||
$queryMesPasado = "SELECT DATE(fecha) AS dia, COUNT(*) AS count FROM entrada
|
||||
WHERE fecha BETWEEN '$fechaMesPasadoInicio' AND '$fechaMesPasadoFin'
|
||||
GROUP BY DATE(fecha) ORDER BY DATE(fecha) ASC";
|
||||
$resultMesPasado = $conexion->query($queryMesPasado);
|
||||
$mesPasadoData = [];
|
||||
while ($row = $resultMesPasado->fetch_assoc()) {
|
||||
$mesPasadoData[$row['dia']] = (int)$row['count'];
|
||||
}
|
||||
$data['mes_pasado'] = $mesPasadoData;
|
||||
|
||||
|
||||
echo json_encode($data);
|
||||
$conexion->close();
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'database/database.php';
|
||||
|
||||
$startDate = $_GET['startDate'] ?? null;
|
||||
$endDate = $_GET['endDate'] ?? null;
|
||||
|
||||
$data = [
|
||||
'fechas_rango' => []
|
||||
];
|
||||
|
||||
if ($startDate && $endDate) {
|
||||
$query = "SELECT DATE(fecha) AS dia, COUNT(*) AS count FROM entrada
|
||||
WHERE fecha BETWEEN ? AND ? GROUP BY DATE(fecha) ORDER BY DATE(fecha) ASC";
|
||||
|
||||
$stmt = $conexion->prepare($query);
|
||||
$stmt->bind_param("ss", $startDate, $endDate);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$data['fechas_rango'][$row['dia']] = (int)$row['count'];
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
echo json_encode($data);
|
||||
$conexion->close();
|
||||
?>
|
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header("Location: sesion.php");
|
||||
exit;
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header("Location: sesion.php");
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LANIA Dashboard</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
<link href="style/dashboard.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* Estilo para el botón de filtro de fecha activo */
|
||||
.date-filter-btn.active {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
|
||||
/* Estilos para los contenedores de los gráficos para asegurar que se muestren correctamente */
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px; /* Ajusta esta altura según sea necesario para tus gráficos */
|
||||
width: 100%;
|
||||
}
|
||||
/* Para que las tarjetas tengan una altura consistente si sus contenidos son diferentes */
|
||||
.card {
|
||||
height: 100%;
|
||||
}
|
||||
.card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100% - 56px); /* Altura del card-header es aproximadamente 56px */
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php include 'sidebar.php'; ?>
|
||||
|
||||
<div class="main-content">
|
||||
|
||||
<div id="estadisticas" class="section">
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Empresas de Origen</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="empresasChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Estados de Procedencia</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="estadosChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Porcentaje de Usuarios por Género</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="generoChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Porcentaje de Usuarios por Rango de Edad</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="edadChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Usuarios Registrados el Mes Pasado</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-container">
|
||||
<canvas id="mesPasadoChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4 justify-content-center">
|
||||
<div class="col-md-8 col-lg-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title">Usuarios por Rango de Fechas</h5>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column align-items-center"> <div class="mb-3 d-flex flex-column align-items-center">
|
||||
<button class="btn btn-outline-secondary mb-2 date-filter-btn" data-period="week" onclick="setQuickDateFilter('week', this)">Última Semana</button>
|
||||
<button class="btn btn-outline-secondary mb-2 date-filter-btn" data-period="last_month_full" onclick="setQuickDateFilter('last_month_full', this)">Último Mes</button>
|
||||
<button class="btn btn-outline-secondary date-filter-btn" data-period="last_year_full" onclick="setQuickDateFilter('last_year_full', this)">Último Año</button>
|
||||
</div>
|
||||
<div class="chart-container mt-4">
|
||||
<canvas id="dateRangeChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 text-end">
|
||||
<button class="btn btn-success me-2" onclick="exportData('csv')">
|
||||
<i class="fas fa-file-csv"></i> Exportar CSV
|
||||
</button>
|
||||
<button class="btn btn-danger" onclick="exportData('pdf')">
|
||||
<i class="fas fa-file-pdf"></i> Exportar PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="entrada" class="section" style="display: none;">
|
||||
<iframe src="FormularioInicial.php" width="100%" height="600px" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="salida" class="section" style="display: none;">
|
||||
<iframe src="FormularioSalida.php" width="100%" height="900px" frameborder="0"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="js/dashboard.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
$host = "localhost";
|
||||
$user = "root";
|
||||
$pass = "Starfox19.";
|
||||
$db = "lania";
|
||||
|
||||
$conexion = new mysqli($host, $user, $pass, $db);
|
||||
|
||||
// Verifica errores de conexión
|
||||
if ($conexion->connect_error) {
|
||||
die("Error de conexión: " . $conexion->connect_error);
|
||||
}
|
||||
|
||||
return $conexion;
|
||||
?>
|
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
// export.php
|
||||
require_once 'database/database.php'; // Ajusta esta ruta si es necesario
|
||||
require('fpdf186/fpdf.php'); // Asegúrate que esta ruta a FPDF sea correcta
|
||||
|
||||
if ($conexion->connect_error) {
|
||||
die("Error de conexión a la base de datos: " . $conexion->connect_error);
|
||||
}
|
||||
|
||||
$type = $_GET['type'] ?? 'csv'; // csv o pdf
|
||||
$report = $_GET['report'] ?? 'all_users'; // all_users o by_date_range
|
||||
|
||||
$startDate = $_GET['startDate'] ?? null;
|
||||
$endDate = $_GET['endDate'] ?? null;
|
||||
|
||||
// Validar y limpiar fechas para evitar SQL Injection (¡muy importante!)
|
||||
if ($startDate) $startDate = $conexion->real_escape_string($startDate);
|
||||
if ($endDate) $endDate = $conexion->real_escape_string($endDate);
|
||||
|
||||
|
||||
$query = "SELECT id_usuario, nombre, correo, telefono, genero, edad, estado_procedencia, identificacion, empresa, examen, certificacion, h_entrada, fecha FROM entrada";
|
||||
$params = [];
|
||||
$paramTypes = "";
|
||||
|
||||
// Construir la consulta SQL basada en el tipo de reporte
|
||||
if ($report === 'by_date_range' && $startDate && $endDate) {
|
||||
// Si queremos "todo el mes anterior" o "todo el año anterior", las fechas ya vendrán
|
||||
// calculadas desde el frontend.
|
||||
$query .= " WHERE fecha BETWEEN ? AND ?";
|
||||
$params = [$startDate, $endDate];
|
||||
$paramTypes = "ss"; // 's' para string, dos strings
|
||||
} else {
|
||||
// Si 'all_users' o no hay fechas válidas, exporta todo.
|
||||
// Esto ya no debería ser el caso si el JS siempre envía fechas.
|
||||
// Podrías poner un error aquí si esperas siempre un rango.
|
||||
// Forzar un rango predeterminado si no llega nada?
|
||||
// current month by default
|
||||
$startDate = date('Y-m-01');
|
||||
$endDate = date('Y-m-t');
|
||||
$query .= " WHERE fecha BETWEEN ? AND ?";
|
||||
$params = [$startDate, $endDate];
|
||||
$paramTypes = "ss";
|
||||
}
|
||||
|
||||
$stmt = $conexion->prepare($query);
|
||||
|
||||
if ($stmt === false) {
|
||||
die("Error al preparar la consulta: " . $conexion->error);
|
||||
}
|
||||
|
||||
if (!empty($params)) {
|
||||
$stmt->bind_param($paramTypes, ...$params);
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result === false) {
|
||||
die("Error al ejecutar la consulta: " . $conexion->error);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$data[] = $row;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conexion->close();
|
||||
|
||||
if (empty($data)) {
|
||||
// No hay datos para exportar
|
||||
if ($type === 'csv') {
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="reporte_vacio.csv"');
|
||||
echo "No hay datos para el rango de fechas seleccionado.";
|
||||
} elseif ($type === 'pdf') {
|
||||
class PDF_Empty extends FPDF {
|
||||
function Header() { $this->SetFont('Arial','B',12); $this->Cell(0,10,utf8_decode('Reporte de Usuarios Registrados'),0,1,'C'); }
|
||||
function Footer() { $this->SetY(-15); $this->SetFont('Arial','I',8); $this->Cell(0,10,utf8_decode('Página ').$this->PageNo().'/{nb}',0,0,'C'); }
|
||||
function Content() {
|
||||
$this->SetFont('Arial','',10);
|
||||
$this->Cell(0, 10, utf8_decode('No hay datos para el rango de fechas seleccionado.'), 0, 1, 'C');
|
||||
}
|
||||
}
|
||||
$pdfEmpty = new PDF_Empty();
|
||||
$pdfEmpty->AddPage();
|
||||
$pdfEmpty->Content();
|
||||
$pdfEmpty->Output('I', 'reporte_vacio.pdf');
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
if ($type === 'csv') {
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="reporte_usuarios_' . date('Ymd_His') . '.csv"');
|
||||
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
// Nombres de las columnas (encabezado del CSV)
|
||||
fputcsv($output, array_keys($data[0]));
|
||||
|
||||
// Datos
|
||||
foreach ($data as $row) {
|
||||
fputcsv($output, $row);
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
} elseif ($type === 'pdf') {
|
||||
class PDF extends FPDF
|
||||
{
|
||||
protected $startDate;
|
||||
protected $endDate;
|
||||
|
||||
function setDateRange($start, $end) {
|
||||
$this->startDate = $start;
|
||||
$this->endDate = $end;
|
||||
}
|
||||
|
||||
function Header()
|
||||
{
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 10, utf8_decode('Reporte de Usuarios Registrados'), 0, 1, 'C');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
if ($this->startDate && $this->endDate) {
|
||||
$this->Cell(0, 10, utf8_decode('Rango de Fechas: ' . $this->startDate . ' a ' . $this->endDate), 0, 1, 'C');
|
||||
}
|
||||
$this->Ln(5);
|
||||
}
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-15);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 10, utf8_decode('Página ') . $this->PageNo() . '/{nb}', 0, 0, 'C');
|
||||
}
|
||||
|
||||
function ImprovedTable($header, $data)
|
||||
{
|
||||
// Anchuras de las columnas (reajusta según tus datos y el tamaño de la página A4 apaisada ~277mm)
|
||||
// Haz pruebas para que quepan todos tus datos. Si tienes demasiadas columnas, considera una fuente más pequeña.
|
||||
$w = [10, 25, 40, 20, 15, 15, 25, 20, 20, 20, 20, 20, 20]; // Suma: 270mm (ajusta hasta 277mm)
|
||||
// id, nombre, correo, telefono, genero, edad, estado, identificacion, empresa, examen, certificacion, h_entrada, fecha
|
||||
|
||||
$this->SetFillColor(200, 220, 255);
|
||||
$this->SetTextColor(0);
|
||||
$this->SetDrawColor(0, 0, 0);
|
||||
$this->SetLineWidth(.3);
|
||||
$this->SetFont('Arial', 'B', 7); // Fuente más pequeña para el encabezado
|
||||
|
||||
// Cabecera
|
||||
for ($i = 0; $i < count($header); $i++) {
|
||||
$this->Cell($w[$i], 7, utf8_decode($header[$i]), 1, 0, 'C', true);
|
||||
}
|
||||
$this->Ln();
|
||||
|
||||
$this->SetFillColor(224, 235, 255);
|
||||
$this->SetTextColor(0);
|
||||
$this->SetFont('Arial', '', 7); // Fuente más pequeña para los datos
|
||||
|
||||
$fill = false;
|
||||
foreach ($data as $row) {
|
||||
// Para calcular la altura de la fila basándose en el contenido de las celdas
|
||||
$maxHeight = 6;
|
||||
$cellContents = [];
|
||||
|
||||
foreach ($row as $key => $value) {
|
||||
$cellContents[$key] = utf8_decode($value);
|
||||
}
|
||||
|
||||
// Ajusta los índices de las columnas y sus anchos correspondientes
|
||||
$columnsToCheck = [
|
||||
'nombre' => $w[1], 'correo' => $w[2], 'estado_procedencia' => $w[6],
|
||||
'empresa' => $w[8], 'examen' => $w[9], 'certificacion' => $w[10]
|
||||
];
|
||||
|
||||
foreach($columnsToCheck as $colName => $colWidth) {
|
||||
// Calcula la altura mínima para el texto si se ajusta al ancho de la celda
|
||||
$neededHeight = $this->GetStringWidth($cellContents[$colName]) / $colWidth * $this->FontSize / 2.5; // Estimación
|
||||
if ($neededHeight > $maxHeight) {
|
||||
$maxHeight = $neededHeight;
|
||||
}
|
||||
}
|
||||
$maxHeight = max($maxHeight, $this->FontSize + 2); // Asegura una altura mínima
|
||||
|
||||
// Verificar si se necesita una nueva página
|
||||
if ($this->GetY() + $maxHeight > $this->PageBreakTrigger) {
|
||||
$this->AddPage($this->CurOrientation);
|
||||
// Volver a imprimir la cabecera de la tabla en la nueva página
|
||||
$this->SetFillColor(200, 220, 255);
|
||||
$this->SetTextColor(0);
|
||||
$this->SetDrawColor(0, 0, 0);
|
||||
$this->SetLineWidth(.3);
|
||||
$this->SetFont('Arial', 'B', 7);
|
||||
for ($i = 0; $i < count($header); $i++) {
|
||||
$this->Cell($w[$i], 7, utf8_decode($header[$i]), 1, 0, 'C', true);
|
||||
}
|
||||
$this->Ln();
|
||||
$this->SetFillColor(224, 235, 255);
|
||||
$this->SetTextColor(0);
|
||||
$this->SetFont('Arial', '', 7);
|
||||
}
|
||||
|
||||
// Imprimir las celdas de la fila
|
||||
$this->Cell($w[0], $maxHeight, $row['id_usuario'], 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[1], $maxHeight, utf8_decode(mb_strimwidth($row['nombre'], 0, floor($w[1]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[2], $maxHeight, utf8_decode(mb_strimwidth($row['correo'], 0, floor($w[2]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[3], $maxHeight, utf8_decode($row['telefono']), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[4], $maxHeight, utf8_decode($row['genero']), 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[5], $maxHeight, utf8_decode($row['edad']), 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[6], $maxHeight, utf8_decode(mb_strimwidth($row['estado_procedencia'], 0, floor($w[6]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[7], $maxHeight, utf8_decode($row['identificacion']), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[8], $maxHeight, utf8_decode(mb_strimwidth($row['empresa'], 0, floor($w[8]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[9], $maxHeight, utf8_decode(mb_strimwidth($row['examen'], 0, floor($w[9]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[10], $maxHeight, utf8_decode(mb_strimwidth($row['certificacion'], 0, floor($w[10]/1.5), "...")), 'LR', 0, 'L', $fill);
|
||||
$this->Cell($w[11], $maxHeight, utf8_decode($row['h_entrada']), 'LR', 0, 'C', $fill);
|
||||
$this->Cell($w[12], $maxHeight, utf8_decode($row['fecha']), 'LR', 0, 'C', $fill);
|
||||
|
||||
$this->Ln($maxHeight);
|
||||
$fill = !$fill;
|
||||
}
|
||||
$this->Cell(array_sum($w), 0, '', 'T');
|
||||
}
|
||||
}
|
||||
|
||||
$pdf = new PDF();
|
||||
$pdf->AliasNbPages();
|
||||
$pdf->AddPage('L'); // 'L' para orientación horizontal
|
||||
$pdf->setDateRange($startDate, $endDate); // Pasar las fechas a la clase PDF
|
||||
|
||||
// Encabezados de la tabla (ajusta estos a tus columnas de la BD)
|
||||
$header = array('ID', 'Nombre', 'Correo', 'Teléfono', 'Género', 'Edad', 'Estado', 'Identificación', 'Empresa', 'Examen', 'Certificación', 'H. Entrada', 'Fecha');
|
||||
|
||||
$pdf->ImprovedTable($header, $data);
|
||||
$pdf->Output('I', 'reporte_usuarios_' . date('Ymd_His') . '.pdf');
|
||||
} else {
|
||||
echo "Tipo de exportación no soportado.";
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
// inicio.php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Logout
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
|
||||
session_destroy();
|
||||
header('Location: inicio.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Selección de rol
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['role'])) {
|
||||
session_unset();
|
||||
$_SESSION['role'] = $_POST['role'];
|
||||
if ($_POST['role'] === 'admin') {
|
||||
header('Location: sesion.php');
|
||||
} else {
|
||||
header('Location: FormularioInicial.php');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Inicio - LANIA</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
background: #001f3f; /* azul marino */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.logo {
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.btn-role {
|
||||
width: 220px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<img src="assets/logo.png" alt="Logo LANIA" class="logo">
|
||||
<form method="post">
|
||||
<button type="submit" name="role" value="aspirant" class="btn btn-outline-light btn-role">
|
||||
Iniciar como Aspirante
|
||||
</button>
|
||||
<button type="submit" name="role" value="admin" class="btn btn-light btn-role">
|
||||
Iniciar como Administrador
|
||||
</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,307 @@
|
|||
|
||||
let generoChartInstance, edadChartInstance, estadosChartInstance, empresasChartInstance, mesPasadoChartInstance, dateRangeChartInstance;
|
||||
|
||||
let currentStartDate = '';
|
||||
let currentEndDate = '';
|
||||
|
||||
|
||||
function showSection(sectionId) {
|
||||
document.querySelectorAll('.section').forEach(section => {
|
||||
section.style.display = 'none';
|
||||
});
|
||||
document.getElementById(sectionId).style.display = 'block';
|
||||
|
||||
document.querySelectorAll('.sidebar-menu li').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
if (sectionId === 'estadisticas') {
|
||||
document.getElementById('menu-estadisticas').classList.add('active');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function toggleSidebar() {
|
||||
document.querySelector('.sidebar').classList.toggle('active');
|
||||
document.querySelector('.main-content').classList.toggle('active');
|
||||
}
|
||||
|
||||
function initCharts() {
|
||||
if (generoChartInstance) generoChartInstance.destroy();
|
||||
if (edadChartInstance) edadChartInstance.destroy();
|
||||
if (estadosChartInstance) estadosChartInstance.destroy();
|
||||
if (empresasChartInstance) empresasChartInstance.destroy();
|
||||
if (mesPasadoChartInstance) mesPasadoChartInstance.destroy();
|
||||
if (dateRangeChartInstance) dateRangeChartInstance.destroy();
|
||||
|
||||
fetch('api_estadisticas.php')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log("Datos recibidos de la API:", data);
|
||||
|
||||
// Gráfico de Empresas de Origen
|
||||
const empresasCtx = document.getElementById('empresasChart');
|
||||
if (empresasCtx) {
|
||||
empresasChartInstance = new Chart(empresasCtx.getContext('2d'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(data.proveedores),
|
||||
datasets: [{
|
||||
data: Object.values(data.proveedores),
|
||||
backgroundColor: ['#1A73E8', '#4CAF50', '#FF9800', '#9C27B0']
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { position: 'bottom' } }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gráfico de Estados de Procedencia
|
||||
const estadosCtx = document.getElementById('estadosChart');
|
||||
if (estadosCtx) {
|
||||
estadosChartInstance = new Chart(estadosCtx.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(data.estados),
|
||||
datasets: [{
|
||||
label: 'Usuarios',
|
||||
data: Object.values(data.estados),
|
||||
backgroundColor: 'rgba(26, 115, 232, 0.7)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: { y: { beginAtZero: true } }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gráfico de Género
|
||||
const generoCtx = document.getElementById('generoChart');
|
||||
if (generoCtx) {
|
||||
generoChartInstance = new Chart(generoCtx.getContext('2d'), {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: Object.keys(data.genero),
|
||||
datasets: [{
|
||||
data: Object.values(data.genero),
|
||||
backgroundColor: ['#E91E63', '#1A73E8', '#9C27B0']
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'bottom' },
|
||||
tooltip: { callbacks: { label: function(context) { let label = context.label || ''; if (label) { label += ': '; } if (context.parsed !== null) { label += context.parsed + '%'; } return label; } } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gráfico de Edad
|
||||
const edadCtx = document.getElementById('edadChart');
|
||||
if (edadCtx) {
|
||||
edadChartInstance = new Chart(edadCtx.getContext('2d'), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: Object.keys(data.edad),
|
||||
datasets: [{
|
||||
data: Object.values(data.edad),
|
||||
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'bottom' },
|
||||
tooltip: { callbacks: { label: function(context) { let label = context.label || ''; if (label) { label += ': '; } if (context.parsed !== null) { label += context.parsed + '%'; } return label; } } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Gráfico de Usuarios del Mes Pasado
|
||||
const mesPasadoCtx = document.getElementById('mesPasadoChart');
|
||||
if (mesPasadoCtx) {
|
||||
mesPasadoChartInstance = new Chart(mesPasadoCtx.getContext('2d'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Object.keys(data.mes_pasado),
|
||||
datasets: [{
|
||||
label: 'Usuarios Registrados',
|
||||
data: Object.values(data.mes_pasado),
|
||||
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 1,
|
||||
tension: 0.4,
|
||||
fill: true
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { display: true } },
|
||||
scales: {
|
||||
x: { title: { display: true, text: 'Día del Mes Pasado' } },
|
||||
y: { beginAtZero: true, title: { display: true, text: 'Cantidad de Usuarios' } }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Inicializar Gráfico para Rango de Fechas (vacío al inicio, se llena con updateChartsByDate)
|
||||
const dateRangeCtx = document.getElementById('dateRangeChart');
|
||||
if (dateRangeCtx) {
|
||||
if (!dateRangeChartInstance) {
|
||||
dateRangeChartInstance = new Chart(dateRangeCtx.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Usuarios Registrados',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.7)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: { y: { beginAtZero: true } }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error al obtener datos de estadísticas:', error);
|
||||
alert('No se pudieron cargar las estadísticas. Por favor, verifica la conexión y los datos.');
|
||||
});
|
||||
}
|
||||
|
||||
function formatDate(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function setQuickDateFilter(period, clickedButton = null) {
|
||||
const today = new Date();
|
||||
let startDate = new Date();
|
||||
let endDate = new Date(); // Por defecto, hoy
|
||||
|
||||
if (period === 'week') {
|
||||
startDate.setDate(today.getDate() - 7);
|
||||
} else if (period === 'last_month_full') {
|
||||
startDate = new Date(today.getFullYear(), today.getMonth() - 1, 1);
|
||||
endDate = new Date(today.getFullYear(), today.getMonth(), 0);
|
||||
} else if (period === 'last_year_full') {
|
||||
startDate = new Date(today.getFullYear() - 1, 0, 1);
|
||||
endDate = new Date(today.getFullYear() - 1, 11, 31);
|
||||
}
|
||||
|
||||
currentStartDate = formatDate(startDate);
|
||||
currentEndDate = formatDate(endDate);
|
||||
|
||||
document.querySelectorAll('.date-filter-btn').forEach(btn => {
|
||||
btn.classList.remove('active');
|
||||
});
|
||||
if (clickedButton) {
|
||||
clickedButton.classList.add('active');
|
||||
}
|
||||
|
||||
updateChartsByDate();
|
||||
}
|
||||
|
||||
function updateChartsByDate() {
|
||||
const startDate = currentStartDate;
|
||||
const endDate = currentEndDate;
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
console.warn('No hay rango de fechas seleccionado para el gráfico de rango. Inicializando con "Último Mes".');
|
||||
setQuickDateFilter('last_month_full', document.querySelector('.date-filter-btn[data-period="last_month_full"]'));
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`api_estadisticas_fecha.php?startDate=${startDate}&endDate=${endDate}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log("Datos por rango de fechas:", data);
|
||||
|
||||
const dateRangeCtx = document.getElementById('dateRangeChart');
|
||||
if (dateRangeCtx) {
|
||||
if (dateRangeChartInstance) {
|
||||
dateRangeChartInstance.destroy();
|
||||
}
|
||||
dateRangeChartInstance = new Chart(dateRangeCtx.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: Object.keys(data.fechas_rango),
|
||||
datasets: [{
|
||||
label: 'Usuarios Registrados',
|
||||
data: Object.values(data.fechas_rango),
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.7)'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { display: false } },
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
padding: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error al obtener datos por rango de fechas:', error);
|
||||
alert('No se pudieron cargar los datos para el rango de fechas seleccionado. Verifica la conexión y los datos en la base de datos.');
|
||||
});
|
||||
}
|
||||
|
||||
function exportData(format) {
|
||||
const startDate = currentStartDate;
|
||||
const endDate = currentEndDate;
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
alert('Por favor, selecciona un rango de fechas (Última Semana, Mes o Año) antes de exportar.');
|
||||
return;
|
||||
}
|
||||
|
||||
let url = `export.php?type=${format}&report=by_date_range&startDate=${startDate}&endDate=${endDate}`;
|
||||
|
||||
console.log("URL de exportación:", url);
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
showSection('estadisticas');
|
||||
initCharts();
|
||||
setQuickDateFilter('last_month_full', document.querySelector('.date-filter-btn[data-period="last_month_full"]'));
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
require_once '../database/database.php';
|
||||
|
||||
$datos = [];
|
||||
|
||||
$sql = "SELECT empresa, estado_procedencia, edad FROM entrada";
|
||||
if ($result = $conexion->query($sql)) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$datos[] = $row;
|
||||
}
|
||||
} else {
|
||||
http_response_code(500);
|
||||
echo json_encode(["error" => "Error en la consulta SQL"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($datos);
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
session_start();
|
||||
$conn = require_once __DIR__ . '/../database/database.php';
|
||||
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$nombre = $_POST['nombre'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
$stmt = $conn->prepare("SELECT * FROM admin WHERE nombre = ?");
|
||||
$stmt->bind_param("s", $nombre);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($admin = $result->fetch_assoc()) {
|
||||
if (password_verify($password, $admin['password'])) {
|
||||
$_SESSION['admin'] = $admin['nombre'];
|
||||
header("Location: dashboard.php");
|
||||
exit;
|
||||
} else {
|
||||
$error = "⚠️ Contraseña incorrecta.";
|
||||
}
|
||||
} else {
|
||||
$error = "⚠️ Usuario no encontrado.";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
require '../database/database.php';
|
||||
date_default_timezone_set('America/Mexico_City');
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$nombre = $_POST['nombre'] ?? '';
|
||||
$correo = $_POST['correo'] ?? '';
|
||||
$telefono = $_POST['telefono'] ?? '';
|
||||
$genero = $_POST['genero'] ?? '';
|
||||
$edad = $_POST['edad'] ?? '';
|
||||
$estado_procedencia = $_POST['estado_procedencia'] ?? '';
|
||||
$identificacion = $_POST['identificacion'] ?? '';
|
||||
$empresa = $_POST['empresa'] ?? '';
|
||||
$examen = $_POST['examen'] ?? '';
|
||||
$certificacion = $_POST['certificacion'] ?? '';
|
||||
$hora_entrada = $_POST['h_entrada'] ?: date("H:i:s"); // si no se proporciona, toma hora actual
|
||||
$fecha = $_POST['fecha'] ?: date("Y-m-d"); // si no se proporciona, toma fecha actual
|
||||
|
||||
$sql = "INSERT INTO entrada (nombre, correo, telefono, genero, edad, estado_procedencia, identificacion, empresa, examen, certificacion, h_entrada, fecha)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$stmt = $conexion->prepare($sql);
|
||||
|
||||
$stmt->bind_param("ssssssssssss", $nombre, $correo, $telefono, $genero, $edad, $estado_procedencia, $identificacion, $empresa, $examen, $certificacion, $hora_entrada, $fecha);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
echo "Entrada guardada correctamente.";
|
||||
} else {
|
||||
echo "Error al guardar: " . $stmt->error;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conexion->close();
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
require_once '../database/database.php';
|
||||
|
||||
// Sanitizar y obtener valores
|
||||
$id_usuario = $_POST['id_usuario'];
|
||||
$hora_salida = $_POST['hora_salida'];
|
||||
$consentimiento = isset($_POST['consentimiento']) ? $_POST['consentimiento'] : 'no';
|
||||
|
||||
// 1. Insertar la salida
|
||||
$sqlSalida = "INSERT INTO salida (id_usuario, hora_salida, consentimiento) VALUES (?, ?, ?)";
|
||||
$stmtSalida = $conexion->prepare($sqlSalida);
|
||||
$stmtSalida->bind_param("iss", $id_usuario, $hora_salida, $consentimiento);
|
||||
$stmtSalida->execute();
|
||||
|
||||
$id_salida = $stmtSalida->insert_id; // Obtener el ID generado
|
||||
|
||||
// 2. Insertar la encuesta (si se llenó)
|
||||
$campos_encuesta = [
|
||||
'atencion_personal',
|
||||
'equipo_funcionando',
|
||||
'ambiente_aula',
|
||||
'calidad_internet',
|
||||
'instrucciones_claras',
|
||||
'respuesta_personal',
|
||||
'recomendacion_lania'
|
||||
];
|
||||
|
||||
$valores_encuesta = [];
|
||||
foreach ($campos_encuesta as $campo) {
|
||||
$valores_encuesta[$campo] = isset($_POST[$campo]) ? intval($_POST[$campo]) : null;
|
||||
}
|
||||
|
||||
// Solo guarda si hay al menos una respuesta
|
||||
if (array_filter($valores_encuesta)) {
|
||||
$sqlEncuesta = "INSERT INTO encuesta_satisfaccion (
|
||||
id_salida, atencion_personal, equipo_funcionando, ambiente_aula, calidad_internet,
|
||||
instrucciones_claras, respuesta_personal, recomendacion_lania
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
$stmtEncuesta = $conexion->prepare($sqlEncuesta);
|
||||
$stmtEncuesta->bind_param(
|
||||
"iiiiiiii",
|
||||
$id_salida,
|
||||
$valores_encuesta['atencion_personal'],
|
||||
$valores_encuesta['equipo_funcionando'],
|
||||
$valores_encuesta['ambiente_aula'],
|
||||
$valores_encuesta['calidad_internet'],
|
||||
$valores_encuesta['instrucciones_claras'],
|
||||
$valores_encuesta['respuesta_personal'],
|
||||
$valores_encuesta['recomendacion_lania']
|
||||
);
|
||||
$stmtEncuesta->execute();
|
||||
}
|
||||
|
||||
echo "Salida y encuesta registradas correctamente.";
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
$conn = require_once __DIR__ . '/../database/database.php';
|
||||
|
||||
// Si la conexión falló, $conn será false
|
||||
if (!($conn instanceof mysqli)) {
|
||||
// error_log('Fallo conexión a BD en register.php');
|
||||
}
|
||||
|
||||
/*
|
||||
* Registro directo al recibir POST en este archivo
|
||||
*/
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['nombre'], $_POST['password'])) {
|
||||
if (!($conn instanceof mysqli)) {
|
||||
$error = "❌ Error interno de conexión.";
|
||||
} else {
|
||||
$nombre = $_POST['nombre'];
|
||||
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $conn->prepare("INSERT INTO admin (nombre, password) VALUES (?, ?)");
|
||||
$stmt->bind_param("ss", $nombre, $password);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$success = "✅ Administrador registrado con éxito.";
|
||||
} else {
|
||||
$error = "❌ Error al registrar: " . $stmt->error;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Función reutilizable: crea un admin y devuelve true o mensaje de error.
|
||||
*/
|
||||
function createAdmin(string $nombre, string $password) {
|
||||
global $conn;
|
||||
if (!($conn instanceof mysqli)) {
|
||||
return "Error de conexión a la base de datos.";
|
||||
}
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $conn->prepare("INSERT INTO admin (nombre, password) VALUES (?, ?)");
|
||||
if (!$stmt) {
|
||||
return "Error en prepare(): " . $conn->error;
|
||||
}
|
||||
$stmt->bind_param("ss", $nombre, $hash);
|
||||
if ($stmt->execute()) {
|
||||
$stmt->close();
|
||||
return true;
|
||||
} else {
|
||||
$err = $stmt->error;
|
||||
$stmt->close();
|
||||
return $err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Función para verificar credenciales. Devuelve true solo si el usuario existe y la contraseña coincide. Si la conexión falla, devuelve false.
|
||||
*/
|
||||
function checkAdmin(string $nombre, string $password): bool {
|
||||
global $conn;
|
||||
if (!($conn instanceof mysqli)) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $conn->prepare("SELECT password FROM admin WHERE nombre = ?");
|
||||
if (!$stmt) {
|
||||
return false;
|
||||
}
|
||||
$stmt->bind_param("s", $nombre);
|
||||
$stmt->execute();
|
||||
$stmt->bind_result($hash);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
$stmt->close();
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
{
|
||||
"name": "proyecto_LANIA",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"mysql2": "^3.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-ssl-profiles": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
||||
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "7.18.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
|
||||
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/lru.min": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz",
|
||||
"integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"bun": ">=1.0.0",
|
||||
"deno": ">=1.30.0",
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wellwelwel"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz",
|
||||
"integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.1",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^5.2.1",
|
||||
"lru.min": "^1.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lru-cache": "^7.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/seq-queue": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
|
||||
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
|
||||
},
|
||||
"node_modules/sqlstring": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
|
||||
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"mysql2": "^3.14.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
s<?php
|
||||
require_once 'model/register.php';
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Si ya está logueado como admin
|
||||
if (isset($_SESSION['admin_logged']) && $_SESSION['admin_logged'] === true) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Procesar registro
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$nombre = $_POST['nombre'];
|
||||
$password = $_POST['password'];
|
||||
$res = createAdmin($nombre, $password);
|
||||
if ($res === true) {
|
||||
$success = 'Administrador registrado correctamente.';
|
||||
} else {
|
||||
$error = 'Error al registrar: ' . $res;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Registro Admin</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
background: #001f3f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 2rem;
|
||||
border-radius: .75rem;
|
||||
background: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card shadow">
|
||||
<h1 class="h4 text-center mb-4">Registrar Administrador</h1>
|
||||
<form method="post">
|
||||
<label for="nombre" class="form-label">Nombre:</label>
|
||||
<input type="text" id="nombre" name="nombre" class="form-control" required><br>
|
||||
<label for="password" class="form-label">Contraseña:</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required><br>
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<button type="submit" class="btn btn-primary">Registrar</button>
|
||||
<a href="sesion.php" class="btn btn-secondary">Ir al Login</a>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (isset($success)): ?>
|
||||
<div class="alert alert-success mt-3"><?= $success ?></div>
|
||||
<?php elseif (isset($error)): ?>
|
||||
<div class="alert alert-danger mt-3"><?= $error ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
require_once 'model/inicio.php';
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Si ya está logueado como admin
|
||||
if (isset($_SESSION['admin_logged']) && $_SESSION['admin_logged'] === true) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Procesar login
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$nombre = $_POST['nombre'];
|
||||
$password = $_POST['password'];
|
||||
require_once 'model/register.php';
|
||||
if (checkAdmin($nombre, $password)) {
|
||||
$_SESSION['admin_logged'] = true;
|
||||
$_SESSION['role'] = 'admin';
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
} else {
|
||||
// En vez de fatal, simple mensaje y form sigue disponible
|
||||
$error = 'Nombre o contraseña incorrectos.';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Inicio de Sesión - Admin</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
background: #001f3f;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 2rem;
|
||||
border-radius: .75rem;
|
||||
background: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card shadow">
|
||||
<h1 class="h4 text-center mb-4">Sesión Administrador</h1>
|
||||
<form method="post">
|
||||
<label for="nombre" class="form-label">Nombre:</label>
|
||||
<input type="text" id="nombre" name="nombre" class="form-control" required><br>
|
||||
|
||||
<label for="password" class="form-label">Contraseña:</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required><br>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<button type="submit" class="btn btn-primary">Iniciar sesión</button>
|
||||
<a href="registro.php" class="btn btn-secondary">Regist. n. admin</a>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger mt-3"><?= $error ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
// 1) Iniciar sesión si no está activa
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// 2) Logout: si cierra sesión destruimos y redirigimos
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
|
||||
session_destroy();
|
||||
header('Location: inicio.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3) Detectar rol actual en $_SESSION['role']
|
||||
$role = $_SESSION['role'] ?? null;
|
||||
?>
|
||||
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header text-center">
|
||||
<h4>LANIA</h4>
|
||||
</div>
|
||||
<ul class="sidebar-menu">
|
||||
<!-- Solo para administradores -->
|
||||
<?php if ($role === 'admin'): ?>
|
||||
<li id="menu-estadisticas"> <a href="dashboard.php">
|
||||
<span class="menu-icon-wrapper"><i class="fas fa-tachometer-alt"></i></span>
|
||||
<span class="menu-text">Estadísticas</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Siempre visibles -->
|
||||
<li id="menu-entrada"> <a href="FormularioInicial.php">
|
||||
<span class="menu-icon-wrapper"><i class="fas fa-user"></i></span>
|
||||
<span class="menu-text">Formulario de Ingreso</span>
|
||||
</a>
|
||||
</li>
|
||||
<li id="menu-salida"> <a href="FormularioSalida.php">
|
||||
<span class="menu-icon-wrapper"><i class="fas fa-user"></i></span>
|
||||
<span class="menu-text">Formulario de Salida</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Cerrar / Iniciar sesión -->
|
||||
<div class="logout-btn text-center">
|
||||
<?php if ($role === 'admin' || $role === 'aspirant'): ?>
|
||||
<a href="?action=logout" class="btn btn-danger">
|
||||
<i class="fas fa-sign-out-alt"></i> Cerrar Sesión
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="inicio.php" class="btn btn-primary">
|
||||
<i class="fas fa-sign-in-alt"></i> Iniciar Sesión
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,213 @@
|
|||
:root {
|
||||
--primary:rgb(51, 76, 109);
|
||||
--secondary: #6c757d;
|
||||
--success: #28a745;
|
||||
--info: #17a2b8;
|
||||
--warning: #ffc107;
|
||||
--danger: #dc3545;
|
||||
--light:hsl(210, 16.70%, 97.60%);
|
||||
--dark: #343a40;
|
||||
--sidebar-bg: #1A2035;
|
||||
--card-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.14), 0 7px 10px -5px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f0f2f5;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
background: #37455a;
|
||||
color: white;
|
||||
transition: all 0.3s;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
color: black;
|
||||
padding: 20px;
|
||||
background:#37455a;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.sidebar-menu li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-radius: 8px;
|
||||
margin: 5px 15px;
|
||||
background-color: transparent;
|
||||
color:#4a515a;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-menu li a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.menu-icon-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
margin-right: 15px;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-icon-wrapper i {
|
||||
color:rgb(108, 108, 108); ;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sidebar-menu li.active a {
|
||||
background-color: white;
|
||||
box-shadow: 0 4px 10px hsla(0, 0.00%, 0.00%, 0.10);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.sidebar-menu li.active .menu-icon-wrapper {
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
.sidebar-menu li.active .menu-icon-wrapper i {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sidebar-menu li.active .menu-text {
|
||||
color: var(--primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 250px;
|
||||
padding: 20px;
|
||||
transition: all 0.3s;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
box-shadow: var(--card-shadow);
|
||||
margin-bottom: 20px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: transparent;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card-body .btn {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-card i {
|
||||
font-size: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.stat-card .count {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.stat-card .label {
|
||||
font-size: 14px;
|
||||
color: var(--secondary);
|
||||
}
|
||||
|
||||
.stat-card .change {
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.change.positive {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.change.negative {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.info-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #5e72e4 100%);
|
||||
}
|
||||
|
||||
.info-card .icon {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
font-size: 60px;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
margin-left: -250px;
|
||||
}
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
.sidebar.active {
|
||||
margin-left: 0;
|
||||
}
|
||||
.main-content.active {
|
||||
margin-left: 250px;
|
||||
}
|
||||
.card-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.card-body .btn {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.5rem 0.8rem;
|
||||
}
|
||||
.chart-container {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue