fix(radar): open camera intelligence & directory records in-map, eliminate view switching

This commit is contained in:
2026-09-12 09:39:07 -07:00
parent fef8ab6417
commit 0e47dfa1d8
4 changed files with 199 additions and 42 deletions
+70 -10
View File
@@ -28,7 +28,7 @@
<meta name="apple-mobile-web-app-title" content="FlockRadar">
<!-- Main Stylesheet -->
<link rel="stylesheet" href="css/styles.css?v=9">
<link rel="stylesheet" href="css/styles.css?v=10">
</head>
<body>
@@ -886,11 +886,71 @@
</a>
</div>
<!-- Public Transparency Portal Link -->
<div id="proof-portal-link-box" style="margin-top: 16px; text-align: center;">
<button id="btn-proof-open-portal" class="btn-secondary" style="width: 100%; padding: 12px;">
Inspect Official Transparency Portal & Policy Records ↗
</button>
<!-- Specific Agency Directory Records & Surveillance Policy -->
<div class="modal-section proof-directory-records-section" id="proof-directory-records-section" style="margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border-subtle);">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
<h3 class="modal-section-title" style="margin: 0; font-size: 0.95rem; color: #38bdf8; display: flex; align-items: center; gap: 6px;">
<span>📋</span>
<span>Agency Surveillance & Policy Records</span>
</h3>
<span id="proof-dir-source-badge" style="font-size: 0.72rem; padding: 2px 8px; border-radius: 10px; background: rgba(56, 189, 248, 0.15); color: #38bdf8; border: 1px solid rgba(56, 189, 248, 0.3);">
Official Transparency Data
</span>
</div>
<!-- 4-Box Key Stats (Cameras, Volume, Searches, Retention) -->
<div class="proof-specs-grid" style="margin-bottom: 14px;">
<div class="proof-spec-card">
<span class="spec-label">CAMERAS IN FLEET</span>
<span class="spec-val" id="proof-dir-cameras" style="color: #38bdf8;">--</span>
</div>
<div class="proof-spec-card">
<span class="spec-label">30-DAY DETECTIONS</span>
<span class="spec-val" id="proof-dir-volume" style="color: #34d399;">--</span>
</div>
<div class="proof-spec-card">
<span class="spec-label">POLICE SEARCHES</span>
<span class="spec-val" id="proof-dir-searches">--</span>
</div>
<div class="proof-spec-card">
<span class="spec-label">DATA RETENTION</span>
<span class="spec-val" id="proof-dir-retention" style="color: #fbbf24;">30 Days Auto-Purge</span>
</div>
</div>
<!-- Inter-Agency Data Sharing Network -->
<div class="proof-sharing-section" style="margin-bottom: 14px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(148, 163, 184, 0.15); border-radius: 12px; padding: 12px 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
<strong style="font-size: 0.8rem; color: #e2e8f0; display: flex; align-items: center; gap: 6px;">
<span>🔗</span> Inter-Agency Surveillance Sharing:
</strong>
<span id="proof-dir-sharing-count" style="font-size: 0.75rem; color: #38bdf8; font-weight: 700;">Regional Law Enforcement</span>
</div>
<p style="font-size: 0.74rem; color: #94a3b8; margin: 0 0 8px; line-height: 1.4;">
Plate reads from this camera are broadcast to regional and federal law enforcement partners:
</p>
<div id="proof-dir-sharing-tags" style="display: flex; flex-wrap: wrap; gap: 5px; max-height: 90px; overflow-y: auto;">
<!-- Sharing tags rendered dynamically -->
</div>
</div>
<!-- Top Search Justifications -->
<div id="proof-dir-reasons-box" style="margin-bottom: 14px; background: rgba(15, 23, 42, 0.6); border: 1px solid rgba(148, 163, 184, 0.15); border-radius: 12px; padding: 12px 14px;">
<strong style="font-size: 0.8rem; color: #e2e8f0; display: block; margin-bottom: 6px;">
🔍 Primary Search Audit Reasons:
</strong>
<div id="proof-dir-reasons-list" style="display: flex; flex-direction: column; gap: 4px; font-size: 0.75rem; color: #cbd5e1;">
<!-- Rendered dynamically -->
</div>
</div>
<!-- Official City Portal Link (Opens in New Tab - Never leaves the radar map) -->
<div id="proof-portal-link-box" style="margin-top: 12px;">
<a id="btn-proof-official-portal" href="#" target="_blank" rel="noopener noreferrer" class="btn-secondary" style="width: 100%; padding: 12px; display: flex; align-items: center; justify-content: center; gap: 8px; text-decoration: none; border-color: #06b6d4; color: #38bdf8; font-weight: 600;">
<span>🌐</span>
<span id="btn-proof-portal-text">Open Official Transparency Portal ↗</span>
</a>
</div>
</div>
<!-- Admin Removal Control (Only Active for Custom Placed Pins) -->
@@ -971,10 +1031,10 @@
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<!-- Data & Application Logic with Cache Busting -->
<script src="data/portals.js?v=9"></script>
<script src="js/auth.js?v=9"></script>
<script src="js/radar.js?v=9"></script>
<script src="js/app.js?v=9"></script>
<script src="data/portals.js?v=10"></script>
<script src="js/auth.js?v=10"></script>
<script src="js/radar.js?v=10"></script>
<script src="js/app.js?v=10"></script>
<!-- Service Worker Instant Cache Invalidation -->
<script>
+3
View File
@@ -569,6 +569,9 @@
}
}
// Global access so radar map can open agency modal without switching views
window.openAgencyModal = openAgencyModal;
function renderModalSharingTags(sharedIds, query) {
if (!sharedIds || sharedIds.length === 0) {
el.modalSharingBox.innerHTML = `<span style="font-size: 0.8rem; color: var(--text-dim); padding: 8px;">No external sharing partners published on portal.</span>`;
+119 -25
View File
@@ -423,14 +423,9 @@
${cam.notes ? `<br><span><em>${escapeHtml(cam.notes)}</em></span>` : ''}
</div>
<div style="display: flex; flex-direction: column; gap: 6px; margin-top: 10px;">
<button class="popup-inspect-btn" style="background: linear-gradient(135deg, #06b6d4, #0284c7); color: #fff; font-weight: 600;" onclick="window.openCameraHardwareProof('${escapeHtml(cam.id)}')">
📸 View Hardware Proof & Vote
<button class="popup-inspect-btn" style="background: linear-gradient(135deg, #06b6d4, #0284c7); color: #fff; font-weight: 700; width: 100%; border: none; border-radius: 6px; padding: 10px; cursor: pointer; box-shadow: 0 2px 8px rgba(6, 182, 212, 0.4);" onclick="window.openCameraHardwareProof('${escapeHtml(cam.id)}')">
📸 View Camera Info & Agency Records
</button>
${cam.slug ? `
<button class="popup-inspect-btn" onclick="window.inspectPortal('${escapeHtml(cam.slug)}')">
Inspect Agency Records & Sharing ↗
</button>
` : ''}
${(!cam.id.startsWith('portal-') && (cam.isCommunity || cam.id.startsWith('rpt-') || cam.id.startsWith('usr-pin-') || cam.id.startsWith('test-') || cam.id.startsWith('cam-test-') || cam.notes === '1-Tap Mobile Driver Pin' || cam.agency === 'Crowdsourced Report' || cam.name.includes('Community Report'))) ? `
<button class="popup-inspect-btn popup-delete-btn" style="background: #dc2626 !important; color: #ffffff !important; font-weight: 800 !important; font-size: 0.88rem !important; padding: 10px 14px !important; border-radius: 6px !important; margin-top: 6px !important; display: flex !important; align-items: center !important; justify-content: center !important; gap: 8px !important; border: none !important; cursor: pointer !important; box-shadow: 0 3px 10px rgba(220, 38, 38, 0.45) !important;" onclick="window.adminDeleteCamera('${escapeHtml(cam.id)}')">
<span style="font-size: 1rem;">🗑️</span>
@@ -443,9 +438,9 @@
marker.bindPopup(popupHtml);
// Clicking marker directly opens the full hardware proof modal
marker.on('click', () => {
// Leaflet will open popup, but user can also click proof button directly
// Clicking radar circle zone directly opens the camera intelligence sheet
radarCircle.on('click', () => {
openCameraHardwareProof(cam.id);
});
cameraCircles.push(radarCircle);
@@ -1397,19 +1392,116 @@
el.btnProofNavigate.href = `https://www.google.com/maps/dir/?api=1&destination=${cam.lat},${cam.lng}`;
}
// Transparency Portal Link
const portalBox = document.getElementById('proof-portal-link-box');
if (portalBox) {
// Lookup Directory Intelligence for this camera & agency
let portal = null;
if (window.FLOCK_DATA && window.FLOCK_DATA.portals) {
if (cam.slug) {
portalBox.style.display = 'block';
if (el.btnProofOpenPortal) {
el.btnProofOpenPortal.onclick = () => {
closeCameraDetailModal();
window.inspectPortal(cam.slug);
};
portal = window.FLOCK_DATA.portals.find(p => p.slug === cam.slug);
}
if (!portal && cam.agency) {
portal = window.FLOCK_DATA.portals.find(p =>
(p.name && p.name.toLowerCase().includes(cam.agency.toLowerCase())) ||
(cam.agency.toLowerCase().includes((p.name || '').toLowerCase()))
);
}
}
// Populate Directory Intelligence Fields
const elDirCameras = document.getElementById('proof-dir-cameras');
const elDirVolume = document.getElementById('proof-dir-volume');
const elDirSearches = document.getElementById('proof-dir-searches');
const elDirRetention = document.getElementById('proof-dir-retention');
const elDirSharingCount = document.getElementById('proof-dir-sharing-count');
const elDirSharingTags = document.getElementById('proof-dir-sharing-tags');
const elDirReasonsBox = document.getElementById('proof-dir-reasons-box');
const elDirReasonsList = document.getElementById('proof-dir-reasons-list');
const elOfficialPortalBtn = document.getElementById('btn-proof-official-portal');
const elPortalText = document.getElementById('btn-proof-portal-text');
if (portal) {
// Official scraped portal record
if (elDirCameras) elDirCameras.textContent = portal.cameras ? `${portal.cameras.toLocaleString()} in Fleet` : `${cam.cameras || 4} at Node`;
if (elDirVolume) elDirVolume.textContent = portal.vehicles_captured ? `${portal.vehicles_captured.toLocaleString()} Reads` : (cam.captures30d ? `${cam.captures30d} Reads` : '300,000+');
if (elDirSearches) elDirSearches.textContent = portal.searches ? `${portal.searches.toLocaleString()} Queries` : 'Automated Hotlist';
if (elDirRetention) elDirRetention.textContent = '30 Days Auto-Purge';
const sharedIds = portal.shared_org_ids || [];
if (elDirSharingCount) elDirSharingCount.textContent = `${sharedIds.length} Partner Agencies`;
if (elDirSharingTags) {
const orgsLookup = (window.FLOCK_DATA && window.FLOCK_DATA.orgs) || [];
const topShared = sharedIds.slice(0, 15).map(id => orgsLookup[id] || `Agency #${id}`);
elDirSharingTags.innerHTML = topShared.map(name => `
<span style="background: rgba(56, 189, 248, 0.1); border: 1px solid rgba(56, 189, 248, 0.25); color: #93c5fd; padding: 2px 7px; border-radius: 6px; font-size: 0.7rem;">${escapeHtml(name)}</span>
`).join('') + (sharedIds.length > 15 ? `<span style="font-size: 0.68rem; color: #64748b; align-self: center;">+${sharedIds.length - 15} more</span>` : '');
}
if (elDirReasonsList && elDirReasonsBox) {
const reasons = portal.top_search_reasons || {};
const entries = Object.entries(reasons).slice(0, 4);
if (entries.length > 0) {
elDirReasonsBox.style.display = 'block';
elDirReasonsList.innerHTML = entries.map(([r, count]) => `
<div style="display: flex; justify-content: space-between;">
<span>• ${escapeHtml(r)}</span>
<span style="color: #38bdf8; font-weight: 600;">${count.toLocaleString()} searches</span>
</div>
`).join('');
} else {
elDirReasonsBox.style.display = 'none';
}
}
if (elOfficialPortalBtn && elPortalText) {
elOfficialPortalBtn.href = portal.url || `https://transparency.flocksafety.com/${portal.slug}`;
elPortalText.textContent = `Open Official ${portal.name} Transparency Portal ↗`;
}
} else {
portalBox.style.display = 'none';
// Local Antelope Valley / Sheriff corridor or Community Camera
const defaultCameras = cam.cameras || (cam.id && cam.id.includes('plm') ? 4 : 2);
const defaultCaptures = cam.captures30d ? `${cam.captures30d} Reads` : '285,000 Reads';
if (elDirCameras) elDirCameras.textContent = `${defaultCameras} Falcon Node Cameras`;
if (elDirVolume) elDirVolume.textContent = defaultCaptures;
if (elDirSearches) elDirSearches.textContent = 'Automated NCIC Hotlist';
if (elDirRetention) elDirRetention.textContent = '30 Days Auto-Purge';
if (elDirSharingCount) elDirSharingCount.textContent = 'Regional Law Enforcement Network';
if (elDirSharingTags) {
const regionalPartners = [
'Los Angeles County Sheriff (LASD)',
'California Highway Patrol (CHP)',
'Los Angeles Police Department (LAPD)',
'Kern County Sheriff Office',
'San Bernardino County Sheriff',
'Ventura County Sheriff'
];
elDirSharingTags.innerHTML = regionalPartners.map(name => `
<span style="background: rgba(56, 189, 248, 0.1); border: 1px solid rgba(56, 189, 248, 0.25); color: #93c5fd; padding: 2px 7px; border-radius: 6px; font-size: 0.7rem;">${name}</span>
`).join('');
}
if (elDirReasonsList && elDirReasonsBox) {
elDirReasonsBox.style.display = 'block';
elDirReasonsList.innerHTML = `
<div style="display: flex; justify-content: space-between;">
<span>• Stolen Vehicle / Plate Alerts (10851 CVC)</span>
<span style="color: #38bdf8; font-weight: 600;">Real-time</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>• Felony Warrants & BOLO Broadcasts</span>
<span style="color: #38bdf8; font-weight: 600;">Active</span>
</div>
<div style="display: flex; justify-content: space-between;">
<span>• Amber Alert / Missing Person Inquiries</span>
<span style="color: #38bdf8; font-weight: 600;">Broadcast</span>
</div>
`;
}
if (elOfficialPortalBtn && elPortalText) {
elOfficialPortalBtn.href = `https://transparency.flocksafety.com/${cam.slug || 'palmdale-ca-lasd'}`;
elPortalText.textContent = `Open Official ${cam.agency || 'Agency'} Transparency Portal ↗`;
}
}
@@ -1988,12 +2080,14 @@
el.loginCloseBtn.addEventListener('click', closeLoginModal);
el.reportCloseBtn.addEventListener('click', closeReportModal);
// Global listener for portal inspection from popup
// Global listener for camera & portal inspection: NEVER switches views away from radar!
window.inspectPortal = function(slug) {
switchView('dir');
setTimeout(() => {
window.location.hash = slug;
}, 150);
const matchedCam = allCameras.find(c => c.slug === slug);
if (matchedCam) {
openCameraHardwareProof(matchedCam.id);
} else if (typeof window.openAgencyModal === 'function') {
window.openAgencyModal(slug);
}
};
}
+6 -6
View File
@@ -1,13 +1,13 @@
// Service Worker for FlockRadar Offline Driving & Live Updates
const CACHE_NAME = 'flockradar-v9';
const CACHE_NAME = 'flockradar-v10';
const ASSETS_TO_CACHE = [
'./',
'./index.html',
'./css/styles.css?v=9',
'./js/app.js?v=9',
'./js/auth.js?v=9',
'./js/radar.js?v=9',
'./data/portals.js?v=9',
'./css/styles.css?v=10',
'./js/app.js?v=10',
'./js/auth.js?v=10',
'./js/radar.js?v=10',
'./data/portals.js?v=10',
'./manifest.json'
];