2026-04-13-3
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
// Intaleq Intelligence Dashboard Control
|
||||
// المتحكم البرمجي في لوحة ذكاء الخرائط
|
||||
|
||||
const API_BASE = '/api';
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
fetchSummary();
|
||||
fetchCandidates();
|
||||
fetchClosures();
|
||||
|
||||
// Event Listeners
|
||||
document.getElementById('run-analysis-btn').addEventListener('click', runAnalysis);
|
||||
document.getElementById('discover-closures-btn').addEventListener('click', discoverClosures);
|
||||
});
|
||||
|
||||
// --- API FETCHERS ---
|
||||
|
||||
async function fetchSummary() {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/map-refinement/summary`);
|
||||
const data = await res.json();
|
||||
|
||||
document.getElementById('stat-telemetry').innerText = Number(data.telemetry.total).toLocaleString();
|
||||
document.getElementById('stat-roads').innerText = Number(data.roads.analyzed).toLocaleString();
|
||||
document.getElementById('stat-pending').innerText = Number(data.candidates.pending).toLocaleString();
|
||||
document.getElementById('stat-closed').innerText = Number(data.roads.closed || 0).toLocaleString();
|
||||
} catch (e) {
|
||||
showToast('Failed to fetch summary');
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCandidates() {
|
||||
const tableBody = document.querySelector('#candidates-table tbody');
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/map-refinement/candidates?status=pending`);
|
||||
const data = await res.json();
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
tableBody.innerHTML = '<tr class="empty-state"><td colspan="6">No pending discoveries 🏜️</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tableBody.innerHTML = data.map(c => `
|
||||
<tr>
|
||||
<td title="${c.id}">${c.id.substring(0,8)}...</td>
|
||||
<td><i class="fas fa-user-tag"></i> ${c.uniqueDriverCount}</td>
|
||||
<td>${c.totalPoints}</td>
|
||||
<td>${Math.round(c.lengthMeters)}m</td>
|
||||
<td><span class="badge-conf ${getConfClass(c.confidence)}">${Math.round(c.confidence * 100)}%</span></td>
|
||||
<td>
|
||||
<button class="btn btn-sm-success" onclick="approveCandidate('${c.id}')"><i class="fas fa-check"></i></button>
|
||||
<button class="btn btn-sm-danger" onclick="rejectCandidate('${c.id}')"><i class="fas fa-times"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
} catch (e) {
|
||||
tableBody.innerHTML = '<tr class="error-state"><td colspan="6">Error loading candidates</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchClosures() {
|
||||
const tableBody = document.querySelector('#closures-table tbody');
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/map-refinement/closures`);
|
||||
const data = await res.json();
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
tableBody.innerHTML = '<tr class="empty-state"><td colspan="3">No segments flagged</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tableBody.innerHTML = data.map(s => `
|
||||
<tr>
|
||||
<td>${s.segmentId}</td>
|
||||
<td>${s.sampleCount} pts</td>
|
||||
<td><span class="badge-conf low">CLOSED</span></td>
|
||||
</tr>
|
||||
`).join('');
|
||||
} catch (e) {
|
||||
tableBody.innerHTML = '<tr><td colspan="3">Error loading closures</td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
// --- ACTIONS ---
|
||||
|
||||
async function approveCandidate(id) {
|
||||
if (!confirm('Add this path to the approved map queue?')) return;
|
||||
try {
|
||||
await fetch(`${API_BASE}/map-refinement/candidates/${id}/approve`, { method: 'PATCH' });
|
||||
showToast('Road Approved ✅');
|
||||
fetchCandidates();
|
||||
fetchSummary();
|
||||
} catch (e) {
|
||||
showToast('Approval failed');
|
||||
}
|
||||
}
|
||||
|
||||
async function rejectCandidate(id) {
|
||||
try {
|
||||
await fetch(`${API_BASE}/map-refinement/candidates/${id}/reject`, { method: 'PATCH' });
|
||||
showToast('Discovery Rejected ❌');
|
||||
fetchCandidates();
|
||||
fetchSummary();
|
||||
} catch (e) {
|
||||
showToast('Rejection failed');
|
||||
}
|
||||
}
|
||||
|
||||
async function runAnalysis() {
|
||||
const btn = document.getElementById('run-analysis-btn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Analyzing...';
|
||||
|
||||
try {
|
||||
await fetch(`${API_BASE}/telemetry/process-intelligence?days=2`, { method: 'POST' });
|
||||
showToast('Deep Intelligence started 🧠. Check Telegram for report soon!');
|
||||
} catch (e) {
|
||||
showToast('Analysis trigger failed');
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-play"></i> Run Intelligence';
|
||||
fetchSummary();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
async function discoverClosures() {
|
||||
showToast('Scanning for closures... 🚧');
|
||||
try {
|
||||
await fetch(`${API_BASE}/map-refinement/discover-closures`, { method: 'POST' });
|
||||
showToast('Closure scan complete');
|
||||
fetchClosures();
|
||||
fetchSummary();
|
||||
} catch (e) {
|
||||
showToast('Closure discovery failed');
|
||||
}
|
||||
}
|
||||
|
||||
// --- HELPERS ---
|
||||
|
||||
function getConfClass(conf) {
|
||||
if (conf > 0.8) return 'high';
|
||||
if (conf > 0.5) return 'med';
|
||||
return 'low';
|
||||
}
|
||||
|
||||
function showToast(msg) {
|
||||
const container = document.getElementById('toast-container');
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'toast';
|
||||
toast.innerText = msg;
|
||||
container.appendChild(toast);
|
||||
setTimeout(() => toast.remove(), 4000);
|
||||
}
|
||||
Reference in New Issue
Block a user