119 lines
4.4 KiB
JavaScript
119 lines
4.4 KiB
JavaScript
/**
|
|
* 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();
|
|
try {
|
|
const res = await fetch('/api/map-refinement/candidates?status=PENDING', { headers });
|
|
if (res.ok) {
|
|
refinement.state.candidates = await res.json();
|
|
refinement.renderTable();
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch candidates', error);
|
|
}
|
|
},
|
|
|
|
renderTable: () => {
|
|
const tbody = document.getElementById('refinement-table-body');
|
|
if (!tbody) return;
|
|
|
|
if (refinement.state.candidates.length === 0) {
|
|
tbody.innerHTML = `
|
|
<tr>
|
|
<td colspan="5" class="py-20 text-center text-slate-500">
|
|
<div class="flex flex-col items-center gap-4">
|
|
<i data-lucide="map-pin" class="w-8 h-8 opacity-20"></i>
|
|
<p>No pending location suggestions.</p>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
lucide.createIcons();
|
|
return;
|
|
}
|
|
|
|
tbody.innerHTML = refinement.state.candidates.map(c => `
|
|
<tr class="hover:bg-white/[0.02] transition-colors border-b border-white/[0.03]">
|
|
<td class="px-8 py-6">
|
|
<div class="font-bold text-white">${c.name_ar || c.name}</div>
|
|
<div class="text-[10px] text-slate-500 uppercase font-bold mt-1">${c.country}</div>
|
|
</td>
|
|
<td class="px-8 py-6">
|
|
<span class="px-2 py-1 rounded-lg bg-blue-500/10 text-blue-400 text-[10px] font-black uppercase tracking-wider">${c.category || 'General'}</span>
|
|
</td>
|
|
<td class="px-8 py-6 font-mono text-xs text-slate-400">
|
|
${parseFloat(c.latitude).toFixed(5)}, ${parseFloat(c.longitude).toFixed(5)}
|
|
</td>
|
|
<td class="px-8 py-6 text-sm text-slate-400 font-medium">
|
|
${c.submittedBy || 'System User'}
|
|
</td>
|
|
<td class="px-8 py-6 text-right space-x-2">
|
|
<button onclick="refinement.reject('${c.id}')" class="p-2.5 rounded-xl bg-red-500/10 text-red-400 hover:bg-red-500/20 transition-all" title="Reject">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
</button>
|
|
<button onclick="refinement.approve('${c.id}')" class="p-2.5 rounded-xl bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 transition-all border border-emerald-500/20 shadow-lg shadow-emerald-500/10" title="Approve">
|
|
<i data-lucide="check" class="w-4 h-4"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
`).join('');
|
|
|
|
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/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/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);
|
|
}
|
|
}
|
|
};
|