Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions src/pages/StudentPortalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,7 @@ import SegmentedToggle from '../components/ui/SegmentedToggle';
import Avatar from '../components/ui/Avatar';
import { Joyride, STATUS } from 'react-joyride';
import type { EventData } from 'react-joyride';
import {
useAssessment,
useAuthoring,
useClasses,
useEssays,
useFlashcards,
useGrading,
useSettings,
useStudents,
} from '../context/AppContext';
import { useStoreActions, useStoreSelector } from '../context/useStore';
import { calcGradeSummary, criterionPercentage } from '../utils/gradeCalc';
// Charts pull in recharts (~297KB) — lazy-loaded so the student-facing portal's initial
// route chunk stays lean; they render only when their (below-the-fold) sections do.
Expand Down Expand Up @@ -141,33 +132,58 @@ const SECTION_TAB: Record<string, PortalTab> = {

export default function StudentPortalPage() {
const { studentId } = useParams<{ studentId: string }>();
const { students } = useStudents();
const { classes } = useClasses();
const { studentRubrics, saveRubricSelfAssessment } = useGrading();

const { rubrics, gradeScales } = useAuthoring();
const {
students: allStudents,
classes,
studentRubrics: allStudentRubrics,
rubrics,
gradeScales,
peerReviews,
selfAssessments,
analysisResults,
tests,
studentTests,
newsFlashes,
newsFlashReads,
flashcardAssignments,
flashcardDecks,
flashcardReviews,
settings,
} = useStoreSelector((s) => ({
students: s.students,
classes: s.classes,
studentRubrics: s.studentRubrics,
rubrics: s.rubrics,
gradeScales: s.gradeScales,
peerReviews: s.peerReviews,
selfAssessments: s.selfAssessments,
analysisResults: s.analysisResults,
tests: s.tests,
studentTests: s.studentTests,
newsFlashes: s.newsFlashes,
newsFlashReads: s.newsFlashReads,
flashcardAssignments: s.flashcardAssignments,
flashcardDecks: s.flashcardDecks,
flashcardReviews: s.flashcardReviews,
settings: s.settings,
}));
// The roster domain hooks filtered soft-deleted rows; keep that behavior here.
const students = useMemo(() => allStudents.filter((s) => !s.archivedAt), [allStudents]);
const studentRubrics = useMemo(() => allStudentRubrics.filter((sr) => !sr.deletedAt), [allStudentRubrics]);
const {
saveRubricSelfAssessment,
fetchMyTestAssignments,
fetchAssignedTestContent,
} = useAssessment();
const {
fetchMyEssayAssignments,
fetchMyMessages,
sendMessageAsStudent,
markMessagesReadByStudent,
newsFlashes,
newsFlashReads,
fetchMyNewsFlashes,
markNewsFlashRead,
markNewsFlashReadAsStudent,
} = useEssays();
const { flashcardAssignments, flashcardDecks, flashcardReviews, fetchMyFlashcardAssignments } = useFlashcards();
const { settings, updateSettings } = useSettings();
fetchMyFlashcardAssignments,
updateSettings,
} = useStoreActions();

const { t, i18n } = useTranslation();
const lang = i18n.language.startsWith('nl') ? 'nl' : 'en';
Expand Down
7 changes: 7 additions & 0 deletions src/pages/__tests__/StudentPortalPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const mockRubricsWithCriteriaArr = [mockRubricWithCriteria];
const mockGradedStudentRubricsArr = [mockGradedStudentRubric, mockGradedStudentRubric2];
const mockPeerReviewsArr = [mockPeerReview];
const mockSaveRubricSelfAssessment = vi.fn();
const mockUpdateSettings = vi.fn();

// A baseline grade plus a colleague's second-marker grade far enough apart (delta 4 >= the
// default moderation threshold of 2) to land in the pending moderation queue.
Expand Down Expand Up @@ -197,6 +198,7 @@ const mockAppValue: Record<string, unknown> = {
fetchMyNewsFlashes: mockFetchMyNewsFlashes,
markNewsFlashRead: mockMarkNewsFlashRead,
markNewsFlashReadAsStudent: mockMarkNewsFlashReadAsStudent,
updateSettings: mockUpdateSettings,
};

vi.mock('../../context/AppContext', () => ({
Expand All @@ -212,6 +214,11 @@ vi.mock('../../context/AppContext', () => ({
usePlatform: () => mockAppValue,
}));

vi.mock('../../context/useStore', () => ({
useStoreSelector: (selector: (state: any) => any) => selector(mockAppValue),
useStoreActions: () => mockAppValue,
}));

vi.mock('recharts', async (importOriginal) => {
const mod = await importOriginal<typeof import('recharts')>();
return {
Expand Down
14 changes: 14 additions & 0 deletions src/pages/__tests__/pages.smoke.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,18 @@ const mockUseApp = {
fetchMyEssayAssignments: vi.fn(() => Promise.resolve([])),
deleteEssaySubmission: vi.fn(),
getEssaySignedUrl: vi.fn(() => Promise.resolve(null)),
// StudentPortalPage mounts fetch/messaging/flashcard/news actions (local mode:
// these resolve empty and the app state below supplies the rows).
saveRubricSelfAssessment: vi.fn(),
fetchMyTestAssignments: vi.fn(() => Promise.resolve([])),
fetchAssignedTestContent: vi.fn(() => Promise.resolve(null)),
fetchMyMessages: vi.fn(() => Promise.resolve([])),
sendMessageAsStudent: vi.fn(() => Promise.resolve()),
markMessagesReadByStudent: vi.fn(() => Promise.resolve()),
fetchMyNewsFlashes: vi.fn(() => Promise.resolve([])),
markNewsFlashRead: vi.fn(),
markNewsFlashReadAsStudent: vi.fn(() => Promise.resolve()),
fetchMyFlashcardAssignments: vi.fn(() => Promise.resolve([])),
};

// ─── Module mocks ──────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -177,6 +189,8 @@ vi.mock('../../context/useStore', () => ({
essayTemplates: [],
flashcardAssignments: [],
flashcardReviews: [],
newsFlashes: [],
newsFlashReads: [],
Comment thread
NesiciCoding marked this conversation as resolved.
...mockUseApp,
}),
useStoreActions: () => mockUseApp,
Expand Down
Loading