v0.4.0 - merge from claude/dev-015sto5mf2MpPCE57TbNKtaF #1
@@ -328,6 +328,11 @@ function populateUnitModal(unit, cardStatusInfo = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUnitStatus(unitId, unit = null) {
|
function getUnitStatus(unitId, unit = null) {
|
||||||
|
// Prefer roster table data if it was rendered with the current view
|
||||||
|
if (window.rosterStatusMap && window.rosterStatusMap[unitId]) {
|
||||||
|
return window.rosterStatusMap[unitId];
|
||||||
|
}
|
||||||
|
|
||||||
// Try to get status from dashboard snapshot if it exists
|
// Try to get status from dashboard snapshot if it exists
|
||||||
if (window.lastStatusSnapshot && window.lastStatusSnapshot.units && window.lastStatusSnapshot.units[unitId]) {
|
if (window.lastStatusSnapshot && window.lastStatusSnapshot.units && window.lastStatusSnapshot.units[unitId]) {
|
||||||
const unitStatus = window.lastStatusSnapshot.units[unitId];
|
const unitStatus = window.lastStatusSnapshot.units[unitId];
|
||||||
|
|||||||
@@ -137,8 +137,8 @@
|
|||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Dark mode toggle -->
|
<!-- Dark mode toggle and utilities -->
|
||||||
<div class="p-4 border-t border-gray-200 dark:border-gray-700">
|
<div class="p-4 border-t border-gray-200 dark:border-gray-700 space-y-2">
|
||||||
<button onclick="toggleDarkMode()" class="w-full flex items-center justify-center px-4 py-3 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
|
<button onclick="toggleDarkMode()" class="w-full flex items-center justify-center px-4 py-3 rounded-lg bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
|
||||||
<svg id="theme-toggle-dark-icon" class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
|
<svg id="theme-toggle-dark-icon" class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||||
@@ -148,6 +148,13 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="ml-3">Toggle theme</span>
|
<span class="ml-3">Toggle theme</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button onclick="hardReload()" class="w-full flex items-center justify-center px-4 py-3 rounded-lg bg-red-100 dark:bg-red-900/30 hover:bg-red-200 dark:hover:bg-red-900/50 text-red-700 dark:text-red-400">
|
||||||
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Clear Cache & Reload</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@@ -206,6 +213,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hard reload function - clears all caches and reloads
|
||||||
|
async function hardReload() {
|
||||||
|
try {
|
||||||
|
// Clear service worker caches
|
||||||
|
if ('caches' in window) {
|
||||||
|
const cacheNames = await caches.keys();
|
||||||
|
await Promise.all(cacheNames.map(name => caches.delete(name)));
|
||||||
|
console.log('Cleared all service worker caches');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister service workers
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||||
|
await Promise.all(registrations.map(reg => reg.unregister()));
|
||||||
|
console.log('Unregistered all service workers');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear IndexedDB
|
||||||
|
if ('indexedDB' in window) {
|
||||||
|
try {
|
||||||
|
indexedDB.deleteDatabase('sfm-offline-db');
|
||||||
|
console.log('Cleared IndexedDB');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Could not clear IndexedDB:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force reload with cache bypass
|
||||||
|
window.location.reload(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during hard reload:', error);
|
||||||
|
// Fallback to regular reload
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load saved theme preference
|
// Load saved theme preference
|
||||||
if (localStorage.getItem('theme') === 'light') {
|
if (localStorage.getItem('theme') === 'light') {
|
||||||
document.documentElement.classList.remove('dark');
|
document.documentElement.classList.remove('dark');
|
||||||
|
|||||||
@@ -365,6 +365,17 @@
|
|||||||
timestampMobileElement.textContent = new Date().toLocaleTimeString();
|
timestampMobileElement.textContent = new Date().toLocaleTimeString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep a lightweight status map around for the mobile modal
|
||||||
|
const rosterUnits = {{ units | tojson }};
|
||||||
|
window.rosterStatusMap = rosterUnits.reduce((acc, unit) => {
|
||||||
|
acc[unit.id] = {
|
||||||
|
status: unit.status || 'Unknown',
|
||||||
|
age: unit.age || 'N/A',
|
||||||
|
last: unit.last_seen || 'Never'
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
// Sorting state
|
// Sorting state
|
||||||
let currentSort = { column: null, direction: 'asc' };
|
let currentSort = { column: null, direction: 'asc' };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user