fix(compare): give every basket entry a real phase
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m13s
PR Checks / Backend Smoke (pull_request) Successful in 9s
PR Checks / Build Backend (no push) (pull_request) Successful in 17s
PR Checks / Build Frontend (no push) (pull_request) Canceled after 1m6s
PR Checks / Build Pipeline (no push) (pull_request) Canceled after 0s
PR Checks / AI Code Review (Claude) (pull_request) Canceled after 0s

Review of the per-phase limit found entries reaching the basket with no
phase, and a phase-less entry holds a slot in both groups:

- Nearby-school cards added without one. The API now returns each
  peer's own phase (its pool is a phase group, so an all-through school
  can sit beside a primary); the button passes it through, and an older
  API simply leaves the conservative both-groups count in place.
- Baskets saved before this change were never migrated. The compare
  page now backfills missing phases from the data it already fetches,
  and never overwrites a phase the basket has.
- "16 plus" counted against both groups; it is secondary, as the API's
  PHASE_GROUPS files it.

Also rewraps the HomeView doc comment the previous commit left awkward.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
TudorandClaude Opus 5.5 committed 2026-09-30 12:04:39 +01:00
1 parent 0a4c051ee5
commit 587cfe3f0b
11 files changed
+175 -6

No files matched your search

+2 -1
View File
@@ -309,7 +309,8 @@ export const MAX_PER_GROUP = 5;
export function compareGroups(phase?: string | null): CompareGroup[] {
const p = (phase ?? '').toLowerCase();
// "Middle deemed secondary" / "Middle deemed primary" match here too.
if (p.includes('secondary')) return ['secondary'];
// "16 plus" is secondary, as the API's PHASE_GROUPS files it.
if (p.includes('secondary') || p === '16 plus') return ['secondary'];
if (p.includes('primary')) return ['primary'];
return ['primary', 'secondary'];
}
+2
View File
@@ -360,6 +360,8 @@ export interface NearbySchool {
distance_miles: number;
school_type: string | null;
age_range: string | null;
/** Optional: a frontend can ship ahead of the API that serves it. */
phase?: string | null;
shared: string[];
metric_value: number | null;
metric_key: string;