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:
@@ -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' }} />
|
||||
|
||||
Reference in New Issue
Block a user