add date on the last Sync display

This commit is contained in:
2026-02-09 23:36:01 +08:00
parent 76d85a97b5
commit 9236cc24c7
+31 -22
View File
@@ -325,31 +325,31 @@
if (loading) loading.style.display = 'inline'; if (loading) loading.style.display = 'inline';
try { try { // <--- THIS WAS MISSING
const response = await fetch('/api/summary'); const response = await fetch('/api/summary');
const result = await response.json(); const result = await response.json();
// 1. Update Sync Time // --- 1. DATE & TIMEZONE FIX ---
if (result.last_sync) { if (syncDisplay && result.last_sync) {
// Ensure format is ISO compliant for mobile browsers const dateStr = result.last_sync.trim().replace(' ', 'T');
const dateStr = result.last_sync.replace(' ', 'T');
const dateObj = new Date(dateStr + (dateStr.includes('Z') ? '' : 'Z')); const dateObj = new Date(dateStr + (dateStr.includes('Z') ? '' : 'Z'));
document.getElementById('lastSyncTime').innerText = dateObj.toLocaleTimeString([], { syncDisplay.innerText = dateObj.toLocaleString([], {
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false month: 'short', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
}); });
syncDisplay.classList.add('text-success');
setTimeout(() => syncDisplay.classList.remove('text-success'), 2000);
} }
// --- FIX: Extract data from result ---
// --- 2. Extract data from result ---
const data = result.data; const data = result.data;
if (!data || data.length === 0) {
tbody.innerHTML = '<tr><td colspan="14" class="text-center signal-neutral">No data found.</td></tr>';
return;
}
let htmlContent = ''; let htmlContent = '';
const todayStr = new Date().toISOString().split('T')[0]; const todayStr = new Date().toISOString().split('T')[0];
if (data && data.length > 0) {
data.forEach(item => { data.forEach(item => {
// --- A. Error Handling --- // --- A. Error Handling ---
if (item.error || !item.last_close || item.last_close === 'N/A') { if (item.error || !item.last_close || item.last_close === 'N/A') {
@@ -377,11 +377,9 @@
const low = parseFloat(item.low_52) || 0; const low = parseFloat(item.low_52) || 0;
const high = parseFloat(item.high_52) || 0; const high = parseFloat(item.high_52) || 0;
// 52W Range calculation
let rangePct = high > low ? Math.min(Math.max(((current - low) / (high - low)) * 100, 0), 100) : 0; let rangePct = high > low ? Math.min(Math.max(((current - low) / (high - low)) * 100, 0), 100) : 0;
const rangeColor = rangePct > 80 ? 'text-danger' : (rangePct < 20 ? 'text-success' : 'text-muted'); const rangeColor = rangePct > 80 ? 'text-danger' : (rangePct < 20 ? 'text-success' : 'text-muted');
// Indicators
const rsiVal = item.rsi !== null ? item.rsi : "N/A"; const rsiVal = item.rsi !== null ? item.rsi : "N/A";
const bbRaw = parseFloat(item.bb_pct); const bbRaw = parseFloat(item.bb_pct);
const bbDisplay = !isNaN(bbRaw) ? (bbRaw * 100).toFixed(0) + '%' : 'N/A'; const bbDisplay = !isNaN(bbRaw) ? (bbRaw * 100).toFixed(0) + '%' : 'N/A';
@@ -391,7 +389,7 @@
const isFresh = item.last_date === todayStr; const isFresh = item.last_date === todayStr;
const dateBadgeClass = isFresh ? 'badge bg-success-subtle text-success border border-success-subtle' : 'text-muted'; const dateBadgeClass = isFresh ? 'badge bg-success-subtle text-success border border-success-subtle' : 'text-muted';
// --- C. Construct Row (14 Columns) --- // --- C. Construct Row ---
htmlContent += ` htmlContent += `
<tr> <tr>
<td><div class="fw-bold">${item.name || item.symbol}</div></td> <td><div class="fw-bold">${item.name || item.symbol}</div></td>
@@ -419,18 +417,22 @@
<td>${typeof formatKD === 'function' ? formatKD(item.kd_values) : 'N/A'}</td> <td>${typeof formatKD === 'function' ? formatKD(item.kd_values) : 'N/A'}</td>
</tr>`; </tr>`;
}); });
} else {
htmlContent = '<tr><td colspan="14" class="text-center">No data available</td></tr>';
}
tbody.innerHTML = htmlContent; tbody.innerHTML = htmlContent;
} catch (error) { } catch (error) {
console.error("Fetch error:", error); console.error("Fetch error:", error);
if (tbody) tbody.innerHTML = '<tr><td colspan="14" class="text-danger p-4 text-center">Error loading data.</td></tr>'; if (tbody) tbody.innerHTML = '<tr><td colspan="14" class="text-danger p-4 text-center">API Error: Check Console</td></tr>';
} finally { } finally {
if (loading) loading.style.display = 'none'; if (loading) loading.style.display = 'none';
} }
} }
// --- 4. Global Sync --- // --- 4. Global Sync ---
async function runGlobalSync() { async function runGlobalSync() {
console.log("Sync triggered...");
const syncBtn = document.getElementById('syncBtn'); const syncBtn = document.getElementById('syncBtn');
const loading = document.getElementById('loading'); const loading = document.getElementById('loading');
if (!syncBtn) return; if (!syncBtn) return;
@@ -457,17 +459,24 @@
// This checks if the Flask server is responding every 30 seconds // This checks if the Flask server is responding every 30 seconds
async function checkStatus() { async function checkStatus() {
const indicator = document.getElementById('statusIndicator'); const indicator = document.getElementById('statusIndicator');
// GUARD: If the element is missing from HTML, exit immediately
if (!indicator) return;
try { try {
const response = await fetch('/api/summary'); // Or a dedicated /health endpoint const response = await fetch('/api/summary');
if (response.ok) { if (response.ok) {
indicator.innerHTML = '● Online'; // Using a span for the dot allows us to animate it separately if desired
indicator.className = 'text-success'; indicator.innerHTML = '<span class="status-dot-pulse">●</span> Online';
indicator.className = 'text-success fw-bold';
} else { } else {
throw new Error(); throw new Error();
} }
} catch (e) { } catch (e) {
if (indicator) {
indicator.innerHTML = '● Offline'; indicator.innerHTML = '● Offline';
indicator.className = 'text-danger'; indicator.className = 'text-danger fw-bold';
}
} }
} }