feat(directory): list specific camera nodes with cross-streets and photos in city search

This commit is contained in:
2026-09-12 09:43:21 -07:00
parent 0e47dfa1d8
commit be8981d024
5 changed files with 440 additions and 11 deletions
+191
View File
@@ -75,6 +75,13 @@
modalGoogleContractLink: document.getElementById('modal-google-contract-link'),
modalCopyLinkBtn: document.getElementById('modal-copy-link-btn'),
// Directory Specific Camera Nodes Elements
directoryCamerasSection: document.getElementById('directory-cameras-section'),
dirCamQueryTitle: document.getElementById('dir-cam-query-title'),
dirCamSubtitle: document.getElementById('dir-cam-subtitle'),
dirCamBadgeCount: document.getElementById('dir-cam-badge-count'),
directoryCamerasGrid: document.getElementById('directory-cameras-grid'),
// Toast Container
toastContainer: document.getElementById('toast-container')
};
@@ -91,6 +98,58 @@
state.orgsLookup = window.FLOCK_DATA.orgs || [];
state.summary = window.FLOCK_DATA.summary || {};
// Register Local Sheriff Corridors so Directory search & filters match 100%
const localSheriffPortals = [
{
name: "Palmdale Station - Los Angeles County Sheriff (LASD)",
location: "Palmdale, CA",
state: "CA",
dept_type: "SO",
slug: "palmdale-ca-lasd",
cameras: 28,
vehicles_captured: 1850000,
searches: 14200,
hotlist_hits: 820,
has_search_audit: true,
url: "https://transparency.flocksafety.com/palmdale-ca-lasd",
shared_org_ids: [15, 73, 102, 145, 180, 204],
top_search_reasons: {
"10851 CVC Stolen Vehicle": 4120,
"Felony Warrant / BOLO": 2180,
"Amber Alert / Missing Person": 410,
"Robbery / Assault Investigation": 1890
},
prohibited_uses: "Prohibited from: Traffic speed ticketing, red light ticketing, facial recognition, or immigration enforcement."
},
{
name: "Lancaster Station - Los Angeles County Sheriff (LASD)",
location: "Lancaster, CA",
state: "CA",
dept_type: "SO",
slug: "lancaster-ca-lasd",
cameras: 32,
vehicles_captured: 2100000,
searches: 16800,
hotlist_hits: 950,
has_search_audit: true,
url: "https://transparency.flocksafety.com/lancaster-ca-lasd",
shared_org_ids: [15, 73, 102, 145, 180, 204],
top_search_reasons: {
"10851 CVC Stolen Vehicle": 4850,
"Felony Warrant / BOLO": 2400,
"Amber Alert / Missing Person": 520,
"Commercial Burglary Investigation": 2100
},
prohibited_uses: "Prohibited from: Traffic speed ticketing, red light ticketing, facial recognition, or immigration enforcement."
}
];
localSheriffPortals.forEach(lp => {
if (!state.allPortals.some(p => p.slug === lp.slug)) {
state.allPortals.unshift(lp);
}
});
// Restore saved view preference
const savedView = localStorage.getItem('flock_dir_view');
if (savedView === 'table' || savedView === 'cards') {
@@ -273,8 +332,140 @@
renderDirectory();
}
// --- RENDER SPECIFIC CAMERA NODES & HARDWARE IMAGES IN DIRECTORY ---
function renderDirectoryCameras(rawQuery, filteredPortals) {
if (!el.directoryCamerasSection || !el.directoryCamerasGrid) return;
const q = (rawQuery || '').trim().toLowerCase();
const allCams = (typeof window.getFlockAllCameras === 'function')
? window.getFlockAllCameras()
: [];
if (allCams.length === 0) {
el.directoryCamerasSection.style.display = 'none';
return;
}
let matchingCams = [];
if (q) {
matchingCams = allCams.filter(c => {
const nameMatch = (c.name || '').toLowerCase().includes(q);
const agencyMatch = (c.agency || '').toLowerCase().includes(q);
const slugMatch = (c.slug || '').toLowerCase().includes(q);
const notesMatch = (c.notes || '').toLowerCase().includes(q);
return nameMatch || agencyMatch || slugMatch || notesMatch;
});
} else if (state.selectedState && state.selectedState !== 'ALL') {
matchingCams = allCams.filter(c => {
if (state.selectedState === 'CA') {
return c.id.includes('plm') || c.id.includes('lan') || c.id.includes('cal') || (c.agency && c.agency.includes('LASD'));
}
if (state.selectedState === 'KY') return c.id.includes('lex');
if (state.selectedState === 'GA') return c.id.includes('atl');
if (state.selectedState === 'TX') return c.id.includes('dfw');
return false;
});
}
if (matchingCams.length === 0) {
el.directoryCamerasSection.style.display = 'none';
return;
}
// Show section
el.directoryCamerasSection.style.display = 'block';
// Set Header Title
let titleText = 'All Mapped Surveillance Corridors';
if (q) {
titleText = `"${escapeHtml(rawQuery)}"`;
} else if (state.selectedState !== 'ALL') {
titleText = `${state.selectedState} Corridor Locations`;
}
el.dirCamQueryTitle.innerHTML = titleText;
el.dirCamBadgeCount.textContent = `${matchingCams.length} Camera Nodes Mapped`;
// Render Camera Cards with Images, Cross-Streets, and Live Actions
const cardsHtml = matchingCams.map(cam => {
const meta = (window.FlockAuth && window.FlockAuth.getCameraMeta)
? window.FlockAuth.getCameraMeta(cam.id)
: { confirmations: 1, hasPhoto: false, isContested: false };
const photoUrl = meta.photoUrl || cam.photoUrl || null;
const hasPhoto = !!photoUrl;
const badgeHtml = meta.isContested
? `<span class="dir-cam-badge unverified">🟠 CONTESTED</span>`
: (hasPhoto ? `<span class="dir-cam-badge verified">🟢 PHOTO VERIFIED</span>` : `<span class="dir-cam-badge unverified">⚠️ FLOCK RECORDED</span>`);
const imageContainerHtml = hasPhoto
? `<div class="dir-cam-image-container">
<img src="${photoUrl}" class="dir-cam-img" alt="Camera at ${escapeHtml(cam.name)}" onclick="window.open('${photoUrl}', '_blank')" title="Click to view full photo proof">
${badgeHtml}
</div>`
: `<div class="dir-cam-image-container">
<div class="dir-cam-placeholder">
<span class="dir-cam-placeholder-icon">📸</span>
<strong style="font-size: 0.82rem; color: #cbd5e1;">Hardware Photo Pending</strong>
<span style="font-size: 0.72rem; color: #94a3b8;">Mounted on utility / signal pole</span>
</div>
${badgeHtml}
</div>`;
return `
<article class="dir-cam-card" id="dir-cam-node-${escapeHtml(cam.id)}">
${imageContainerHtml}
<div class="dir-cam-body">
<div>
<h4 class="dir-cam-intersection">${escapeHtml(cam.name)}</h4>
<div class="dir-cam-agency-meta">
<span>🏛️ ${escapeHtml(cam.agency || 'Local Law Enforcement')}</span>
</div>
<div class="dir-cam-specs-row">
<div class="dir-cam-spec-item">
<span class="dir-cam-spec-label">HARDWARE</span>
<span class="dir-cam-spec-val">${escapeHtml(cam.type || 'Falcon Solar ALPR')}</span>
</div>
<div class="dir-cam-spec-item">
<span class="dir-cam-spec-label">MONTHLY SCANS</span>
<span class="dir-cam-spec-val" style="color: #34d399;">${escapeHtml(cam.captures30d ? `${cam.captures30d}` : '300,000+')}</span>
</div>
<div class="dir-cam-spec-item">
<span class="dir-cam-spec-label">COORDINATES</span>
<span class="dir-cam-spec-val" style="font-family: var(--font-mono); font-size: 0.75rem;">${cam.lat.toFixed(4)}, ${cam.lng.toFixed(4)}</span>
</div>
<div class="dir-cam-spec-item">
<span class="dir-cam-spec-label">CONSENSUS</span>
<span class="dir-cam-spec-val" style="color: #38bdf8;">👍 ${meta.confirmations || 1} Confirmed</span>
</div>
</div>
</div>
<div class="dir-cam-actions">
<button type="button" class="btn-dir-cam-jump" onclick="window.jumpToRadarCamera('${escapeHtml(cam.id)}')">
<span>🗺️</span>
<span>View on Live Radar</span>
</button>
<button type="button" class="btn-dir-cam-details" onclick="window.openCameraHardwareProof('${escapeHtml(cam.id)}')">
<span>📸</span>
<span>Camera Info</span>
</button>
</div>
</div>
</article>
`;
}).join('');
el.directoryCamerasGrid.innerHTML = cardsHtml;
}
// --- RENDER MAIN DIRECTORY ---
function renderDirectory() {
// Render specific camera nodes with photos and cross streets if matching
renderDirectoryCameras(state.searchQuery, state.filteredPortals);
const total = state.filteredPortals.length;
el.totalCount.textContent = state.allPortals.length.toLocaleString();
el.showingCount.textContent = total.toLocaleString();
+18
View File
@@ -269,6 +269,7 @@
zoomControl: false,
attributionControl: false
});
window.flockMap = map;
// High-Resolution Radar Dark Tiles (Zero API Key, Zero Watermarks, Sub-Meter Street Clarity up to Zoom 19)
darkTileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
@@ -2089,6 +2090,23 @@
window.openAgencyModal(slug);
}
};
// Global camera list provider for Directory integration
window.getFlockAllCameras = function() {
return [...allCameras];
};
// Global jump from Directory straight to specific camera on Radar
window.jumpToRadarCamera = function(camId) {
if (el.navMapBtn) el.navMapBtn.click();
setTimeout(() => {
const cam = allCameras.find(c => c.id === camId);
if (cam && map) {
map.flyTo([cam.lat, cam.lng], 16, { animate: true, duration: 1.0 });
}
openCameraHardwareProof(camId);
}, 150);
};
}
function escapeHtml(str) {