feat: connect system logs to database and refine structure

This commit is contained in:
2026-01-09 13:17:25 +05:30
Unverified
parent 914a6aa3eb
commit 2ecb665359
4 changed files with 176 additions and 229 deletions

View File

@@ -6,7 +6,7 @@
#### A real-time map-based reporting system for campus infrastructure issues, built to improve visibility, accountability, and resolution efficiency.
[![status](https://img.shields.io/badge/status-active-brightgreen.svg?style=flat)](https://github.com/xodivorce/infra-xodivorce-in/)
[![version](https://img.shields.io/badge/version-v1.4.0-yellow.svg?style=flat)](https://github.com/xodivorce/infra-xodivorce-in/)
[![version](https://img.shields.io/badge/version-v1.3.5-yellow.svg?style=flat)](https://github.com/xodivorce/infra-xodivorce-in/)
[![PRs](https://img.shields.io/badge/PRs-welcome-blue.svg?style=flat)](https://github.com/xodivorce/infra-xodivorce-in/)
> **🥰 Like this project? Please consider giving it a Star (🌟) on GitHub to show us your appreciation. Thank you!**

View File

@@ -1,244 +1,191 @@
<?php
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] != 1) {
echo "<div class='text-red-500 p-6'>Access Denied</div>";
exit;
}
$sql = "SELECT r.id, r.title, r.priority, r.status, r.created_at, r.updated_at, u.username, u.email, u.is_admin
FROM reports r
JOIN users u ON r.user_id = u.id";
$result = $conn->query($sql);
$logs = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$logs[] = [
'timestamp' => strtotime($row['created_at']),
'formatted_time' => date("M j, Y • H:i", strtotime($row['created_at'])),
'actor_name' => $row['username'],
'actor_email' => $row['email'],
'actor_role' => $row['is_admin'] ? 'Administrator' : 'User',
'action' => 'Created report',
'report_id' => $row['id'],
'title' => htmlspecialchars($row['title']),
'priority' => $row['priority'],
'status' => 'Opened',
'is_update' => false
];
if (strtotime($row['updated_at']) > strtotime($row['created_at'])) {
$actionText = 'Updated status';
if ($row['status'] === 'Resolved') $actionText = 'Resolved report';
if ($row['status'] === 'In Progress') $actionText = 'Marked In Progress';
$logs[] = [
'timestamp' => strtotime($row['updated_at']),
'formatted_time' => date("M j, Y • H:i", strtotime($row['updated_at'])),
'actor_name' => 'System Admin',
'actor_email' => 'admin@system',
'actor_role' => 'Administrator',
'action' => $actionText,
'report_id' => $row['id'],
'title' => htmlspecialchars($row['title']),
'priority' => $row['priority'],
'status' => $row['status'],
'is_update' => true
];
}
}
}
usort($logs, function($a, $b) {
return $b['timestamp'] - $a['timestamp'];
});
$itemsPerPage = 6;
$totalItems = count($logs);
$totalPages = ceil($totalItems / $itemsPerPage);
$page = isset($_GET['p']) ? (int)$_GET['p'] : 1;
if ($page < 1) $page = 1;
if ($page > $totalPages && $totalPages > 0) $page = $totalPages;
$offset = ($page - 1) * $itemsPerPage;
$currentLogs = array_slice($logs, $offset, $itemsPerPage);
?>
<section class="space-y-6">
<header>
<h1 class="text-2xl font-semibold text-white">Activity Logs</h1>
<p class="mt-1 text-sm text-neutral-400">Recent system and user activities</p>
<h1 class="text-2xl font-semibold text-white">System Logs</h1>
<p class="mt-1 text-sm text-neutral-400">
Administrative and user actions across reports
</p>
</header>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label class="block text-xs font-medium text-neutral-400 mb-1.5">Event Type</label>
<select class="w-full bg-neutral-900 border border-neutral-700 text-white text-sm rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option>All</option>
<option>Reports</option>
<option>Status Changes</option>
<option>Admin Actions</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-neutral-400 mb-1.5">Severity</label>
<select class="w-full bg-neutral-900 border border-neutral-700 text-white text-sm rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option>All</option>
<option>Info</option>
<option>Warning</option>
<option>Critical</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-neutral-400 mb-1.5">Date Range</label>
<select class="w-full bg-neutral-900 border border-neutral-700 text-white text-sm rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<option>Today</option>
<option>Last 7 days</option>
<option>Last 30 days</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-neutral-400 mb-1.5">Search</label>
<input type="text" placeholder="Search activity…" class="w-full bg-neutral-900 border border-neutral-700 text-white placeholder-neutral-500 text-sm rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
<div class="overflow-x-auto">
<div class="bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden shadow-sm">
<div class="overflow-x-auto custom-scrollbar">
<table class="w-full">
<thead class="bg-neutral-900 border-b border-neutral-700">
<thead class="bg-neutral-900/50 border-b border-neutral-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Timestamp</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Time</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Actor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Action</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Related Entity</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Severity</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Report</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Priority</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-700">
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 14:32:15</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">admin@system</td>
<td class="px-6 py-4 text-sm text-neutral-300">Updated service configuration</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">API Gateway - US-East</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-900 text-blue-200">Info</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 14:28:43</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">System</td>
<td class="px-6 py-4 text-sm text-neutral-300">Service health check failed</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">Database - EU-West</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-900 text-yellow-200">Warning</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 14:15:22</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">john.doe@company.com</td>
<td class="px-6 py-4 text-sm text-neutral-300">Generated monthly report</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">Report #1247</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-900 text-blue-200">Info</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 13:58:09</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">System</td>
<td class="px-6 py-4 text-sm text-neutral-300">Critical alert triggered</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">Load Balancer - APAC</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-900 text-red-200">Critical</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 13:45:31</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">admin@system</td>
<td class="px-6 py-4 text-sm text-neutral-300">Modified user permissions</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">User: jane.smith@company.com</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-900 text-blue-200">Info</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 13:22:17</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">System</td>
<td class="px-6 py-4 text-sm text-neutral-300">Backup completed successfully</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">All Services</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-900 text-blue-200">Info</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 12:58:44</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">mark.wilson@company.com</td>
<td class="px-6 py-4 text-sm text-neutral-300">Updated service status</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">CDN - US-West</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-900 text-blue-200">Info</span>
</td>
</tr>
<tr class="hover:bg-neutral-750">
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-300">2025-12-27 12:31:05</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-white">System</td>
<td class="px-6 py-4 text-sm text-neutral-300">High memory usage detected</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-neutral-400">Cache Server - EU-Central</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-900 text-yellow-200">Warning</span>
</td>
</tr>
<tbody class="divide-y divide-neutral-700/50">
<?php if (empty($currentLogs)): ?>
<tr>
<td colspan="6" class="px-6 py-8 text-center text-neutral-500">
No system logs found.
</td>
</tr>
<?php else: ?>
<?php foreach ($currentLogs as $log): ?>
<?php
$priorityColor = match($log['priority']) {
'High' => 'text-red-400',
'Medium' => 'text-yellow-400',
'Low' => 'text-blue-400',
default => 'text-neutral-400'
};
$statusConfig = match($log['status']) {
'Resolved' => ['text' => 'text-green-400', 'bg' => 'bg-green-500/10', 'border' => 'border-green-500/20', 'icon' => 'text-green-500', 'path' => 'M5 13l4 4L19 7'],
'In Progress' => ['text' => 'text-yellow-400', 'bg' => 'bg-yellow-500/10', 'border' => 'border-yellow-500/20', 'icon' => 'text-yellow-500', 'path' => 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z'],
'Opened' => ['text' => 'text-blue-400', 'bg' => 'bg-blue-500/10', 'border' => 'border-blue-500/20', 'icon' => 'text-blue-500', 'path' => 'M12 4v16m8-8H4'],
default => ['text' => 'text-neutral-400', 'bg' => 'bg-neutral-500/10', 'border' => 'border-neutral-500/20', 'icon' => 'text-neutral-500', 'path' => 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z']
};
?>
<tr class="hover:bg-neutral-700/30 transition-colors">
<td class="px-6 py-4 text-sm text-neutral-300 font-mono text-xs whitespace-nowrap">
<?php echo $log['formatted_time']; ?>
</td>
<td class="px-6 py-4 text-sm text-white">
<div class="font-medium"><?php echo htmlspecialchars($log['actor_name']); ?></div>
<div class="text-[11px] text-neutral-500"><?php echo htmlspecialchars($log['actor_role']); ?></div>
</td>
<td class="px-6 py-4 text-sm text-neutral-300">
<?php echo $log['action']; ?>
</td>
<td class="px-6 py-4 text-sm text-neutral-200">
<span class="text-neutral-500 font-mono mr-1">#<?php echo $log['report_id']; ?></span>
<?php echo $log['title']; ?>
</td>
<td class="px-6 py-4">
<span class="text-xs font-medium <?php echo $priorityColor; ?>">
<?php echo $log['priority']; ?>
</span>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-2">
<div class="w-6 h-6 rounded-full <?php echo $statusConfig['bg'] . ' ' . $statusConfig['border']; ?> border flex items-center justify-center">
<svg class="w-3.5 h-3.5 <?php echo $statusConfig['icon']; ?>" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="<?php echo $statusConfig['path']; ?>" />
</svg>
</div>
<span class="text-xs <?php echo $statusConfig['text']; ?>">
<?php echo $log['status']; ?>
</span>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<div class="hidden bg-neutral-800 rounded-lg border border-neutral-700 p-12">
<div class="flex flex-col items-center justify-center text-center">
<svg class="w-16 h-16 text-neutral-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="text-lg font-medium text-white mb-1">No activity logs available</h3>
<p class="text-sm text-neutral-400">System and user actions will appear here</p>
<div class="bg-neutral-900 border-t border-neutral-700 px-6 py-4 flex items-center justify-between">
<span class="text-sm text-neutral-400">
Showing page <span class="font-medium text-white"><?php echo $page; ?></span> of <?php echo max(1, $totalPages); ?>
</span>
<div class="flex gap-2">
<?php
$prevParams = array_merge($_GET, ['p' => max(1, $page - 1)]);
$nextParams = array_merge($_GET, ['p' => min($totalPages, $page + 1)]);
$prevDisabled = ($page <= 1) ? 'opacity-50 cursor-not-allowed pointer-events-none' : '';
$nextDisabled = ($page >= $totalPages) ? 'opacity-50 cursor-not-allowed pointer-events-none' : '';
?>
<a href="?<?php echo http_build_query($prevParams); ?>" class="px-3 py-1 bg-neutral-800 hover:bg-neutral-700 border border-neutral-700 rounded-lg text-sm text-white transition-colors <?php echo $prevDisabled; ?>">
Previous
</a>
<a href="?<?php echo http_build_query($nextParams); ?>" class="px-3 py-1 bg-neutral-800 hover:bg-neutral-700 border border-neutral-700 rounded-lg text-sm text-white transition-colors <?php echo $nextDisabled; ?>">
Next
</a>
</div>
</div>
</div>
<div class="hidden bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-neutral-900 border-b border-neutral-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Timestamp</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Actor</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Action</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Related Entity</th>
<th class="px-6 py-3 text-left text-xs font-medium text-neutral-400 uppercase tracking-wider">Severity</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-700">
<tr class="animate-pulse">
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-28"></div>
</td>
<td class="px-6 py-4">
<div class="h-4 bg-neutral-700 rounded w-48"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-36"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-5 bg-neutral-700 rounded-full w-16"></div>
</td>
</tr>
<tr class="animate-pulse">
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-24"></div>
</td>
<td class="px-6 py-4">
<div class="h-4 bg-neutral-700 rounded w-56"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-40"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-5 bg-neutral-700 rounded-full w-16"></div>
</td>
</tr>
<tr class="animate-pulse">
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-36"></div>
</td>
<td class="px-6 py-4">
<div class="h-4 bg-neutral-700 rounded w-44"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-5 bg-neutral-700 rounded-full w-16"></div>
</td>
</tr>
<tr class="animate-pulse">
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-20"></div>
</td>
<td class="px-6 py-4">
<div class="h-4 bg-neutral-700 rounded w-52"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-44"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-5 bg-neutral-700 rounded-full w-16"></div>
</td>
</tr>
<tr class="animate-pulse">
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-32"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-28"></div>
</td>
<td class="px-6 py-4">
<div class="h-4 bg-neutral-700 rounded w-48"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-4 bg-neutral-700 rounded w-40"></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="h-5 bg-neutral-700 rounded-full w-16"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>

View File

@@ -13,7 +13,7 @@ $recentSql = "SELECT r.*, u.username
FROM reports r
JOIN users u ON r.user_id = u.id
ORDER BY r.updated_at DESC
LIMIT 5";
LIMIT 6";
$recentResult = $conn->query($recentSql);
$recentActivity = $recentResult->fetch_all(MYSQLI_ASSOC);

View File

@@ -229,7 +229,7 @@ if (isset($_GET['code'])) {
Need Help? Checkout Github Docs</a>
<span class="w-px h-3 bg-slate-800"></span>
<span
class="text-xs font-bold text-blue-400 bg-blue-500/10 px-2 py-0.5 rounded-full border border-blue-500/20">v1.3.3</span>
class="text-xs font-bold text-blue-400 bg-blue-500/10 px-2 py-0.5 rounded-full border border-blue-500/20">v1.3.5</span>
</div>
<div class="flex items-center gap-2">