From 007c57ce957066ac95c3bae73b95a049ab80b009 Mon Sep 17 00:00:00 2001 From: NesiciCoding Date: Thu, 13 Aug 2026 16:32:51 +0200 Subject: [PATCH 1/2] perf: migrate TestListPage to selector store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the five domain-hook subscriptions with useStoreSelector/useStoreActions so the component reads a single store slice. 🤖 Generated with Codebuff Co-Authored-By: Codebuff --- src/pages/TestListPage.tsx | 17 ++++++++++------- src/pages/__tests__/TestListPage.test.tsx | 5 +++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/pages/TestListPage.tsx b/src/pages/TestListPage.tsx index 6e4a1121..06c9395c 100644 --- a/src/pages/TestListPage.tsx +++ b/src/pages/TestListPage.tsx @@ -19,7 +19,7 @@ import { import { DragDropContext, Droppable, Draggable, type DropResult } from '@hello-pangea/dnd'; import { useTranslation } from 'react-i18next'; import Topbar from '../components/Layout/Topbar'; -import { useAssessment, useAuthoring, useClasses, useSettings, useStudents } from '../context/AppContext'; +import { useStoreActions, useStoreSelector } from '../context/useStore'; import { useToast } from '../hooks/useToast'; import { logAuditEvent } from '../services/database/AuditLogger'; import { nanoid } from '../utils/nanoid'; @@ -40,12 +40,15 @@ import { calcClassAveragePercentage } from '../utils/testCalc'; export default function TestListPage() { const { t } = useTranslation(); const navigate = useNavigate(); - const { students } = useStudents(); - const { classes } = useClasses(); - - const { exportTemplates } = useAuthoring(); - const { tests, addTest, updateTest, deleteTest, studentTests, saveStudentTest } = useAssessment(); - const { settings } = useSettings(); + const { students, classes, exportTemplates, tests, studentTests, settings } = useStoreSelector((s) => ({ + students: s.students, + classes: s.classes, + exportTemplates: s.exportTemplates, + tests: s.tests, + studentTests: s.studentTests, + settings: s.settings, + })); + const { addTest, updateTest, deleteTest, saveStudentTest } = useStoreActions(); const activeStyleTemplate = exportTemplates.find((t) => t.kind === 'style' && t.id === settings.styleTemplateId); const [cohortFilter, setCohortFilter] = useState(ALL_COHORTS); diff --git a/src/pages/__tests__/TestListPage.test.tsx b/src/pages/__tests__/TestListPage.test.tsx index 40c117f6..4b37b4a5 100644 --- a/src/pages/__tests__/TestListPage.test.tsx +++ b/src/pages/__tests__/TestListPage.test.tsx @@ -92,6 +92,11 @@ vi.mock('../../context/AppContext', () => ({ usePlatform: () => mockUseApp, })); +vi.mock('../../context/useStore', () => ({ + useStoreSelector: (selector: (state: any) => any) => selector(mockUseApp), + useStoreActions: () => mockUseApp, +})); + vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string, params?: Record) => { From 435b0651dead5601c1f1957070f689f5946203d6 Mon Sep 17 00:00:00 2001 From: NesiciCoding Date: Tue, 18 Aug 2026 09:20:57 +0200 Subject: [PATCH 2/2] fix(render): exclude archived students on TestListPage; type store mock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The roster domain hooks filtered soft-deleted rows; the selector-store migration read the raw slice. Restore the active-student filtering so archived students don't appear in cohort filters or class averages, and type the useStoreSelector mock against StoreData. 🤖 Generated with Codebuff Co-Authored-By: Codebuff --- src/pages/TestListPage.tsx | 12 +++++++++++- src/pages/__tests__/TestListPage.test.tsx | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/pages/TestListPage.tsx b/src/pages/TestListPage.tsx index 06c9395c..65564661 100644 --- a/src/pages/TestListPage.tsx +++ b/src/pages/TestListPage.tsx @@ -40,7 +40,14 @@ import { calcClassAveragePercentage } from '../utils/testCalc'; export default function TestListPage() { const { t } = useTranslation(); const navigate = useNavigate(); - const { students, classes, exportTemplates, tests, studentTests, settings } = useStoreSelector((s) => ({ + const { + students: allStudents, + classes, + exportTemplates, + tests, + studentTests, + settings, + } = useStoreSelector((s) => ({ students: s.students, classes: s.classes, exportTemplates: s.exportTemplates, @@ -48,6 +55,9 @@ export default function TestListPage() { studentTests: s.studentTests, settings: s.settings, })); + // The roster domain hooks filtered soft-deleted rows; archived students must not + // appear in cohort filters or per-student statistics. + const students = React.useMemo(() => allStudents.filter((s) => !s.archivedAt), [allStudents]); const { addTest, updateTest, deleteTest, saveStudentTest } = useStoreActions(); const activeStyleTemplate = exportTemplates.find((t) => t.kind === 'style' && t.id === settings.styleTemplateId); diff --git a/src/pages/__tests__/TestListPage.test.tsx b/src/pages/__tests__/TestListPage.test.tsx index 4b37b4a5..1800cdb4 100644 --- a/src/pages/__tests__/TestListPage.test.tsx +++ b/src/pages/__tests__/TestListPage.test.tsx @@ -4,6 +4,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { MemoryRouter } from 'react-router-dom'; import { DEFAULT_FORMAT } from '../../types'; import type { AppSettings, Class, GradeScale, Student, Test as RmTest, StudentTest } from '../../types'; +import type { StoreData } from '../../store/storage'; const mockSettings: AppSettings = { defaultGradeScaleId: 'gs1', @@ -93,7 +94,7 @@ vi.mock('../../context/AppContext', () => ({ })); vi.mock('../../context/useStore', () => ({ - useStoreSelector: (selector: (state: any) => any) => selector(mockUseApp), + useStoreSelector: (selector: (state: StoreData) => T): T => selector(mockUseApp as unknown as StoreData), useStoreActions: () => mockUseApp, }));