/**
* Map Refinement (Place Audit) logic
*/
const refinement = {
state: {
candidates: []
},
init: async () => {
console.log('📍 Initializing Map Refinement Logic...');
await refinement.fetchCandidates();
},
fetchCandidates: async () => {
const headers = auth.getAuthHeader();
const url = '/api/map-refinement/places/candidates?status=PENDING';
console.log('🔍 [PlaceAudit] Fetching:', url);
console.log('🔑 [PlaceAudit] Auth headers:', JSON.stringify(headers));
const tbody = document.getElementById('refinement-table-body');
try {
const res = await fetch(url, { headers });
console.log('📡 [PlaceAudit] Response status:', res.status);
if (res.ok) {
refinement.state.candidates = await res.json();
console.log('✅ [PlaceAudit] Loaded', refinement.state.candidates.length, 'candidates');
refinement.renderTable();
} else {
const errorText = await res.text();
console.error('❌ [PlaceAudit] API Error:', res.status, errorText);
if (tbody) {
tbody.innerHTML = `
|
API Error ${res.status}: ${errorText.substring(0, 100)}
|
`;
}
}
} catch (error) {
console.error('❌ [PlaceAudit] Network Error:', error);
if (tbody) {
tbody.innerHTML = `|
Network Error: ${error.message}
|
`;
}
}
},
renderTable: () => {
const tbody = document.getElementById('refinement-table-body');
if (!tbody) return;
if (refinement.state.candidates.length === 0) {
tbody.innerHTML = `
No pending location suggestions.
|
`;
lucide.createIcons();
return;
}
try {
tbody.innerHTML = refinement.state.candidates.map(c => {
const trustColor = c.confidence_score >= 80 ? 'emerald' : (c.confidence_score >= 50 ? 'amber' : 'slate');
const trustText = c.confidence_score >= 80 ? 'High Trust' : (c.confidence_score >= 50 ? 'Partial Match' : 'Unverified');
// Safety checks for coordinates
const lat = typeof c.latitude === 'number' ? c.latitude.toFixed(4) : 'N/A';
const lng = typeof c.longitude === 'number' ? c.longitude.toFixed(4) : 'N/A';
return `
${c.name_ar || c.name || 'Untitled'}
${c.category || 'Location'}
|
${c.verified_name || 'No external match'}
${trustText} (${c.confidence_score || 0}%)
|
${lat}, ${lng}
${c.neighborhood_name || 'Unknown Neighborhood'}
|
|
`;
}).join('');
lucide.createIcons();
} catch (error) {
console.error('❌ [PlaceAudit] Rendering error:', error);
tbody.innerHTML = `| Rendering Error: See console for details |
`;
}
},
showMapModal: (lat, lng, name) => {
// Remove existing modal if any
const existing = document.getElementById('map-preview-modal');
if (existing) existing.remove();
const modal = document.createElement('div');
modal.id = 'map-preview-modal';
modal.className = 'fixed inset-0 z-[100] flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-sm animate-in fade-in duration-300';
modal.innerHTML = `
`;
document.body.appendChild(modal);
lucide.createIcons();
},
approve: async (id) => {
if (!confirm('Are you sure you want to approve this location and add it to the production map?')) return;
const headers = auth.getAuthHeader();
try {
const res = await fetch(`/api/map-refinement/places/candidates/${id}/approve`, {
method: 'PATCH',
headers
});
if (res.ok) {
await refinement.fetchCandidates();
} else {
alert('Approval failed.');
}
} catch (error) {
console.error(error);
}
},
reject: async (id) => {
const reason = prompt('Please enter a reason for rejection:');
if (reason === null) return;
const headers = auth.getAuthHeader();
try {
const res = await fetch(`/api/map-refinement/places/candidates/${id}/reject`, {
method: 'PATCH',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ reason })
});
if (res.ok) {
await refinement.fetchCandidates();
} else {
alert('Rejection failed.');
}
} catch (error) {
console.error(error);
}
}
};