mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-09 11:19:36 +01:00
[Fix]: Fix stats page
This commit is contained in:
parent
d2d187d4d5
commit
0c8db0b597
3 changed files with 149 additions and 125 deletions
|
|
@ -6,6 +6,8 @@
|
|||
// Store chart instance globally for updates
|
||||
let statsChart = null;
|
||||
let chartContext = null;
|
||||
let lastReloadTime = 0;
|
||||
const RELOAD_COOLDOWN_MS = 2000; // 2 second cooldown between reloads
|
||||
|
||||
// Initialize stats chart if on stats page
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
|
@ -14,7 +16,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
|
||||
// Get data from global variables (set by template)
|
||||
if (typeof window.initialStatsData === 'undefined') {
|
||||
console.error('Initial stats data not found');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -22,25 +23,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
const historicalData = window.historicalData || [];
|
||||
const predictionData = window.predictionData || [];
|
||||
|
||||
console.log(`Loaded data: ${historicalData.length} historical, ${realtimeData.length} realtime, ${predictionData.length} predictions`);
|
||||
|
||||
// If no data yet (lazy loading), add a placeholder message
|
||||
const hasData = historicalData.length > 0 || realtimeData.length > 0 || predictionData.length > 0;
|
||||
if (!hasData) {
|
||||
console.log('No chart data yet - will populate after lazy load');
|
||||
}
|
||||
|
||||
// Set up Socket.IO connection for real-time updates
|
||||
// Set up Socket.IO connection for real-time updates with rate limiting
|
||||
if (typeof io !== 'undefined') {
|
||||
const socket = io();
|
||||
|
||||
socket.on('stats-update', () => {
|
||||
console.log('Stats update received (page will not auto-reload)');
|
||||
// Don't auto-reload - user can manually refresh if needed
|
||||
});
|
||||
|
||||
socket.on('reconnect', () => {
|
||||
console.log('Reconnected to server');
|
||||
const now = Date.now();
|
||||
if (now - lastReloadTime >= RELOAD_COOLDOWN_MS) {
|
||||
lastReloadTime = now;
|
||||
reloadStatsData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +249,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
*/
|
||||
function rebuildStatsChart() {
|
||||
if (!statsChart || !chartContext) {
|
||||
console.log('Chart not initialized, skipping rebuild');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -265,8 +256,6 @@ function rebuildStatsChart() {
|
|||
const historicalData = window.historicalData || [];
|
||||
const predictionData = window.predictionData || [];
|
||||
|
||||
console.log(`Rebuilding chart with: ${historicalData.length} historical, ${realtimeData.length} realtime, ${predictionData.length} predictions`);
|
||||
|
||||
const allTimePoints = [
|
||||
...historicalData.map(d => ({...d, type: 'historical' })),
|
||||
...realtimeData.map(d => ({...d, type: 'realtime' })),
|
||||
|
|
@ -274,7 +263,6 @@ function rebuildStatsChart() {
|
|||
].sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
if (allTimePoints.length === 0) {
|
||||
console.log('No data points to chart');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -311,24 +299,36 @@ function lazyLoadStats() {
|
|||
// Check if this is a lazy-loaded page (has placeholder data)
|
||||
const currentCountEl = document.getElementById('currentCount');
|
||||
if (!currentCountEl) {
|
||||
console.log('Stats lazy load: currentCount element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentText = currentCountEl.textContent.trim();
|
||||
console.log('Stats lazy load: current count text is:', currentText);
|
||||
|
||||
if (currentText !== '...') {
|
||||
console.log('Stats lazy load: already loaded with real data, skipping');
|
||||
return; // Already loaded with real data
|
||||
}
|
||||
|
||||
console.log('Stats lazy load: fetching data from /stats/api');
|
||||
reloadStatsData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload statistics data from API and update DOM
|
||||
*/
|
||||
function reloadStatsData() {
|
||||
fetch('/stats/api')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Stats lazy load: received data', data);
|
||||
updateStatsDOM(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error reloading stats:', error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update DOM with stats data
|
||||
*/
|
||||
function updateStatsDOM(data) {
|
||||
// Update main stat cards
|
||||
document.getElementById('currentCount').textContent = data.currentCount || '0';
|
||||
document.getElementById('historicalTotal').textContent = data.allTimeTotal || '0';
|
||||
|
|
@ -340,7 +340,7 @@ function lazyLoadStats() {
|
|||
if (data.enhanced) {
|
||||
const topSenderDomains = document.querySelector('[data-stats="top-sender-domains"]');
|
||||
const topRecipientDomains = document.querySelector('[data-stats="top-recipient-domains"]');
|
||||
|
||||
const busiestHours = document.querySelector('[data-stats="busiest-hours"]');
|
||||
if (topSenderDomains && data.enhanced.topSenderDomains && data.enhanced.topSenderDomains.length > 0) {
|
||||
let html = '';
|
||||
data.enhanced.topSenderDomains.slice(0, 5).forEach(item => {
|
||||
|
|
@ -357,28 +357,54 @@ function lazyLoadStats() {
|
|||
topRecipientDomains.innerHTML = html;
|
||||
}
|
||||
|
||||
if (busiestHours && data.enhanced.busiestHours && data.enhanced.busiestHours.length > 0) {
|
||||
let html = '';
|
||||
data.enhanced.busiestHours.forEach(item => {
|
||||
html += `<li class="stat-list-item"><span class="stat-list-label">${item.hour}:00 - ${item.hour + 1}:00</span><span class="stat-list-value">${item.count}</span></li>`;
|
||||
});
|
||||
busiestHours.innerHTML = html;
|
||||
}
|
||||
|
||||
// Update unique domains count
|
||||
const uniqueSenderDomains = document.querySelector('[data-stats="unique-sender-domains"]');
|
||||
if (uniqueSenderDomains && data.enhanced.uniqueSenderDomains) {
|
||||
if (uniqueSenderDomains && data.enhanced.uniqueSenderDomains !== undefined) {
|
||||
uniqueSenderDomains.textContent = data.enhanced.uniqueSenderDomains;
|
||||
}
|
||||
|
||||
// Update average email size
|
||||
const avgSize = document.querySelector('[data-stats="average-email-size"]');
|
||||
if (avgSize && data.enhanced.averageEmailSize) {
|
||||
avgSize.textContent = data.enhanced.averageEmailSize;
|
||||
const uniqueRecipientDomains = document.querySelector('[data-stats="unique-recipient-domains"]');
|
||||
if (uniqueRecipientDomains && data.enhanced.uniqueRecipientDomains !== undefined) {
|
||||
uniqueRecipientDomains.textContent = data.enhanced.uniqueRecipientDomains;
|
||||
}
|
||||
|
||||
// Update peak hour
|
||||
const peakHour = document.querySelector('[data-stats="peak-hour"]');
|
||||
if (peakHour && data.enhanced.peakHour) {
|
||||
peakHour.textContent = data.enhanced.peakHour;
|
||||
// Update Quick Insights values
|
||||
const avgSubjectLength = document.querySelector('[data-stats="average-subject-length"]');
|
||||
if (avgSubjectLength && data.enhanced.averageSubjectLength !== undefined) {
|
||||
avgSubjectLength.textContent = data.enhanced.averageSubjectLength;
|
||||
}
|
||||
|
||||
// Update 24h prediction
|
||||
const prediction24h = document.querySelector('[data-stats="prediction-24h"]');
|
||||
if (prediction24h && data.enhanced.prediction24h) {
|
||||
prediction24h.textContent = data.enhanced.prediction24h;
|
||||
const uniqueSenderDomainsValue = document.querySelector('[data-stats="unique-sender-domains-value"]');
|
||||
if (uniqueSenderDomainsValue && data.enhanced.uniqueSenderDomains !== undefined) {
|
||||
uniqueSenderDomainsValue.textContent = data.enhanced.uniqueSenderDomains;
|
||||
}
|
||||
|
||||
const uniqueRecipientDomainsValue = document.querySelector('[data-stats="unique-recipient-domains-value"]');
|
||||
if (uniqueRecipientDomainsValue && data.enhanced.uniqueRecipientDomains !== undefined) {
|
||||
uniqueRecipientDomainsValue.textContent = data.enhanced.uniqueRecipientDomains;
|
||||
}
|
||||
|
||||
const peakHourPercentage = document.querySelector('[data-stats="peak-hour-percentage"]');
|
||||
if (peakHourPercentage && data.enhanced.peakHourPercentage !== undefined) {
|
||||
peakHourPercentage.textContent = data.enhanced.peakHourPercentage + '%';
|
||||
}
|
||||
|
||||
const emailsPerHour = document.querySelector('[data-stats="emails-per-hour"]');
|
||||
if (emailsPerHour && data.enhanced.emailsPerHour !== undefined) {
|
||||
emailsPerHour.textContent = data.enhanced.emailsPerHour;
|
||||
}
|
||||
|
||||
const dayPercentage = document.querySelector('[data-stats="day-percentage"]');
|
||||
if (dayPercentage && data.enhanced.dayPercentage !== undefined) {
|
||||
dayPercentage.textContent = data.enhanced.dayPercentage + '%';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -389,9 +415,4 @@ function lazyLoadStats() {
|
|||
|
||||
// Rebuild chart with new data
|
||||
rebuildStatsChart();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading stats:', error);
|
||||
// Stats remain as placeholder
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,10 +34,13 @@ router.get('/', async(req, res) => {
|
|||
enhanced: {
|
||||
topSenderDomains: [],
|
||||
topRecipientDomains: [],
|
||||
busiestHours: [],
|
||||
uniqueSenderDomains: '...',
|
||||
averageEmailSize: '...',
|
||||
peakHour: '...',
|
||||
prediction24h: '...'
|
||||
uniqueRecipientDomains: '...',
|
||||
averageSubjectLength: '...',
|
||||
peakHourPercentage: '...',
|
||||
emailsPerHour: '...',
|
||||
dayPercentage: '...'
|
||||
},
|
||||
historical: [],
|
||||
prediction: []
|
||||
|
|
|
|||
|
|
@ -96,19 +96,19 @@
|
|||
<h3 class="section-header-small">
|
||||
Top Sender Domains
|
||||
</h3>
|
||||
{% if stats.enhanced.topSenderDomains|length > 0 %}
|
||||
<ul class="stat-list" data-stats="top-sender-domains">
|
||||
{% if stats.enhanced.topSenderDomains|length > 0 %}
|
||||
{% for item in stats.enhanced.topSenderDomains|slice(0, 5) %}
|
||||
<li class="stat-list-item">
|
||||
<span class="stat-list-label">{{ item.domain }}</span>
|
||||
<span class="stat-list-value">{{ item.count }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="stat-empty">Loading...</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<p class="stat-footer"><span data-stats="unique-sender-domains">{{ stats.enhanced.uniqueSenderDomains }}</span> unique domains</p>
|
||||
{% else %}
|
||||
<p class="stat-empty">No data yet</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Top Recipient Domains -->
|
||||
|
|
@ -116,19 +116,19 @@
|
|||
<h3 class="section-header-small">
|
||||
Top Recipient Domains
|
||||
</h3>
|
||||
{% if stats.enhanced.topRecipientDomains|length > 0 %}
|
||||
<ul class="stat-list" data-stats="top-recipient-domains">
|
||||
{% if stats.enhanced.topRecipientDomains|length > 0 %}
|
||||
{% for item in stats.enhanced.topRecipientDomains|slice(0, 5) %}
|
||||
<li class="stat-list-item">
|
||||
<span class="stat-list-label">{{ item.domain }}</span>
|
||||
<span class="stat-list-value">{{ item.count }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li class="stat-empty">Loading...</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<p class="stat-footer"><span data-stats="unique-recipient-domains">{{ stats.enhanced.uniqueRecipientDomains }}</span> unique domains</p>
|
||||
{% else %}
|
||||
<p class="stat-empty">No data yet</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Busiest Hours -->
|
||||
|
|
@ -136,18 +136,18 @@
|
|||
<h3 class="section-header-small">
|
||||
Busiest Hours
|
||||
</h3>
|
||||
<ul class="stat-list" data-stats="busiest-hours">
|
||||
{% if stats.enhanced.busiestHours|length > 0 %}
|
||||
<ul class="stat-list">
|
||||
{% for item in stats.enhanced.busiestHours %}
|
||||
<li class="stat-list-item">
|
||||
<span class="stat-list-label">{{ item.hour }}:00 - {{ item.hour + 1 }}:00</span>
|
||||
<span class="stat-list-value">{{ item.count }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="stat-empty">No data yet</p>
|
||||
<li class="stat-empty">Loading...</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Quick Stats -->
|
||||
|
|
@ -157,27 +157,27 @@
|
|||
</h3>
|
||||
<div class="quick-stats">
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.averageSubjectLength }}</div>
|
||||
<div class="quick-stat-value" data-stats="average-subject-length">{{ stats.enhanced.averageSubjectLength }}</div>
|
||||
<div class="quick-stat-label">Avg Subject Length</div>
|
||||
</div>
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.uniqueSenderDomains }}</div>
|
||||
<div class="quick-stat-value" data-stats="unique-sender-domains-value">{{ stats.enhanced.uniqueSenderDomains }}</div>
|
||||
<div class="quick-stat-label">Unique Senders</div>
|
||||
</div>
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.uniqueRecipientDomains }}</div>
|
||||
<div class="quick-stat-value" data-stats="unique-recipient-domains-value">{{ stats.enhanced.uniqueRecipientDomains }}</div>
|
||||
<div class="quick-stat-label">Unique Recipients</div>
|
||||
</div>
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.peakHourPercentage }}%</div>
|
||||
<div class="quick-stat-value" data-stats="peak-hour-percentage">{{ stats.enhanced.peakHourPercentage }}%</div>
|
||||
<div class="quick-stat-label">Peak Hour Traffic</div>
|
||||
</div>
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.emailsPerHour }}</div>
|
||||
<div class="quick-stat-value" data-stats="emails-per-hour">{{ stats.enhanced.emailsPerHour }}</div>
|
||||
<div class="quick-stat-label">Emails per Hour</div>
|
||||
</div>
|
||||
<div class="quick-stat-item">
|
||||
<div class="quick-stat-value">{{ stats.enhanced.dayPercentage }}%</div>
|
||||
<div class="quick-stat-value" data-stats="day-percentage">{{ stats.enhanced.dayPercentage }}%</div>
|
||||
<div class="quick-stat-label">Daytime (6am-6pm)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue