![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/admin_includes/ |
<?php
// Online Users Tab
// This file displays all currently online users with detailed information
// First, ensure users table has last_activity column
try {
$columns = $pdo->query("SHOW COLUMNS FROM users LIKE 'last_activity'")->fetchAll();
if (empty($columns)) {
$pdo->exec("ALTER TABLE users ADD COLUMN last_activity TIMESTAMP NULL DEFAULT NULL");
$pdo->exec("CREATE INDEX idx_users_last_activity ON users(last_activity)");
}
} catch (Exception $e) {
// Column might already exist or table doesn't exist, ignore error
error_log("Could not add last_activity column: " . $e->getMessage());
}
// Get currently online users (active within last 15 minutes)
// Try multiple sources: last_activity column, page_visits, then user_login_history
try {
// First, check if page_visits table exists and has data
$pageVisitsExists = false;
try {
$pdo->query("SELECT 1 FROM page_visits LIMIT 1");
$pageVisitsExists = true;
} catch (Exception $e) {
// Table doesn't exist or has no data
}
// First, try using last_activity column in users table (most reliable)
$userColumns = $pdo->query("SHOW COLUMNS FROM users")->fetchAll(PDO::FETCH_COLUMN);
$hasLastActivity = in_array('last_activity', $userColumns);
if ($hasLastActivity) {
// Get users with recent last_activity
try {
$online_users = $pdo->query("
SELECT DISTINCT
u.id,
u.name,
u.email,
u.plan,
u.is_admin,
u.credits,
u.created_at as account_created,
u.last_activity,
COALESCE(
(SELECT MAX(pv.ip_address) FROM page_visits pv WHERE pv.user_id = u.id AND pv.visit_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)),
(SELECT MAX(ulh.ip_address) FROM user_login_history ulh WHERE ulh.user_id = u.id),
'Unknown'
) as last_ip,
COALESCE(
(SELECT MAX(pv.country) FROM page_visits pv WHERE pv.user_id = u.id AND pv.visit_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)),
(SELECT MAX(ulh.country) FROM user_login_history ulh WHERE ulh.user_id = u.id),
NULL
) as country,
COALESCE(
(SELECT MAX(pv.city) FROM page_visits pv WHERE pv.user_id = u.id AND pv.visit_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR)),
(SELECT MAX(ulh.city) FROM user_login_history ulh WHERE ulh.user_id = u.id),
NULL
) as city,
0 as login_count_today,
TIMESTAMPDIFF(MINUTE, u.last_activity, NOW()) as minutes_ago,
(SELECT pv.page_url FROM page_visits pv WHERE pv.user_id = u.id ORDER BY pv.visit_time DESC LIMIT 1) as current_page
FROM users u
WHERE u.last_activity IS NOT NULL
AND u.last_activity >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
ORDER BY u.last_activity DESC
")->fetchAll();
// Get user stats (tracks created) for last_activity users
foreach ($online_users as &$user) {
$stats = $pdo->prepare("
SELECT
COUNT(*) as total_tracks,
COUNT(CASE WHEN status = 'complete' THEN 1 END) as completed_tracks
FROM music_tracks
WHERE user_id = ?
");
$stats->execute([$user['id']]);
$track_stats = $stats->fetch(PDO::FETCH_ASSOC);
$user['total_tracks'] = $track_stats['total_tracks'] ?? 0;
$user['completed_tracks'] = $track_stats['completed_tracks'] ?? 0;
}
unset($user);
} catch (Exception $queryError) {
error_log("Error in last_activity query: " . $queryError->getMessage());
$online_users = [];
}
// If no results, try page_visits
if (empty($online_users) && $pageVisitsExists) {
throw new Exception("No last_activity found, trying page_visits");
} elseif (empty($online_users)) {
throw new Exception("No last_activity found, trying login history");
}
} elseif ($pageVisitsExists) {
// Use page_visits table - this tracks actual user activity
$online_users = $pdo->query("
SELECT DISTINCT
u.id,
u.name,
u.email,
u.plan,
u.is_admin,
u.credits,
u.created_at as account_created,
MAX(pv.visit_time) as last_activity,
MAX(pv.ip_address) as last_ip,
MAX(pv.country) as country,
MAX(pv.city) as city,
COUNT(DISTINCT DATE(pv.visit_time)) as login_count_today,
TIMESTAMPDIFF(MINUTE, MAX(pv.visit_time), NOW()) as minutes_ago,
MAX(pv.page_url) as current_page
FROM users u
INNER JOIN page_visits pv ON u.id = pv.user_id
WHERE pv.user_id IS NOT NULL
AND pv.visit_time >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
GROUP BY u.id, u.name, u.email, u.plan, u.is_admin, u.credits, u.created_at
ORDER BY last_activity DESC
")->fetchAll();
// Get user stats (tracks created) for page_visits users
foreach ($online_users as &$user) {
$stats = $pdo->prepare("
SELECT
COUNT(*) as total_tracks,
COUNT(CASE WHEN status = 'complete' THEN 1 END) as completed_tracks
FROM music_tracks
WHERE user_id = ?
");
$stats->execute([$user['id']]);
$track_stats = $stats->fetch(PDO::FETCH_ASSOC);
$user['total_tracks'] = $track_stats['total_tracks'] ?? 0;
$user['completed_tracks'] = $track_stats['completed_tracks'] ?? 0;
}
unset($user);
// If no results from page_visits, try login_history as fallback
if (empty($online_users)) {
throw new Exception("No page visits found, trying login history");
}
} else {
throw new Exception("page_visits table not available");
}
} catch (Exception $e) {
// Fallback to user_login_history if page_visits doesn't work
try {
// Check which columns exist in user_login_history table
$historyColumns = $pdo->query("SHOW COLUMNS FROM user_login_history")->fetchAll(PDO::FETCH_COLUMN);
$hasLoginTime = in_array('login_time', $historyColumns);
$hasSuccess = in_array('success', $historyColumns);
$hasLoginSuccess = in_array('login_success', $historyColumns);
$hasCountry = in_array('country', $historyColumns);
$hasCity = in_array('city', $historyColumns);
// Determine which success column to use
$hasSuccessColumn = $hasSuccess || $hasLoginSuccess;
$successColumn = $hasSuccess ? 'success' : ($hasLoginSuccess ? 'login_success' : null);
// Determine which time column to use
$timeColumn = $hasLoginTime ? 'login_time' : 'created_at';
// Build success condition
if ($hasSuccessColumn && $successColumn) {
$successCondition = "((ulh.{$successColumn} = 1 OR ulh.{$successColumn} = TRUE OR ulh.{$successColumn} IS TRUE) OR ulh.{$successColumn} IS NULL) AND ";
} else {
$successCondition = "";
}
// Get online users from login history
$online_users = $pdo->query("
SELECT DISTINCT
u.id,
u.name,
u.email,
u.plan,
u.is_admin,
u.credits,
u.created_at as account_created,
MAX(ulh.{$timeColumn}) as last_activity,
MAX(ulh.ip_address) as last_ip,
" . ($hasCountry ? "MAX(ulh.country) as country," : "NULL as country,") . "
" . ($hasCity ? "MAX(ulh.city) as city," : "NULL as city,") . "
COUNT(DISTINCT ulh.id) as login_count_today,
TIMESTAMPDIFF(MINUTE, MAX(ulh.{$timeColumn}), NOW()) as minutes_ago,
NULL as current_page
FROM users u
INNER JOIN user_login_history ulh ON u.id = ulh.user_id
WHERE {$successCondition}ulh.{$timeColumn} >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
GROUP BY u.id, u.name, u.email, u.plan, u.is_admin, u.credits, u.created_at
ORDER BY last_activity DESC
")->fetchAll();
// Get user stats (tracks created)
foreach ($online_users as &$user) {
$stats = $pdo->prepare("
SELECT
COUNT(*) as total_tracks,
COUNT(CASE WHEN status = 'complete' THEN 1 END) as completed_tracks
FROM music_tracks
WHERE user_id = ?
");
$stats->execute([$user['id']]);
$track_stats = $stats->fetch(PDO::FETCH_ASSOC);
$user['total_tracks'] = $track_stats['total_tracks'] ?? 0;
$user['completed_tracks'] = $track_stats['completed_tracks'] ?? 0;
}
unset($user);
} catch (Exception $e) {
error_log("Error fetching online users from login history: " . $e->getMessage());
$online_users = [];
}
}
// Ensure variables are always defined
if (!isset($online_users)) {
$online_users = [];
}
$online_count = count($online_users);
// Get anonymous visitors (non-logged-in users) from page_visits
$anonymous_visitors = [];
$all_recent_visits = [];
$debug_info = [];
try {
// Check if page_visits table exists
$pdo->query("SELECT 1 FROM page_visits LIMIT 1");
// DEBUG: Get ALL visits in last hour to see what's being recorded
$debug_query = $pdo->query("
SELECT
pv.id,
pv.user_id,
pv.ip_address,
pv.page_url,
pv.visit_time,
NOW() as server_time
FROM page_visits pv
ORDER BY pv.visit_time DESC
LIMIT 20
");
$all_recent_visits = $debug_query->fetchAll(PDO::FETCH_ASSOC);
// Get total count
$count_query = $pdo->query("SELECT COUNT(*) as total FROM page_visits");
$debug_info['total_visits'] = $count_query->fetch()['total'];
$debug_info['server_time'] = date('Y-m-d H:i:s');
// Get anonymous visitors active in last 15 minutes
$anon_query = $pdo->query("
SELECT
pv.ip_address,
pv.country,
pv.city,
pv.user_agent,
MAX(pv.visit_time) as last_activity,
COUNT(*) as page_views,
MAX(pv.page_url) as current_page,
MIN(pv.visit_time) as session_start,
TIMESTAMPDIFF(MINUTE, MAX(pv.visit_time), NOW()) as minutes_ago
FROM page_visits pv
WHERE (pv.user_id IS NULL OR pv.user_id = 0)
AND pv.visit_time >= DATE_SUB(NOW(), INTERVAL 15 MINUTE)
GROUP BY pv.ip_address, pv.country, pv.city, pv.user_agent
ORDER BY last_activity DESC
LIMIT 50
");
$anonymous_visitors = $anon_query->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
// page_visits table doesn't exist or query failed
error_log("Error fetching anonymous visitors: " . $e->getMessage());
$debug_info['error'] = $e->getMessage();
}
$anonymous_count = count($anonymous_visitors);
// Check if IP belongs to known bot ranges
function isKnownBotIP($ip) {
// Google bot IP ranges (66.249.x.x, 64.233.x.x, etc.)
$botRanges = [
'66.249.', // Googlebot
'64.233.', // Google
'72.14.', // Google
'209.85.', // Google
'216.239.', // Google
'74.125.', // Google
'207.46.', // Bingbot
'40.77.', // Bingbot
'157.55.', // Bingbot
'199.30.', // Apple
'17.0.', // Apple
];
foreach ($botRanges as $range) {
if (strpos($ip, $range) === 0) {
return true;
}
}
return false;
}
// Parse user agent to get browser/device info
function parseUserAgent($ua, $ip = null) {
$browser = 'Unknown';
$device = 'Desktop';
$os = 'Unknown';
if (empty($ua)) return ['browser' => $browser, 'device' => $device, 'os' => $os];
// Detect browser
if (preg_match('/Firefox\//i', $ua)) $browser = 'Firefox';
elseif (preg_match('/Edg\//i', $ua)) $browser = 'Edge';
elseif (preg_match('/Chrome\//i', $ua)) $browser = 'Chrome';
elseif (preg_match('/Safari\//i', $ua) && !preg_match('/Chrome/i', $ua)) $browser = 'Safari';
elseif (preg_match('/Opera|OPR\//i', $ua)) $browser = 'Opera';
elseif (preg_match('/MSIE|Trident/i', $ua)) $browser = 'IE';
// Detect device - check user agent AND IP for bots
if (preg_match('/Mobile|Android|iPhone|iPad/i', $ua)) {
$device = preg_match('/iPad/i', $ua) ? 'Tablet' : 'Mobile';
}
if (preg_match('/bot|crawl|spider|slurp|googlebot|bingbot|yandex|baidu|duckduck/i', $ua)) {
$device = 'Bot';
}
// Also check IP for known bot ranges
if ($ip && isKnownBotIP($ip)) {
$device = 'Bot';
$browser = 'Googlebot'; // Most common
}
// Detect OS
if (preg_match('/Windows/i', $ua)) $os = 'Windows';
elseif (preg_match('/Mac OS X/i', $ua)) $os = 'macOS';
elseif (preg_match('/Linux/i', $ua)) $os = 'Linux';
elseif (preg_match('/Android/i', $ua)) $os = 'Android';
elseif (preg_match('/iPhone|iPad/i', $ua)) $os = 'iOS';
return ['browser' => $browser, 'device' => $device, 'os' => $os];
}
?>
<style>
.online-users-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding: 1.5rem;
background: linear-gradient(135deg, rgba(72, 187, 120, 0.1), rgba(56, 161, 105, 0.1));
border-radius: 12px;
border: 1px solid rgba(72, 187, 120, 0.3);
}
.online-indicator {
display: inline-block;
width: 12px;
height: 12px;
background: #48bb78;
border-radius: 50%;
margin-right: 0.5rem;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.user-card {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.user-card:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(72, 187, 120, 0.5);
transform: translateY(-2px);
}
.user-card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.user-avatar-large {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, #48bb78, #38a169);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.5rem;
color: white;
position: relative;
flex-shrink: 0;
}
.user-avatar-large::after {
content: '';
position: absolute;
bottom: 2px;
right: 2px;
width: 16px;
height: 16px;
background: #48bb78;
border: 3px solid #0f172a;
border-radius: 50%;
}
.user-info-main {
flex: 1;
}
.user-name-large {
font-size: 1.6rem;
font-weight: 700;
color: white;
margin-bottom: 0.3rem;
}
.user-email {
font-size: 1.2rem;
color: #60a5fa;
}
.user-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.8rem;
font-weight: 700;
color: #667eea;
margin-bottom: 0.3rem;
}
.stat-label {
font-size: 1.1rem;
color: #a0aec0;
}
.location-info {
display: flex;
align-items: center;
gap: 0.5rem;
color: #a0aec0;
font-size: 1.2rem;
}
.refresh-btn {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.refresh-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(72, 187, 120, 0.4);
}
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: #94a3b8;
}
.empty-state i {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
}
.time-ago {
color: #48bb78;
font-weight: 600;
font-size: 1.2rem;
}
/* Anonymous Visitors Styles */
.anonymous-section {
margin-top: 3rem;
padding-top: 2rem;
border-top: 2px solid rgba(255, 255, 255, 0.1);
}
.anonymous-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
padding: 1rem 1.5rem;
background: linear-gradient(135deg, rgba(99, 102, 241, 0.1), rgba(139, 92, 246, 0.1));
border-radius: 12px;
border: 1px solid rgba(99, 102, 241, 0.3);
}
.visitor-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
padding: 1rem 1.25rem;
display: flex;
align-items: center;
gap: 1rem;
transition: all 0.2s ease;
}
.visitor-card:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(99, 102, 241, 0.4);
}
.visitor-icon {
width: 42px;
height: 42px;
border-radius: 50%;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.1rem;
flex-shrink: 0;
}
.visitor-icon.bot {
background: linear-gradient(135deg, #f59e0b, #d97706);
}
.visitor-info {
flex: 1;
min-width: 0;
}
.visitor-ip {
font-weight: 600;
color: #e2e8f0;
font-size: 1rem;
font-family: 'Consolas', monospace;
}
.visitor-location {
color: #94a3b8;
font-size: 0.9rem;
margin-top: 0.2rem;
}
.visitor-meta {
display: flex;
gap: 1rem;
flex-wrap: wrap;
align-items: center;
}
.visitor-badge {
background: rgba(255, 255, 255, 0.1);
padding: 0.25rem 0.6rem;
border-radius: 6px;
font-size: 0.8rem;
color: #cbd5e1;
}
.visitor-badge.browser {
background: rgba(59, 130, 246, 0.2);
color: #93c5fd;
}
.visitor-badge.os {
background: rgba(16, 185, 129, 0.2);
color: #6ee7b7;
}
.visitor-badge.pages {
background: rgba(249, 115, 22, 0.2);
color: #fdba74;
}
.visitor-page {
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #64748b;
font-size: 0.85rem;
}
.visitors-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
gap: 0.75rem;
}
@media (max-width: 768px) {
.visitors-grid {
grid-template-columns: 1fr;
}
.visitor-card {
flex-wrap: wrap;
}
}
</style>
<div class="online-users-header">
<div>
<h2 style="color: white; margin: 0; display: flex; align-items: center;">
<span class="online-indicator"></span>
Online Activity
</h2>
<p style="color: #a0aec0; margin: 0.5rem 0 0 0; font-size: 1.2rem;">
<strong style="color: #48bb78;"><?= $online_count ?></strong> logged-in user<?= $online_count != 1 ? 's' : '' ?>
+ <strong style="color: #8b5cf6;"><?= $anonymous_count ?></strong> anonymous visitor<?= $anonymous_count != 1 ? 's' : '' ?>
<span style="color: #64748b;">(last 15 min)</span>
</p>
</div>
<button class="refresh-btn" onclick="location.reload()">
<i class="fas fa-sync-alt"></i>
Refresh
</button>
</div>
<?php if (empty($online_users)): ?>
<div class="empty-state">
<i class="fas fa-user-slash"></i>
<h3 style="color: white; font-size: 2rem; margin-bottom: 0.5rem;">No Users Online</h3>
<p style="font-size: 1.4rem;">No users have been active in the last 15 minutes.</p>
</div>
<?php else: ?>
<!-- Summary Stats -->
<div class="stats-grid" style="margin-bottom: 2rem;">
<div class="stat-card" style="background: linear-gradient(135deg, rgba(72, 187, 120, 0.2), rgba(56, 161, 105, 0.2)); border: 1px solid rgba(72, 187, 120, 0.3);">
<div class="stat-number" style="color: #48bb78;"><?= $online_count ?></div>
<div class="stat-label">Logged-in Users</div>
</div>
<div class="stat-card" style="background: linear-gradient(135deg, rgba(139, 92, 246, 0.2), rgba(99, 102, 241, 0.2)); border: 1px solid rgba(139, 92, 246, 0.3);">
<div class="stat-number" style="color: #8b5cf6;"><?= $anonymous_count ?></div>
<div class="stat-label">Anonymous</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= count(array_filter($online_users, function($u) { return $u['is_admin'] == 1; })) ?></div>
<div class="stat-label">Admins Online</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= count(array_filter($online_users, function($u) { return strtolower($u['plan']) != 'free'; })) ?></div>
<div class="stat-label">Paid Users</div>
</div>
<div class="stat-card">
<div class="stat-number"><?= $online_count + $anonymous_count ?></div>
<div class="stat-label">Total Active</div>
</div>
</div>
<!-- Online Users List -->
<div style="display: grid; gap: 1.5rem;">
<?php foreach ($online_users as $user): ?>
<div class="user-card">
<div class="user-card-header">
<div class="user-avatar-large">
<?= strtoupper(substr($user['name'], 0, 1)) ?>
</div>
<div class="user-info-main">
<div class="user-name-large">
<?= htmlspecialchars($user['name']) ?>
<?php if ($user['is_admin']): ?>
<span style="background: linear-gradient(135deg, #f59e0b, #d97706); color: white; padding: 0.2rem 0.6rem; border-radius: 6px; font-size: 1rem; margin-left: 0.5rem;">ADMIN</span>
<?php endif; ?>
</div>
<div class="user-email"><?= htmlspecialchars($user['email']) ?></div>
</div>
<div style="text-align: right;">
<div class="time-ago">
<?php
$minutes = $user['minutes_ago'] ?? 0;
if ($minutes < 1) {
echo 'Just now';
} elseif ($minutes == 1) {
echo '1 minute ago';
} else {
echo $minutes . ' minutes ago';
}
?>
</div>
<div style="color: #a0aec0; font-size: 1.1rem; margin-top: 0.3rem;">
<?= date('H:i:s', strtotime($user['last_activity'] ?? $user['last_login'] ?? 'now')) ?>
</div>
<?php if (!empty($user['current_page'])): ?>
<div style="color: #64748b; font-size: 1rem; margin-top: 0.3rem; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<?= htmlspecialchars($user['current_page']) ?>">
<i class="fas fa-file"></i> <?= htmlspecialchars(basename(parse_url($user['current_page'], PHP_URL_PATH))) ?>
</div>
<?php endif; ?>
</div>
</div>
<div class="user-stats-grid">
<div class="stat-item">
<div class="stat-value"><?= ucfirst($user['plan']) ?></div>
<div class="stat-label">Plan</div>
</div>
<div class="stat-item">
<div class="stat-value"><?= $user['credits'] ?></div>
<div class="stat-label">Credits</div>
</div>
<div class="stat-item">
<div class="stat-value"><?= $user['total_tracks'] ?></div>
<div class="stat-label">Total Tracks</div>
</div>
<div class="stat-item">
<div class="stat-value" style="color: #48bb78;"><?= $user['completed_tracks'] ?></div>
<div class="stat-label">Completed</div>
</div>
<div class="stat-item">
<div class="stat-value"><?= $user['login_count_today'] ?? 0 ?></div>
<div class="stat-label">Logins Today</div>
</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255, 255, 255, 0.1);">
<div class="location-info">
<i class="fas fa-map-marker-alt"></i>
<span>
<?php if ($user['city'] || $user['country']): ?>
<?= htmlspecialchars($user['city'] ?? '') ?><?= ($user['city'] && $user['country']) ? ', ' : '' ?><?= htmlspecialchars($user['country'] ?? '') ?>
<?php else: ?>
Location unknown
<?php endif; ?>
</span>
<span style="color: #64748b; margin-left: 1rem;">
<i class="fas fa-network-wired"></i> <?= htmlspecialchars($user['last_ip'] ?? 'Unknown IP') ?>
</span>
</div>
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-primary btn-sm" onclick="loginAsUser(<?= $user['id'] ?>)" title="Login as this user">
<i class="fas fa-sign-in-alt"></i> Login As
</button>
<button class="btn btn-secondary btn-sm" onclick="location.href='?tab=users&user_id=<?= $user['id'] ?>'" title="View user details">
<i class="fas fa-user"></i> View
</button>
<button class="btn btn-secondary btn-sm" onclick="location.href='/artist_profile.php?id=<?= $user['id'] ?>'" title="View profile" target="_blank">
<i class="fas fa-external-link-alt"></i> Profile
</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Anonymous Visitors Section -->
<div class="anonymous-section">
<div class="anonymous-header">
<div>
<h3 style="color: white; margin: 0; display: flex; align-items: center; gap: 0.5rem;">
<i class="fas fa-user-secret" style="color: #8b5cf6;"></i>
Anonymous Visitors
</h3>
<p style="color: #a0aec0; margin: 0.3rem 0 0 0; font-size: 1rem;">
<?= $anonymous_count ?> visitor<?= $anonymous_count != 1 ? 's' : '' ?> browsing without login
</p>
</div>
</div>
<?php if (empty($anonymous_visitors)): ?>
<div style="text-align: center; padding: 2rem; color: #64748b;">
<i class="fas fa-ghost" style="font-size: 2rem; margin-bottom: 0.5rem; opacity: 0.5;"></i>
<p>No anonymous visitors in the last 15 minutes</p>
</div>
<?php else: ?>
<div class="visitors-grid">
<?php foreach ($anonymous_visitors as $visitor):
$ua_info = parseUserAgent($visitor['user_agent'] ?? '', $visitor['ip_address'] ?? null);
$is_bot = $ua_info['device'] === 'Bot';
?>
<div class="visitor-card">
<div class="visitor-icon <?= $is_bot ? 'bot' : '' ?>">
<i class="fas fa-<?= $is_bot ? 'robot' : ($ua_info['device'] === 'Mobile' ? 'mobile-alt' : 'desktop') ?>"></i>
</div>
<div class="visitor-info">
<div class="visitor-ip"><?= htmlspecialchars($visitor['ip_address'] ?? 'Unknown') ?></div>
<div class="visitor-location">
<i class="fas fa-map-marker-alt" style="margin-right: 0.3rem;"></i>
<?php if ($visitor['city'] || $visitor['country']): ?>
<?= htmlspecialchars($visitor['city'] ?? '') ?><?= ($visitor['city'] && $visitor['country']) ? ', ' : '' ?><?= htmlspecialchars($visitor['country'] ?? '') ?>
<?php else: ?>
Unknown location
<?php endif; ?>
</div>
</div>
<div class="visitor-meta">
<span class="visitor-badge browser" title="Browser">
<i class="fas fa-globe"></i> <?= $ua_info['browser'] ?>
</span>
<span class="visitor-badge os" title="OS">
<i class="fas fa-laptop"></i> <?= $ua_info['os'] ?>
</span>
<span class="visitor-badge pages" title="Page views">
<i class="fas fa-file"></i> <?= $visitor['page_views'] ?> pages
</span>
</div>
<div style="text-align: right; min-width: 80px;">
<div style="color: #8b5cf6; font-weight: 600; font-size: 0.9rem;">
<?php
$mins = $visitor['minutes_ago'] ?? 0;
echo $mins < 1 ? 'Now' : $mins . 'm ago';
?>
</div>
<?php if (!empty($visitor['current_page'])): ?>
<div class="visitor-page" title="<?= htmlspecialchars($visitor['current_page']) ?>">
<?= htmlspecialchars(basename(parse_url($visitor['current_page'], PHP_URL_PATH) ?: '/')) ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- DEBUG: Recent page visits -->
<div style="margin-top: 3rem; padding: 1.5rem; background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 12px;">
<h3 style="color: #ef4444; margin: 0 0 1rem 0;"><i class="fas fa-bug"></i> Debug: Recent Page Visits</h3>
<p style="color: #a0aec0; margin-bottom: 1rem;">
Server time: <strong><?= $debug_info['server_time'] ?? 'N/A' ?></strong> |
Total visits in DB: <strong><?= $debug_info['total_visits'] ?? 0 ?></strong>
<?php if (isset($debug_info['error'])): ?>
| <span style="color: #ef4444;">Error: <?= htmlspecialchars($debug_info['error']) ?></span>
<?php endif; ?>
</p>
<?php if (empty($all_recent_visits)): ?>
<p style="color: #f87171;">No visits recorded in the database at all!</p>
<?php else: ?>
<table style="width: 100%; border-collapse: collapse; font-size: 0.9rem;">
<thead>
<tr style="border-bottom: 1px solid rgba(255,255,255,0.2);">
<th style="padding: 0.5rem; text-align: left; color: #94a3b8;">ID</th>
<th style="padding: 0.5rem; text-align: left; color: #94a3b8;">User ID</th>
<th style="padding: 0.5rem; text-align: left; color: #94a3b8;">IP</th>
<th style="padding: 0.5rem; text-align: left; color: #94a3b8;">Page</th>
<th style="padding: 0.5rem; text-align: left; color: #94a3b8;">Visit Time</th>
</tr>
</thead>
<tbody>
<?php foreach ($all_recent_visits as $visit): ?>
<tr style="border-bottom: 1px solid rgba(255,255,255,0.1);">
<td style="padding: 0.5rem; color: #e2e8f0;"><?= $visit['id'] ?></td>
<td style="padding: 0.5rem; color: <?= $visit['user_id'] ? '#48bb78' : '#8b5cf6' ?>;">
<?= $visit['user_id'] ?? 'Anonymous' ?>
</td>
<td style="padding: 0.5rem; color: #e2e8f0; font-family: monospace;"><?= htmlspecialchars($visit['ip_address']) ?></td>
<td style="padding: 0.5rem; color: #94a3b8; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
<?= htmlspecialchars(basename($visit['page_url'])) ?>
</td>
<td style="padding: 0.5rem; color: #94a3b8;"><?= $visit['visit_time'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<script>
// Auto-refresh every 30 seconds
setInterval(function() {
if (document.visibilityState === 'visible') {
location.reload();
}
}, 30000);
</script>