refactor(detail): align secondary school page with primary — scroll-to-section nav
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 32s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m10s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 32s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
All checks were successful
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 32s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 1m10s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 32s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s
Replace tab-based show/hide with always-visible sections and anchor link navigation, matching the primary school detail page behaviour. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabBtn:hover {
|
.tabBtn:hover {
|
||||||
@@ -206,24 +207,6 @@
|
|||||||
color: var(--text-primary, #1a1612);
|
color: var(--text-primary, #1a1612);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabBtnActive {
|
|
||||||
background: var(--accent-coral, #e07256);
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabBtnActive:hover {
|
|
||||||
background: var(--accent-coral-dark, #c45a3f);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Tab content area ────────────────────────────────── */
|
|
||||||
.tabContent {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0; /* cards have their own margin-bottom */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Card ────────────────────────────────────────────── */
|
/* ── Card ────────────────────────────────────────────── */
|
||||||
.card {
|
.card {
|
||||||
background: var(--bg-card, white);
|
background: var(--bg-card, white);
|
||||||
@@ -232,6 +215,7 @@
|
|||||||
padding: 1.25rem 1.5rem;
|
padding: 1.25rem 1.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
box-shadow: var(--shadow-soft);
|
box-shadow: var(--shadow-soft);
|
||||||
|
scroll-margin-top: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Section Title ───────────────────────────────────── */
|
/* ── Section Title ───────────────────────────────────── */
|
||||||
@@ -581,22 +565,6 @@
|
|||||||
color: var(--text-primary, #1a1612);
|
color: var(--text-primary, #1a1612);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab link (inline button styled as link) */
|
|
||||||
.tabLink {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--accent-teal, #2d7d7d);
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabLink:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Admissions ──────────────────────────────────────── */
|
/* ── Admissions ──────────────────────────────────────── */
|
||||||
.admissionsTypeBadge {
|
.admissionsTypeBadge {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* SecondarySchoolDetailView Component
|
* SecondarySchoolDetailView Component
|
||||||
* Dedicated detail view for secondary schools with tabbed navigation:
|
* Dedicated detail view for secondary schools with scroll-to-section navigation.
|
||||||
* Overview | Academic | Admissions | Wellbeing | Parents | Finance
|
* All sections render at once; the sticky nav scrolls to each.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
@@ -39,7 +39,18 @@ const RC_CATEGORIES = [
|
|||||||
{ key: 'rc_sixth_form' as const, label: 'Sixth Form' },
|
{ key: 'rc_sixth_form' as const, label: 'Sixth Form' },
|
||||||
];
|
];
|
||||||
|
|
||||||
type Tab = 'overview' | 'academic' | 'admissions' | 'wellbeing' | 'parents' | 'finance';
|
function progressClass(val: number | null | undefined, modStyles: Record<string, string>): string {
|
||||||
|
if (val == null) return '';
|
||||||
|
if (val > 0) return modStyles.progressPositive;
|
||||||
|
if (val < 0) return modStyles.progressNegative;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function deprivationDesc(decile: number): string {
|
||||||
|
if (decile <= 3) return `This school is in one of England's most deprived areas (decile ${decile}/10). Many pupils may face additional challenges at home.`;
|
||||||
|
if (decile <= 7) return `This school is in an area with average levels of deprivation (decile ${decile}/10).`;
|
||||||
|
return `This school is in one of England's less deprived areas (decile ${decile}/10).`;
|
||||||
|
}
|
||||||
|
|
||||||
interface SecondarySchoolDetailViewProps {
|
interface SecondarySchoolDetailViewProps {
|
||||||
schoolInfo: School;
|
schoolInfo: School;
|
||||||
@@ -55,19 +66,6 @@ interface SecondarySchoolDetailViewProps {
|
|||||||
finance: SchoolFinance | null;
|
finance: SchoolFinance | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function progressClass(val: number | null | undefined, modStyles: Record<string, string>): string {
|
|
||||||
if (val == null) return '';
|
|
||||||
if (val > 0) return modStyles.progressPositive;
|
|
||||||
if (val < 0) return modStyles.progressNegative;
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function deprivationDesc(decile: number): string {
|
|
||||||
if (decile <= 3) return `This school is in one of England's most deprived areas (decile ${decile}/10). Many pupils may face additional challenges at home.`;
|
|
||||||
if (decile <= 7) return `This school is in an area with average levels of deprivation (decile ${decile}/10).`;
|
|
||||||
return `This school is in one of England's less deprived areas (decile ${decile}/10).`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SecondarySchoolDetailView({
|
export function SecondarySchoolDetailView({
|
||||||
schoolInfo, yearlyData,
|
schoolInfo, yearlyData,
|
||||||
ofsted, parentView, admissions, senDetail, deprivation, finance, absenceData,
|
ofsted, parentView, admissions, senDetail, deprivation, finance, absenceData,
|
||||||
@@ -78,7 +76,6 @@ export function SecondarySchoolDetailView({
|
|||||||
|
|
||||||
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
|
const latestResults = yearlyData.length > 0 ? yearlyData[yearlyData.length - 1] : null;
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<Tab>('overview');
|
|
||||||
const [nationalAvg, setNationalAvg] = useState<NationalAverages | null>(null);
|
const [nationalAvg, setNationalAvg] = useState<NationalAverages | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -92,9 +89,11 @@ export function SecondarySchoolDetailView({
|
|||||||
|
|
||||||
const hasSixthForm = schoolInfo.age_range?.includes('18') ?? false;
|
const hasSixthForm = schoolInfo.age_range?.includes('18') ?? false;
|
||||||
const hasFinance = finance != null && finance.per_pupil_spend != null;
|
const hasFinance = finance != null && finance.per_pupil_spend != null;
|
||||||
const hasParents = parentView != null || ofsted != null;
|
const hasParents = parentView != null && parentView.total_responses != null && parentView.total_responses > 0;
|
||||||
const hasDeprivation = deprivation != null && deprivation.idaci_decile != null;
|
const hasDeprivation = deprivation != null && deprivation.idaci_decile != null;
|
||||||
|
const hasWellbeing = (latestResults?.sen_support_pct != null || latestResults?.sen_ehcp_pct != null) || hasDeprivation;
|
||||||
const p8Suspended = latestResults != null && latestResults.year >= 202425;
|
const p8Suspended = latestResults != null && latestResults.year >= 202425;
|
||||||
|
const hasResults = latestResults?.attainment_8_score != null;
|
||||||
|
|
||||||
const admissionsTag = (() => {
|
const admissionsTag = (() => {
|
||||||
const policy = schoolInfo.admissions_policy?.toLowerCase() ?? '';
|
const policy = schoolInfo.admissions_policy?.toLowerCase() ?? '';
|
||||||
@@ -104,15 +103,6 @@ export function SecondarySchoolDetailView({
|
|||||||
return null;
|
return null;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const tabs: { key: Tab; label: string }[] = [
|
|
||||||
{ key: 'overview', label: 'Overview' },
|
|
||||||
{ key: 'academic', label: 'Academic' },
|
|
||||||
{ key: 'admissions', label: 'Admissions' },
|
|
||||||
{ key: 'wellbeing', label: 'Wellbeing' },
|
|
||||||
...(hasParents ? [{ key: 'parents' as Tab, label: 'Parents' }] : []),
|
|
||||||
...(hasFinance ? [{ key: 'finance' as Tab, label: 'Finance' }] : []),
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleComparisonToggle = () => {
|
const handleComparisonToggle = () => {
|
||||||
if (isInComparison) {
|
if (isInComparison) {
|
||||||
removeSchool(schoolInfo.urn);
|
removeSchool(schoolInfo.urn);
|
||||||
@@ -121,6 +111,16 @@ export function SecondarySchoolDetailView({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Build nav items dynamically based on available data
|
||||||
|
const navItems: { id: string; label: string }[] = [];
|
||||||
|
if (ofsted) navItems.push({ id: 'ofsted', label: 'Ofsted' });
|
||||||
|
if (hasParents) navItems.push({ id: 'parents', label: 'Parents' });
|
||||||
|
if (hasResults) navItems.push({ id: 'gcse', label: 'GCSEs' });
|
||||||
|
if (admissions) navItems.push({ id: 'admissions', label: 'Admissions' });
|
||||||
|
if (hasWellbeing) navItems.push({ id: 'wellbeing', label: 'Wellbeing' });
|
||||||
|
if (hasFinance) navItems.push({ id: 'finances', label: 'Finances' });
|
||||||
|
if (yearlyData.length > 1) navItems.push({ id: 'history', label: 'History' });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{/* ── Header ─────────────────────────────────────── */}
|
{/* ── Header ─────────────────────────────────────── */}
|
||||||
@@ -189,34 +189,25 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* ── Tab navigation (sticky) ─────────────────────── */}
|
{/* ── Sticky section navigation ─────────────────────── */}
|
||||||
<nav className={styles.tabNav} aria-label="Detail sections">
|
<nav className={styles.tabNav} aria-label="Page sections">
|
||||||
<div className={styles.tabNavInner}>
|
<div className={styles.tabNavInner}>
|
||||||
<button onClick={() => router.back()} className={styles.backBtn}>← Back</button>
|
<button onClick={() => router.back()} className={styles.backBtn}>← Back</button>
|
||||||
<div className={styles.tabNavDivider} />
|
{navItems.length > 0 && <div className={styles.tabNavDivider} />}
|
||||||
{tabs.map(({ key, label }) => (
|
{navItems.map(({ id, label }) => (
|
||||||
<button
|
<a key={id} href={`#${id}`} className={styles.tabBtn}>{label}</a>
|
||||||
key={key}
|
|
||||||
onClick={() => setActiveTab(key)}
|
|
||||||
className={`${styles.tabBtn} ${activeTab === key ? styles.tabBtnActive : ''}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* ── Overview Tab ─────────────────────────────────── */}
|
{/* ── Ofsted ─────────────────────────────────────── */}
|
||||||
{activeTab === 'overview' && (
|
|
||||||
<div className={styles.tabContent}>
|
|
||||||
{/* Ofsted summary card */}
|
|
||||||
{ofsted && (
|
{ofsted && (
|
||||||
<div className={styles.card}>
|
<section id="ofsted" className={styles.card}>
|
||||||
<h2 className={styles.sectionTitle}>
|
<h2 className={styles.sectionTitle}>
|
||||||
{ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'}
|
{ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'}
|
||||||
{ofsted.inspection_date && (
|
{ofsted.inspection_date && (
|
||||||
<span className={styles.ofstedDate}>
|
<span className={styles.ofstedDate}>
|
||||||
{' '}Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { month: 'long', year: 'numeric' })}
|
{' '}Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<a
|
<a
|
||||||
@@ -229,15 +220,67 @@ export function SecondarySchoolDetailView({
|
|||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
{ofsted.framework === 'ReportCard' ? (
|
{ofsted.framework === 'ReportCard' ? (
|
||||||
<p className={styles.sectionSubtitle}>
|
<>
|
||||||
From November 2025, Ofsted replaced single overall grades with Report Cards.
|
<p className={styles.ofstedDisclaimer}>
|
||||||
|
From November 2025, Ofsted replaced single overall grades with Report Cards rating schools across several areas.
|
||||||
</p>
|
</p>
|
||||||
|
<div className={styles.metricsGrid}>
|
||||||
|
{ofsted.rc_safeguarding_met != null && (
|
||||||
|
<div className={styles.metricCard}>
|
||||||
|
<div className={styles.metricLabel}>Safeguarding</div>
|
||||||
|
<div className={`${styles.metricValue} ${ofsted.rc_safeguarding_met ? styles.safeguardingMet : styles.safeguardingNotMet}`}>
|
||||||
|
{ofsted.rc_safeguarding_met ? 'Met' : 'Not met'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{RC_CATEGORIES.filter(({ key }) => key !== 'rc_early_years' || ofsted[key] != null).map(({ key, label }) => {
|
||||||
|
const value = ofsted[key] as number | null;
|
||||||
|
return value != null ? (
|
||||||
|
<div key={key} className={styles.metricCard}>
|
||||||
|
<div className={styles.metricLabel}>{label}</div>
|
||||||
|
<div className={`${styles.metricValue} ${styles[`rcGrade${value}`]}`}>
|
||||||
|
{RC_LABELS[value]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
) : ofsted.overall_effectiveness ? (
|
) : ofsted.overall_effectiveness ? (
|
||||||
|
<>
|
||||||
<div className={styles.ofstedHeader}>
|
<div className={styles.ofstedHeader}>
|
||||||
<span className={`${styles.ofstedGrade} ${styles[`ofstedGrade${ofsted.overall_effectiveness}`]}`}>
|
<span className={`${styles.ofstedGrade} ${styles[`ofstedGrade${ofsted.overall_effectiveness}`]}`}>
|
||||||
{OFSTED_LABELS[ofsted.overall_effectiveness]}
|
{OFSTED_LABELS[ofsted.overall_effectiveness]}
|
||||||
</span>
|
</span>
|
||||||
|
{ofsted.previous_overall != null &&
|
||||||
|
ofsted.previous_overall !== ofsted.overall_effectiveness && (
|
||||||
|
<span className={styles.ofstedPrevious}>
|
||||||
|
Previously: {OFSTED_LABELS[ofsted.previous_overall]}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<p className={styles.ofstedDisclaimer}>
|
||||||
|
From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.
|
||||||
|
</p>
|
||||||
|
<div className={styles.metricsGrid}>
|
||||||
|
{[
|
||||||
|
{ label: 'Quality of Teaching', value: ofsted.quality_of_education },
|
||||||
|
{ label: 'Behaviour in School', value: ofsted.behaviour_attitudes },
|
||||||
|
{ label: 'Pupils\' Wider Development', value: ofsted.personal_development },
|
||||||
|
{ label: 'School Leadership', value: ofsted.leadership_management },
|
||||||
|
...(ofsted.early_years_provision != null
|
||||||
|
? [{ label: 'Early Years (Reception)', value: ofsted.early_years_provision }]
|
||||||
|
: []),
|
||||||
|
].map(({ label, value }) => value != null && (
|
||||||
|
<div key={label} className={styles.metricCard}>
|
||||||
|
<div className={styles.metricLabel}>{label}</div>
|
||||||
|
<div className={`${styles.metricValue} ${styles[`ofstedGrade${value}`]}`}>
|
||||||
|
{OFSTED_LABELS[value]}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p className={styles.sectionSubtitle}>
|
<p className={styles.sectionSubtitle}>
|
||||||
@@ -260,123 +303,37 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{parentView?.q_recommend_pct != null && parentView.total_responses != null && parentView.total_responses > 0 && (
|
{hasParents && (
|
||||||
<p className={styles.parentRecommendLine}>
|
<p className={styles.parentRecommendLine}>
|
||||||
<strong>{Math.round(parentView.q_recommend_pct)}%</strong> of parents would recommend this school ({parentView.total_responses.toLocaleString()} responses)
|
<strong>{Math.round(parentView!.q_recommend_pct!)}%</strong> of parents would recommend this school ({parentView!.total_responses!.toLocaleString()} responses)
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Attainment 8 headline */}
|
{/* ── Parent View ────────────────────────────────── */}
|
||||||
{latestResults?.attainment_8_score != null && (
|
{hasParents && parentView && (
|
||||||
<div className={styles.card}>
|
<section id="parents" className={styles.card}>
|
||||||
<h2 className={styles.sectionTitle}>
|
<h2 className={styles.sectionTitle}>
|
||||||
GCSE Performance at a Glance ({formatAcademicYear(latestResults.year)})
|
What Parents Say
|
||||||
|
<span className={styles.responseBadge}>
|
||||||
|
{parentView.total_responses!.toLocaleString()} responses
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<div className={styles.metricsGrid}>
|
<p className={styles.sectionSubtitle}>
|
||||||
<div className={styles.metricCard}>
|
From the Ofsted Parent View survey — parents share their experience of this school.
|
||||||
<div className={styles.metricLabel}>Attainment 8</div>
|
</p>
|
||||||
<div className={styles.metricValue}>{latestResults.attainment_8_score.toFixed(1)}</div>
|
|
||||||
{secondaryAvg.attainment_8_score != null && (
|
|
||||||
<div className={styles.metricHint}>National avg: {secondaryAvg.attainment_8_score.toFixed(1)}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{latestResults.english_maths_standard_pass_pct != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>English & Maths Grade 4+</div>
|
|
||||||
<div className={styles.metricValue}>{formatPercentage(latestResults.english_maths_standard_pass_pct)}</div>
|
|
||||||
{secondaryAvg.english_maths_standard_pass_pct != null && (
|
|
||||||
<div className={styles.metricHint}>National avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{admissions?.oversubscribed != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>Admissions</div>
|
|
||||||
<div className={`${styles.metricValue} ${admissions.oversubscribed ? styles.statusWarn : styles.statusGood}`}>
|
|
||||||
{admissions.oversubscribed ? 'Oversubscribed' : 'Places available'}
|
|
||||||
</div>
|
|
||||||
<div className={styles.metricHint}>Last year</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{p8Suspended && (
|
|
||||||
<div className={styles.p8Banner}>
|
|
||||||
Progress 8 scores for 2024/25 are not used for accountability purposes following the KS2 assessment disruption. Treat with caution.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Admissions summary */}
|
|
||||||
{admissions && (admissions.published_admission_number != null || admissions.total_applications != null) && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>Admissions at a Glance</h2>
|
|
||||||
<div className={styles.metricsGrid}>
|
|
||||||
{admissions.published_admission_number != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>Year 7 places (PAN)</div>
|
|
||||||
<div className={styles.metricValue}>{admissions.published_admission_number}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{admissions.total_applications != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>Total applications</div>
|
|
||||||
<div className={styles.metricValue}>{admissions.total_applications.toLocaleString()}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{admissions.first_preference_offer_pct != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>1st choice offer rate</div>
|
|
||||||
<div className={styles.metricValue}>{formatPercentage(admissions.first_preference_offer_pct)}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<button onClick={() => setActiveTab('admissions')} className={styles.tabLink}>
|
|
||||||
Full admissions details →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* SEN & school context summary */}
|
|
||||||
{(latestResults?.sen_support_pct != null || latestResults?.sen_ehcp_pct != null || latestResults?.total_pupils != null) && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>School Context</h2>
|
|
||||||
<div className={styles.metricsGrid}>
|
|
||||||
{latestResults?.total_pupils != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>Total pupils</div>
|
|
||||||
<div className={styles.metricValue}>{latestResults.total_pupils.toLocaleString()}</div>
|
|
||||||
{schoolInfo.capacity != null && (
|
|
||||||
<div className={styles.metricHint}>Capacity: {schoolInfo.capacity}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{latestResults?.sen_support_pct != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>SEN support</div>
|
|
||||||
<div className={styles.metricValue}>{formatPercentage(latestResults.sen_support_pct)}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{latestResults?.sen_ehcp_pct != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>EHCP</div>
|
|
||||||
<div className={styles.metricValue}>{formatPercentage(latestResults.sen_ehcp_pct)}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Top Parent View scores */}
|
|
||||||
{parentView != null && parentView.total_responses != null && parentView.total_responses > 0 && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>What Parents Say</h2>
|
|
||||||
<div className={styles.parentViewGrid}>
|
<div className={styles.parentViewGrid}>
|
||||||
{[
|
{[
|
||||||
{ label: 'My child is happy here', pct: parentView.q_happy_pct },
|
|
||||||
{ label: 'Would recommend this school', pct: parentView.q_recommend_pct },
|
{ label: 'Would recommend this school', pct: parentView.q_recommend_pct },
|
||||||
|
{ label: 'My child is happy here', pct: parentView.q_happy_pct },
|
||||||
|
{ label: 'My child feels safe here', pct: parentView.q_safe_pct },
|
||||||
|
{ label: 'Teaching is good', pct: parentView.q_teaching_pct },
|
||||||
|
{ label: 'My child makes good progress', pct: parentView.q_progress_pct },
|
||||||
|
{ label: 'School looks after pupils\' wellbeing', pct: parentView.q_wellbeing_pct },
|
||||||
|
{ label: 'Behaviour is well managed', pct: parentView.q_behaviour_pct },
|
||||||
|
{ label: 'School deals well with bullying', pct: parentView.q_bullying_pct },
|
||||||
|
{ label: 'Communicates well with parents', pct: parentView.q_communication_pct },
|
||||||
].filter(q => q.pct != null).map(({ label, pct }) => (
|
].filter(q => q.pct != null).map(({ label, pct }) => (
|
||||||
<div key={label} className={styles.parentViewRow}>
|
<div key={label} className={styles.parentViewRow}>
|
||||||
<span className={styles.parentViewLabel}>{label}</span>
|
<span className={styles.parentViewLabel}>{label}</span>
|
||||||
@@ -387,18 +344,12 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => setActiveTab('parents')} className={styles.tabLink}>
|
</section>
|
||||||
See all parent feedback →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Academic Tab ─────────────────────────────────── */}
|
{/* ── GCSE Results ───────────────────────────────── */}
|
||||||
{activeTab === 'academic' && latestResults && (
|
{hasResults && latestResults && (
|
||||||
<div className={styles.tabContent}>
|
<section id="gcse" className={styles.card}>
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>
|
<h2 className={styles.sectionTitle}>
|
||||||
GCSE Results ({formatAcademicYear(latestResults.year)})
|
GCSE Results ({formatAcademicYear(latestResults.year)})
|
||||||
</h2>
|
</h2>
|
||||||
@@ -467,7 +418,7 @@ export function SecondarySchoolDetailView({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* A8 component breakdown */}
|
{/* Progress 8 component breakdown */}
|
||||||
{(latestResults.progress_8_english != null || latestResults.progress_8_maths != null ||
|
{(latestResults.progress_8_english != null || latestResults.progress_8_maths != null ||
|
||||||
latestResults.progress_8_ebacc != null || latestResults.progress_8_open != null) && (
|
latestResults.progress_8_ebacc != null || latestResults.progress_8_open != null) && (
|
||||||
<>
|
<>
|
||||||
@@ -525,12 +476,11 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Performance over time */}
|
{/* Performance chart */}
|
||||||
{yearlyData.length > 0 && (
|
{yearlyData.length > 0 && (
|
||||||
<div className={styles.card}>
|
<>
|
||||||
<h2 className={styles.sectionTitle}>Results Over Time</h2>
|
<h3 className={styles.subSectionTitle} style={{ marginTop: '1.25rem' }}>Results Over Time</h3>
|
||||||
<div className={styles.chartContainer}>
|
<div className={styles.chartContainer}>
|
||||||
<PerformanceChart
|
<PerformanceChart
|
||||||
data={yearlyData}
|
data={yearlyData}
|
||||||
@@ -538,44 +488,14 @@ export function SecondarySchoolDetailView({
|
|||||||
isSecondary={true}
|
isSecondary={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{yearlyData.length > 1 && (
|
|
||||||
<>
|
|
||||||
<p className={styles.historicalSubtitle}>Detailed year-by-year figures</p>
|
|
||||||
<div className={styles.tableWrapper}>
|
|
||||||
<table className={styles.dataTable}>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Year</th>
|
|
||||||
<th>Attainment 8</th>
|
|
||||||
<th>Progress 8</th>
|
|
||||||
<th>Eng & Maths 4+</th>
|
|
||||||
<th>EBacc entry %</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{yearlyData.map((result) => (
|
|
||||||
<tr key={result.year}>
|
|
||||||
<td className={styles.yearCell}>{formatAcademicYear(result.year)}</td>
|
|
||||||
<td>{result.attainment_8_score != null ? result.attainment_8_score.toFixed(1) : '-'}</td>
|
|
||||||
<td>{result.progress_8_score != null ? formatProgress(result.progress_8_score) : '-'}</td>
|
|
||||||
<td>{result.english_maths_standard_pass_pct != null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'}</td>
|
|
||||||
<td>{result.ebacc_entry_pct != null ? formatPercentage(result.ebacc_entry_pct) : '-'}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</section>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Admissions Tab ───────────────────────────────── */}
|
{/* ── Admissions ─────────────────────────────────── */}
|
||||||
{activeTab === 'admissions' && (
|
{admissions && (
|
||||||
<div className={styles.tabContent}>
|
<section id="admissions" className={styles.card}>
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>Admissions</h2>
|
<h2 className={styles.sectionTitle}>Admissions</h2>
|
||||||
|
|
||||||
{admissionsTag && (
|
{admissionsTag && (
|
||||||
@@ -587,8 +507,6 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{admissions ? (
|
|
||||||
<>
|
|
||||||
<div className={styles.metricsGrid}>
|
<div className={styles.metricsGrid}>
|
||||||
{admissions.published_admission_number != null && (
|
{admissions.published_admission_number != null && (
|
||||||
<div className={styles.metricCard}>
|
<div className={styles.metricCard}>
|
||||||
@@ -622,30 +540,24 @@ export function SecondarySchoolDetailView({
|
|||||||
: '✓ Places were available last year'}
|
: '✓ Places were available last year'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className={styles.sectionSubtitle} style={{ marginTop: '1rem' }}>
|
|
||||||
Historical distance cut-off data is not available for this school. Contact the admissions authority for oversubscription criteria details.
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className={styles.sectionSubtitle}>Admissions data for this school is not yet available.</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hasSixthForm && (
|
{hasSixthForm && (
|
||||||
<div className={styles.sixthFormNote}>
|
<div className={styles.sixthFormNote}>
|
||||||
This school has a sixth form (Post-16 provision). Post-16 destination data coming soon.
|
This school has a sixth form (Post-16 provision). Post-16 destination data coming soon.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── Wellbeing Tab ────────────────────────────────── */}
|
{/* ── Wellbeing ──────────────────────────────────── */}
|
||||||
{activeTab === 'wellbeing' && (
|
{hasWellbeing && (
|
||||||
<div className={styles.tabContent}>
|
<section id="wellbeing" className={styles.card}>
|
||||||
|
<h2 className={styles.sectionTitle}>Wellbeing & Context</h2>
|
||||||
|
|
||||||
{/* SEN */}
|
{/* SEN */}
|
||||||
{(latestResults?.sen_support_pct != null || latestResults?.sen_ehcp_pct != null) && (
|
{(latestResults?.sen_support_pct != null || latestResults?.sen_ehcp_pct != null) && (
|
||||||
<div className={styles.card}>
|
<>
|
||||||
<h2 className={styles.sectionTitle}>Special Educational Needs (SEN)</h2>
|
<h3 className={styles.subSectionTitle}>Special Educational Needs (SEN)</h3>
|
||||||
<div className={styles.metricsGrid}>
|
<div className={styles.metricsGrid}>
|
||||||
{latestResults?.sen_support_pct != null && (
|
{latestResults?.sen_support_pct != null && (
|
||||||
<div className={styles.metricCard}>
|
<div className={styles.metricCard}>
|
||||||
@@ -671,19 +583,22 @@ export function SecondarySchoolDetailView({
|
|||||||
<div className={styles.metricCard}>
|
<div className={styles.metricCard}>
|
||||||
<div className={styles.metricLabel}>Total pupils</div>
|
<div className={styles.metricLabel}>Total pupils</div>
|
||||||
<div className={styles.metricValue}>{latestResults.total_pupils.toLocaleString()}</div>
|
<div className={styles.metricValue}>{latestResults.total_pupils.toLocaleString()}</div>
|
||||||
|
{schoolInfo.capacity != null && (
|
||||||
|
<div className={styles.metricHint}>Capacity: {schoolInfo.capacity}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Deprivation */}
|
{/* Deprivation */}
|
||||||
{hasDeprivation && deprivation && (
|
{hasDeprivation && deprivation && (
|
||||||
<div className={styles.card}>
|
<>
|
||||||
<h2 className={styles.sectionTitle}>
|
<h3 className={styles.subSectionTitle} style={{ marginTop: '1.25rem' }}>
|
||||||
Local Area Context
|
Local Area Context
|
||||||
<MetricTooltip metricKey="idaci_decile" />
|
<MetricTooltip metricKey="idaci_decile" />
|
||||||
</h2>
|
</h3>
|
||||||
<div className={styles.deprivationDots}>
|
<div className={styles.deprivationDots}>
|
||||||
{Array.from({ length: 10 }, (_, i) => (
|
{Array.from({ length: 10 }, (_, i) => (
|
||||||
<div
|
<div
|
||||||
@@ -698,153 +613,14 @@ export function SecondarySchoolDetailView({
|
|||||||
<span>Least deprived</span>
|
<span>Least deprived</span>
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.deprivationDesc}>{deprivationDesc(deprivation.idaci_decile!)}</p>
|
<p className={styles.deprivationDesc}>{deprivationDesc(deprivation.idaci_decile!)}</p>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{latestResults?.sen_support_pct == null && latestResults?.sen_ehcp_pct == null && !hasDeprivation && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<p className={styles.sectionSubtitle}>Wellbeing data is not yet available for this school.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── Parents Tab ─────────────────────────────────── */}
|
|
||||||
{activeTab === 'parents' && (
|
|
||||||
<div className={styles.tabContent}>
|
|
||||||
{/* Full Ofsted detail */}
|
|
||||||
{ofsted && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>
|
|
||||||
{ofsted.framework === 'ReportCard' ? 'Ofsted Report Card' : 'Ofsted Rating'}
|
|
||||||
{ofsted.inspection_date && (
|
|
||||||
<span className={styles.ofstedDate}>
|
|
||||||
{' '}Inspected {new Date(ofsted.inspection_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<a
|
|
||||||
href={`https://reports.ofsted.gov.uk/provider/21/${schoolInfo.urn}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={styles.ofstedReportLink}
|
|
||||||
>
|
|
||||||
Full report ↗
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
{ofsted.framework === 'ReportCard' ? (
|
|
||||||
<>
|
|
||||||
<p className={styles.ofstedDisclaimer}>
|
|
||||||
From November 2025, Ofsted replaced single overall grades with Report Cards rating schools across several areas.
|
|
||||||
</p>
|
|
||||||
<div className={styles.metricsGrid}>
|
|
||||||
{ofsted.rc_safeguarding_met != null && (
|
|
||||||
<div className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>Safeguarding</div>
|
|
||||||
<div className={`${styles.metricValue} ${ofsted.rc_safeguarding_met ? styles.safeguardingMet : styles.safeguardingNotMet}`}>
|
|
||||||
{ofsted.rc_safeguarding_met ? 'Met' : 'Not met'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{RC_CATEGORIES.filter(({ key }) => key !== 'rc_early_years' || ofsted[key] != null).map(({ key, label }) => {
|
|
||||||
const value = ofsted[key] as number | null;
|
|
||||||
return value != null ? (
|
|
||||||
<div key={key} className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>{label}</div>
|
|
||||||
<div className={`${styles.metricValue} ${styles[`rcGrade${value}`]}`}>
|
|
||||||
{RC_LABELS[value]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className={styles.ofstedHeader}>
|
|
||||||
<span className={`${styles.ofstedGrade} ${styles[`ofstedGrade${ofsted.overall_effectiveness}`]}`}>
|
|
||||||
{ofsted.overall_effectiveness ? OFSTED_LABELS[ofsted.overall_effectiveness] : 'Not rated'}
|
|
||||||
</span>
|
|
||||||
{ofsted.previous_overall != null &&
|
|
||||||
ofsted.previous_overall !== ofsted.overall_effectiveness && (
|
|
||||||
<span className={styles.ofstedPrevious}>
|
|
||||||
Previously: {OFSTED_LABELS[ofsted.previous_overall]}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className={styles.ofstedDisclaimer}>
|
|
||||||
From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.
|
|
||||||
</p>
|
|
||||||
<div className={styles.metricsGrid}>
|
|
||||||
{[
|
|
||||||
{ label: 'Quality of Teaching', value: ofsted.quality_of_education },
|
|
||||||
{ label: 'Behaviour in School', value: ofsted.behaviour_attitudes },
|
|
||||||
{ label: 'Pupils\' Wider Development', value: ofsted.personal_development },
|
|
||||||
{ label: 'School Leadership', value: ofsted.leadership_management },
|
|
||||||
...(ofsted.early_years_provision != null
|
|
||||||
? [{ label: 'Early Years (Reception)', value: ofsted.early_years_provision }]
|
|
||||||
: []),
|
|
||||||
].map(({ label, value }) => value != null && (
|
|
||||||
<div key={label} className={styles.metricCard}>
|
|
||||||
<div className={styles.metricLabel}>{label}</div>
|
|
||||||
<div className={`${styles.metricValue} ${styles[`ofstedGrade${value}`]}`}>
|
|
||||||
{OFSTED_LABELS[value]}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</section>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Parent View survey */}
|
{/* ── Finances ───────────────────────────────────── */}
|
||||||
{parentView && parentView.total_responses != null && parentView.total_responses > 0 && (
|
{hasFinance && finance && (
|
||||||
<div className={styles.card}>
|
<section id="finances" className={styles.card}>
|
||||||
<h2 className={styles.sectionTitle}>
|
|
||||||
What Parents Say
|
|
||||||
<span className={styles.responseBadge}>
|
|
||||||
{parentView.total_responses.toLocaleString()} responses
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<p className={styles.sectionSubtitle}>
|
|
||||||
From the Ofsted Parent View survey — parents share their experience of this school.
|
|
||||||
</p>
|
|
||||||
<div className={styles.parentViewGrid}>
|
|
||||||
{[
|
|
||||||
{ label: 'Would recommend this school', pct: parentView.q_recommend_pct },
|
|
||||||
{ label: 'My child is happy here', pct: parentView.q_happy_pct },
|
|
||||||
{ label: 'My child feels safe here', pct: parentView.q_safe_pct },
|
|
||||||
{ label: 'Teaching is good', pct: parentView.q_teaching_pct },
|
|
||||||
{ label: 'My child makes good progress', pct: parentView.q_progress_pct },
|
|
||||||
{ label: 'School looks after pupils\' wellbeing', pct: parentView.q_wellbeing_pct },
|
|
||||||
{ label: 'Behaviour is well managed', pct: parentView.q_behaviour_pct },
|
|
||||||
{ label: 'School deals well with bullying', pct: parentView.q_bullying_pct },
|
|
||||||
{ label: 'Communicates well with parents', pct: parentView.q_communication_pct },
|
|
||||||
].filter(q => q.pct != null).map(({ label, pct }) => (
|
|
||||||
<div key={label} className={styles.parentViewRow}>
|
|
||||||
<span className={styles.parentViewLabel}>{label}</span>
|
|
||||||
<div className={styles.parentViewBar}>
|
|
||||||
<div className={styles.parentViewFill} style={{ width: `${pct}%` }} />
|
|
||||||
</div>
|
|
||||||
<span className={styles.parentViewPct}>{Math.round(pct!)}%</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!ofsted && (!parentView || parentView.total_responses == null || parentView.total_responses === 0) && (
|
|
||||||
<div className={styles.card}>
|
|
||||||
<p className={styles.sectionSubtitle}>Parent and Ofsted data is not yet available for this school.</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── Finance Tab ─────────────────────────────────── */}
|
|
||||||
{activeTab === 'finance' && hasFinance && finance && (
|
|
||||||
<div className={styles.tabContent}>
|
|
||||||
<div className={styles.card}>
|
|
||||||
<h2 className={styles.sectionTitle}>School Finances ({formatAcademicYear(finance.year)})</h2>
|
<h2 className={styles.sectionTitle}>School Finances ({formatAcademicYear(finance.year)})</h2>
|
||||||
<p className={styles.sectionSubtitle}>
|
<p className={styles.sectionSubtitle}>
|
||||||
Per-pupil spending shows how much the school has to spend on each child's education.
|
Per-pupil spending shows how much the school has to spend on each child's education.
|
||||||
@@ -874,8 +650,38 @@ export function SecondarySchoolDetailView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ── History table ──────────────────────────────── */}
|
||||||
|
{yearlyData.length > 1 && (
|
||||||
|
<section id="history" className={styles.card}>
|
||||||
|
<h2 className={styles.sectionTitle}>Historical Results</h2>
|
||||||
|
<div className={styles.tableWrapper}>
|
||||||
|
<table className={styles.dataTable}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Year</th>
|
||||||
|
<th>Attainment 8</th>
|
||||||
|
<th>Progress 8</th>
|
||||||
|
<th>Eng & Maths 4+</th>
|
||||||
|
<th>EBacc entry %</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{yearlyData.map((result) => (
|
||||||
|
<tr key={result.year}>
|
||||||
|
<td className={styles.yearCell}>{formatAcademicYear(result.year)}</td>
|
||||||
|
<td>{result.attainment_8_score != null ? result.attainment_8_score.toFixed(1) : '-'}</td>
|
||||||
|
<td>{result.progress_8_score != null ? formatProgress(result.progress_8_score) : '-'}</td>
|
||||||
|
<td>{result.english_maths_standard_pass_pct != null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'}</td>
|
||||||
|
<td>{result.ebacc_entry_pct != null ? formatPercentage(result.ebacc_entry_pct) : '-'}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user