diff --git a/nextjs-app/components/Navigation.tsx b/nextjs-app/components/Navigation.tsx
index b0d9469..10ac348 100644
--- a/nextjs-app/components/Navigation.tsx
+++ b/nextjs-app/components/Navigation.tsx
@@ -48,7 +48,9 @@ export function Navigation() {
>
Compare
{selectedSchools.length > 0 && (
- {selectedSchools.length}
+
+ {selectedSchools.length}
+
)}
void;
isSelected: (urn: number) => boolean;
canAddMore: boolean;
+ isInitialized: boolean;
mutate: () => void;
}
diff --git a/nextjs-app/context/ComparisonProvider.tsx b/nextjs-app/context/ComparisonProvider.tsx
index 99fe774..ea5cee5 100644
--- a/nextjs-app/context/ComparisonProvider.tsx
+++ b/nextjs-app/context/ComparisonProvider.tsx
@@ -1,18 +1,98 @@
/**
* ComparisonProvider
- * Provides comparison state to all components
+ * Provides shared comparison state to all components
*/
'use client';
-import { useComparison } from '@/hooks/useComparison';
+import { useState, useEffect, useCallback } from 'react';
+import { getFromLocalStorage, setToLocalStorage } from '@/lib/utils';
+import type { School } from '@/lib/types';
import { ComparisonContext } from './ComparisonContext';
+const STORAGE_KEY = 'selectedSchools';
+const MAX_SCHOOLS = 5;
+
export function ComparisonProvider({ children }: { children: React.ReactNode }) {
- const comparisonState = useComparison();
+ const [selectedSchools, setSelectedSchools] = useState([]);
+ const [isInitialized, setIsInitialized] = useState(false);
+
+ // Load from localStorage on mount
+ useEffect(() => {
+ const stored = getFromLocalStorage(STORAGE_KEY, []);
+ setSelectedSchools(stored);
+ setIsInitialized(true);
+ }, []);
+
+ // Save to localStorage when schools change
+ useEffect(() => {
+ if (isInitialized) {
+ setToLocalStorage(STORAGE_KEY, selectedSchools);
+ }
+ }, [selectedSchools, isInitialized]);
+
+ // Listen for storage changes from other tabs
+ useEffect(() => {
+ const handleStorageChange = (e: StorageEvent) => {
+ if (e.key === STORAGE_KEY && e.newValue) {
+ try {
+ const parsed = JSON.parse(e.newValue);
+ setSelectedSchools(parsed);
+ } catch {
+ // Ignore parse errors
+ }
+ }
+ };
+
+ window.addEventListener('storage', handleStorageChange);
+ return () => window.removeEventListener('storage', handleStorageChange);
+ }, []);
+
+ const addSchool = useCallback((school: School) => {
+ setSelectedSchools((prev) => {
+ if (prev.some((s) => s.urn === school.urn)) {
+ return prev;
+ }
+ if (prev.length >= MAX_SCHOOLS) {
+ alert(`Maximum ${MAX_SCHOOLS} schools can be compared`);
+ return prev;
+ }
+ return [...prev, school];
+ });
+ }, []);
+
+ const removeSchool = useCallback((urn: number) => {
+ setSelectedSchools((prev) => prev.filter((s) => s.urn !== urn));
+ }, []);
+
+ const clearAll = useCallback(() => {
+ setSelectedSchools([]);
+ }, []);
+
+ const isSelected = useCallback(
+ (urn: number) => selectedSchools.some((s) => s.urn === urn),
+ [selectedSchools]
+ );
+
+ // Placeholder mutate - actual SWR mutate is in useComparison hook
+ const mutate = useCallback(() => {}, []);
return (
-
+
{children}
);
diff --git a/nextjs-app/hooks/useComparison.ts b/nextjs-app/hooks/useComparison.ts
index b42392f..7a165bc 100644
--- a/nextjs-app/hooks/useComparison.ts
+++ b/nextjs-app/hooks/useComparison.ts
@@ -1,35 +1,25 @@
/**
* Custom hook for managing school comparison state
+ * Uses shared context for real-time updates across components
*/
'use client';
-import { useState, useEffect, useCallback } from 'react';
import useSWR from 'swr';
import { fetcher } from '@/lib/api';
-import { getFromLocalStorage, setToLocalStorage } from '@/lib/utils';
-import type { School, ComparisonResponse } from '@/lib/types';
-
-const STORAGE_KEY = 'selectedSchools';
-const MAX_SCHOOLS = 5;
+import { useComparisonContext } from '@/context/ComparisonContext';
+import type { ComparisonResponse } from '@/lib/types';
export function useComparison() {
- const [selectedSchools, setSelectedSchools] = useState([]);
- const [isInitialized, setIsInitialized] = useState(false);
-
- // Load from localStorage on mount
- useEffect(() => {
- const stored = getFromLocalStorage(STORAGE_KEY, []);
- setSelectedSchools(stored);
- setIsInitialized(true);
- }, []);
-
- // Save to localStorage when schools change
- useEffect(() => {
- if (isInitialized) {
- setToLocalStorage(STORAGE_KEY, selectedSchools);
- }
- }, [selectedSchools, isInitialized]);
+ const {
+ selectedSchools,
+ addSchool,
+ removeSchool,
+ clearAll,
+ isSelected,
+ canAddMore,
+ isInitialized,
+ } = useComparisonContext();
// Fetch comparison data for selected schools
const urns = selectedSchools.map((s) => s.urn).join(',');
@@ -38,40 +28,10 @@ export function useComparison() {
fetcher,
{
revalidateOnFocus: false,
- dedupingInterval: 10000, // 10 seconds
+ dedupingInterval: 10000,
}
);
- const addSchool = useCallback((school: School) => {
- setSelectedSchools((prev) => {
- // Check if already selected
- if (prev.some((s) => s.urn === school.urn)) {
- return prev;
- }
-
- // Check max limit
- if (prev.length >= MAX_SCHOOLS) {
- alert(`Maximum ${MAX_SCHOOLS} schools can be compared`);
- return prev;
- }
-
- return [...prev, school];
- });
- }, []);
-
- const removeSchool = useCallback((urn: number) => {
- setSelectedSchools((prev) => prev.filter((s) => s.urn !== urn));
- }, []);
-
- const clearAll = useCallback(() => {
- setSelectedSchools([]);
- }, []);
-
- const isSelected = useCallback(
- (urn: number) => selectedSchools.some((s) => s.urn === urn),
- [selectedSchools]
- );
-
return {
selectedSchools,
comparisonData: data?.comparison,
@@ -81,7 +41,8 @@ export function useComparison() {
removeSchool,
clearAll,
isSelected,
- canAddMore: selectedSchools.length < MAX_SCHOOLS,
+ canAddMore,
+ isInitialized,
mutate,
};
}