Hoghoghi / frontend /reports.html
Really-amin's picture
Upload 149 files
8d3cb40 verified
raw
history blame
34.7 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Legal Dashboard - Reports &amp; Analytics</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Load API Client and Core System -->
<script src="js/api-client.js"></script>
<script src="js/core.js"></script>
<script src="js/notifications.js"></script>
<style>
:root {
--primary-color: #007bff;
--success-color: #28a745;
--warning-color: #ffc107;
--danger-color: #dc3545;
--info-color: #17a2b8;
}
body {
background-color: #f8f9fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.navbar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.navbar-brand {
font-weight: bold;
color: white !important;
}
.nav-link {
color: rgba(255,255,255,0.9) !important;
transition: color 0.3s ease;
}
.nav-link:hover {
color: white !important;
}
.main-content {
padding: 2rem 0;
}
.card {
border: none;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
margin-bottom: 1.5rem;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
}
.card-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 15px 15px 0 0 !important;
padding: 1.5rem;
}
.metric-card {
text-align: center;
padding: 2rem;
}
.metric-value {
font-size: 2.5rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.metric-label {
color: #666;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 1px;
}
.metric-change {
font-size: 0.8rem;
margin-top: 0.5rem;
}
.metric-change.positive {
color: var(--success-color);
}
.metric-change.negative {
color: var(--danger-color);
}
.chart-container {
position: relative;
height: 400px;
margin: 1rem 0;
}
.table-responsive {
border-radius: 10px;
overflow: hidden;
}
.table {
margin-bottom: 0;
}
.table thead th {
background-color: #f8f9fa;
border: none;
font-weight: 600;
color: #495057;
}
.table tbody tr {
transition: background-color 0.2s ease;
}
.table tbody tr:hover {
background-color: #f8f9fa;
}
.btn-export {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
color: white;
padding: 0.5rem 1.5rem;
border-radius: 25px;
transition: all 0.3s ease;
}
.btn-export:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
color: white;
}
.status-badge {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
.status-badge.success {
background-color: #d4edda;
color: #155724;
}
.status-badge.warning {
background-color: #fff3cd;
color: #856404;
}
.status-badge.error {
background-color: #f8d7da;
color: #721c24;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid var(--primary-color);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.filter-section {
background: white;
border-radius: 15px;
padding: 1.5rem;
margin-bottom: 2rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.date-range {
display: flex;
gap: 1rem;
align-items: center;
}
.date-input {
border: 1px solid #ddd;
border-radius: 8px;
padding: 0.5rem;
font-size: 0.9rem;
}
.refresh-btn {
background: var(--primary-color);
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.refresh-btn:hover {
background: #0056b3;
transform: translateY(-1px);
}
.export-section {
display: flex;
gap: 1rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.progress-ring {
width: 120px;
height: 120px;
margin: 0 auto;
}
.progress-ring circle {
fill: none;
stroke-width: 8;
}
.progress-ring .bg {
stroke: #e9ecef;
}
.progress-ring .progress {
stroke: var(--primary-color);
stroke-linecap: round;
transition: stroke-dasharray 0.3s ease;
}
.progress-text {
text-align: center;
margin-top: 1rem;
}
.progress-value {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
.progress-label {
font-size: 0.9rem;
color: #666;
}
</style>
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="#">
<i class="fas fa-chart-line me-2"></i>
Legal Dashboard - Reports
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="index.html">
<i class="fas fa-home me-1"></i>Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="documents.html">
<i class="fas fa-file-alt me-1"></i>Documents
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="reports.html">
<i class="fas fa-chart-bar me-1"></i>Reports
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" onclick="logout()">
<i class="fas fa-sign-out-alt me-1"></i>Logout
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="container main-content">
<!-- Filter Section -->
<div class="filter-section">
<div class="row align-items-center">
<div class="col-md-6">
<h5 class="mb-3">
<i class="fas fa-filter me-2"></i>Filter Options
</h5>
<div class="date-range">
<label class="me-2">Date Range:</label>
<input type="date" id="startDate" class="date-input">
<span>to</span>
<input type="date" id="endDate" class="date-input">
<button type="button" class="refresh-btn" onclick="loadReports()">
<i class="fas fa-sync-alt me-1"></i>Refresh
</button>
</div>
</div>
<div class="col-md-6">
<div class="export-section">
<button type="button" class="btn btn-export" onclick="exportReport('summary')">
<i class="fas fa-download me-1"></i>Export Summary
</button>
<button type="button" class="btn btn-export" onclick="exportReport('user_activity')">
<i class="fas fa-users me-1"></i>Export User Activity
</button>
<button type="button" class="btn btn-export" onclick="exportReport('document_analytics')">
<i class="fas fa-file-alt me-1"></i>Export Document Analytics
</button>
</div>
</div>
</div>
</div>
<!-- Summary Metrics -->
<div class="row" id="summaryMetrics">
<div class="col-md-3">
<div class="card metric-card">
<div class="metric-value" id="totalDocuments">-</div>
<div class="metric-label">Total Documents</div>
<div class="metric-change positive" id="documentsChange">+12% from last month</div>
</div>
</div>
<div class="col-md-3">
<div class="card metric-card">
<div class="metric-value" id="totalUsers">-</div>
<div class="metric-label">Active Users</div>
<div class="metric-change positive" id="usersChange">+5% from last month</div>
</div>
</div>
<div class="col-md-3">
<div class="card metric-card">
<div class="metric-value" id="successRate">-</div>
<div class="metric-label">Success Rate</div>
<div class="metric-change positive" id="successChange">+3% from last month</div>
</div>
</div>
<div class="col-md-3">
<div class="card metric-card">
<div class="metric-value" id="avgProcessingTime">-</div>
<div class="metric-label">Avg Processing Time</div>
<div class="metric-change negative" id="timeChange">+0.5s from last month</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-chart-line me-2"></i>Document Processing Trends
</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="trendsChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-pie-chart me-2"></i>Processing Status
</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="statusChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Performance Metrics -->
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-tachometer-alt me-2"></i>System Performance
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-6">
<div class="progress-ring">
<svg width="120" height="120">
<circle class="bg" cx="60" cy="60" r="50"></circle>
<circle class="progress" cx="60" cy="60" r="50"
stroke-dasharray="314" stroke-dashoffset="78.5"></circle>
</svg>
</div>
<div class="progress-text">
<div class="progress-value">75%</div>
<div class="progress-label">CPU Usage</div>
</div>
</div>
<div class="col-6">
<div class="progress-ring">
<svg width="120" height="120">
<circle class="bg" cx="60" cy="60" r="50"></circle>
<circle class="progress" cx="60" cy="60" r="50"
stroke-dasharray="314" stroke-dashoffset="157"></circle>
</svg>
</div>
<div class="progress-text">
<div class="progress-value">50%</div>
<div class="progress-label">Memory Usage</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-clock me-2"></i>Response Times
</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="responseChart"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- User Activity Table -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-users me-2"></i>User Activity
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="userActivityTable">
<thead>
<tr>
<th scope="col">User</th>
<th scope="col">Documents Processed</th>
<th scope="col">Last Activity</th>
<th scope="col">Success Rate</th>
<th scope="col">Avg Processing Time</th>
</tr>
</thead>
<tbody id="userActivityBody">
<tr>
<td colspan="5" class="text-center">
<div class="loading">
<div class="spinner"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Document Analytics Table -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-file-alt me-2"></i>Recent Document Analytics
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover" id="documentAnalyticsTable">
<thead>
<tr>
<th scope="col">Document</th>
<th scope="col">Processing Time</th>
<th scope="col">OCR Accuracy</th>
<th scope="col">File Size</th>
<th scope="col">Status</th>
<th scope="col">Created</th>
</tr>
</thead>
<tbody id="documentAnalyticsBody">
<tr>
<td colspan="6" class="text-center">
<div class="loading">
<div class="spinner"></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script src="js/notifications.js"></script>
<script>
// Global variables
let trendsChart, statusChart, responseChart;
let currentData = {};
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
// Set default date range (last 30 days)
const endDate = new Date();
const startDate = new Date();
startDate.setDate(startDate.getDate() - 30);
document.getElementById('endDate').value = endDate.toISOString().split('T')[0];
document.getElementById('startDate').value = startDate.toISOString().split('T')[0];
// Load initial data
loadReports();
});
// Load reports data
async function loadReports() {
try {
showLoading();
// Load summary metrics
await loadSummaryMetrics();
// Load charts
await loadCharts();
// Load tables
await loadUserActivity();
await loadDocumentAnalytics();
hideLoading();
} catch (error) {
console.error('Error loading reports:', error);
showNotification('error', 'Error', 'Failed to load reports data');
hideLoading();
}
}
// Load summary metrics
async function loadSummaryMetrics() {
try {
const response = await fetch('/api/reports/summary', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (!response.ok) throw new Error('Failed to load summary');
const data = await response.json();
currentData.summary = data;
// Update metrics
document.getElementById('totalDocuments').textContent = data.total_documents;
document.getElementById('totalUsers').textContent = data.total_users;
document.getElementById('successRate').textContent = `${data.success_rate.toFixed(1)}%`;
document.getElementById('avgProcessingTime').textContent = `${data.avg_processing_time.toFixed(1)}s`;
} catch (error) {
console.error('Error loading summary metrics:', error);
// Set default values
document.getElementById('totalDocuments').textContent = '0';
document.getElementById('totalUsers').textContent = '0';
document.getElementById('successRate').textContent = '0%';
document.getElementById('avgProcessingTime').textContent = '0s';
}
}
// Load charts
async function loadCharts() {
try {
// Load trends data
const trendsResponse = await fetch('/api/reports/trends?days=30', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (trendsResponse.ok) {
const trendsData = await trendsResponse.json();
createTrendsChart(trendsData.daily_trends);
}
// Create status chart (mock data for now)
createStatusChart();
// Create response time chart (mock data for now)
createResponseChart();
} catch (error) {
console.error('Error loading charts:', error);
}
}
// Create trends chart
function createTrendsChart(data) {
const ctx = document.getElementById('trendsChart').getContext('2d');
if (trendsChart) {
trendsChart.destroy();
}
trendsChart = new Chart(ctx, {
type: 'line',
data: {
labels: data.map(d => d.date),
datasets: [{
label: 'Documents Processed',
data: data.map(d => d.documents_processed),
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
tension: 0.4
}, {
label: 'Success Rate (%)',
data: data.map(d => d.success_rate),
borderColor: '#28a745',
backgroundColor: 'rgba(40, 167, 69, 0.1)',
tension: 0.4,
yAxisID: 'y1'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Documents'
}
},
y1: {
position: 'right',
beginAtZero: true,
max: 100,
title: {
display: true,
text: 'Success Rate (%)'
}
}
},
plugins: {
legend: {
position: 'top'
}
}
}
});
}
// Create status chart
function createStatusChart() {
const ctx = document.getElementById('statusChart').getContext('2d');
if (statusChart) {
statusChart.destroy();
}
statusChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Completed', 'Processing', 'Failed', 'Pending'],
datasets: [{
data: [65, 15, 10, 10],
backgroundColor: [
'#28a745',
'#ffc107',
'#dc3545',
'#6c757d'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
// Create response time chart
function createResponseChart() {
const ctx = document.getElementById('responseChart').getContext('2d');
if (responseChart) {
responseChart.destroy();
}
responseChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Documents', 'OCR', 'Search', 'Analytics'],
datasets: [{
label: 'Response Time (ms)',
data: [150, 2500, 200, 300],
backgroundColor: [
'#667eea',
'#764ba2',
'#f093fb',
'#f5576c'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: 'Time (ms)'
}
}
},
plugins: {
legend: {
display: false
}
}
}
});
}
// Load user activity
async function loadUserActivity() {
try {
const response = await fetch('/api/reports/user-activity?days=30', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (!response.ok) throw new Error('Failed to load user activity');
const data = await response.json();
currentData.userActivity = data;
const tbody = document.getElementById('userActivityBody');
tbody.innerHTML = '';
data.forEach(user => {
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${user.username}</strong></td>
<td>${user.documents_processed}</td>
<td>${formatDate(user.last_activity)}</td>
<td><span class="status-badge success">${user.success_rate.toFixed(1)}%</span></td>
<td>${user.total_processing_time.toFixed(1)}s</td>
`;
tbody.appendChild(row);
});
} catch (error) {
console.error('Error loading user activity:', error);
document.getElementById('userActivityBody').innerHTML =
'<tr><td colspan="5" class="text-center text-muted">No data available</td></tr>';
}
}
// Load document analytics
async function loadDocumentAnalytics() {
try {
const response = await fetch('/api/reports/document-analytics?limit=20', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (!response.ok) throw new Error('Failed to load document analytics');
const data = await response.json();
currentData.documentAnalytics = data;
const tbody = document.getElementById('documentAnalyticsBody');
tbody.innerHTML = '';
data.forEach(doc => {
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${doc.filename}</strong></td>
<td>${doc.processing_time.toFixed(1)}s</td>
<td>${doc.ocr_accuracy ? doc.ocr_accuracy.toFixed(1) + '%' : 'N/A'}</td>
<td>${formatFileSize(doc.file_size)}</td>
<td><span class="status-badge ${getStatusClass(doc.status)}">${doc.status}</span></td>
<td>${formatDate(doc.created_at)}</td>
`;
tbody.appendChild(row);
});
} catch (error) {
console.error('Error loading document analytics:', error);
document.getElementById('documentAnalyticsBody').innerHTML =
'<tr><td colspan="6" class="text-center text-muted">No data available</td></tr>';
}
}
// Export report
async function exportReport(type) {
try {
const response = await fetch(`/api/reports/export/csv?report_type=${type}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
}
});
if (!response.ok) throw new Error('Failed to export report');
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${type}_report_${new Date().toISOString().split('T')[0]}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
showNotification('success', 'Export Successful', `${type} report has been downloaded`);
} catch (error) {
console.error('Error exporting report:', error);
showNotification('error', 'Export Failed', 'Failed to export report');
}
}
// Utility functions
function formatDate(dateString) {
if (!dateString || dateString === 'Never') return 'Never';
const date = new Date(dateString);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function getStatusClass(status) {
switch (status.toLowerCase()) {
case 'completed': return 'success';
case 'processing': return 'warning';
case 'failed': return 'error';
default: return 'warning';
}
}
function showLoading() {
// Show loading indicators
const loadingElements = document.querySelectorAll('.loading');
loadingElements.forEach(el => el.style.display = 'flex');
}
function hideLoading() {
// Hide loading indicators
const loadingElements = document.querySelectorAll('.loading');
loadingElements.forEach(el => el.style.display = 'none');
}
function logout() {
localStorage.removeItem('auth_token');
window.location.href = 'index.html';
}
// Auto-refresh every 5 minutes
setInterval(loadReports, 300000);
</script>
</body>
</html>