Files
giglez/static/js/main.js
T
Trilltechnician 48fcb00241 Phase 3 Complete: Web Interface MVP
Major Achievements:
-  Full web interface (1,520+ lines of frontend code)
-  Interactive Leaflet.js map with marker clustering
-  Drag-and-drop upload system with GPS input
-  Search & filter UI with multi-criteria
-  Statistics dashboard with Chart.js
-  Responsive mobile-friendly design

Backend:
-  FastAPI static file serving
-  Simplified server mode (main_simple.py)
-  Improved startup script with port auto-selection
-  PostgreSQL schema ready (requires setup)

Database:
-  SQLite populated with 85 Flipper Zero signatures
-  Device matching system operational
-  Frequency-based search working

Documentation:
-  PHASE_3_COMPLETE.md - Technical summary
-  WEB_INTERFACE_README.md - User guide
-  WEBAPP_STARTUP_GUIDE.md - Troubleshooting
-  POSTGRESQL_SETUP_EXPLANATION.md - DB setup guide
-  DATABASE_POPULATION_SUCCESS.md - Import report
-  DEVICE_IDENTIFICATION_REPORT.md - Matching analysis

Files Created:
- templates/index.html (260 lines)
- static/css/main.css (500 lines)
- static/js/*.js (760 lines total)
- src/api/main_simple.py (simplified server)
- start_web.sh (auto port selection)

Status: Production MVP Ready
Next: Phase 4 - API & Integration

🛰️ Generated with Claude Code
https://claude.com/claude-code

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-12 18:21:11 -08:00

135 lines
3.6 KiB
JavaScript

// GigLez - Main Application Logic
// Navigation
document.addEventListener('DOMContentLoaded', () => {
setupNavigation();
initializeApp();
});
function setupNavigation() {
const navLinks = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.section');
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const target = link.getAttribute('href').substring(1);
// Don't navigate for external links
if (link.hasAttribute('target')) {
window.open(link.getAttribute('href'), '_blank');
return;
}
// Update active nav link
navLinks.forEach(l => l.classList.remove('active'));
link.classList.add('active');
// Show target section
sections.forEach(s => s.classList.remove('active'));
const targetSection = document.getElementById(`${target}-section`);
if (targetSection) {
targetSection.classList.add('active');
// Trigger section-specific actions
onSectionChange(target);
}
});
});
}
function onSectionChange(sectionName) {
switch (sectionName) {
case 'map':
// Refresh map tiles
if (window.map) {
setTimeout(() => window.map.invalidateSize(), 100);
}
break;
case 'stats':
// Load statistics
if (typeof loadStatistics === 'function') {
loadStatistics();
}
break;
}
}
function initializeApp() {
// Check API connection
checkAPIConnection();
// Load initial data
loadInitialData();
// Set up periodic refresh
setInterval(refreshData, 60000); // Refresh every minute
}
async function checkAPIConnection() {
try {
const response = await fetch('/health');
const data = await response.json();
if (data.status === 'healthy') {
console.log('✅ API connection healthy');
} else {
console.warn('⚠️ API connection unhealthy:', data);
}
} catch (error) {
console.error('❌ API connection failed:', error);
}
}
async function loadInitialData() {
// Load captures for map
if (typeof loadCaptures === 'function') {
await loadCaptures();
}
}
async function refreshData() {
// Refresh map data if on map view
const mapSection = document.getElementById('map-section');
if (mapSection.classList.contains('active')) {
if (typeof window.reloadMapData === 'function') {
window.reloadMapData();
}
}
}
// Utility functions
function showNotification(message, type = 'info') {
// Simple notification system
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => notification.remove(), 300);
}, 3000);
}
function formatFrequency(hz) {
return (hz / 1e6).toFixed(2) + ' MHz';
}
function formatDate(dateString) {
return new Date(dateString).toLocaleString();
}
function formatCoordinates(lat, lon) {
return `${lat.toFixed(6)}, ${lon.toFixed(6)}`;
}
// Export utilities
window.showNotification = showNotification;
window.formatFrequency = formatFrequency;
window.formatDate = formatDate;
window.formatCoordinates = formatCoordinates;