48fcb00241
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>
97 lines
3.4 KiB
JavaScript
97 lines
3.4 KiB
JavaScript
// GigLez - Search Functionality
|
|
|
|
async function searchCaptures() {
|
|
const query = document.getElementById('search-query').value;
|
|
const frequency = document.getElementById('search-frequency').value;
|
|
const protocol = document.getElementById('search-protocol').value;
|
|
const dateStart = document.getElementById('search-date-start').value;
|
|
const dateEnd = document.getElementById('search-date-end').value;
|
|
const lat = document.getElementById('search-lat').value;
|
|
const lon = document.getElementById('search-lon').value;
|
|
const radius = document.getElementById('search-radius').value;
|
|
|
|
// Build query parameters
|
|
const params = new URLSearchParams();
|
|
|
|
if (query) params.append('q', query);
|
|
if (frequency) params.append('frequency', parseFloat(frequency) * 1e6);
|
|
if (protocol) params.append('protocol', protocol);
|
|
if (dateStart) params.append('date_start', dateStart);
|
|
if (dateEnd) params.append('date_end', dateEnd);
|
|
|
|
// Geographic search
|
|
if (lat && lon && radius) {
|
|
params.append('latitude', lat);
|
|
params.append('longitude', lon);
|
|
params.append('radius_km', radius);
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/v1/query/captures?${params.toString()}`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Search failed');
|
|
}
|
|
|
|
const data = await response.json();
|
|
displaySearchResults(data.captures || []);
|
|
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
alert(`Search failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
function displaySearchResults(captures) {
|
|
const resultsDiv = document.getElementById('search-results');
|
|
const resultsList = document.getElementById('results-list');
|
|
|
|
resultsDiv.style.display = 'block';
|
|
|
|
if (captures.length === 0) {
|
|
resultsList.innerHTML = '<p>No results found. Try adjusting your search criteria.</p>';
|
|
return;
|
|
}
|
|
|
|
resultsList.innerHTML = captures.map(capture => createResultCard(capture)).join('');
|
|
}
|
|
|
|
function createResultCard(capture) {
|
|
const freqMHz = (capture.frequency / 1e6).toFixed(2);
|
|
const date = new Date(capture.captured_at).toLocaleString();
|
|
|
|
const deviceName = capture.device_name || 'Unknown Device';
|
|
const protocol = capture.protocol || 'RAW';
|
|
const confidence = capture.match_confidence
|
|
? `${(capture.match_confidence * 100).toFixed(0)}%`
|
|
: 'N/A';
|
|
|
|
return `
|
|
<div class="result-card" onclick="viewCaptureDetails('${capture.file_hash}')">
|
|
<div class="result-header">
|
|
<div class="result-title">${deviceName}</div>
|
|
<div class="result-frequency">${freqMHz} MHz</div>
|
|
</div>
|
|
<div class="result-details">
|
|
<div>
|
|
<strong>Protocol:</strong> ${protocol}
|
|
</div>
|
|
<div>
|
|
<strong>Confidence:</strong> ${confidence}
|
|
</div>
|
|
<div>
|
|
<strong>Captured:</strong> ${date}
|
|
</div>
|
|
<div>
|
|
<strong>Location:</strong> ${capture.latitude.toFixed(4)}, ${capture.longitude.toFixed(4)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function viewCaptureDetails(fileHash) {
|
|
// Show detail modal or navigate to detail page
|
|
alert(`Viewing details for: ${fileHash}\n\nDetail view coming soon!`);
|
|
}
|