157 lines
5.3 KiB
JavaScript
157 lines
5.3 KiB
JavaScript
// 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);
|
|
}
|