diff --git a/static/js/workers.js b/static/js/workers.js
index 1d9287b..462b0b0 100644
--- a/static/js/workers.js
+++ b/static/js/workers.js
@@ -4,9 +4,7 @@
let workerData = null;
let refreshTimer;
let pageLoadTime = Date.now();
-let currentProgress = 0;
-const PROGRESS_MAX = 60; // 60 seconds for a complete cycle
-let lastUpdateTime = Date.now();
+let lastManualRefreshTime = 0;
let filterState = {
currentFilter: 'all',
searchTerm: ''
@@ -19,189 +17,114 @@ let serverTimeOffset = 0;
let serverStartTime = null;
// New variable to track custom refresh timing
-let lastManualRefreshTime = 0;
const MIN_REFRESH_INTERVAL = 10000; // Minimum 10 seconds between refreshes
+// Hashrate Normalization Utilities
+// Helper function to normalize hashrate to TH/s for consistent graphing
+function normalizeHashrate(value, unit) {
+ if (!value || isNaN(value)) return 0;
+
+ unit = (unit || 'th/s').toLowerCase();
+ if (unit.includes('ph/s')) {
+ return value * 1000; // Convert PH/s to TH/s
+ } else if (unit.includes('eh/s')) {
+ return value * 1000000; // Convert EH/s to TH/s
+ } else if (unit.includes('gh/s')) {
+ return value / 1000; // Convert GH/s to TH/s
+ } else if (unit.includes('mh/s')) {
+ return value / 1000000; // Convert MH/s to TH/s
+ } else if (unit.includes('kh/s')) {
+ return value / 1000000000; // Convert KH/s to TH/s
+ } else if (unit.includes('h/s') && !unit.includes('th/s') && !unit.includes('ph/s') &&
+ !unit.includes('eh/s') && !unit.includes('gh/s') && !unit.includes('mh/s') &&
+ !unit.includes('kh/s')) {
+ return value / 1000000000000; // Convert H/s to TH/s
+ } else {
+ // Assume TH/s if unit is not recognized
+ return value;
+ }
+}
+
+// Helper function to format hashrate values for display
+function formatHashrateForDisplay(value, unit) {
+ if (isNaN(value) || value === null || value === undefined) return "N/A";
+
+ // Always normalize to TH/s first if unit is provided
+ let normalizedValue = unit ? normalizeHashrate(value, unit) : value;
+
+ // Select appropriate unit based on magnitude
+ if (normalizedValue >= 1000000) { // EH/s range
+ return (normalizedValue / 1000000).toFixed(2) + ' EH/s';
+ } else if (normalizedValue >= 1000) { // PH/s range
+ return (normalizedValue / 1000).toFixed(2) + ' PH/s';
+ } else if (normalizedValue >= 1) { // TH/s range
+ return normalizedValue.toFixed(2) + ' TH/s';
+ } else if (normalizedValue >= 0.001) { // GH/s range
+ return (normalizedValue * 1000).toFixed(2) + ' GH/s';
+ } else { // MH/s range or smaller
+ return (normalizedValue * 1000000).toFixed(2) + ' MH/s';
+ }
+}
+
// Initialize the page
-$(document).ready(function() {
+$(document).ready(function () {
+ console.log("Worker page initializing...");
+
// Set up initial UI
initializePage();
-
+
// Get server time for uptime calculation
updateServerTime();
-
- // Set up refresh synchronization with main dashboard
- setupRefreshSync();
-
+
+ // Define global refresh function for BitcoinMinuteRefresh
+ window.manualRefresh = fetchWorkerData;
+
+ // Wait before initializing BitcoinMinuteRefresh to ensure DOM is ready
+ setTimeout(function () {
+ // Initialize BitcoinMinuteRefresh with our refresh function
+ if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.initialize) {
+ BitcoinMinuteRefresh.initialize(window.manualRefresh);
+ console.log("BitcoinMinuteRefresh initialized with refresh function");
+ } else {
+ console.warn("BitcoinMinuteRefresh not available");
+ }
+ }, 500);
+
// Fetch worker data immediately on page load
fetchWorkerData();
-
- // Set up refresh timer
- setInterval(updateProgressBar, 1000);
-
- // Set up uptime timer - synced with main dashboard
- setInterval(updateUptime, 1000);
-
- // Start server time polling - same as main dashboard
- setInterval(updateServerTime, 30000);
-
- // Auto-refresh worker data - aligned with main dashboard if possible
- setInterval(function() {
- // Check if it's been at least PROGRESS_MAX seconds since last update
- const timeSinceLastUpdate = Date.now() - lastUpdateTime;
- if (timeSinceLastUpdate >= PROGRESS_MAX * 1000) {
- // Check if there was a recent manual refresh
- const timeSinceManualRefresh = Date.now() - lastManualRefreshTime;
- if (timeSinceManualRefresh >= MIN_REFRESH_INTERVAL) {
- console.log("Auto-refresh triggered after time interval");
- fetchWorkerData();
- }
- }
- }, 10000); // Check every 10 seconds to align better with main dashboard
-
+
// Set up filter button click handlers
- $('.filter-button').click(function() {
+ $('.filter-button').click(function () {
$('.filter-button').removeClass('active');
$(this).addClass('active');
filterState.currentFilter = $(this).data('filter');
filterWorkers();
});
-
+
// Set up search input handler
- $('#worker-search').on('input', function() {
+ $('#worker-search').on('input', function () {
filterState.searchTerm = $(this).val().toLowerCase();
filterWorkers();
});
});
-// Set up refresh synchronization with main dashboard
-function setupRefreshSync() {
- // Listen for storage events (triggered by main dashboard)
- window.addEventListener('storage', function(event) {
- // Check if this is our dashboard refresh event
- if (event.key === 'dashboardRefreshEvent') {
- console.log("Detected dashboard refresh event");
-
- // Prevent too frequent refreshes
- const now = Date.now();
- const timeSinceLastRefresh = now - lastUpdateTime;
-
- if (timeSinceLastRefresh >= MIN_REFRESH_INTERVAL) {
- console.log("Syncing refresh with main dashboard");
- // Reset progress bar and immediately fetch
- resetProgressBar();
- // Refresh the worker data
- fetchWorkerData();
- } else {
- console.log("Skipping too-frequent refresh", timeSinceLastRefresh);
- // Just reset the progress bar to match main dashboard
- resetProgressBar();
- }
- }
- });
-
- // On page load, check if we should align with main dashboard timing
- try {
- const lastDashboardRefresh = localStorage.getItem('dashboardRefreshTime');
- if (lastDashboardRefresh) {
- const lastRefreshTime = parseInt(lastDashboardRefresh);
- const timeSinceLastDashboardRefresh = Date.now() - lastRefreshTime;
-
- // If main dashboard refreshed recently, adjust our timer
- if (timeSinceLastDashboardRefresh < PROGRESS_MAX * 1000) {
- console.log("Adjusting timer to align with main dashboard");
- currentProgress = Math.floor(timeSinceLastDashboardRefresh / 1000);
- updateProgressBar(currentProgress);
-
- // Calculate when next update will happen (roughly 60 seconds from last dashboard refresh)
- const timeUntilNextRefresh = (PROGRESS_MAX * 1000) - timeSinceLastDashboardRefresh;
-
- // Schedule a one-time check near the expected refresh time
- if (timeUntilNextRefresh > 0) {
- console.log(`Scheduling coordinated refresh in ${Math.floor(timeUntilNextRefresh/1000)} seconds`);
- setTimeout(function() {
- // Check if a refresh happened in the last few seconds via localStorage event
- const newLastRefresh = parseInt(localStorage.getItem('dashboardRefreshTime') || '0');
- const secondsSinceLastRefresh = (Date.now() - newLastRefresh) / 1000;
-
- // If dashboard hasn't refreshed in the last 5 seconds, do our own refresh
- if (secondsSinceLastRefresh > 5) {
- console.log("Coordinated refresh time reached, fetching data");
- fetchWorkerData();
- } else {
- console.log("Dashboard already refreshed recently, skipping coordinated refresh");
- }
- }, timeUntilNextRefresh);
- }
- }
- }
- } catch (e) {
- console.error("Error reading dashboard refresh time:", e);
- }
-
- // Check for dashboard refresh periodically
- setInterval(function() {
- try {
- const lastDashboardRefresh = parseInt(localStorage.getItem('dashboardRefreshTime') || '0');
- const now = Date.now();
- const timeSinceLastRefresh = (now - lastUpdateTime) / 1000;
- const timeSinceDashboardRefresh = (now - lastDashboardRefresh) / 1000;
-
- // If dashboard refreshed more recently than we did and we haven't refreshed in at least 10 seconds
- if (lastDashboardRefresh > lastUpdateTime && timeSinceLastRefresh > 10) {
- console.log("Catching up with dashboard refresh");
- resetProgressBar();
- fetchWorkerData();
- }
- } catch (e) {
- console.error("Error in periodic dashboard check:", e);
- }
- }, 5000); // Check every 5 seconds
-}
-
-// Server time update via polling - same as main.js
-function updateServerTime() {
- $.ajax({
- url: "/api/time",
- method: "GET",
- timeout: 5000,
- success: function(data) {
- serverTimeOffset = new Date(data.server_timestamp).getTime() - Date.now();
- serverStartTime = new Date(data.server_start_time).getTime();
- },
- error: function(jqXHR, textStatus, errorThrown) {
- console.error("Error fetching server time:", textStatus, errorThrown);
- }
- });
-}
-
-// Update uptime display - synced with main dashboard
-function updateUptime() {
- if (serverStartTime) {
- const currentServerTime = Date.now() + serverTimeOffset;
- const diff = currentServerTime - serverStartTime;
- const hours = Math.floor(diff / (1000 * 60 * 60));
- const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
- const seconds = Math.floor((diff % (1000 * 60)) / 1000);
- $("#uptimeTimer").html("Uptime: " + hours + "h " + minutes + "m " + seconds + "s");
- }
-}
-
// Initialize page elements
function initializePage() {
+ console.log("Initializing page elements...");
+
// Initialize mini chart for total hashrate if the element exists
if (document.getElementById('total-hashrate-chart')) {
initializeMiniChart();
}
-
+
// Show loading state
$('#worker-grid').html('
+
No workers found. Try refreshing the page.
+
+ `);
+ return;
+ }
+
workerData = data;
- lastUpdateTime = Date.now();
-
+
+ // Notify BitcoinMinuteRefresh that we've refreshed the data
+ if (typeof BitcoinMinuteRefresh !== 'undefined' && BitcoinMinuteRefresh.notifyRefresh) {
+ BitcoinMinuteRefresh.notifyRefresh();
+ }
+
// Update UI with new data
updateWorkerGrid();
updateSummaryStats();
updateMiniChart();
updateLastUpdated();
-
+
// Hide retry button
$('#retry-button').hide();
-
+
// Reset connection retry count
connectionRetryCount = 0;
-
+
console.log("Worker data updated successfully");
},
- error: function(xhr, status, error) {
+ error: function (xhr, status, error) {
console.error("Error fetching worker data:", error);
-
+
// Show error in worker grid
$('#worker-grid').html(`
@@ -257,39 +244,40 @@ function fetchWorkerData(forceRefresh = false) {
Error loading worker data: ${error || 'Unknown error'}
`);
-
+
// Show retry button
$('#retry-button').show();
-
+
// Implement exponential backoff for automatic retry
connectionRetryCount++;
const delay = Math.min(30000, 1000 * Math.pow(1.5, Math.min(5, connectionRetryCount)));
- console.log(`Will retry in ${delay/1000} seconds (attempt ${connectionRetryCount})`);
-
+ console.log(`Will retry in ${delay / 1000} seconds (attempt ${connectionRetryCount})`);
+
setTimeout(() => {
fetchWorkerData(true); // Force refresh on retry
}, delay);
},
- complete: function() {
+ complete: function () {
$('#worker-grid').removeClass('loading-fade');
}
});
}
// Update the worker grid with data
-// UPDATED FUNCTION
function updateWorkerGrid() {
+ console.log("Updating worker grid...");
+
if (!workerData || !workerData.workers) {
console.error("No worker data available");
return;
}
-
+
const workerGrid = $('#worker-grid');
workerGrid.empty();
-
+
// Apply current filters before rendering
const filteredWorkers = filterWorkersData(workerData.workers);
-
+
if (filteredWorkers.length === 0) {
workerGrid.html(`
@@ -299,82 +287,66 @@ function updateWorkerGrid() {
`);
return;
}
-
- // Calculate total unpaid earnings (from the dashboard)
- const totalUnpaidEarnings = workerData.total_earnings || 0;
-
- // Sum up hashrates of online workers to calculate share percentages
- const totalHashrate = workerData.workers
- .filter(w => w.status === 'online')
- .reduce((sum, w) => sum + parseFloat(w.hashrate_3hr || 0), 0);
-
- // Calculate share percentage for each worker
- const onlineWorkers = workerData.workers.filter(w => w.status === 'online');
- const offlineWorkers = workerData.workers.filter(w => w.status === 'offline');
-
- // Allocate 95% to online workers, 5% to offline workers
- const onlinePool = totalUnpaidEarnings * 0.95;
- const offlinePool = totalUnpaidEarnings * 0.05;
-
+
// Generate worker cards
filteredWorkers.forEach(worker => {
- // Calculate earnings share based on hashrate proportion
- let earningsDisplay = worker.earnings;
-
- // Explicitly recalculate earnings share for display consistency
- if (worker.status === 'online' && totalHashrate > 0) {
- const hashrateShare = parseFloat(worker.hashrate_3hr || 0) / totalHashrate;
- earningsDisplay = (onlinePool * hashrateShare).toFixed(8);
- } else if (worker.status === 'offline' && offlineWorkers.length > 0) {
- earningsDisplay = (offlinePool / offlineWorkers.length).toFixed(8);
- }
-
// Create worker card
const card = $('
');
-
+
// Add class based on status
if (worker.status === 'online') {
card.addClass('worker-card-online');
} else {
card.addClass('worker-card-offline');
}
-
+
// Add worker type badge
card.append(`
${worker.type}
`);
-
+
// Add worker name
card.append(`
${worker.name}
`);
-
+
// Add status badge
if (worker.status === 'online') {
card.append('
ONLINE
');
} else {
card.append('
OFFLINE
');
}
-
- // Add hashrate bar
+
+ // Add hashrate bar with normalized values for consistent display
const maxHashrate = 200; // TH/s - adjust based on your fleet
- const hashratePercent = Math.min(100, (worker.hashrate_3hr / maxHashrate) * 100);
+ const normalizedHashrate = normalizeHashrate(
+ worker.hashrate_3hr,
+ worker.hashrate_3hr_unit || 'th/s'
+ );
+ const hashratePercent = Math.min(100, (normalizedHashrate / maxHashrate) * 100);
+
+ // Format hashrate for display with appropriate unit
+ const formattedHashrate = formatHashrateForDisplay(
+ worker.hashrate_3hr,
+ worker.hashrate_3hr_unit || 'th/s'
+ );
+
card.append(`
Hashrate (3hr):
-
${worker.hashrate_3hr} ${worker.hashrate_3hr_unit}
+
${formattedHashrate}
`);
-
- // Add additional stats - NOTE: Using recalculated earnings
+
+ // Add additional stats
card.append(`
Last Share:
-
${worker.last_share.split(' ')[1]}
+
${typeof worker.last_share === 'string' ? worker.last_share.split(' ')[1] || worker.last_share : 'N/A'}
Earnings:
-
${earningsDisplay}
+
${worker.earnings.toFixed(8)}
Accept Rate:
@@ -386,35 +358,22 @@ function updateWorkerGrid() {
`);
-
+
// Add card to grid
workerGrid.append(card);
});
-
- // Verify the sum of displayed earnings equals the total
- console.log(`Total unpaid earnings: ${totalUnpaidEarnings} BTC`);
- console.log(`Sum of worker displayed earnings: ${
- filteredWorkers.reduce((sum, w) => {
- if (w.status === 'online' && totalHashrate > 0) {
- const hashrateShare = parseFloat(w.hashrate_3hr || 0) / totalHashrate;
- return sum + parseFloat((onlinePool * hashrateShare).toFixed(8));
- } else if (w.status === 'offline' && offlineWorkers.length > 0) {
- return sum + parseFloat((offlinePool / offlineWorkers.length).toFixed(8));
- }
- return sum;
- }, 0)
- } BTC`);
}
// Filter worker data based on current filter state
function filterWorkersData(workers) {
if (!workers) return [];
-
+
return workers.filter(worker => {
- const workerName = worker.name.toLowerCase();
+ // Default to empty string if name is undefined
+ const workerName = (worker.name || '').toLowerCase();
const isOnline = worker.status === 'online';
- const workerType = worker.type.toLowerCase();
-
+ const workerType = (worker.type || '').toLowerCase();
+
// Check if worker matches filter
let matchesFilter = false;
if (filterState.currentFilter === 'all') {
@@ -428,10 +387,10 @@ function filterWorkersData(workers) {
} else if (filterState.currentFilter === 'fpga' && workerType === 'fpga') {
matchesFilter = true;
}
-
+
// Check if worker matches search term
- const matchesSearch = workerName.includes(filterState.searchTerm);
-
+ const matchesSearch = filterState.searchTerm === '' || workerName.includes(filterState.searchTerm);
+
return matchesFilter && matchesSearch;
});
}
@@ -439,35 +398,37 @@ function filterWorkersData(workers) {
// Apply filter to rendered worker cards
function filterWorkers() {
if (!workerData || !workerData.workers) return;
-
+
// Re-render the worker grid with current filters
updateWorkerGrid();
}
-// Modified updateSummaryStats function for workers.js
+// Modified updateSummaryStats function with normalized hashrate display
function updateSummaryStats() {
if (!workerData) return;
-
+
// Update worker counts
$('#workers-count').text(workerData.workers_total || 0);
$('#workers-online').text(workerData.workers_online || 0);
$('#workers-offline').text(workerData.workers_offline || 0);
-
+
// Update worker ring percentage
- const onlinePercent = workerData.workers_total > 0 ?
+ const onlinePercent = workerData.workers_total > 0 ?
workerData.workers_online / workerData.workers_total : 0;
$('.worker-ring').css('--online-percent', onlinePercent);
-
- // IMPORTANT: Update total hashrate using EXACT format matching main dashboard
- // This ensures the displayed value matches exactly what's on the main page
+
+ // Display normalized hashrate with appropriate unit
if (workerData.total_hashrate !== undefined) {
- // Format with exactly 1 decimal place - matches main dashboard format
- const formattedHashrate = Number(workerData.total_hashrate).toFixed(1);
- $('#total-hashrate').text(`${formattedHashrate} ${workerData.hashrate_unit || 'TH/s'}`);
+ // Format with proper unit conversion
+ const formattedHashrate = formatHashrateForDisplay(
+ workerData.total_hashrate,
+ workerData.hashrate_unit || 'TH/s'
+ );
+ $('#total-hashrate').text(formattedHashrate);
} else {
- $('#total-hashrate').text(`0.0 ${workerData.hashrate_unit || 'TH/s'}`);
+ $('#total-hashrate').text(`0.0 TH/s`);
}
-
+
// Update other summary stats
$('#total-earnings').text(`${(workerData.total_earnings || 0).toFixed(8)} BTC`);
$('#daily-sats').text(`${numberWithCommas(workerData.daily_sats || 0)} sats`);
@@ -476,12 +437,18 @@ function updateSummaryStats() {
// Initialize mini chart
function initializeMiniChart() {
- const ctx = document.getElementById('total-hashrate-chart').getContext('2d');
-
+ console.log("Initializing mini chart...");
+
+ const ctx = document.getElementById('total-hashrate-chart');
+ if (!ctx) {
+ console.error("Mini chart canvas not found");
+ return;
+ }
+
// Generate some sample data to start
const labels = Array(24).fill('').map((_, i) => i);
- const data = [750, 760, 755, 770, 780, 775, 760, 765, 770, 775, 780, 790, 785, 775, 770, 765, 780, 785, 775, 770, 775, 780, 775, 774.8];
-
+ const data = Array(24).fill(0).map(() => Math.random() * 100 + 700);
+
miniChart = new Chart(ctx, {
type: 'line',
data: {
@@ -501,7 +468,7 @@ function initializeMiniChart() {
maintainAspectRatio: false,
scales: {
x: { display: false },
- y: {
+ y: {
display: false,
min: Math.min(...data) * 0.9,
max: Math.max(...data) * 1.1
@@ -521,113 +488,49 @@ function initializeMiniChart() {
});
}
-// Update mini chart with real data
+// Update mini chart with real data and normalization
function updateMiniChart() {
- if (!miniChart || !workerData || !workerData.hashrate_history) return;
-
+ if (!miniChart || !workerData || !workerData.hashrate_history) {
+ console.log("Skipping mini chart update - missing data");
+ return;
+ }
+
// Extract hashrate data from history
const historyData = workerData.hashrate_history;
- if (!historyData || historyData.length === 0) return;
-
- // Get the values for the chart
- const values = historyData.map(item => parseFloat(item.value) || 0);
+ if (!historyData || historyData.length === 0) {
+ console.log("No hashrate history data available");
+ return;
+ }
+
+ // Get the normalized values for the chart
+ const values = historyData.map(item => {
+ const val = parseFloat(item.value) || 0;
+ const unit = item.unit || workerData.hashrate_unit || 'th/s';
+ return normalizeHashrate(val, unit);
+ });
const labels = historyData.map(item => item.time);
-
+
// Update chart data
miniChart.data.labels = labels;
miniChart.data.datasets[0].data = values;
-
+
// Update y-axis range
- const min = Math.min(...values);
- const max = Math.max(...values);
+ const min = Math.min(...values.filter(v => v > 0)) || 0;
+ const max = Math.max(...values) || 1;
miniChart.options.scales.y.min = min * 0.9;
miniChart.options.scales.y.max = max * 1.1;
-
+
// Update the chart
miniChart.update('none');
}
-// Update progress bar
-function updateProgressBar() {
- if (currentProgress < PROGRESS_MAX) {
- currentProgress++;
- const progressPercent = (currentProgress / PROGRESS_MAX) * 100;
- $("#bitcoin-progress-inner").css("width", progressPercent + "%");
-
- // Add glowing effect when close to completion
- if (progressPercent > 80) {
- $("#bitcoin-progress-inner").addClass("glow-effect");
- } else {
- $("#bitcoin-progress-inner").removeClass("glow-effect");
- }
-
- // Update remaining seconds text
- let remainingSeconds = PROGRESS_MAX - currentProgress;
- if (remainingSeconds <= 0) {
- $("#progress-text").text("Waiting for update...");
- $("#bitcoin-progress-inner").addClass("waiting-for-update");
- } else {
- $("#progress-text").text(remainingSeconds + "s to next update");
- $("#bitcoin-progress-inner").removeClass("waiting-for-update");
- }
-
- // Check for main dashboard refresh near the end to ensure sync
- if (currentProgress >= 55) { // When we're getting close to refresh time
- try {
- const lastDashboardRefresh = parseInt(localStorage.getItem('dashboardRefreshTime') || '0');
- const secondsSinceDashboardRefresh = (Date.now() - lastDashboardRefresh) / 1000;
-
- // If main dashboard just refreshed (within last 5 seconds)
- if (secondsSinceDashboardRefresh <= 5) {
- console.log("Detected recent dashboard refresh, syncing now");
- resetProgressBar();
- fetchWorkerData();
- return;
- }
- } catch (e) {
- console.error("Error checking dashboard refresh status:", e);
- }
- }
- } else {
- // Reset progress bar if it's time to refresh
- // But first check if the main dashboard refreshed recently
- try {
- const lastDashboardRefresh = parseInt(localStorage.getItem('dashboardRefreshTime') || '0');
- const secondsSinceDashboardRefresh = (Date.now() - lastDashboardRefresh) / 1000;
-
- // If dashboard refreshed in the last 10 seconds, wait for it instead of refreshing ourselves
- if (secondsSinceDashboardRefresh < 10) {
- console.log("Waiting for dashboard refresh event instead of refreshing independently");
- return;
- }
- } catch (e) {
- console.error("Error checking dashboard refresh status:", e);
- }
-
- // If main dashboard hasn't refreshed recently, do our own refresh
- if (Date.now() - lastUpdateTime > PROGRESS_MAX * 1000) {
- console.log("Progress bar expired, fetching data");
- fetchWorkerData();
- }
- }
-}
-
-// Reset progress bar
-function resetProgressBar() {
- currentProgress = 0;
- $("#bitcoin-progress-inner").css("width", "0%");
- $("#bitcoin-progress-inner").removeClass("glow-effect");
- $("#bitcoin-progress-inner").removeClass("waiting-for-update");
- $("#progress-text").text(PROGRESS_MAX + "s to next update");
-}
-
// Update the last updated timestamp
function updateLastUpdated() {
if (!workerData || !workerData.timestamp) return;
-
+
try {
const timestamp = new Date(workerData.timestamp);
- $("#lastUpdated").html("
Last Updated: " +
+ $("#lastUpdated").html("
Last Updated: " +
timestamp.toLocaleString() + "
");
} catch (e) {
console.error("Error formatting timestamp:", e);
@@ -638,4 +541,4 @@ function updateLastUpdated() {
function numberWithCommas(x) {
if (x == null) return "N/A";
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
-}
\ No newline at end of file
+}