feat(utils): add buildOfstedListBadge helper and fetchNationalAverages
- Add ofsted_framework field to School type - Add OfstedListBadge interface and buildOfstedListBadge pure function to utils.ts - Add fetchNationalAverages API function that calls GET /api/national-averages - Add test suite for buildOfstedListBadge (all 6 new tests pass) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
calculateTrend,
|
calculateTrend,
|
||||||
isValidPostcode,
|
isValidPostcode,
|
||||||
debounce,
|
debounce,
|
||||||
|
buildOfstedListBadge,
|
||||||
} from '@/lib/utils';
|
} from '@/lib/utils';
|
||||||
|
|
||||||
describe('formatPercentage', () => {
|
describe('formatPercentage', () => {
|
||||||
@@ -102,3 +103,41 @@ describe('debounce', () => {
|
|||||||
|
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('buildOfstedListBadge', () => {
|
||||||
|
it('returns grade word + year for OEIF Outstanding', () => {
|
||||||
|
const badge = buildOfstedListBadge({ ofsted_grade: 1, ofsted_date: '2023-11-15', ofsted_framework: 'OEIF' });
|
||||||
|
expect(badge.label).toBe('Outstanding · 2023');
|
||||||
|
expect(badge.cssClass).toBe('ofsted1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns grade word for each OEIF grade', () => {
|
||||||
|
expect(buildOfstedListBadge({ ofsted_grade: 2, ofsted_date: '2022-05-01' }).label).toBe('Good · 2022');
|
||||||
|
expect(buildOfstedListBadge({ ofsted_grade: 3, ofsted_date: '2021-01-01' }).label).toBe('Req. Improvement · 2021');
|
||||||
|
expect(buildOfstedListBadge({ ofsted_grade: 4, ofsted_date: '2020-03-01' }).label).toBe('Inadequate · 2020');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns grade word without year when date is missing', () => {
|
||||||
|
const badge = buildOfstedListBadge({ ofsted_grade: 2, ofsted_date: null });
|
||||||
|
expect(badge.label).toBe('Good');
|
||||||
|
expect(badge.cssClass).toBe('ofsted2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns Report Card badge when framework is ReportCard', () => {
|
||||||
|
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: '2025-11-01', ofsted_framework: 'ReportCard' });
|
||||||
|
expect(badge.label).toBe('Report Card · 2025');
|
||||||
|
expect(badge.cssClass).toBe('ofstedRc');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns pending badge when no grade and no ReportCard framework', () => {
|
||||||
|
const badge = buildOfstedListBadge({ ofsted_grade: null, ofsted_date: null, ofsted_framework: null });
|
||||||
|
expect(badge.label).toBe('Not yet inspected');
|
||||||
|
expect(badge.cssClass).toBe('ofstedPending');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns pending badge when all fields are undefined', () => {
|
||||||
|
const badge = buildOfstedListBadge({});
|
||||||
|
expect(badge.label).toBe('Not yet inspected');
|
||||||
|
expect(badge.cssClass).toBe('ofstedPending');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type {
|
|||||||
RankingsParams,
|
RankingsParams,
|
||||||
APIError,
|
APIError,
|
||||||
LAaveragesResponse,
|
LAaveragesResponse,
|
||||||
|
NationalAverages,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -261,6 +262,26 @@ export async function fetchLAaverages(
|
|||||||
return handleResponse<LAaveragesResponse>(response);
|
return handleResponse<LAaveragesResponse>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch official DfE KS2 national averages (primary) and computed KS4 secondary averages.
|
||||||
|
* Returns latest year snapshot plus per-year history for chart reference lines.
|
||||||
|
*/
|
||||||
|
export async function fetchNationalAverages(
|
||||||
|
options: RequestInit = {}
|
||||||
|
): Promise<NationalAverages> {
|
||||||
|
const url = `${API_BASE_URL}/national-averages`;
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
next: {
|
||||||
|
revalidate: 3600,
|
||||||
|
...options.next,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return handleResponse<NationalAverages>(response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch database statistics and info
|
* Fetch database statistics and info
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export interface School {
|
|||||||
// Ofsted (for list view — summary only)
|
// Ofsted (for list view — summary only)
|
||||||
ofsted_grade?: 1 | 2 | 3 | 4 | null;
|
ofsted_grade?: 1 | 2 | 3 | 4 | null;
|
||||||
ofsted_date?: string | null;
|
ofsted_date?: string | null;
|
||||||
|
ofsted_framework?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -570,3 +570,49 @@ export function buildSchoolSummary(
|
|||||||
|
|
||||||
return parts.join(', ') + '.';
|
return parts.join(', ') + '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── List-level Ofsted badge ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface OfstedListBadge {
|
||||||
|
/** Display text for the badge (e.g. "Outstanding · 2023", "Report Card · 2025") */
|
||||||
|
label: string;
|
||||||
|
/** CSS module class key — one of: ofsted1 | ofsted2 | ofsted3 | ofsted4 | ofstedRc | ofstedPending */
|
||||||
|
cssClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the Ofsted badge for a school card in the list/map view.
|
||||||
|
* Three states:
|
||||||
|
* - OEIF school (ofsted_grade set): grade word + year, colour-keyed
|
||||||
|
* - ReportCard school (ofsted_framework === 'ReportCard'): "Report Card · YYYY" in purple
|
||||||
|
* - No inspection: "Not yet inspected" in grey
|
||||||
|
*/
|
||||||
|
export function buildOfstedListBadge(school: {
|
||||||
|
ofsted_grade?: number | null;
|
||||||
|
ofsted_date?: string | null;
|
||||||
|
ofsted_framework?: string | null;
|
||||||
|
}): OfstedListBadge {
|
||||||
|
const year = school.ofsted_date
|
||||||
|
? new Date(school.ofsted_date).getFullYear()
|
||||||
|
: null;
|
||||||
|
const yearStr = year ? ` · ${year}` : '';
|
||||||
|
|
||||||
|
if (school.ofsted_grade) {
|
||||||
|
const labels: Record<number, string> = {
|
||||||
|
1: 'Outstanding',
|
||||||
|
2: 'Good',
|
||||||
|
3: 'Req. Improvement',
|
||||||
|
4: 'Inadequate',
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
label: `${labels[school.ofsted_grade]}${yearStr}`,
|
||||||
|
cssClass: `ofsted${school.ofsted_grade}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (school.ofsted_framework === 'ReportCard') {
|
||||||
|
return { label: `Report Card${yearStr}`, cssClass: 'ofstedRc' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { label: 'Not yet inspected', cssClass: 'ofstedPending' };
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user