Add centralized error handling with UI toast for user-facing errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
16861ba88a
commit
40198fa0cf
2 changed files with 39 additions and 8 deletions
|
|
@ -26,6 +26,17 @@
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
function debugLog(...args) { if (DEBUG) console.log(...args); }
|
function debugLog(...args) { if (DEBUG) console.log(...args); }
|
||||||
|
|
||||||
|
function handleError(context, error, showUI = false) {
|
||||||
|
console.error(`[${context}]`, error);
|
||||||
|
if (showUI) {
|
||||||
|
const msg = document.createElement('div');
|
||||||
|
msg.className = 'error-toast';
|
||||||
|
msg.textContent = `${context}: ${error.message || 'Unknown error'}`;
|
||||||
|
document.body.appendChild(msg);
|
||||||
|
setTimeout(() => msg.remove(), GLOW_DURATION_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- DOM references --------------------------------------- */
|
/* ---------- DOM references --------------------------------------- */
|
||||||
const wrap = document.getElementById('mapContainer');
|
const wrap = document.getElementById('mapContainer');
|
||||||
const group = document.getElementById('mapGroup');
|
const group = document.getElementById('mapGroup');
|
||||||
|
|
@ -574,7 +585,7 @@ async function fetchHeatmapData() {
|
||||||
debugLog(`Loaded ${heatmapData.length} heat map points from last ${data.hours_window} hours`);
|
debugLog(`Loaded ${heatmapData.length} heat map points from last ${data.hours_window} hours`);
|
||||||
renderHeatmap();
|
renderHeatmap();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch heat map data:', err);
|
handleError('Heatmap', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -659,7 +670,7 @@ async function fetchPortalData() {
|
||||||
debugLog(`Loaded ${portalData.length} portals from last hour`);
|
debugLog(`Loaded ${portalData.length} portals from last hour`);
|
||||||
renderPortals();
|
renderPortals();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to fetch portal data:', err);
|
handleError('Portals', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1057,8 +1068,8 @@ function showInventoryWindow(name) {
|
||||||
invContent.appendChild(count);
|
invContent.appendChild(count);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
handleError('Inventory', err, true);
|
||||||
loading.textContent = `Failed to load inventory: ${err.message}`;
|
loading.textContent = `Failed to load inventory: ${err.message}`;
|
||||||
console.error('Inventory fetch failed:', err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
debugLog('Inventory window created for:', name);
|
debugLog('Inventory window created for:', name);
|
||||||
|
|
@ -1365,7 +1376,7 @@ async function pollLive() {
|
||||||
renderTrails(trails);
|
renderTrails(trails);
|
||||||
renderList();
|
renderList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Live or trails fetch failed:', e);
|
handleError('Player update', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1375,7 +1386,7 @@ async function pollTotalRares() {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
updateTotalRaresDisplay(data);
|
updateTotalRaresDisplay(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Total rares fetch failed:', e);
|
handleError('Rare counter', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1394,7 +1405,7 @@ async function pollTotalKills() {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
updateTotalKillsDisplay(data);
|
updateTotalKillsDisplay(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Total kills fetch failed:', e);
|
handleError('Kill counter', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1411,7 +1422,7 @@ async function pollServerHealth() {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
updateServerStatusDisplay(data);
|
updateServerStatusDisplay(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Server health fetch failed:', e);
|
handleError('Server health', e);
|
||||||
updateServerStatusDisplay({ status: 'error' });
|
updateServerStatusDisplay({ status: 'error' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1835,7 +1846,7 @@ function initWebSocket() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
socket.addEventListener('close', () => setTimeout(initWebSocket, 2000));
|
socket.addEventListener('close', () => setTimeout(initWebSocket, 2000));
|
||||||
socket.addEventListener('error', e => console.error('WebSocket error:', e));
|
socket.addEventListener('error', e => handleError('WebSocket', e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display or create a chat window for a character
|
// Display or create a chat window for a character
|
||||||
|
|
|
||||||
|
|
@ -1570,3 +1570,23 @@ body.noselect, body.noselect * {
|
||||||
color: #88ccff;
|
color: #88ccff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Error Toast */
|
||||||
|
.error-toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: rgba(220, 38, 38, 0.9);
|
||||||
|
color: white;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
z-index: 99999;
|
||||||
|
animation: toastFadeIn 0.3s ease;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toastFadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue