Added warning about lack of progress results, moved add to compare button
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m26s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m26s
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
venv
|
venv
|
||||||
|
backend/__pycache__
|
||||||
|
|||||||
@@ -118,6 +118,15 @@ const TERM_DEFINITIONS = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Warning definitions for alerts/notices
|
||||||
|
const WARNING_DEFINITIONS = {
|
||||||
|
progress_scores_unavailable: {
|
||||||
|
title: "Progress Scores Unavailable",
|
||||||
|
description:
|
||||||
|
"The DfE will not publish primary school progress measures for 2023-24 or 2024-25, as KS1 SATs were cancelled in 2020 and 2021.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an info trigger button for a term tooltip
|
* Creates an info trigger button for a term tooltip
|
||||||
* @param {string} termKey - Key from TERM_DEFINITIONS
|
* @param {string} termKey - Key from TERM_DEFINITIONS
|
||||||
@@ -132,6 +141,20 @@ function createInfoTrigger(termKey) {
|
|||||||
return `<button class="info-trigger" type="button" data-term="${termKey}" aria-label="${label}" aria-expanded="false"><svg class="info-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="8" cy="8" r="6.5"/><path d="M8 7v4"/><circle cx="8" cy="5" r="0.5" fill="currentColor" stroke="none"/></svg></button>`;
|
return `<button class="info-trigger" type="button" data-term="${termKey}" aria-label="${label}" aria-expanded="false"><svg class="info-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="8" cy="8" r="6.5"/><path d="M8 7v4"/><circle cx="8" cy="5" r="0.5" fill="currentColor" stroke="none"/></svg></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a warning trigger button for warning tooltips
|
||||||
|
* @param {string} warningKey - Key from WARNING_DEFINITIONS
|
||||||
|
* @returns {string} HTML string for the warning trigger
|
||||||
|
*/
|
||||||
|
function createWarningTrigger(warningKey) {
|
||||||
|
const definition = WARNING_DEFINITIONS[warningKey];
|
||||||
|
if (!definition) return "";
|
||||||
|
|
||||||
|
const label = definition.title;
|
||||||
|
|
||||||
|
return `<button class="warning-trigger" type="button" data-warning="${warningKey}" aria-label="${label}" aria-expanded="false"><svg class="warning-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M8 1.5L14.5 13H1.5L8 1.5z"/><path d="M8 6v3"/><circle cx="8" cy="11" r="0.5" fill="currentColor" stroke="none"/></svg></button>`;
|
||||||
|
}
|
||||||
|
|
||||||
// Map instances (stored to allow cleanup)
|
// Map instances (stored to allow cleanup)
|
||||||
const schoolMaps = new Map();
|
const schoolMaps = new Map();
|
||||||
|
|
||||||
@@ -1368,7 +1391,7 @@ async function openSchoolModal(urn) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-stats-section">
|
<div class="modal-stats-section">
|
||||||
<h4>Progress Scores</h4>
|
<h4>Progress Scores ${createWarningTrigger("progress_scores_unavailable")}</h4>
|
||||||
<div class="modal-stats-grid">
|
<div class="modal-stats-grid">
|
||||||
<div class="modal-stat">
|
<div class="modal-stat">
|
||||||
<div class="modal-stat-value ${getProgressClass(latest.reading_progress)}">${formatMetricValue(latest.reading_progress, "reading_progress")}</div>
|
<div class="modal-stat-value ${getProgressClass(latest.reading_progress)}">${formatMetricValue(latest.reading_progress, "reading_progress")}</div>
|
||||||
@@ -1908,7 +1931,7 @@ class TooltipManager {
|
|||||||
|
|
||||||
handleMouseEnter(e) {
|
handleMouseEnter(e) {
|
||||||
if (!e.target || !e.target.closest) return;
|
if (!e.target || !e.target.closest) return;
|
||||||
const trigger = e.target.closest(".info-trigger");
|
const trigger = e.target.closest(".info-trigger, .warning-trigger");
|
||||||
if (!trigger) return;
|
if (!trigger) return;
|
||||||
|
|
||||||
clearTimeout(this.hideTimeout);
|
clearTimeout(this.hideTimeout);
|
||||||
@@ -1919,7 +1942,7 @@ class TooltipManager {
|
|||||||
|
|
||||||
handleMouseLeave(e) {
|
handleMouseLeave(e) {
|
||||||
if (!e.target || !e.target.closest) return;
|
if (!e.target || !e.target.closest) return;
|
||||||
const trigger = e.target.closest(".info-trigger");
|
const trigger = e.target.closest(".info-trigger, .warning-trigger");
|
||||||
const tooltip = e.target.closest(".tooltip");
|
const tooltip = e.target.closest(".tooltip");
|
||||||
|
|
||||||
if (!trigger && !tooltip) return;
|
if (!trigger && !tooltip) return;
|
||||||
@@ -1927,7 +1950,7 @@ class TooltipManager {
|
|||||||
// Check if moving between trigger and tooltip
|
// Check if moving between trigger and tooltip
|
||||||
const relatedTarget = e.relatedTarget;
|
const relatedTarget = e.relatedTarget;
|
||||||
if (
|
if (
|
||||||
relatedTarget?.closest?.(".info-trigger") === this.activeTooltip ||
|
relatedTarget?.closest?.(".info-trigger, .warning-trigger") === this.activeTooltip ||
|
||||||
relatedTarget?.closest?.(".tooltip")
|
relatedTarget?.closest?.(".tooltip")
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -1941,7 +1964,7 @@ class TooltipManager {
|
|||||||
|
|
||||||
handleFocusIn(e) {
|
handleFocusIn(e) {
|
||||||
if (!e.target || !e.target.closest) return;
|
if (!e.target || !e.target.closest) return;
|
||||||
const trigger = e.target.closest(".info-trigger");
|
const trigger = e.target.closest(".info-trigger, .warning-trigger");
|
||||||
if (!trigger) return;
|
if (!trigger) return;
|
||||||
|
|
||||||
clearTimeout(this.hideTimeout);
|
clearTimeout(this.hideTimeout);
|
||||||
@@ -1950,7 +1973,7 @@ class TooltipManager {
|
|||||||
|
|
||||||
handleFocusOut(e) {
|
handleFocusOut(e) {
|
||||||
if (!e.target || !e.target.closest) return;
|
if (!e.target || !e.target.closest) return;
|
||||||
const trigger = e.target.closest(".info-trigger");
|
const trigger = e.target.closest(".info-trigger, .warning-trigger");
|
||||||
if (!trigger) return;
|
if (!trigger) return;
|
||||||
|
|
||||||
this.hideTimeout = setTimeout(() => {
|
this.hideTimeout = setTimeout(() => {
|
||||||
@@ -1959,7 +1982,7 @@ class TooltipManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleTouchClick(e) {
|
handleTouchClick(e) {
|
||||||
const trigger = e.target.closest(".info-trigger");
|
const trigger = e.target.closest(".info-trigger, .warning-trigger");
|
||||||
|
|
||||||
if (trigger) {
|
if (trigger) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -1980,12 +2003,26 @@ class TooltipManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show(trigger) {
|
show(trigger) {
|
||||||
const termKey = trigger.dataset.term;
|
// Check if it's an info trigger or warning trigger
|
||||||
const definition = TERM_DEFINITIONS[termKey];
|
const isWarning = trigger.classList.contains("warning-trigger");
|
||||||
|
let definition;
|
||||||
|
|
||||||
if (!definition) {
|
if (isWarning) {
|
||||||
console.warn(`No definition found for term: ${termKey}`);
|
const warningKey = trigger.dataset.warning;
|
||||||
return;
|
definition = WARNING_DEFINITIONS[warningKey];
|
||||||
|
if (!definition) {
|
||||||
|
console.warn(`No definition found for warning: ${warningKey}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tooltipEl.classList.add("tooltip-warning");
|
||||||
|
} else {
|
||||||
|
const termKey = trigger.dataset.term;
|
||||||
|
definition = TERM_DEFINITIONS[termKey];
|
||||||
|
if (!definition) {
|
||||||
|
console.warn(`No definition found for term: ${termKey}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tooltipEl.classList.remove("tooltip-warning");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build tooltip content
|
// Build tooltip content
|
||||||
|
|||||||
@@ -327,10 +327,10 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
<button class="btn btn-primary modal-compare-btn" id="add-to-compare">Add to Compare</button>
|
||||||
<h2 id="modal-school-name"></h2>
|
<h2 id="modal-school-name"></h2>
|
||||||
<div class="modal-meta" id="modal-meta"></div>
|
<div class="modal-meta" id="modal-meta"></div>
|
||||||
<div class="modal-details" id="modal-details"></div>
|
<div class="modal-details" id="modal-details"></div>
|
||||||
<button class="btn btn-primary" id="add-to-compare">Add to Compare</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="modal-chart-container">
|
<div class="modal-chart-container">
|
||||||
|
|||||||
@@ -1059,6 +1059,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
|
position: relative;
|
||||||
padding: 2rem 2rem 1rem;
|
padding: 2rem 2rem 1rem;
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1070,7 @@ body {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
padding-right: 3rem;
|
padding-right: 10rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-meta {
|
.modal-meta {
|
||||||
@@ -1088,8 +1089,10 @@ body {
|
|||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header .btn {
|
.modal-compare-btn {
|
||||||
margin-top: 1rem;
|
position: absolute;
|
||||||
|
top: 2rem;
|
||||||
|
right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-details .modal-age-range {
|
.modal-details .modal-age-range {
|
||||||
@@ -1517,6 +1520,62 @@ body {
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Warning Trigger Button */
|
||||||
|
.warning-trigger {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: help;
|
||||||
|
color: var(--accent-coral);
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: var(--transition);
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-trigger:hover,
|
||||||
|
.warning-trigger:focus {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-trigger:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px var(--accent-coral);
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-trigger:focus:not(:focus-visible) {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-trigger:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px var(--accent-coral);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warning Icon SVG */
|
||||||
|
.warning-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Warning Tooltip Styling */
|
||||||
|
.tooltip.tooltip-warning {
|
||||||
|
background: #8b4513;
|
||||||
|
border-left: 3px solid var(--accent-coral);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip-warning[data-placement="top"]::after {
|
||||||
|
border-top-color: #8b4513;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip-warning[data-placement="bottom"]::after {
|
||||||
|
border-bottom-color: #8b4513;
|
||||||
|
}
|
||||||
|
|
||||||
/* Label wrapper for inline icon */
|
/* Label wrapper for inline icon */
|
||||||
.stat-label-with-info {
|
.stat-label-with-info {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user