Sindbad~EG File Manager

Current Path : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/attendance/
Upload File :
Current File : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/attendance/live-qr.php

<?php
require_once '../../config/config.php';
checkLogin();

$pageTitle = "Live QR Attendance - " . APP_NAME;
$db = Database::getInstance()->getConnection();
$success = '';
$error = '';

// Handle QR code scan check-in (AJAX)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['qr_checkin'])) {
    header('Content-Type: application/json');
    
    try {
        $qrData = json_decode($_POST['qr_data'], true);
        $memberId = $qrData['member_id'] ?? null;
        $programId = $qrData['program_id'] ?? null;
        
        if (!$memberId || !$programId) {
            echo json_encode(['success' => false, 'message' => 'Invalid QR code']);
            exit();
        }
        
        // Check if member exists
        $memberStmt = $db->prepare("SELECT m.*, a.assembly_name, d.district_name 
                                    FROM members m 
                                    LEFT JOIN assemblies a ON m.assembly_id = a.id
                                    LEFT JOIN districts d ON m.district_id = d.id
                                    WHERE m.id = :id");
        $memberStmt->execute(['id' => $memberId]);
        $member = $memberStmt->fetch();
        
        if (!$member) {
            echo json_encode(['success' => false, 'message' => 'Member not found']);
            exit();
        }
        
        // Get program details
        $progStmt = $db->prepare("SELECT program_name FROM programs WHERE id = :id");
        $progStmt->execute(['id' => $programId]);
        $program = $progStmt->fetch();
        
        if (!$program) {
            echo json_encode(['success' => false, 'message' => 'Program not found']);
            exit();
        }
        
        $selectedDate = date('Y-m-d');
        
        // Check if already checked in
        $checkStmt = $db->prepare("
            SELECT id, check_in_time, check_out_time FROM program_attendance 
            WHERE program_id = :program_id AND member_id = :member_id AND attendance_date = :date
        ");
        $checkStmt->execute([
            'program_id' => $programId,
            'member_id' => $memberId,
            'date' => $selectedDate
        ]);
        
        $attendance = $checkStmt->fetch();
        
        if ($attendance) {
            // Already has attendance record
            if ($attendance['check_out_time']) {
                // Already checked out - error
                echo json_encode([
                    'success' => false, 
                    'message' => $member['first_name'] . ' ' . $member['last_name'] . ' already checked out from ' . $program['program_name']
                ]);
                exit();
            } else {
                // Checked in but not out - perform check-out
                $stmt = $db->prepare("
                    UPDATE program_attendance 
                    SET check_out_time = NOW(), marked_by = :marked_by
                    WHERE id = :id
                ");
                
                $stmt->execute([
                    'id' => $attendance['id'],
                    'marked_by' => $_SESSION['user_id']
                ]);
                
                echo json_encode([
                    'success' => true,
                    'message' => 'Check-out successful!',
                    'action' => 'checkout',
                    'member_name' => $member['first_name'] . ' ' . $member['last_name'],
                    'program_name' => $program['program_name'],
                    'district' => $member['district_name'] ?? 'N/A',
                    'assembly' => $member['assembly_name'] ?? 'N/A',
                    'time' => date('g:i A')
                ]);
                exit();
            }
        }
        
        // Insert new check-in
        $stmt = $db->prepare("
            INSERT INTO program_attendance (program_id, member_id, attendance_date, status, marked_by, check_in_time)
            VALUES (:program_id, :member_id, :attendance_date, 'present', :marked_by, NOW())
        ");
        
        $stmt->execute([
            'program_id' => $programId,
            'member_id' => $memberId,
            'attendance_date' => $selectedDate,
            'marked_by' => $_SESSION['user_id']
        ]);
        
        echo json_encode([
            'success' => true,
            'message' => 'Check-in successful!',
            'action' => 'checkin',
            'member_name' => $member['first_name'] . ' ' . $member['last_name'],
            'program_name' => $program['program_name'],
            'district' => $member['district_name'] ?? 'N/A',
            'assembly' => $member['assembly_name'] ?? 'N/A',
            'time' => date('g:i A')
        ]);
        exit();
        
    } catch (Exception $e) {
        echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        exit();
    }
}

// Get today's attendance stats across all programs
$statsStmt = $db->prepare("
    SELECT 
        COUNT(DISTINCT pa.member_id) as checked_in_today,
        COUNT(DISTINCT pa.program_id) as active_programs,
        (SELECT COUNT(*) FROM members WHERE is_active = 1) as total_members
    FROM program_attendance pa
    WHERE pa.attendance_date = :date
");
$statsStmt->execute(['date' => date('Y-m-d')]);
$stats = $statsStmt->fetch();

// Get recent check-ins across all programs today
$recentStmt = $db->prepare("
    SELECT m.id, m.first_name, m.last_name, m.membershipcard_id, 
           p.program_name, pa.check_in_time, pa.check_out_time,
           a.assembly_name, d.district_name
    FROM program_attendance pa
    JOIN members m ON pa.member_id = m.id
    JOIN programs p ON pa.program_id = p.id
    LEFT JOIN assemblies a ON m.assembly_id = a.id
    LEFT JOIN districts d ON m.district_id = d.id
    WHERE pa.attendance_date = :date
    ORDER BY COALESCE(pa.check_out_time, pa.check_in_time) DESC
    LIMIT 100
");
$recentStmt->execute(['date' => date('Y-m-d')]);
$recentCheckIns = $recentStmt->fetchAll();

include '../../includes/header.php';
?>

<?php include '../../includes/sidebar.php'; ?>

<main class="flex-1 md:ml-64 mt-16">
<div class="container mx-auto px-4 py-8">
    <div class="max-w-7xl mx-auto">
        
        <!-- Header -->
        <div class="mb-6">
            <h1 class="text-3xl font-bold text-gray-800">
                <i class="fas fa-qrcode mr-2 text-blue-500"></i>Live QR Attendance
            </h1>
            <p class="text-gray-600 mt-2">Real-time attendance tracking via QR code scanning - <?php echo date('F j, Y'); ?></p>
        </div>
        
        <!-- Today's Stats -->
        <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
            <div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl shadow-lg p-6 text-white">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-sm opacity-90">Checked In Today</p>
                        <p class="text-4xl font-bold" id="checkedInCount"><?php echo $stats['checked_in_today']; ?></p>
                    </div>
                    <i class="fas fa-user-check text-5xl opacity-20"></i>
                </div>
            </div>
            
            <div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl shadow-lg p-6 text-white">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-sm opacity-90">Active Programs</p>
                        <p class="text-4xl font-bold" id="activeProgramsCount"><?php echo $stats['active_programs']; ?></p>
                    </div>
                    <i class="fas fa-calendar text-5xl opacity-20"></i>
                </div>
            </div>
            
            <div class="bg-gradient-to-r from-purple-500 to-purple-600 rounded-xl shadow-lg p-6 text-white">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-sm opacity-90">Total Members</p>
                        <p class="text-4xl font-bold"><?php echo $stats['total_members']; ?></p>
                    </div>
                    <i class="fas fa-users text-5xl opacity-20"></i>
                </div>
            </div>
            
            <div class="bg-gradient-to-r from-orange-500 to-red-500 rounded-xl shadow-lg p-6 text-white">
                <div class="flex items-center justify-between">
                    <div>
                        <p class="text-sm opacity-90">Attendance Rate</p>
                        <p class="text-4xl font-bold" id="attendanceRate">
                            <?php echo $stats['total_members'] > 0 ? round(($stats['checked_in_today'] / $stats['total_members']) * 100) : 0; ?>%
                        </p>
                    </div>
                    <i class="fas fa-chart-pie text-5xl opacity-20"></i>
                </div>
            </div>
        </div>
        
        <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
            
            <!-- QR Scanner -->
            <div class="lg:col-span-1">
                <div class="bg-white rounded-xl shadow-lg p-6 sticky top-24">
                    <h3 class="text-xl font-bold text-gray-800 mb-4 text-center">
                        <i class="fas fa-camera mr-2 text-blue-500"></i>Scan QR Code
                    </h3>
                    
                    <div class="bg-gray-100 p-4 rounded-lg mb-4">
                        <div id="reader" class="w-full"></div>
                    </div>
                    
                    <div id="scanStatus" class="text-center text-sm text-gray-600 mb-4">
                        Ready to scan...
                    </div>
                    
                    <button id="startScanBtn" onclick="startScanner()" 
                            class="w-full bg-gradient-to-r from-blue-500 to-blue-600 text-white px-6 py-3 rounded-lg hover:shadow-lg transition">
                        <i class="fas fa-camera mr-2"></i>Start Scanner
                    </button>
                    
                    <button id="stopScanBtn" onclick="stopScanner()" 
                            class="w-full bg-gradient-to-r from-red-500 to-red-600 text-white px-6 py-3 rounded-lg hover:shadow-lg transition hidden">
                        <i class="fas fa-stop mr-2"></i>Stop Scanner
                    </button>
                    
                    <!-- Last Scanned Info -->
                    <div id="lastScanned" class="mt-4 p-4 bg-green-50 border border-green-200 rounded-lg hidden">
                        <h4 class="font-semibold text-green-800 mb-2">
                            <i class="fas fa-check-circle mr-2"></i>Last Check-In
                        </h4>
                        <div id="lastScannedInfo" class="text-sm text-green-700"></div>
                    </div>
                </div>
            </div>
            
            <!-- Live Feed -->
            <div class="lg:col-span-2">
                <div class="bg-white rounded-xl shadow-lg p-6">
                    <div class="flex justify-between items-center mb-4">
                        <h3 class="text-xl font-bold text-gray-800">
                            <i class="fas fa-stream mr-2 text-green-500"></i>Live Feed
                            <span class="text-sm font-normal text-gray-500">(Auto-updates)</span>
                        </h3>
                        <button onclick="location.reload()" class="text-blue-600 hover:text-blue-800">
                            <i class="fas fa-sync-alt mr-1"></i>Refresh
                        </button>
                    </div>
                    
                    <div id="recentCheckIns" class="space-y-3 max-h-[650px] overflow-y-auto">
                        <?php if (empty($recentCheckIns)): ?>
                            <p class="text-gray-500 text-center py-12">No check-ins today yet</p>
                        <?php else: ?>
                            <?php foreach ($recentCheckIns as $checkin): ?>
                            <div class="flex items-center justify-between p-4 bg-gradient-to-r from-gray-50 to-white rounded-lg border border-gray-200 hover:shadow-md transition checkin-item">
                                <div class="flex items-center flex-1">
                                    <div class="h-12 w-12 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white font-bold mr-4">
                                        <?php echo strtoupper(substr($checkin['first_name'], 0, 1) . substr($checkin['last_name'], 0, 1)); ?>
                                    </div>
                                    <div class="flex-1">
                                        <p class="font-semibold text-gray-900">
                                            <?php echo htmlspecialchars($checkin['first_name'] . ' ' . $checkin['last_name']); ?>
                                        </p>
                                        <p class="text-xs text-gray-500">
                                            <i class="fas fa-id-card mr-1"></i><?php echo htmlspecialchars($checkin['membershipcard_id']); ?> | 
                                            <i class="fas fa-church mr-1"></i><?php echo htmlspecialchars($checkin['assembly_name'] ?? 'N/A'); ?>
                                        </p>
                                        <p class="text-xs text-blue-600 mt-1">
                                            <i class="fas fa-calendar mr-1"></i><?php echo htmlspecialchars($checkin['program_name']); ?>
                                        </p>
                                    </div>
                                </div>
                                <div class="text-right">
                                    <?php if ($checkin['check_out_time']): ?>
                                        <p class="text-sm text-blue-600 font-semibold">
                                            <i class="fas fa-sign-out-alt mr-1"></i>Checked Out
                                        </p>
                                        <p class="text-xs text-gray-500">
                                            <i class="fas fa-clock mr-1"></i>In: <?php echo date('g:i A', strtotime($checkin['check_in_time'])); ?>
                                        </p>
                                        <p class="text-xs text-gray-500">
                                            <i class="fas fa-clock mr-1"></i>Out: <?php echo date('g:i A', strtotime($checkin['check_out_time'])); ?>
                                        </p>
                                    <?php else: ?>
                                        <p class="text-sm text-green-600 font-semibold">
                                            <i class="fas fa-check-circle mr-1"></i>Checked In
                                        </p>
                                        <p class="text-xs text-gray-500">
                                            <i class="fas fa-clock mr-1"></i><?php echo date('g:i A', strtotime($checkin['check_in_time'])); ?>
                                        </p>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <?php endforeach; ?>
                        <?php endif; ?>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</main>

<!-- QR Scanner Library -->
<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>

<script>
let html5Qrcode;
let isScanning = false;

function startScanner() {
    if (isScanning) return;
    
    document.getElementById('startScanBtn').classList.add('hidden');
    document.getElementById('stopScanBtn').classList.remove('hidden');
    document.getElementById('scanStatus').innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Initializing camera...';
    
    html5Qrcode = new Html5Qrcode("reader");
    
    html5Qrcode.start(
        { facingMode: "environment" },
        {
            fps: 10,
            qrbox: { width: 250, height: 250 }
        },
        onScanSuccess,
        onScanError
    ).then(() => {
        isScanning = true;
        document.getElementById('scanStatus').innerHTML = '<i class="fas fa-camera mr-2 text-green-600"></i>Scanning... Point camera at QR code';
    }).catch(err => {
        document.getElementById('scanStatus').innerHTML = '<i class="fas fa-exclamation-triangle mr-2 text-red-600"></i>Camera error: ' + err;
        document.getElementById('startScanBtn').classList.remove('hidden');
        document.getElementById('stopScanBtn').classList.add('hidden');
    });
}

function stopScanner() {
    if (!isScanning || !html5Qrcode) return;
    
    html5Qrcode.stop().then(() => {
        isScanning = false;
        document.getElementById('startScanBtn').classList.remove('hidden');
        document.getElementById('stopScanBtn').classList.add('hidden');
        document.getElementById('scanStatus').innerHTML = 'Scanner stopped';
    }).catch(err => {
        console.error('Error stopping scanner:', err);
    });
}

function onScanSuccess(decodedText) {
    // Prevent multiple scans
    if (!isScanning) return;
    
    document.getElementById('scanStatus').innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Processing...';
    
    try {
        const qrData = JSON.parse(decodedText);
        
        if (qrData.member_id && qrData.program_id) {
            // Send check-in request
            const formData = new FormData();
            formData.append('qr_checkin', '1');
            formData.append('qr_data', decodedText);
            
            fetch('', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    // Determine icon and color based on action
                    const isCheckout = data.action === 'checkout';
                    const iconClass = isCheckout ? 'fa-sign-out-alt' : 'fa-check-circle';
                    const colorClass = isCheckout ? 'text-blue-600' : 'text-green-600';
                    const actionText = isCheckout ? 'Checked Out' : 'Checked In';
                    
                    // Show success message
                    document.getElementById('scanStatus').innerHTML = `<i class="fas ${iconClass} mr-2 ${colorClass}"></i>${data.message}`;
                    
                    // Update last scanned info
                    const lastScanned = document.getElementById('lastScanned');
                    const lastScannedInfo = document.getElementById('lastScannedInfo');
                    
                    // Change box color based on action
                    if (isCheckout) {
                        lastScanned.className = 'mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg';
                        lastScannedInfo.innerHTML = `
                            <h4 class="font-semibold text-blue-800 mb-2">
                                <i class="fas fa-sign-out-alt mr-2"></i>Last Check-Out
                            </h4>
                        `;
                    } else {
                        lastScanned.className = 'mt-4 p-4 bg-green-50 border border-green-200 rounded-lg';
                        lastScannedInfo.innerHTML = `
                            <h4 class="font-semibold text-green-800 mb-2">
                                <i class="fas fa-check-circle mr-2"></i>Last Check-In
                            </h4>
                        `;
                    }
                    
                    lastScannedInfo.innerHTML += `
                        <div class="text-sm ${isCheckout ? 'text-blue-700' : 'text-green-700'}">
                            <p><strong>${data.member_name}</strong></p>
                            <p class="mt-1"><i class="fas fa-calendar mr-1"></i>${data.program_name}</p>
                            <p class="mt-1"><i class="fas fa-church mr-1"></i>${data.assembly}</p>
                            <p class="mt-1"><i class="fas fa-clock mr-1"></i>${data.time}</p>
                        </div>
                    `;
                    lastScanned.classList.remove('hidden');
                    
                    // Update stats only for check-ins
                    if (!isCheckout) {
                        document.getElementById('checkedInCount').textContent = parseInt(document.getElementById('checkedInCount').textContent) + 1;
                    }
                    
                    // Reload after 2 seconds to show in feed
                    setTimeout(() => {
                        location.reload();
                    }, 2000);
                    
                } else {
                    document.getElementById('scanStatus').innerHTML = `<i class="fas fa-exclamation-triangle mr-2 text-red-600"></i>${data.message}`;
                    
                    // Resume scanning after error
                    setTimeout(() => {
                        if (isScanning) {
                            document.getElementById('scanStatus').innerHTML = '<i class="fas fa-camera mr-2 text-green-600"></i>Scanning...';
                        }
                    }, 3000);
                }
            })
            .catch(err => {
                document.getElementById('scanStatus').innerHTML = '<i class="fas fa-exclamation-triangle mr-2 text-red-600"></i>Error: ' + err;
            });
        } else {
            document.getElementById('scanStatus').innerHTML = '<i class="fas fa-exclamation-triangle mr-2 text-red-600"></i>Invalid QR code format';
        }
    } catch (e) {
        document.getElementById('scanStatus').innerHTML = '<i class="fas fa-exclamation-triangle mr-2 text-red-600"></i>Invalid QR code';
    }
}

function onScanError(errorMessage) {
    // Silent - continuous scanning
}

// Auto-refresh every 30 seconds
setInterval(() => {
    if (!isScanning) {
        location.reload();
    }
}, 30000);
</script>

<?php include '../../includes/footer.php'; ?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists