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 | 3x 48x 48x 68x 68x 48x 48x 48x 48x 48x 18x 18x 48x 16x 14x 61x 14x 48x 31x 48x 1x 1x 48x 48x 1x 2x | /**
* Modified by Allan Ninal — 2026-08-14
*
* Two fixes and a de-duplication. This is the teacher's "view as student" preview
* of the take-assignment countdown, and it had drifted from the real one:
*
* 1. It seeded from `remainingTime` in the STUDENT slice, which nothing on the
* preview path ever sets (StudentPreviewPage.jsx imports `_setRemainingTime`
* and never dispatches it), so it always fell through to a hardcoded 3600 and
* showed 01:00:00 whatever the teacher had configured. It now reads the same
* `step3.timeAllowed` it already consults one line up to decide `isUnlimited`.
* 2. At zero it POSTed to the real STUDENT submit endpoint. Nothing is submitted
* from a preview — the assignment does not exist yet — and the only thing that
* stopped a live request was `specificAssignmentData` being null in a teacher
* session, which made it throw inside the try. The dialog also told the teacher
* their work "will now be submitted automatically", which was never true here.
*
* The parse/format helpers were private copies of the student ones, minus their
* guards; they now come from takeAssignmentTimer.js, which is the tested original.
* That module is where the drift started, so sharing it is the fix that lasts.
*/
import { Box, Button, Typography } from "@mui/material";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { _updateShowTimer } from "@/store/slices/student/StudentQuestionPage";
import { useNavigate } from "react-router-dom";
import OKOption from "@/utils/components/dialogs/OKOption";
import {
formatSecondsToTime,
parseTimeStringToSeconds,
} from "@/app/student-page/page/assignments/take-assignment/components/takeAssignmentTimer";
const Timer = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { settings } = useSelector((state) => state.StudentQuestionPage);
// This preview runs off the in-progress wizard data (assignment isn't saved yet),
// same source PageHeader.jsx in this directory uses for the other settings.
const { assignmentData } = useSelector((state) => state.TeacherCreateAssignment);
const timeAllowed = assignmentData?.step3?.timeAllowed;
const isUnlimited = !timeAllowed;
const [totalSeconds, setTotalSeconds] = useState(() => parseTimeStringToSeconds(timeAllowed) ?? 0);
const [timeOver, setTimeOver] = useState(false);
// The wizard's Step 3 can be edited while this preview is mounted, so the clock
// follows the configured limit rather than only reading it once.
useEffect(() => {
setTotalSeconds(parseTimeStringToSeconds(timeAllowed) ?? 0);
setTimeOver(false);
}, [timeAllowed]);
useEffect(() => {
if (isUnlimited) return;
const interval = setInterval(() => {
setTotalSeconds((prev) => (prev > 1 ? prev - 1 : 0));
}, 1000);
return () => clearInterval(interval);
}, [isUnlimited]);
// Raising the dialog from an effect rather than from inside the setState updater:
// an updater must be pure, and React may run it more than once per tick.
useEffect(() => {
if (!isUnlimited && totalSeconds === 0) setTimeOver(true);
}, [isUnlimited, totalSeconds]);
const clickOk = () => {
setTimeOver(false);
navigate(-1);
};
const formattedTime = formatSecondsToTime(totalSeconds);
return (
<React.Fragment>
<OKOption
isDialog={timeOver}
setDialog={clickOk}
title="Time Expired"
message="Time has expired for this assignment. This is a preview, so nothing was submitted."
/>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
{settings.showTimer ? (
<>
<Typography>{isUnlimited ? "Unlimited time" : `Remaining time: ${formattedTime}`}</Typography>
<Button variant="text" color="primary" onClick={() => dispatch(_updateShowTimer())}>
Hide
</Button>
</>
) : (
<Button variant="text" color="primary" onClick={() => dispatch(_updateShowTimer())}>
Show Timer
</Button>
)}
</Box>
</React.Fragment>
);
};
export default Timer;
|