All files / src/app/student-page/page/assignments AssignmentsPage.jsx

100% Statements 37/37
100% Branches 10/10
100% Functions 12/12
100% Lines 34/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                                    3x 51x 7x   44x 8x   36x 6x   30x 6x   24x     3x 19x 19x 19x   19x 19x 19x 19x   19x           30x 30x                 30x       19x 18x     19x 1x 1x     31x       1x           19x 35x                     19x 18x     19x                                          
import { MRT_GlobalFilterTextField } from "material-react-table";
import PinnedTable from "@/utils/components/tables/pinned-table/PinnedTable";
import { Box, Chip, MenuItem } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import { getHeroSoftToolbarSx, getHeroSoftSearchFieldSx } from "@/utils/components/tables/heroTableStyles";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import { useDispatch } from "react-redux";
import { useEffect } from "react";
import { _studentfetchAllAssignments } from "@/store/slices/student/StudentAssignments";
import { useSelector } from "react-redux";
import { _studentFetchSpecificAssignmentById } from "@/store/slices/student/StudentSpecificAssignment";
 
/**
 * Renders the grade cell value for the student assignments list table.
 *
 * - "incomplete"  → "No submission"  (assignment is still in progress / not yet submitted)
 * - "missed"      → Chip labelled "Missed" in error colour  (past close date, never submitted)
 * - "pending"     → Chip labelled "Pending Review"  (late submission awaiting teacher approval, EI-1210)
 * - "rejected"    → Chip labelled "Rejected"  (late submission the teacher declined, EI-1210)
 * - numeric string (e.g. "0", "85") → displayed as-is (a real score, including a legitimate zero)
 *
 * These states/labels mirror the teacher gradebook exactly (EI-1195) so the two
 * views never appear to disagree for the same assignment.
 *
 * Exported for unit testing.
 */
export const renderGrade = (grade) => {
    if (grade === "incomplete") {
        return "No submission";
    }
    if (grade === "missed") {
        return <Chip label="Missed" color="error" size="small" />;
    }
    if (grade === "pending") {
        return <Chip label="Pending Review" color="warning" size="small" />;
    }
    if (grade === "rejected") {
        return <Chip label="Rejected" color="error" size="small" />;
    }
    return grade;
};
 
const MainTable = () => {
    const theme = useTheme();
    const { assignmentsData, isLoading } = useSelector((state) => state.StudentAssignments);
    const { classCode } = useParams();
 
    const location = useLocation();
    const currentPath = location.pathname;
    const navigate = useNavigate();
    const dispatch = useDispatch();
 
    const columns = [
        { accessorKey: "title", header: "Title" },
        {
            accessorKey: "questions",
            header: "Total Questions",
            Cell: ({ cell }) => {
                const questions = cell.getValue();
                return questions?.length;
            },
        },
        { accessorKey: "date_open", header: "Date open" },
        { accessorKey: "date_close", header: "Date close" },
        { accessorKey: "type", header: "Type" },
        {
            accessorKey: "grade",
            header: "Grade",
            Cell: ({ cell }) => renderGrade(cell.getValue()),
        },
    ];
 
    const getCurrentEntries = () => {
        return (assignmentsData ?? []).filter((assignment) => assignment.category === "current");
    };
 
    const viewAssignment = (row) => {
        dispatch(_studentFetchSpecificAssignmentById(row.original._id));
        navigate(`${currentPath}/assignment/${row.original.title}`, { state: { assignmentId: row.original._id } });
    };
 
    const rowActions = ({ row }) => [
        <MenuItem
            id="student-current-assignment-actions-item-view"
            key="viewAssignment"
            onClick={() => viewAssignment(row)}
        >
            View
        </MenuItem>,
    ];
 
    const topToolbar = ({ table }) => {
        return (
            <Box sx={{ ...getHeroSoftToolbarSx(theme), alignItems: "center" }}>
                <MRT_GlobalFilterTextField
                    table={table}
                    sx={getHeroSoftSearchFieldSx(theme)}
                    placeholder="Search assignments"
                />
            </Box>
        );
    };
 
    useEffect(() => {
        dispatch(_studentfetchAllAssignments(classCode));
    }, []);
 
    return (
        <Box sx={{ width: "100%", minHeight: 420, display: "flex", flexDirection: "column" }}>
            <Box sx={{ flex: 1, minHeight: 360, display: "flex", flexDirection: "column" }}>
                <PinnedTable
                    tableId="student-class-assignments"
                    variant="soft"
                    columns={columns}
                    data={getCurrentEntries()}
                    topToolbar={topToolbar}
                    pinnedColumn={false}
                    rowActions={rowActions}
                    isLoading={isLoading}
                    enableStickyHeader
                    muiTableContainerProps={{ sx: { minHeight: 260 } }}
                />
            </Box>
        </Box>
    );
};
 
export default MainTable;