fix(map): add effect deps, escape HTML in popup, document Att8 delta threshold
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 24s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 53s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 24s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 53s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 13s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
This commit is contained in:
@@ -33,6 +33,10 @@ interface LeafletMapInnerProps {
|
||||
// Popup helpers (must work in plain JS string templates — no React / CSS Modules)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function escapeHtml(s: string): string {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
interface PopupBadge {
|
||||
label: string;
|
||||
style: string;
|
||||
@@ -135,8 +139,10 @@ export default function LeafletMapInner({ schools, center, zoom, referencePoint,
|
||||
if (laAvg != null) {
|
||||
const diff = Math.round((score - laAvg) * 10) / 10;
|
||||
const sign = diff >= 0 ? '+' : '';
|
||||
// Att8 scores range 0–90 in 0.1 increments; ±0.5 is meaningful here
|
||||
// vs primary RWM % where ±2 pts is the threshold
|
||||
const colour = diff >= 0.5 ? '#2d7d7d' : diff <= -0.5 ? '#e07256' : '#8a847a';
|
||||
const laName = school.local_authority ?? 'LA';
|
||||
const laName = escapeHtml(school.local_authority ?? 'LA');
|
||||
deltaLine = `<div style="font-size:11px;font-weight:600;color:${colour}">${sign}${diff} vs ${laName} avg</div>`;
|
||||
}
|
||||
metricHtml = `<div style="margin-bottom:4px">
|
||||
@@ -167,11 +173,11 @@ export default function LeafletMapInner({ schools, center, zoom, referencePoint,
|
||||
|
||||
const popupContent = `<div style="font-family:system-ui,sans-serif;min-width:240px;max-width:280px;padding:0">
|
||||
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:8px;margin-bottom:6px">
|
||||
<strong style="font-size:13px;color:#1a1612;line-height:1.3">${school.school_name}</strong>
|
||||
<strong style="font-size:13px;color:#1a1612;line-height:1.3">${escapeHtml(school.school_name)}</strong>
|
||||
<span style="font-size:10px;font-weight:700;padding:2px 6px;border-radius:3px;white-space:nowrap;flex-shrink:0;${badge.style}">${badge.label}</span>
|
||||
</div>
|
||||
<div style="font-size:11px;color:#8a847a;margin-bottom:8px">
|
||||
${phaseLabel}${school.local_authority ? ` · ${school.local_authority}` : ''}${distanceStr}
|
||||
${phaseLabel}${school.local_authority ? ` · ${escapeHtml(school.local_authority)}` : ''}${distanceStr}
|
||||
</div>
|
||||
${metricHtml}
|
||||
<a href="${slug}" style="display:block;text-align:center;padding:6px;background:#2d7d7d;color:white;border-radius:5px;text-decoration:none;font-size:12px;font-weight:600;margin-top:8px">View Details →</a>
|
||||
@@ -201,7 +207,7 @@ export default function LeafletMapInner({ schools, center, zoom, referencePoint,
|
||||
return () => {
|
||||
// Don't destroy map on every update, just clean markers
|
||||
};
|
||||
}, [schools, center, zoom, referencePoint, onMarkerClick]);
|
||||
}, [schools, center, zoom, referencePoint, onMarkerClick, nationalAvgRwm, laAverages]);
|
||||
|
||||
// Cleanup map on unmount
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user