// ... existing code ... $error_message = ""; // Data Arrays $users = []; $customers = []; $bookings = []; $status_data = []; $trend_data = []; // Analytics Counts $total_users = 0; $total_customers = 0; $total_bookings = 0; $approved_bookings = 0; $pending_bookings = 0; $past_approved = 0; $upcoming_approved = 0; try { $conn = new PDO("mysql:host=" . $host . ";dbname=" . $db_name, $username, $password); $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Fetch Data (Limited to 100 for performance on single page) $users = $conn->query("SELECT ID, user_login, user_email, user_registered FROM wp_users ORDER BY ID DESC LIMIT 100")->fetchAll(PDO::FETCH_ASSOC); $customers = $conn->query("SELECT * FROM wp_latepoint_customers ORDER BY id DESC LIMIT 100")->fetchAll(PDO::FETCH_ASSOC); $bookings = $conn->query("SELECT * FROM wp_latepoint_bookings ORDER BY id DESC LIMIT 100")->fetchAll(PDO::FETCH_ASSOC); // Attach Booking History to Customers Data foreach ($customers as &$customer) { // Find bookings matching the customer_id $customer_bookings = array_filter($bookings, function($booking) use ($customer) { return $booking['customer_id'] == $customer['id']; }); // Store as JSON for the frontend $customer['booking_history'] = json_encode(array_values($customer_bookings)); } unset($customer); // Break the reference // Fetch Counts $total_users = $conn->query("SELECT COUNT(*) FROM wp_users")->fetchColumn(); $total_customers = $conn->query("SELECT COUNT(*) FROM wp_latepoint_customers")->fetchColumn(); $total_bookings = $conn->query("SELECT COUNT(*) FROM wp_latepoint_bookings")->fetchColumn(); // Advanced Analytics Counts $approved_bookings = $conn->query("SELECT COUNT(*) FROM wp_latepoint_bookings WHERE status = 'approved'")->fetchColumn(); $pending_bookings = $conn->query("SELECT COUNT(*) FROM wp_latepoint_bookings WHERE status = 'pending'")->fetchColumn(); // Past vs Upcoming Approved (Comparing start_date to today) $today = date('Y-m-d'); $past_approved = $conn->query("SELECT COUNT(*) FROM wp_latepoint_bookings WHERE status = 'approved' AND start_date < '$today'")->fetchColumn(); $upcoming_approved = $conn->query("SELECT COUNT(*) FROM wp_latepoint_bookings WHERE status = 'approved' AND start_date >= '$today'")->fetchColumn(); // Analytics Data (Charts) $status_data = $conn->query("SELECT COALESCE(status, 'unknown') as status, COUNT(*) as count FROM wp_latepoint_bookings GROUP BY status")->fetchAll(PDO::FETCH_ASSOC); $trend_results = $conn->query("SELECT start_date, COUNT(*) as count FROM wp_latepoint_bookings WHERE start_date IS NOT NULL GROUP BY start_date ORDER BY start_date DESC LIMIT 14")->fetchAll(PDO::FETCH_ASSOC); // ... existing code ...

Total Patients

0

Total Bookings

0

Pending Approvals

0

Total Approved

0

Past Approved

0

Appointments before today

Upcoming Approved

0

Appointments today and future

// ... existing code ...
// ... existing code ... // ... existing code ...
Actions
// ... existing code ...
Actions
ID Date Time Status
// ... existing code ... function filterTable(tableId, query) { let filter = query.toLowerCase(); let tr = document.getElementById(tableId).getElementsByTagName("tr"); for (let i = 1; i < tr.length; i++) { let text = tr[i].innerText.toLowerCase(); tr[i].style.display = text.includes(filter) ? "" : "none"; } } // --- 2.5 Table Sorting Logic --- function sortTable(tableId, n) { let table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; table = document.getElementById(tableId); switching = true; dir = "asc"; // Visual indicator update const headers = table.getElementsByTagName("th"); for (let j = 0; j < headers.length; j++) { let icon = headers[j].querySelector('i'); if(icon) icon.className = 'ph ph-caret-up text-xs opacity-0 group-hover:opacity-100 transition-opacity ml-1'; } let clickedHeaderIcon = headers[n].querySelector('i'); if(clickedHeaderIcon) clickedHeaderIcon.className = 'ph ph-caret-up text-xs opacity-100 ml-1 text-emerald-400'; while (switching) { switching = false; rows = table.rows; for (i = 1; i < (rows.length - 1); i++) { shouldSwitch = false; x = rows[i].getElementsByTagName("TD")[n]; y = rows[i + 1].getElementsByTagName("TD")[n]; let xContent = x.innerText.toLowerCase(); let yContent = y.innerText.toLowerCase(); // Basic numeric sort if applicable if(!isNaN(xContent) && !isNaN(yContent)) { xContent = parseFloat(xContent); yContent = parseFloat(yContent); } if (dir == "asc") { if (xContent > yContent) { shouldSwitch = true; break; } } else if (dir == "desc") { if (xContent < yContent) { shouldSwitch = true; break; } } } if (shouldSwitch) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; switchcount ++; } else { if (switchcount == 0 && dir == "asc") { dir = "desc"; if(clickedHeaderIcon) clickedHeaderIcon.className = 'ph ph-caret-down text-xs opacity-100 ml-1 text-emerald-400'; switching = true; } } } } // --- 3. Universal Modal Logic --- const modal = document.getElementById('universalModal'); const modalCard = document.getElementById('modalCard'); function openModal(buttonElement, type) { const data = JSON.parse(buttonElement.getAttribute('data-json')); const dataGrid = document.getElementById('modalDataGrid'); const modalTitle = document.getElementById('modalTitle'); const modalIcon = document.getElementById('modalIcon'); const historySection = document.getElementById('modalHistorySection'); const historyBody = document.getElementById('modalHistoryBody'); const historyEmpty = document.getElementById('modalHistoryEmpty'); const historyTable = document.getElementById('modalHistoryTable'); // Set Header Styling // ... existing code ... if (key === 'status') { textCol = value === 'approved' ? 'text-emerald-400' : (value === 'pending' ? 'text-amber-400' : 'text-red-400'); } // Skip the hidden JSON string if (key === 'booking_history') continue; dataGrid.innerHTML += `

${formattedKey}

${displayValue}

`; } // Handle Booking History for Patients if (type === 'Patient') { historySection.classList.remove('hidden'); historyBody.innerHTML = ''; // Clear old history let historyData = []; try { // It should be passed as a stringified array from PHP historyData = typeof data.booking_history === 'string' ? JSON.parse(data.booking_history) : data.booking_history; } catch(e) { console.error("Error parsing history"); } if (historyData && historyData.length > 0) { historyTable.classList.remove('hidden'); historyEmpty.classList.add('hidden'); historyData.forEach(booking => { let statusColor = booking.status === 'approved' ? 'text-emerald-400' : (booking.status === 'pending' ? 'text-amber-400' : 'text-slate-400'); historyBody.innerHTML += ` #${booking.id || '-'} ${booking.start_date || '-'} ${booking.start_time ? booking.start_time.substring(0,5) : '-'} ${booking.status || 'Unknown'} `; }); } else { historyTable.classList.add('hidden'); historyEmpty.classList.remove('hidden'); } } else { historySection.classList.add('hidden'); } // Animate In // ... existing code ...