v0.4.0 - merge from claude/dev-015sto5mf2MpPCE57TbNKtaF #1

Merged
serversdown merged 32 commits from claude/dev-015sto5mf2MpPCE57TbNKtaF into main 2026-01-02 16:10:54 -05:00
3 changed files with 61 additions and 2 deletions
Showing only changes of commit 3aff0cb076 - Show all commits

View File

@@ -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];

View File

@@ -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');

View File

@@ -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' };