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)
|
// 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 {
|
interface PopupBadge {
|
||||||
label: string;
|
label: string;
|
||||||
style: string;
|
style: string;
|
||||||
@@ -135,8 +139,10 @@ export default function LeafletMapInner({ schools, center, zoom, referencePoint,
|
|||||||
if (laAvg != null) {
|
if (laAvg != null) {
|
||||||
const diff = Math.round((score - laAvg) * 10) / 10;
|
const diff = Math.round((score - laAvg) * 10) / 10;
|
||||||
const sign = diff >= 0 ? '+' : '';
|
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 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>`;
|
deltaLine = `<div style="font-size:11px;font-weight:600;color:${colour}">${sign}${diff} vs ${laName} avg</div>`;
|
||||||
}
|
}
|
||||||
metricHtml = `<div style="margin-bottom:4px">
|
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">
|
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">
|
<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>
|
<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>
|
||||||
<div style="font-size:11px;color:#8a847a;margin-bottom:8px">
|
<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>
|
</div>
|
||||||
${metricHtml}
|
${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>
|
<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 () => {
|
return () => {
|
||||||
// Don't destroy map on every update, just clean markers
|
// 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
|
// Cleanup map on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user