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

+13 -1
View File
@@ -53,7 +53,8 @@ export function ComparisonView({
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { selectedSchools, removeSchool, replaceSchools, isInitialized } = useComparison();
const { selectedSchools, removeSchool, replaceSchools, backfillPhases, isInitialized } =
useComparison();
const [selectedMetric, setSelectedMetric] = useState(initialMetric);
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -157,6 +158,17 @@ export function ComparisonView({
};
}, [urnKey, isInitialized]);
useEffect(() => {
if (!comparisonData) return;
backfillPhases(
Object.fromEntries(
Object.values(comparisonData)
.filter((d) => d?.school_info)
.map((d) => [d.school_info.urn, d.school_info.phase]),
),
);
}, [comparisonData, backfillPhases]);
const primarySchools = selectedSchools.filter((school) => {
const info = comparisonData?.[school.urn]?.school_info;
const hasPrimaryData =
+5 -4
View File
@@ -150,10 +150,11 @@ interface ValueProp {
*
* Every claim here must name something the product actually does. Two of the
* four previously did not: "up to three schools" contradicted the basket limit
* (now MAX_PER_GROUP = 5 per phase, in lib/compareLogic.ts) (and the card further down the page, which
* correctly said five), and "class sizes" described data the codebase has never
* held — grep for it and this line was the only hit. Both are corrected below
* against the real fields, which live in components/school/InclusionSection.tsx.
* of five (and the card further down the page, which correctly said five), and
* "class sizes" described data the codebase has never held — grep for it and
* this line was the only hit. Both are corrected below against the real
* fields, which live in components/school/InclusionSection.tsx.
* The limit is now five per phase: MAX_PER_GROUP in lib/compareLogic.ts.
*/
const VALUE_PROPS: ValueProp[] = [
{
@@ -28,6 +28,9 @@ export function AddToCompareButton({ school }: { school: NearbySchool }) {
school_name: school.school_name,
school_type: school.school_type,
age_range: school.age_range,
// The basket limits each phase separately. Missing (an older API)
// counts against both groups, which is safe, just stricter.
phase: school.phase ?? null,
} as School);
};