diff --git a/src/pages/RubricBuilder.tsx b/src/pages/RubricBuilder.tsx index a9e93e97..f4df7d9a 100644 --- a/src/pages/RubricBuilder.tsx +++ b/src/pages/RubricBuilder.tsx @@ -33,7 +33,7 @@ import { } from 'lucide-react'; import { DragDropContext, Droppable, DropResult } from '@hello-pangea/dnd'; import Topbar from '../components/Layout/Topbar'; -import { useAssessment, useAuthoring, useGrading, useSettings } from '../context/AppContext'; +import { useStoreActions, useStoreSelector } from '../context/useStore'; import { useTranslation, Trans } from 'react-i18next'; import type { Rubric, @@ -101,25 +101,35 @@ export default function RubricBuilder() { const navigate = useNavigate(); const { id } = useParams(); const location = useLocation(); - const { studentRubrics } = useGrading(); - const { + studentRubrics: allStudentRubrics, rubrics, + gradeScales, + peerReviews, + settings, + } = useStoreSelector((s) => ({ + studentRubrics: s.studentRubrics, + rubrics: s.rubrics, + gradeScales: s.gradeScales, + peerReviews: s.peerReviews, + settings: s.settings, + })); + // The roster domain hooks filtered soft-deleted rows; keep that behavior here. + const studentRubrics = React.useMemo(() => allStudentRubrics.filter((sr) => !sr.deletedAt), [allStudentRubrics]); + + const { addRubric, updateRubric, syncRubricSnapshot, fetchRubricVersions, saveRubricVersion, restoreRubricVersion, - gradeScales, addVocabularyItem, updateVocabularyItem, deleteVocabularyItem, deleteVocabularyItems, saveUserTemplate, - } = useAuthoring(); - const { peerReviews } = useAssessment(); - const { settings } = useSettings(); + } = useStoreActions(); const existing = id ? rubrics.find((r) => r.id === id) : undefined; const template = location.state?.template as Partial | undefined; diff --git a/src/pages/__tests__/RubricBuilder.test.tsx b/src/pages/__tests__/RubricBuilder.test.tsx index 4501cb8f..e976c8ff 100644 --- a/src/pages/__tests__/RubricBuilder.test.tsx +++ b/src/pages/__tests__/RubricBuilder.test.tsx @@ -105,6 +105,7 @@ const mockShowToast = vi.fn(); const mockExportPdf = vi.fn(); const mockExportDocx = vi.fn(); const mockSyncRubricSnapshot = vi.fn(); +const mockSaveUserTemplate = vi.fn(); const mockFetchRubricVersions = vi.fn(async () => [] as typeof mockVersions); const mockSaveRubricVersion = vi.fn(async () => {}); const mockRestoreRubricVersion = vi.fn(); @@ -126,6 +127,7 @@ const makeAppContextMock = () => ({ fetchRubricVersions: mockFetchRubricVersions, saveRubricVersion: mockSaveRubricVersion, restoreRubricVersion: mockRestoreRubricVersion, + saveUserTemplate: mockSaveUserTemplate, gradeScales: [mockGradeScale], settings: mockSettings, addVocabularyItem: vi.fn(), @@ -149,6 +151,11 @@ vi.mock('../../context/AppContext', () => ({ usePlatform: () => makeAppContextMock(), })); +vi.mock('../../context/useStore', () => ({ + useStoreSelector: (selector: (state: any) => any) => selector(makeAppContextMock()), + useStoreActions: () => makeAppContextMock(), +})); + vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string, opts?: string | Record) => { @@ -514,9 +521,14 @@ describe('RubricBuilder', () => { // ── Save-as-template, print, JSON export ────────────────────────────────────── it('saves the rubric as a template from the export menu', () => { + mockSaveUserTemplate.mockClear(); + mockShowToast.mockClear(); renderEdit(); fireEvent.click(screen.getByText('rubricBuilder.action_export')); fireEvent.click(screen.getByText('rubricBuilder.action_save_as_template')); + expect(mockSaveUserTemplate).toHaveBeenCalledWith( + expect.objectContaining({ name: mockRubric.name, subject: mockRubric.subject }) + ); expect(mockShowToast).toHaveBeenCalled(); });