sandbox-reports/report.php
DJP facacc94d4 Update AI Tools Usage Report System - Multi-Tool Support
Enhanced the VEO3 usage report system to support all AI tool types:
- Added support for 6 tool types: VEO3, TEXT2IMAGE, TEXT2VOICE, SPEECH2SPEECH, DOCUMENT_TRANSLATION, VIDEOQUERY
- Updated Python report generator (veo3_report.py) with dynamic tool detection
- Updated PHP report page (report.php) with tool usage breakdown section
- Changed all "Prompts" references to "Requests" for clarity
- Updated refresh button to fetch only last 12 weeks (84 days) for better performance
- Made system dynamic to handle unknown tool types automatically
- Renamed report titles from "VEO3 Usage Report" to "AI Tools Usage Report"

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2026-01-08 14:50:04 -05:00

946 lines
35 KiB
PHP

<?php
// Configuration
$responseFile = 'webhook_response.json';
// Cost configuration per tool type (adjust as needed)
$toolCosts = [
'VEO3' => 3.20, // $3.20 per 8-second video
'TEXT2IMAGE' => 0.04, // Average cost per image generation
'TEXT2VOICE' => 0.30, // Cost per TTS conversion
'SPEECH2SPEECH' => 0.50, // Cost per speech-to-speech conversion
'DOCUMENT_TRANSLATION' => 0.10, // Cost per document translation
'VIDEOQUERY' => 0.25, // Cost per video analysis query
];
// Tool display names
$toolNames = [
'VEO3' => 'VEO3 Video Generation',
'TEXT2IMAGE' => 'Text to Image',
'TEXT2VOICE' => 'Text to Voice',
'SPEECH2SPEECH' => 'Speech to Speech',
'DOCUMENT_TRANSLATION' => 'Document Translation',
'VIDEOQUERY' => 'Video Query Analysis',
];
// SSO Authentication
require_once __DIR__ . '/AuthMiddleware.php';
$auth = new AuthMiddleware();
$user = $auth->requireAuth(); // This will redirect to login if not authenticated
// Load and parse JSON
if (!file_exists($responseFile)) {
die("Error: Response file not found. Please run webhook_caller.php first.");
}
$jsonContent = file_get_contents($responseFile);
$data = json_decode($jsonContent, true);
if (!$data) {
die("Error: Unable to parse JSON data.");
}
// Initialize arrays for analysis
$userCounts = [];
$dailyCounts = [];
$monthlyCounts = [];
$promptLengths = [];
$toolCounts = [];
$toolUserCounts = [];
$totalRequests = count($data);
$totalCost = 0;
// Period-specific analysis
$now = new DateTime('now', new DateTimeZone('UTC'));
$last24Hours = clone $now;
$last24Hours->sub(new DateInterval('P1D'));
$last7Days = clone $now;
$last7Days->sub(new DateInterval('P7D'));
$last30Days = clone $now;
$last30Days->sub(new DateInterval('P30D'));
$userCounts24h = [];
$userCounts7d = [];
$userCounts30d = [];
$totalPrompts24h = 0;
$totalPrompts7d = 0;
$totalPrompts30d = 0;
// Process each record
foreach ($data as $record) {
$item = $record['data'];
// User counts
$user = $item['USER'];
$tool = $item['TOOL'] ?? 'UNKNOWN';
if (!isset($userCounts[$user])) {
$userCounts[$user] = 0;
}
$userCounts[$user]++;
// Tool counts
if (!isset($toolCounts[$tool])) {
$toolCounts[$tool] = 0;
}
$toolCounts[$tool]++;
// Tool user counts
if (!isset($toolUserCounts[$tool])) {
$toolUserCounts[$tool] = [];
}
if (!isset($toolUserCounts[$tool][$user])) {
$toolUserCounts[$tool][$user] = 0;
}
$toolUserCounts[$tool][$user]++;
// Calculate cost - use default of 0 for unknown tools
$costPerRequest = $toolCosts[$tool] ?? 0;
$totalCost += $costPerRequest;
// Date parsing
$date = $item['Date'];
$dateObj = new DateTime($date);
$dateStr = $dateObj->format('Y-m-d');
$monthStr = $dateObj->format('Y-m');
// Period filtering
if ($dateObj >= $last24Hours) {
if (!isset($userCounts24h[$user])) $userCounts24h[$user] = 0;
$userCounts24h[$user]++;
$totalPrompts24h++;
}
if ($dateObj >= $last7Days) {
if (!isset($userCounts7d[$user])) $userCounts7d[$user] = 0;
$userCounts7d[$user]++;
$totalPrompts7d++;
}
if ($dateObj >= $last30Days) {
if (!isset($userCounts30d[$user])) $userCounts30d[$user] = 0;
$userCounts30d[$user]++;
$totalPrompts30d++;
}
// Daily counts
if (!isset($dailyCounts[$dateStr])) {
$dailyCounts[$dateStr] = 0;
}
$dailyCounts[$dateStr]++;
// Monthly counts
if (!isset($monthlyCounts[$monthStr])) {
$monthlyCounts[$monthStr] = 0;
}
$monthlyCounts[$monthStr]++;
// Prompt length analysis (if prompt exists)
if (isset($item['PROMPT']) && $item['PROMPT']) {
$promptLength = strlen($item['PROMPT']);
$promptLengths[] = $promptLength;
}
}
// Sort tool counts by usage
arsort($toolCounts);
// Sort period-specific user counts
arsort($userCounts24h);
arsort($userCounts7d);
arsort($userCounts30d);
// Get top 25 for each period
$topUsers24h = array_slice($userCounts24h, 0, 25, true);
$topUsers7d = array_slice($userCounts7d, 0, 25, true);
$topUsers30d = array_slice($userCounts30d, 0, 25, true);
// Note: totalCost is already calculated in the loop above
// Calculate period-specific costs (we'll need to recalculate by tool for accuracy)
$cost24h = 0;
$cost7d = 0;
$cost30d = 0;
// Recalculate period costs
foreach ($data as $record) {
$item = $record['data'];
$tool = $item['TOOL'] ?? 'UNKNOWN';
$costPerRequest = $toolCosts[$tool] ?? 0;
$dateObj = new DateTime($item['Date']);
if ($dateObj >= $last24Hours) $cost24h += $costPerRequest;
if ($dateObj >= $last7Days) $cost7d += $costPerRequest;
if ($dateObj >= $last30Days) $cost30d += $costPerRequest;
}
// Sort data
arsort($userCounts);
ksort($dailyCounts);
ksort($monthlyCounts);
// Calculate statistics
$uniqueUsers = count($userCounts);
$avgRequestsPerUser = round($totalRequests / $uniqueUsers, 2);
$avgPromptLength = count($promptLengths) > 0 ? round(array_sum($promptLengths) / count($promptLengths), 0) : 0;
$maxPromptLength = count($promptLengths) > 0 ? max($promptLengths) : 0;
$minPromptLength = count($promptLengths) > 0 ? min($promptLengths) : 0;
// Get date range
$dates = array_keys($dailyCounts);
$startDate = reset($dates);
$endDate = end($dates);
// Get top 20 users
$topUsers = array_slice($userCounts, 0, 20, true);
// Get recent daily activity (last 30 days)
$recentDaily = array_slice($dailyCounts, -30, 30, true);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Tools Usage Report</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
background-color: #f5f7fa;
padding: 20px;
color: #333;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
h1 {
color: #2c3e50;
margin-bottom: 10px;
font-size: 32px;
}
.subtitle {
color: #7f8c8d;
margin-bottom: 30px;
font-size: 14px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 25px;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border-left: 4px solid #FFC407;
}
.stat-card h3 {
color: #7f8c8d;
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
}
.stat-card .value {
font-size: 36px;
font-weight: 700;
color: #2c3e50;
}
.stat-card .subvalue {
font-size: 14px;
color: #95a5a6;
margin-top: 5px;
}
.section {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
margin-bottom: 30px;
}
.section h2 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 22px;
border-bottom: 2px solid #ecf0f1;
padding-bottom: 10px;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ecf0f1;
}
th {
background-color: #f8f9fa;
color: #2c3e50;
font-weight: 600;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
tr:hover {
background-color: #f8f9fa;
}
.bar-cell {
display: flex;
align-items: center;
gap: 10px;
}
.bar {
height: 24px;
background: linear-gradient(90deg, #FFC407, #f5b800);
border-radius: 4px;
transition: width 0.3s ease;
}
.chart-container {
height: 300px;
position: relative;
margin-top: 20px;
}
.chart {
display: flex;
align-items: flex-end;
height: 100%;
gap: 4px;
padding: 20px 0;
}
.chart-bar {
flex: 1;
background: linear-gradient(180deg, #FFC407, #f5b800);
border-radius: 4px 4px 0 0;
position: relative;
min-width: 2px;
cursor: pointer;
transition: opacity 0.2s;
}
.chart-bar:hover {
opacity: 0.8;
}
.chart-bar .tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #2c3e50;
color: white;
padding: 8px 12px;
border-radius: 6px;
font-size: 12px;
white-space: nowrap;
display: none;
margin-bottom: 5px;
z-index: 10;
}
.chart-bar:hover .tooltip {
display: block;
}
.chart-labels {
display: flex;
justify-content: space-between;
margin-top: 10px;
font-size: 11px;
color: #7f8c8d;
}
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
}
.badge-gold {
background-color: #f39c12;
color: white;
}
.badge-silver {
background-color: #95a5a6;
color: white;
}
.badge-bronze {
background-color: #cd7f32;
color: white;
}
.rank {
font-weight: 700;
color: #FFC407;
min-width: 30px;
display: inline-block;
}
.daily-table {
max-height: 400px;
overflow-y: auto;
}
.daily-table table {
font-size: 13px;
}
.daily-table td, .daily-table th {
padding: 8px 12px;
}
.refresh-button {
background: linear-gradient(135deg, #FFC407, #f5b800);
color: #2c3e50;
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
box-shadow: 0 2px 8px rgba(255,196,7,0.3);
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.refresh-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255,196,7,0.4);
}
.refresh-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.collapsible {
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.collapsible:hover {
color: #FFC407;
}
.collapsible-icon {
transition: transform 0.3s ease;
font-size: 20px;
color: #FFC407;
}
.collapsible-icon.collapsed {
transform: rotate(-90deg);
}
.collapsible-content {
max-height: 600px;
overflow: hidden;
transition: max-height 0.3s ease;
}
.collapsible-content.collapsed {
max-height: 0;
}
.header-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.spinner {
display: inline-block;
width: 14px;
height: 14px;
border: 2px solid #2c3e50;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<div class="header-actions">
<div>
<h1 style="margin: 0;">AI Tools Usage Report</h1>
<p class="subtitle" style="margin: 5px 0 0 0;">Data from <?php echo $startDate; ?> to <?php echo $endDate; ?></p>
</div>
<button id="refreshBtn" class="refresh-button" onclick="refreshData()">
<span id="refreshIcon">🔄</span>
<span id="refreshText">Refresh Data</span>
</button>
</div>
<div class="stats-grid">
<div class="stat-card">
<h3>Total Requests</h3>
<div class="value"><?php echo number_format($totalRequests); ?></div>
<div class="subvalue">All AI tool requests</div>
</div>
<div class="stat-card">
<h3>Total Cost</h3>
<div class="value">$<?php echo number_format($totalCost, 2); ?></div>
<div class="subvalue">All tools combined</div>
</div>
<div class="stat-card">
<h3>Unique Users</h3>
<div class="value"><?php echo number_format($uniqueUsers); ?></div>
<div class="subvalue">Active accounts</div>
</div>
<div class="stat-card">
<h3>Avg Requests/User</h3>
<div class="value"><?php echo $avgRequestsPerUser; ?></div>
<div class="subvalue">Mean usage per account</div>
</div>
</div>
<div class="stats-grid" style="margin-top: 20px;">
<div class="stat-card">
<h3>Last 24 Hours</h3>
<div class="value" style="font-size: 28px;"><?php echo number_format($totalPrompts24h); ?> requests</div>
<div class="subvalue" style="font-size: 16px; color: #2c3e50; font-weight: 600; margin-top: 5px;">$<?php echo number_format($cost24h, 2); ?></div>
</div>
<div class="stat-card">
<h3>Last 7 Days</h3>
<div class="value" style="font-size: 28px;"><?php echo number_format($totalPrompts7d); ?> requests</div>
<div class="subvalue" style="font-size: 16px; color: #2c3e50; font-weight: 600; margin-top: 5px;">$<?php echo number_format($cost7d, 2); ?></div>
</div>
<div class="stat-card">
<h3>Last 30 Days</h3>
<div class="value" style="font-size: 28px;"><?php echo number_format($totalPrompts30d); ?> requests</div>
<div class="subvalue" style="font-size: 16px; color: #2c3e50; font-weight: 600; margin-top: 5px;">$<?php echo number_format($cost30d, 2); ?></div>
</div>
<div class="stat-card">
<h3>Avg Prompt Length</h3>
<div class="value" style="font-size: 28px;"><?php echo number_format($avgPromptLength); ?></div>
<div class="subvalue">Characters</div>
</div>
</div>
<div class="section">
<h2>Tool Usage Breakdown</h2>
<p style="color: #7f8c8d; margin-bottom: 15px; font-size: 14px;">
Usage distribution across all AI tools
</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Requests</th>
<th>Percentage</th>
<th>Top User</th>
</tr>
</thead>
<tbody>
<?php foreach ($toolCounts as $tool => $count):
$toolName = $toolNames[$tool] ?? $tool;
$percentage = ($count / $totalRequests) * 100;
// Get top user for this tool
$topUser = '';
$topUserCount = 0;
if (isset($toolUserCounts[$tool])) {
arsort($toolUserCounts[$tool]);
$topUserData = array_slice($toolUserCounts[$tool], 0, 1, true);
if (!empty($topUserData)) {
$topUser = array_key_first($topUserData);
$topUserCount = reset($topUserData);
}
}
?>
<tr>
<td><strong><?php echo htmlspecialchars($toolName); ?></strong></td>
<td><?php echo number_format($count); ?></td>
<td><?php echo number_format($percentage, 1); ?>%</td>
<td>
<?php if ($topUser): ?>
<?php echo htmlspecialchars($topUser); ?> (<?php echo $topUserCount; ?>)
<?php else: ?>
-
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="section">
<h2>Daily Usage - Last 30 Days</h2>
<div class="chart-container">
<div class="chart">
<?php
$maxDaily = max($recentDaily);
foreach ($recentDaily as $date => $count):
$height = ($count / $maxDaily) * 100;
?>
<div class="chart-bar" style="height: <?php echo $height; ?>%;">
<div class="tooltip">
<strong><?php echo $date; ?></strong><br>
<?php echo $count; ?> requests
</div>
</div>
<?php endforeach; ?>
</div>
<div class="chart-labels">
<span><?php echo array_key_first($recentDaily); ?></span>
<span><?php echo array_key_last($recentDaily); ?></span>
</div>
</div>
<div class="daily-table">
<table>
<thead>
<tr>
<th>Date</th>
<th>Day of Week</th>
<th>Requests</th>
<th>Activity Level</th>
</tr>
</thead>
<tbody>
<?php
$maxDailyRecent = max($recentDaily);
foreach (array_reverse($recentDaily, true) as $date => $count):
$dateObj = new DateTime($date);
$dayOfWeek = $dateObj->format('l');
$barWidth = ($count / $maxDailyRecent) * 100;
?>
<tr>
<td><strong><?php echo $date; ?></strong></td>
<td><?php echo $dayOfWeek; ?></td>
<td><?php echo number_format($count); ?></td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="section">
<h2>Monthly Usage Breakdown</h2>
<table>
<thead>
<tr>
<th>Month</th>
<th>Requests</th>
<th>Distribution</th>
</tr>
</thead>
<tbody>
<?php
$maxMonthly = max($monthlyCounts);
foreach ($monthlyCounts as $month => $count):
$percentage = ($count / $totalRequests) * 100;
$barWidth = ($count / $maxMonthly) * 100;
?>
<tr>
<td><strong><?php echo date('F Y', strtotime($month . '-01')); ?></strong></td>
<td><?php echo number_format($count); ?> <span style="color: #95a5a6; font-size: 12px;">(<?php echo number_format($percentage, 1); ?>%)</span></td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="section">
<h2>📅 Last 24 Hours - Top 25 Users</h2>
<?php if ($totalPrompts24h > 0): ?>
<p style="color: #7f8c8d; margin-bottom: 15px; font-size: 14px;">
<?php echo number_format($totalPrompts24h); ?> requests from <?php echo count($userCounts24h); ?> users
<strong style="color: #2c3e50; margin-left: 15px;">Cost: $<?php echo number_format($cost24h, 2); ?></strong>
</p>
<table>
<thead>
<tr>
<th style="width: 50px;">Rank</th>
<th>User</th>
<th>Requests</th>
<th>Percentage</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
<?php
$rank = 1;
$maxCount24h = !empty($topUsers24h) ? max($topUsers24h) : 1;
foreach ($topUsers24h as $user => $count):
$percentage = ($count / $totalPrompts24h) * 100;
$barWidth = ($count / $maxCount24h) * 100;
$badge = '';
if ($rank === 1) $badge = '<span class="badge badge-gold">🥇 #1</span>';
elseif ($rank === 2) $badge = '<span class="badge badge-silver">🥈 #2</span>';
elseif ($rank === 3) $badge = '<span class="badge badge-bronze">🥉 #3</span>';
?>
<tr>
<td><span class="rank">#<?php echo $rank; ?></span></td>
<td>
<?php echo htmlspecialchars($user); ?>
<?php if ($badge) echo ' ' . $badge; ?>
</td>
<td><strong><?php echo number_format($count); ?></strong></td>
<td><?php echo number_format($percentage, 2); ?>%</td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php
$rank++;
endforeach;
?>
</tbody>
</table>
<?php else: ?>
<p style="color: #95a5a6; padding: 20px; text-align: center;">No activity in the last 24 hours</p>
<?php endif; ?>
</div>
<div class="section">
<h2>📊 Last 7 Days - Top 25 Users</h2>
<?php if ($totalPrompts7d > 0): ?>
<p style="color: #7f8c8d; margin-bottom: 15px; font-size: 14px;">
<?php echo number_format($totalPrompts7d); ?> requests from <?php echo count($userCounts7d); ?> users
<strong style="color: #2c3e50; margin-left: 15px;">Cost: $<?php echo number_format($cost7d, 2); ?></strong>
</p>
<table>
<thead>
<tr>
<th style="width: 50px;">Rank</th>
<th>User</th>
<th>Requests</th>
<th>Percentage</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
<?php
$rank = 1;
$maxCount7d = !empty($topUsers7d) ? max($topUsers7d) : 1;
foreach ($topUsers7d as $user => $count):
$percentage = ($count / $totalPrompts7d) * 100;
$barWidth = ($count / $maxCount7d) * 100;
$badge = '';
if ($rank === 1) $badge = '<span class="badge badge-gold">🥇 #1</span>';
elseif ($rank === 2) $badge = '<span class="badge badge-silver">🥈 #2</span>';
elseif ($rank === 3) $badge = '<span class="badge badge-bronze">🥉 #3</span>';
?>
<tr>
<td><span class="rank">#<?php echo $rank; ?></span></td>
<td>
<?php echo htmlspecialchars($user); ?>
<?php if ($badge) echo ' ' . $badge; ?>
</td>
<td><strong><?php echo number_format($count); ?></strong></td>
<td><?php echo number_format($percentage, 2); ?>%</td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php
$rank++;
endforeach;
?>
</tbody>
</table>
<?php else: ?>
<p style="color: #95a5a6; padding: 20px; text-align: center;">No activity in the last 7 days</p>
<?php endif; ?>
</div>
<div class="section">
<h2>📈 Last 30 Days - Top 25 Users</h2>
<?php if ($totalPrompts30d > 0): ?>
<p style="color: #7f8c8d; margin-bottom: 15px; font-size: 14px;">
<?php echo number_format($totalPrompts30d); ?> requests from <?php echo count($userCounts30d); ?> users
<strong style="color: #2c3e50; margin-left: 15px;">Cost: $<?php echo number_format($cost30d, 2); ?></strong>
</p>
<table>
<thead>
<tr>
<th style="width: 50px;">Rank</th>
<th>User</th>
<th>Requests</th>
<th>Percentage</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
<?php
$rank = 1;
$maxCount30d = !empty($topUsers30d) ? max($topUsers30d) : 1;
foreach ($topUsers30d as $user => $count):
$percentage = ($count / $totalPrompts30d) * 100;
$barWidth = ($count / $maxCount30d) * 100;
$badge = '';
if ($rank === 1) $badge = '<span class="badge badge-gold">🥇 #1</span>';
elseif ($rank === 2) $badge = '<span class="badge badge-silver">🥈 #2</span>';
elseif ($rank === 3) $badge = '<span class="badge badge-bronze">🥉 #3</span>';
?>
<tr>
<td><span class="rank">#<?php echo $rank; ?></span></td>
<td>
<?php echo htmlspecialchars($user); ?>
<?php if ($badge) echo ' ' . $badge; ?>
</td>
<td><strong><?php echo number_format($count); ?></strong></td>
<td><?php echo number_format($percentage, 2); ?>%</td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php
$rank++;
endforeach;
?>
</tbody>
</table>
<?php else: ?>
<p style="color: #95a5a6; padding: 20px; text-align: center;">No activity in the last 30 days</p>
<?php endif; ?>
</div>
<div class="section">
<h2>All Time - Top 20 Users</h2>
<table>
<thead>
<tr>
<th style="width: 50px;">Rank</th>
<th>User</th>
<th>Prompts</th>
<th>Percentage</th>
<th>Activity</th>
</tr>
</thead>
<tbody>
<?php
$rank = 1;
$maxCount = max($topUsers);
foreach ($topUsers as $user => $count):
$percentage = ($count / $totalRequests) * 100;
$barWidth = ($count / $maxCount) * 100;
$badge = '';
if ($rank === 1) $badge = '<span class="badge badge-gold">🥇 #1</span>';
elseif ($rank === 2) $badge = '<span class="badge badge-silver">🥈 #2</span>';
elseif ($rank === 3) $badge = '<span class="badge badge-bronze">🥉 #3</span>';
?>
<tr>
<td><span class="rank">#<?php echo $rank; ?></span></td>
<td>
<?php echo htmlspecialchars($user); ?>
<?php if ($badge) echo ' ' . $badge; ?>
</td>
<td><strong><?php echo number_format($count); ?></strong></td>
<td><?php echo number_format($percentage, 2); ?>%</td>
<td>
<div class="bar-cell">
<div class="bar" style="width: <?php echo $barWidth . "%"; ?>;"></div>
</div>
</td>
</tr>
<?php
$rank++;
endforeach;
?>
</tbody>
</table>
</div>
<div class="section">
<h2 class="collapsible" onclick="toggleCollapse()">
<span>All Users Summary (<?php echo count($userCounts); ?> users)</span>
<span class="collapsible-icon">▼</span>
</h2>
<div class="collapsible-content collapsed" id="allUsersContent">
<table>
<thead>
<tr>
<th>User</th>
<th>Total Requests</th>
<th>Percentage of Total</th>
</tr>
</thead>
<tbody>
<?php foreach ($userCounts as $user => $count):
$percentage = ($count / $totalRequests) * 100;
?>
<tr>
<td><?php echo htmlspecialchars($user); ?></td>
<td><?php echo number_format($count); ?></td>
<td><?php echo number_format($percentage, 2); ?>%</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<script>
function toggleCollapse() {
const content = document.getElementById('allUsersContent');
const icon = document.querySelector('.collapsible-icon');
content.classList.toggle('collapsed');
icon.classList.toggle('collapsed');
}
function refreshData() {
const btn = document.getElementById('refreshBtn');
const icon = document.getElementById('refreshIcon');
const text = document.getElementById('refreshText');
// Disable button
btn.disabled = true;
icon.innerHTML = '<span class="spinner"></span>';
text.textContent = 'Refreshing...';
// Get default dates (last 12 weeks = 84 days)
const endDate = new Date().toISOString().split('T')[0];
const startDate = new Date(Date.now() - 84*24*60*60*1000).toISOString().split('T')[0];
// Call webhook
fetch('webhook_caller.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `start_date=${startDate}&end_date=${endDate}&ajax=1`
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Reload the page to show new data
window.location.reload();
} else {
alert('Error refreshing data: ' + (data.error || 'Unknown error'));
btn.disabled = false;
icon.textContent = '🔄';
text.textContent = 'Refresh Data';
}
})
.catch(error => {
alert('Error refreshing data: ' + error);
btn.disabled = false;
icon.textContent = '🔄';
text.textContent = 'Refresh Data';
});
}
</script>
</body>
</html>