2026-04-16-1

This commit is contained in:
Hamza-Ayed
2026-04-16 03:39:49 +03:00
parent 3d61362602
commit 58f06eeba3
15 changed files with 579 additions and 106 deletions
+75 -10
View File
@@ -14,14 +14,36 @@ const refinement = {
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('/api/map-refinement/candidates?status=PENDING', { headers });
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 = `<tr><td colspan="5" class="py-20 text-center text-red-400 font-bold">
API Error ${res.status}: ${errorText.substring(0, 100)}
</td></tr>`;
}
}
} catch (error) {
console.error('Failed to fetch candidates', error);
console.error('❌ [PlaceAudit] Network Error:', error);
if (tbody) {
tbody.innerHTML = `<tr><td colspan="5" class="py-20 text-center text-red-400 font-bold">
Network Error: ${error.message}
</td></tr>`;
}
}
},
@@ -48,18 +70,19 @@ const refinement = {
<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>
<div class="text-[10px] text-blue-400 font-extrabold uppercase mt-1">ID: ${c.id}</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 class="px-8 py-6">
<div class="text-sm text-slate-300 font-medium">${c.governorate_name || 'Unknown Region'}</div>
<div class="text-[11px] text-slate-500 mt-1">${c.neighborhood_name || 'Unknown Neighborhood'}</div>
</td>
<td class="px-8 py-6 text-right space-x-2">
<button onclick="refinement.showMapModal('${c.latitude}', '${c.longitude}', '${c.name_ar || c.name}')" class="p-2.5 rounded-xl bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 transition-all" title="View on Google Maps">
<i data-lucide="eye" class="w-4 h-4"></i>
</button>
<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>
@@ -73,12 +96,54 @@ const refinement = {
lucide.createIcons();
},
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 = `
<div class="bg-[#101014] border border-white/10 rounded-[2.5rem] w-full max-w-5xl overflow-hidden shadow-2xl animate-in zoom-in-95 duration-300">
<div class="px-8 py-6 border-b border-white/5 flex justify-between items-center bg-white/[0.02]">
<div>
<h3 class="text-xl font-bold text-white">${name}</h3>
<p class="text-xs text-slate-500 font-mono mt-1">${lat}, ${lng}</p>
</div>
<button onclick="document.getElementById('map-preview-modal').remove()" class="p-2 hover:bg-white/10 rounded-full transition-colors">
<i data-lucide="x" class="w-6 h-6 text-slate-400"></i>
</button>
</div>
<div class="aspect-video w-full bg-slate-900">
<iframe
width="100%"
height="100%"
frameborder="0"
scrolling="no"
marginheight="0"
marginwidth="0"
src="https://maps.google.com/maps?q=${lat},${lng}&t=k&z=19&ie=UTF8&iwloc=&output=embed">
</iframe>
</div>
<div class="px-8 py-6 bg-white/[0.01] flex justify-between items-center">
<p class="text-xs text-slate-500 font-medium">Verify location visual details against satellite imagery</p>
<a href="https://www.google.com/maps/search/?api=1&query=${lat},${lng}" target="_blank" class="px-4 py-2 bg-blue-600/10 text-blue-400 rounded-lg text-xs font-bold hover:bg-blue-600/20 transition-all border border-blue-500/10">
Open in Full Google Maps
</a>
</div>
</div>
`;
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/candidates/${id}/approve`, {
const res = await fetch(`/api/map-refinement/places/candidates/${id}/approve`, {
method: 'PATCH',
headers
});
@@ -98,7 +163,7 @@ const refinement = {
const headers = auth.getAuthHeader();
try {
const res = await fetch(`/api/map-refinement/candidates/${id}/reject`, {
const res = await fetch(`/api/map-refinement/places/candidates/${id}/reject`, {
method: 'PATCH',
headers: {
...headers,