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
26 changes: 26 additions & 0 deletions src/components/Exam/PreventBackButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect } from 'react';

export const PreventBackButton = () => {
useEffect(() => {
const preventBack = (event:any) => {
event.preventDefault();
event.returnValue = '';
window.history.pushState(null, document.title, window.location.href); // Push a new state to the history stack
};

// Add event listener for popstate event
window.addEventListener('popstate', preventBack);

// Push a state to prevent going back initially
window.history.pushState(null, document.title, window.location.href);

return () => {
// Clean up the event listener on component unmount
window.removeEventListener('popstate', preventBack);
};
}, []);

return null;
};

export default PreventBackButton;
29 changes: 27 additions & 2 deletions src/components/Exam/TimeContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { QuestionIndexes } from './QuestionIndexes';
import { CountdownTimer } from './CountdownTimer';
import axios from 'axios';
import { useEffect } from 'react';
import './styles.css';

interface TimeContainerProps {
Expand All @@ -22,6 +23,15 @@ export const TimeContainer = ({
}: TimeContainerProps) => {
const navigate = useNavigate();

// Calculate remaining time
const endTime = sessionStorage.getItem('endTime');
const remainingTimeInMilliseconds =
endTime ? new Date(endTime).getTime() - new Date().getTime() : 0;

const remainingTimeInSeconds = Math.max(0, Math.floor(remainingTimeInMilliseconds / 1000));
const remainingMinutes = Math.floor(remainingTimeInSeconds / 60);
const remainingSeconds = remainingTimeInSeconds % 60;

const handleSubmitExam = async () => {
const token = sessionStorage.getItem('accessToken');
const sessionId = sessionStorage.getItem('sessionId');
Expand All @@ -30,7 +40,6 @@ export const TimeContainer = ({
message.error('Failed to submit exam. Please try again.');
return;
}


try {
const response = await axios.put(
Expand All @@ -55,13 +64,28 @@ export const TimeContainer = ({
}
};

// Use effect to automatically submit the exam when time expires
useEffect(() => {
if (remainingTimeInSeconds <= 0) {
handleSubmitExam();
}
}, [remainingTimeInSeconds]);

return (
<Col span={6} push={18} className="time-container">
<Col span={24}>
<div className="time-content">
<div className="countdown-timer">
<span className="topic">Time Remaining</span>
<CountdownTimer initialHours={0} initialMinutes={60} initialSeconds={0} />
{remainingTimeInSeconds > 0 ? (
<CountdownTimer
initialHours={0}
initialMinutes={remainingMinutes}
initialSeconds={remainingSeconds}
/>
) : (
<span className="expired">Time's up!</span>
)}
</div>
<Divider />
<QuestionIndexes
Expand All @@ -78,6 +102,7 @@ export const TimeContainer = ({
size={'large'}
className="submit-button"
onClick={handleSubmitExam}
disabled={remainingTimeInSeconds <= 0}
>
Submit Exam
</Button>
Expand Down
1 change: 1 addition & 0 deletions src/components/Exam/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export {QuestionIndexes} from './QuestionIndexes.tsx';
export {EssayQuestionView} from './EssayQuestionView.tsx';
export {McqQuestionView} from './McqQuestionView.tsx';
export {SortingComponent} from './SortingComponent.tsx';
export {PreventBackButton} from './PreventBackButton.tsx';
52 changes: 46 additions & 6 deletions src/pages/candidate/CandidateDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
} from 'antd';
import { getLoggedInUser } from '../../utils/authUtils';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { getCandidateExams } from '../../api/services/candidate';
import { Exam } from '../../types';
import moment from 'moment';
Expand All @@ -33,18 +34,57 @@

// Example API call simulation
useEffect(() => {
// Simulate checking for an ongoing exam
const checkOngoingExam = async () => {
const ongoingExam = true; // Replace this with actual API call
if (ongoingExam) {
setIsModalVisible(false);
const userString = sessionStorage.getItem('user'); // Retrieve the user string
const token = sessionStorage.getItem('accessToken'); // Retrieve the access token

if (!userString || !token) {
console.error("User information or token not found in session storage.");
return;
}

const user = JSON.parse(userString); // Parse the user object
const candidateId = user?.id; // Extract the candidateId

if (!candidateId) {
console.error("Candidate ID not found in user object.");
return;
}

try {
const response = await axios.post(
'http://localhost:8080/api/v1/candidate/check-active-session',
{ candidateId },
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);

const data = response.data;
if (data.examId && data.examName && data.examType) {
// If an active exam session exists, show the modal
setIsModalVisible(true);
// Store the exam data for navigation
sessionStorage.setItem('ongoingExam', JSON.stringify(data));
sessionStorage.setItem('sessionId', data.sessionId);
sessionStorage.setItem('endTime', data.endTime);
}
} catch (error) {
console.error('Error checking active session:', error);
}
};

checkOngoingExam();
}, []);

const handleOk = () => {
navigate('/exam/view'); // Navigate to the exam view page
const ongoingExam = sessionStorage.getItem('ongoingExam');
if (ongoingExam) {
const { examId, examName, examType } = JSON.parse(ongoingExam);
navigate('/candidate/exam/view', { state: { id: examId, name: examName, type: examType } });
}
};

const handleCancel = () => {
Expand Down Expand Up @@ -94,7 +134,7 @@
const dateCellRender = (value: moment.Moment) => {
const dateString = value.format('YYYY-MM-DD');
const examOnDate = exams.filter(
(exam) => moment(exam.startTime).format('YYYY-MM-DD') === dateString

Check failure on line 137 in src/pages/candidate/CandidateDashboard.tsx

View workflow job for this annotation

GitHub Actions / build

Parameter 'exam' implicitly has an 'any' type.
);
//"2024-12-16T02:30" === "2024-12-16" do the convertion

Expand All @@ -103,7 +143,7 @@
<Tooltip
title={
<ul style={{ margin: 0, padding: 0 }}>
{examOnDate.map((exam) => (

Check failure on line 146 in src/pages/candidate/CandidateDashboard.tsx

View workflow job for this annotation

GitHub Actions / build

Parameter 'exam' implicitly has an 'any' type.
<li key={exam.name} style={{ listStyle: 'none' }}>
{exam.title}
</li>
Expand Down Expand Up @@ -230,7 +270,7 @@
</Col>

<Col xs={24} sm={12} lg={16}>
<Calendar fullscreen={false} dateCellRender={dateCellRender} />

Check failure on line 273 in src/pages/candidate/CandidateDashboard.tsx

View workflow job for this annotation

GitHub Actions / build

Type '(value: moment.Moment) => JSX.Element | null' is not assignable to type '(date: Dayjs) => ReactNode'.
</Col>

<Col lg={16}>
Expand Down
Loading
Loading