fix(roads): fix road approval & deletion persistence, add robust endpoints, delete candidate from DB upon reject/delete, and add clear all pending button

This commit is contained in:
Hamza-Ayed
2026-08-19 16:29:24 +03:00
parent 3d13d31101
commit b98ce56622
2 changed files with 193 additions and 157 deletions
+62 -11
View File
@@ -413,20 +413,49 @@ const IntelligenceDashboard: React.FC = () => {
method: 'PATCH',
headers: { 'x-api-key': apiKey }
});
if (a === 'approve') {
const d = await res.json().catch(() => ({} as any));
setMsg(d.connected ? '✅ Approved & wired into the network.' : '✅ Approved — drawn on the map.');
candsRef.current = candsRef.current.map(c => c.id === id ? { ...c, status: 'approved' } : c);
pushMapData();
} else {
setMsg('🚫 Rejected.');
candsRef.current = candsRef.current.filter(c => c.id !== id);
pushMapData();
if (!res.ok) {
const errData = await res.json().catch(() => ({}));
throw new Error(errData.message || `HTTP ${res.status}`);
}
const d = await res.json().catch(() => ({} as any));
if (a === 'approve') {
setMsg(d.connected ? '✅ Approved & wired into the network.' : '✅ Approved — published on the map.');
} else {
setMsg('🚫 Rejected & removed.');
}
// Immediately remove from pending candidate list in UI
setCands(prev => prev.filter(c => c.id !== id));
candsRef.current = candsRef.current.filter(c => c.id !== id);
pushMapData();
setSel(null);
(leftMapRef.current?.getSource('hl') as maplibregl.GeoJSONSource)?.setData({ type: 'FeatureCollection', features: [] });
load();
} catch { setMsg('Action failed.'); }
} catch (e: any) {
setMsg(`Action failed: ${e?.message || 'Server error'}`);
}
setTimeout(() => setMsg(''), 5000);
};
const clearAllCandidates = async () => {
if (!window.confirm('Are you sure you want to clear and delete all pending candidate roads?')) return;
try {
const res = await fetch(`${API}/map-refinement/roads/candidates?status=pending`, {
method: 'DELETE',
headers: { 'x-api-key': apiKey }
});
if (!res.ok) throw new Error('Failed to clear candidates');
const data = await res.json().catch(() => ({}));
setMsg(`🧹 Cleared ${data.deletedCount ?? ''} candidate roads.`);
setCands([]);
candsRef.current = [];
pushMapData();
load();
} catch (e: any) {
setMsg(`Clear failed: ${e?.message || 'Server error'}`);
}
setTimeout(() => setMsg(''), 5000);
};
@@ -438,10 +467,12 @@ const IntelligenceDashboard: React.FC = () => {
});
if (!res.ok) throw new Error('Failed to delete');
setMsg(`🗑️ Deleted ${type} road successfully.`);
setCands(prev => prev.filter(c => c.id !== id));
candsRef.current = candsRef.current.filter(c => c.id !== id);
pushMapData();
setSel(null);
(leftMapRef.current?.getSource('hl') as maplibregl.GeoJSONSource)?.setData({ type: 'FeatureCollection', features: [] });
load();
// Force MapLibre to drop cached vector tiles completely for approved roads
if (type === 'approved') {
setTimeout(() => window.location.reload(), 1500);
}
@@ -611,6 +642,26 @@ const IntelligenceDashboard: React.FC = () => {
<div style={{ flex: 1, overflowY: 'auto', padding: '0.8rem' }}>
{tab === 'candidates' && (
<div style={{ display: 'grid', gap: '8px' }}>
{cands.length > 0 && (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
<span style={{ fontSize: '0.72rem', color: '#94a3b8' }}>Pending ({cands.length})</span>
<button
onClick={clearAllCandidates}
style={{
background: 'rgba(239, 68, 68, 0.12)',
border: '1px solid rgba(239, 68, 68, 0.3)',
color: '#f87171',
padding: '4px 10px',
borderRadius: '6px',
fontSize: '0.68rem',
fontWeight: 700,
cursor: 'pointer'
}}
>
🗑️ Clear All ({cands.length})
</button>
</div>
)}
{cands.length === 0
? <div style={{ textAlign: 'center', padding: '3rem 1rem', color: '#475569' }}>
<Activity size={36} style={{ opacity: 0.2, display: 'block', margin: '0 auto 0.8rem' }} />