0.3.2
0.3.2
This commit is contained in:
60
CHANGELOG.md
60
CHANGELOG.md
@@ -5,6 +5,64 @@ All notable changes to Seismo Fleet Manager will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.3.2] - 2025-12-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Progressive Web App (PWA) Mobile Optimization**: Complete mobile-first redesign for field deployment usage
|
||||||
|
- **Responsive Navigation**: Hamburger menu with slide-in sidebar for mobile, always-visible sidebar for desktop
|
||||||
|
- **Bottom Navigation Bar**: Quick access to Dashboard, Roster, Add Unit, and Settings (mobile only)
|
||||||
|
- **Mobile Card View**: Compact card layout for roster units with status dots, location, and project ID
|
||||||
|
- **Tap-to-Navigate**: Location addresses and coordinates are clickable and open in user's default navigation app (Google Maps, Apple Maps, Waze, etc.)
|
||||||
|
- **Unit Detail Modal**: Bottom sheet modal showing full unit details with edit capabilities (tap any unit card to open)
|
||||||
|
- **Touch Optimization**: 44x44px minimum button targets following iOS/Android accessibility guidelines
|
||||||
|
- **Service Worker**: Network-first caching strategy for offline-capable operation
|
||||||
|
- **IndexedDB Storage**: Offline data persistence for unit information and pending edits
|
||||||
|
- **Background Sync**: Queues edits made while offline and syncs automatically when connection returns
|
||||||
|
- **Offline Indicator**: Visual banner showing offline status with manual sync button
|
||||||
|
- **PWA Manifest**: Installable as a standalone app on mobile devices with custom icons
|
||||||
|
- **Hard Reload Button**: "Clear Cache & Reload" utility in sidebar menu to force fresh JavaScript/CSS
|
||||||
|
- **Mobile-Specific Files**:
|
||||||
|
- `backend/static/mobile.css` - Mobile UI styles, hamburger menu, bottom nav, cards, modals
|
||||||
|
- `backend/static/mobile.js` - Mobile interactions, offline sync, modal management
|
||||||
|
- `backend/static/sw.js` - Service worker for PWA functionality
|
||||||
|
- `backend/static/offline-db.js` - IndexedDB wrapper for offline storage
|
||||||
|
- `backend/static/manifest.json` - PWA configuration
|
||||||
|
- `backend/static/icons/` - 8 PWA icon sizes (72px-512px)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Dashboard Alerts**: Only show Missing units in notifications (Pending units no longer appear in alerts)
|
||||||
|
- **Roster Template**: Mobile card view shows status from server-side render instead of fetching separately
|
||||||
|
- **Mobile Status Display**: Benched units show "Benched" label instead of "Unknown" or "N/A"
|
||||||
|
- **Base Template**: Added cache-busting query parameters to JavaScript files (e.g., `mobile.js?v=0.3.2`)
|
||||||
|
- **Sidebar Menu**: Added utility section with "Toggle theme" and "Clear Cache & Reload" buttons
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Modal Status Display**: Fixed unit detail modal showing "Unknown" status by passing status data from card to modal
|
||||||
|
- **Mobile Card Status**: Fixed grey dot with "Unknown" label for benched units - now properly shows deployment state
|
||||||
|
- **Status Data Passing**: Roster cards now pass status and age to modal via function parameters and global status map
|
||||||
|
- **Service Worker Caching**: Aggressive browser caching issue resolved with version query parameters and hard reload function
|
||||||
|
|
||||||
|
### Technical Details
|
||||||
|
- Mobile breakpoint at 768px (`md:` prefix in TailwindCSS)
|
||||||
|
- PWA installable via Add to Home Screen on iOS/Android
|
||||||
|
- Service worker caches all static assets with network-first strategy
|
||||||
|
- Google Maps search API used for universal navigation links (works across all map apps)
|
||||||
|
- Status map stored in `window.rosterStatusMap` from server-side rendered data
|
||||||
|
- Hard reload function clears service worker caches, unregisters workers, and deletes IndexedDB
|
||||||
|
|
||||||
|
## [0.3.1] - 2025-12-12
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Dashboard Notifications**: Removed Pending units from alert list - only Missing units now trigger notifications
|
||||||
|
- **Status Dots**: Verified deployed units display correct status dots (OK=green, Pending=yellow, Missing=red) in both active and benched tables
|
||||||
|
- **Mobile Card View**: Fixed roster cards showing "Unknown" status by using `.get()` with defaults in backend routes
|
||||||
|
- **Backend Status Handling**: Added default values for status, age, last_seen fields to prevent KeyError exceptions
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Backend roster partial routes (`/partials/roster-deployed`, `/partials/roster-benched`) now use `.get()` method with sensible defaults
|
||||||
|
- Deployed units default to "Unknown" status when data unavailable
|
||||||
|
- Benched units default to "N/A" status when data unavailable
|
||||||
|
|
||||||
## [0.3.0] - 2025-12-09
|
## [0.3.0] - 2025-12-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -151,6 +209,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Photo management per unit
|
- Photo management per unit
|
||||||
- Automated status categorization (OK/Pending/Missing)
|
- Automated status categorization (OK/Pending/Missing)
|
||||||
|
|
||||||
|
[0.3.2]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.3.1...v0.3.2
|
||||||
|
[0.3.1]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.3.0...v0.3.1
|
||||||
[0.3.0]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.2.1...v0.3.0
|
[0.3.0]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.2.1...v0.3.0
|
||||||
[0.2.1]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.2.0...v0.2.1
|
[0.2.1]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.2.0...v0.2.1
|
||||||
[0.2.0]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.1.1...v0.2.0
|
[0.2.0]: https://github.com/serversdwn/seismo-fleet-manager/compare/v0.1.1...v0.2.0
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -3,6 +3,12 @@ Backend API and HTMX-powered web interface for managing a mixed fleet of seismog
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- **Progressive Web App (PWA)**: Mobile-first responsive design optimized for field deployment operations
|
||||||
|
- **Install as App**: Add to home screen on iOS/Android for native app experience
|
||||||
|
- **Offline Capable**: Service worker caching with IndexedDB storage for offline operation
|
||||||
|
- **Touch Optimized**: 44x44px minimum touch targets, hamburger menu, bottom navigation bar
|
||||||
|
- **Mobile Card View**: Compact unit cards with status dots, tap-to-navigate locations, and detail modals
|
||||||
|
- **Background Sync**: Queue edits while offline and automatically sync when connection returns
|
||||||
- **Web Dashboard**: Modern, responsive UI with dark/light mode, live HTMX updates, and integrated fleet map
|
- **Web Dashboard**: Modern, responsive UI with dark/light mode, live HTMX updates, and integrated fleet map
|
||||||
- **Fleet Monitoring**: Track deployed, benched, retired, and ignored units in separate buckets with unknown-emitter triage
|
- **Fleet Monitoring**: Track deployed, benched, retired, and ignored units in separate buckets with unknown-emitter triage
|
||||||
- **Roster Management**: Full CRUD + CSV import/export, device-type aware metadata, and inline actions from the roster tables
|
- **Roster Management**: Full CRUD + CSV import/export, device-type aware metadata, and inline actions from the roster tables
|
||||||
@@ -11,9 +17,8 @@ Backend API and HTMX-powered web interface for managing a mixed fleet of seismog
|
|||||||
- **Status Management**: Automatically mark deployed units as OK, Pending (>12h), or Missing (>24h) based on recent telemetry
|
- **Status Management**: Automatically mark deployed units as OK, Pending (>12h), or Missing (>24h) based on recent telemetry
|
||||||
- **Data Ingestion**: Accept reports from emitter scripts via REST API
|
- **Data Ingestion**: Accept reports from emitter scripts via REST API
|
||||||
- **Photo Management**: Upload and view photos for each unit
|
- **Photo Management**: Upload and view photos for each unit
|
||||||
- **Interactive Maps**: Leaflet-based maps showing unit locations
|
- **Interactive Maps**: Leaflet-based maps showing unit locations with tap-to-navigate for mobile
|
||||||
- **SQLite Storage**: Lightweight, file-based database for easy deployment
|
- **SQLite Storage**: Lightweight, file-based database for easy deployment
|
||||||
- **PWA Mobile Version**: Optimized for ease of use during remote deployment operations.
|
|
||||||
|
|
||||||
## Roster Manager & Settings
|
## Roster Manager & Settings
|
||||||
|
|
||||||
@@ -432,6 +437,19 @@ docker compose down -v
|
|||||||
|
|
||||||
## Release Highlights
|
## Release Highlights
|
||||||
|
|
||||||
|
### v0.3.2 — 2025-12-12
|
||||||
|
- **Progressive Web App (PWA)**: Complete mobile optimization with offline support, installable as standalone app
|
||||||
|
- **Mobile-First UI**: Hamburger menu, bottom navigation bar, card-based roster view optimized for touch
|
||||||
|
- **Tap-to-Navigate**: Location links open in user's preferred navigation app (Google Maps, Apple Maps, Waze)
|
||||||
|
- **Offline Editing**: Service worker + IndexedDB for offline operation with automatic sync when online
|
||||||
|
- **Unit Detail Modals**: Bottom sheet modals for quick unit info access with full edit capabilities
|
||||||
|
- **Hard Reload Utility**: "Clear Cache & Reload" button to force fresh assets (helpful for development)
|
||||||
|
|
||||||
|
### v0.3.1 — 2025-12-12
|
||||||
|
- **Dashboard Alerts**: Only Missing units show in notifications (Pending units no longer alert)
|
||||||
|
- **Status Fixes**: Fixed "Unknown" status issues in mobile card views and detail modals
|
||||||
|
- **Backend Improvements**: Safer data access with `.get()` defaults to prevent errors
|
||||||
|
|
||||||
### v0.3.0 — 2025-12-09
|
### v0.3.0 — 2025-12-09
|
||||||
- **Series 4 Support**: New `/api/series4/heartbeat` endpoint with auto-detection for Micromate units (UM##### pattern)
|
- **Series 4 Support**: New `/api/series4/heartbeat` endpoint with auto-detection for Micromate units (UM##### pattern)
|
||||||
- **Settings Redesign**: Completely redesigned Settings page with 4-tab interface (General, Data Management, Advanced, Danger Zone)
|
- **Settings Redesign**: Completely redesigned Settings page with 4-tab interface (General, Data Management, Advanced, Danger Zone)
|
||||||
@@ -476,9 +494,13 @@ MIT
|
|||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
**Current: 0.3.0** — Series 4 support, settings redesign, user preferences (2025-12-09)
|
**Current: 0.3.2** — Progressive Web App with mobile optimization (2025-12-12)
|
||||||
|
|
||||||
Previous: 0.2.1 — Settings & roster manager refresh (2025-12-03)
|
Previous: 0.3.1 — Dashboard alerts and status fixes (2025-12-12)
|
||||||
|
|
||||||
|
0.3.0 — Series 4 support, settings redesign, user preferences (2025-12-09)
|
||||||
|
|
||||||
|
0.2.1 — Settings & roster manager refresh (2025-12-03)
|
||||||
|
|
||||||
0.2.0 — Device-type aware roster + ignore list (2025-12-03)
|
0.2.0 — Device-type aware roster + ignore list (2025-12-03)
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Base.metadata.create_all(bind=engine)
|
|||||||
ENVIRONMENT = os.getenv("ENVIRONMENT", "production")
|
ENVIRONMENT = os.getenv("ENVIRONMENT", "production")
|
||||||
|
|
||||||
# Initialize FastAPI app
|
# Initialize FastAPI app
|
||||||
VERSION = "0.3.1"
|
VERSION = "0.3.2"
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Seismo Fleet Manager",
|
title="Seismo Fleet Manager",
|
||||||
description="Backend API for managing seismograph fleet status",
|
description="Backend API for managing seismograph fleet status",
|
||||||
|
|||||||
@@ -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');
|
||||||
@@ -325,10 +368,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Offline Database -->
|
<!-- Offline Database -->
|
||||||
<script src="/static/offline-db.js?v=0.3.1"></script>
|
<script src="/static/offline-db.js?v=0.3.2"></script>
|
||||||
|
|
||||||
<!-- Mobile JavaScript -->
|
<!-- Mobile JavaScript -->
|
||||||
<script src="/static/mobile.js?v=0.3.1"></script>
|
<script src="/static/mobile.js?v=0.3.2"></script>
|
||||||
|
|
||||||
{% block extra_scripts %}{% endblock %}
|
{% block extra_scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -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