fix(refactor): Level page review pretest navigator and Refactor history and review services for improved error handling and code consistency

This commit is contained in:
Resh 2024-12-11 10:44:47 +07:00
parent 9a3c1f678d
commit f44fe0a9b9
5 changed files with 686 additions and 494 deletions

View File

@ -1,54 +1,75 @@
import axiosInstance from '../../../../utils/axiosInstance';
import axiosInstance from '../../../../utils/axiosInstance'
const fetchTopic = async () => {
try {
const response = await axiosInstance.get(`/topic`);
return response.data;
const response = await axiosInstance.get(`/topic`)
return response.data
} catch (error) {
throw error;
console.error(error)
throw error
}
}
};
const fetchAllHistory = async () => {
try {
const response = await axiosInstance.get(`/learningHistory`);
const historyData = response.data.payload.history;
const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null);
const response = await axiosInstance.get(`/learningHistory`)
const historyData = response.data.payload.history
const validHistory = historyData.filter(
history => history.STUDENT_FINISH !== null
)
return {message: response.data.message, payload: validHistory, status: response.data.statusCode,};
} catch (error) {
throw error;
return {
message: response.data.message,
payload: validHistory,
status: response.data.statusCode,
}
} catch (error) {
console.error(error)
throw error
}
}
};
const fetchHistoryBySection = async (section) => {
const fetchHistoryBySection = async section => {
try {
const response = await axiosInstance.get(`/learningHistory/section/${section}`);
const historyData = response.data.payload.history;
const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null);
const response = await axiosInstance.get(
`/learningHistory/section/${section}`
)
const historyData = response.data.payload.history
const validHistory = historyData.filter(
history => history.STUDENT_FINISH !== null
)
return {message: response.data.message, payload: validHistory, status: response.data.statusCode};
} catch (error) {
return {payload: []};
return {
message: response.data.message,
payload: validHistory,
status: response.data.statusCode,
}
} catch (error) {
return { payload: [] }
}
}
};
const fetchHistoryByTopic = async (topic) => {
const fetchHistoryByTopic = async topic => {
try {
const response = await axiosInstance.get(`/learningHistory/topic/${topic}`);
const historyData = response.data.payload.history;
const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null);
const response = await axiosInstance.get(`/learningHistory/topic/${topic}`)
const historyData = response.data.payload.history
const validHistory = historyData.filter(
history => history.STUDENT_FINISH !== null
)
return {message: response.data.message, payload: validHistory, status: response.data.statusCode,};
} catch (error) {
return {payload: []};
return {
message: response.data.message,
payload: validHistory,
status: response.data.statusCode,
}
} catch (error) {
return { payload: [] }
}
}
};
export default {
fetchTopic,
fetchAllHistory,
fetchHistoryBySection,
fetchHistoryByTopic
};
fetchHistoryByTopic,
}

View File

@ -1,9 +1,19 @@
import React from 'react';
import { Container, Row, Col, Card, Button, Dropdown, Breadcrumb, Tab, Nav } from 'react-bootstrap';
import useHistories from '../hooks/useHirtories';
import { Link } from 'react-router-dom';
import newBie from '../../../../assets/images/illustration/emptyJourney.png';
import Skeleton from 'react-loading-skeleton';
/* eslint-disable no-mixed-spaces-and-tabs */
import {
Container,
Row,
Col,
Card,
Button,
Dropdown,
Breadcrumb,
Tab,
Nav,
} from 'react-bootstrap'
import useHistories from '../hooks/useHirtories'
import { Link } from 'react-router-dom'
import newBie from '../../../../assets/images/illustration/emptyJourney.png'
import Skeleton from 'react-loading-skeleton'
const ExerciseHistory = () => {
const {
@ -17,19 +27,31 @@ const ExerciseHistory = () => {
setActiveTab,
formatLocalDate,
compareLevels,
setSelectedTopic
} = useHistories();
setSelectedTopic,
} = useHistories()
if (error) return <p>{error}</p>;
if (error) return <p>{error}</p>
const topics = activeTab !== 'all' ? topicsBySection[activeTab] || []: [];
const topics = activeTab !== 'all' ? topicsBySection[activeTab] || [] : []
return (
<div className='exercise-history'>
<h3 className='fw-bold text-blue'>Your Exercise History!</h3>
<p className='text-secondary mb-4'>Track your progress with a personalized overview of all your workouts.</p>
<Container fluid className='mb-4 d-flex align-items-center bg-white rounded rounded-4'>
<Tab.Container id="left-tabs-example" defaultActiveKey="all" onSelect={(k) => { setActiveTab(k); setSelectedTopic(''); }}>
<div className="exercise-history">
<h3 className="fw-bold text-blue">Your Exercise History!</h3>
<p className="text-secondary mb-4">
Track your progress with a personalized overview of all your workouts.
</p>
<Container
fluid
className="mb-4 d-flex align-items-center bg-white rounded rounded-4"
>
<Tab.Container
id="left-tabs-example"
defaultActiveKey="all"
onSelect={k => {
setActiveTab(k)
setSelectedTopic('')
}}
>
<Row>
<Col>
<Nav variant="pills" className="p-2 bg-white">
@ -46,13 +68,26 @@ const ExerciseHistory = () => {
</Row>
</Tab.Container>
{topics.length > 0 && (
<Dropdown className='ms-auto' onSelect={(topicId) => setSelectedTopic(topicId)}>
<Dropdown.Toggle id="dropdown-basic" size='sm' variant="outline-blue rounded-3" style={{padding:"6px 16px"}}>
{selectedTopic ? topics.find(t => t.ID_TOPIC === selectedTopic)?.NAME_TOPIC : 'Select a Topic'}
<Dropdown
className="ms-auto"
onSelect={topicId => setSelectedTopic(topicId)}
>
<Dropdown.Toggle
id="dropdown-basic"
size="sm"
variant="outline-blue rounded-3"
style={{ padding: '6px 16px' }}
>
{selectedTopic
? topics.find(t => t.ID_TOPIC === selectedTopic)?.NAME_TOPIC
: 'Select a Topic'}
</Dropdown.Toggle>
<Dropdown.Menu>
{topics.map((topic, index) => (
<Dropdown.Item key={topic.ID_TOPIC ? topic.ID_TOPIC : index} eventKey={topic.ID_TOPIC ? topic.ID_TOPIC : index}>
<Dropdown.Item
key={topic.ID_TOPIC ? topic.ID_TOPIC : index}
eventKey={topic.ID_TOPIC ? topic.ID_TOPIC : index}
>
{topic.NAME_TOPIC}
</Dropdown.Item>
))}
@ -60,30 +95,53 @@ const ExerciseHistory = () => {
</Dropdown>
)}
</Container>
<Container fluid className='bg-white rounded rounded-4 p-4'>
<Container fluid className="bg-white rounded rounded-4 p-4">
<Row xs={1}>
{loading ? (
<Col>
<Card className='flex-column p-3 border rounded-4'>
<Card className="flex-column p-3 border rounded-4">
<div className="w-100 d-flex justify-content-between">
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"15vw", height:"2vw"}} />
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"25vw", height:"2vw"}} />
<Skeleton
containerClassName="mb-2"
className="mb-1 rounded-4"
style={{ width: '15vw', height: '2vw' }}
/>
<Skeleton
containerClassName="mb-2"
className="mb-1 rounded-4"
style={{ width: '25vw', height: '2vw' }}
/>
</div>
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"40vw", height:"2vw"}} />
<Skeleton
containerClassName="mb-2"
className="mb-1 rounded-4"
style={{ width: '40vw', height: '2vw' }}
/>
<div className="w-100 d-flex justify-content-between">
<div className='d-flex'>
<Skeleton containerClassName="mb-2" className='mb-1 me-2 rounded-4' style={{width:"4vw", height:"5vw"}} />
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"15vw", height:"5vw"}} />
<div className="d-flex">
<Skeleton
containerClassName="mb-2"
className="mb-1 me-2 rounded-4"
style={{ width: '4vw', height: '5vw' }}
/>
<Skeleton
containerClassName="mb-2"
className="mb-1 rounded-4"
style={{ width: '15vw', height: '5vw' }}
/>
</div>
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"30vw", height:"5vw"}} />
<Skeleton
containerClassName="mb-2"
className="mb-1 rounded-4"
style={{ width: '30vw', height: '5vw' }}
/>
</div>
</Card>
</Col>
) :
historyData.length > 0 ? (
) : historyData.length > 0 ? (
historyData.map((history, index) => (
<Col key={index} className='mb-3'>
<Card className='flex-row p-3 border-primary rounded-4'>
<Col key={index} className="mb-3">
<Card className="flex-row p-3 border-primary rounded-4">
{/* <Card.Body className='p-0 d-flex flex-column justify-content-between'>
<Card.Title className='mb-1 w-100 d-flex justify-content-between'>
<h4 className='mb-0'>{history.CURRENT_LEVEL}</h4>
@ -124,62 +182,83 @@ const ExerciseHistory = () => {
</Card.Body> */}
<Card.Body className="p-2 d-flex justify-content-between">
<div className="d-flex flex-column">
<h4 className='mb-0'>{history.CURRENT_LEVEL}</h4>
<Breadcrumb className='mt-2 custom-breadcrumb'>
<Breadcrumb.Item>{history.SECTION_NAME}</Breadcrumb.Item>
<Breadcrumb.Item active>{history.TOPIC_NAME}</Breadcrumb.Item>
<h4 className="mb-0">{history.CURRENT_LEVEL}</h4>
<Breadcrumb className="mt-2 custom-breadcrumb">
<Breadcrumb.Item>
{history.SECTION_NAME}
</Breadcrumb.Item>
<Breadcrumb.Item active>
{history.TOPIC_NAME}
</Breadcrumb.Item>
</Breadcrumb>
<p className='mt-2 mb-0 submit-time'>Submission: {formatLocalDate(history.STUDENT_FINISH)}</p>
<h3 className='mt-4 mb-0'>
<p className="mt-2 mb-0 submit-time">
Submission: {formatLocalDate(history.STUDENT_FINISH)}
</p>
<h3 className="mt-4 mb-0">
<i className="bi bi-trophy-fill text-warning"></i>
<span className='ms-3 text-blue fw-bold'>{history.SCORE}/100</span>
<span className="ms-3 text-blue fw-bold">
{history.SCORE}/100
</span>
</h3>
</div>
<div className="d-flex flex-column justify-content-between align-items-end">
{history.IS_PASS == 1 ? (
<h5 className='text-green'>
<h5 className="text-green">
<i className="bi bi-check2-all me-1"></i>
Topic Finished!
</h5>
):(
compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'jump' ?(
<h5 className='text-blue'>
) : compareLevels(
history.CURRENT_LEVEL,
history.NEXT_LEVEL
) === 'jump' ? (
<h5 className="text-blue">
<i className="bi bi-arrow-up me-1"></i>
Jump to {history.NEXT_LEVEL}
</h5>
) : (
compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'down' ?(
<h5 className='text-red'>
) : compareLevels(
history.CURRENT_LEVEL,
history.NEXT_LEVEL
) === 'down' ? (
<h5 className="text-red">
<i className="bi bi-arrow-down me-1"></i>
Go Down to {history.NEXT_LEVEL}
</h5>
) : (
<h5 className='text-dark'>
<h5 className="text-dark">
<i className="bi bi-arrow-repeat me-1"></i>
Stay in {history.NEXT_LEVEL}
</h5>
)
)
)}
<Link to={`/learning/review/${history.ID_STUDENT_LEARNING}`} className='btn btn-blue py-2 px-5 rounded-35'>Review</Link>
<Link
to={`/learning/review/${history.ID_STUDENT_LEARNING}`}
className="btn btn-blue py-2 px-5 rounded-35"
>
Review
</Link>
</div>
</Card.Body>
</Card>
</Col>
))
) : (
<Col className='d-flex flex-column items-center'>
<h4 className='mb-0'>Still new?</h4>
<p className='fs-5 text-muted fw-light'>Begin your journey!</p>
<Col className="d-flex flex-column items-center">
<h4 className="mb-0">Still new?</h4>
<p className="fs-5 text-muted fw-light">Begin your journey!</p>
<img src={newBie} alt="" />
<Button as={Link} to={'/learning/module'} variant='warning' className='mt-4 py-2 px-5 rounded-35'>Explore</Button>
<Button
as={Link}
to={'/learning/module'}
variant="warning"
className="mt-4 py-2 px-5 rounded-35"
>
Explore
</Button>
</Col>
)
}
)}
</Row>
</Container>
</div>
);
};
)
}
export default ExerciseHistory;
export default ExerciseHistory

View File

@ -1,42 +1,70 @@
import React from 'react';
import { Container, Row, Col, Button, Breadcrumb } from 'react-bootstrap';
import { Link, useParams, Navigate } from 'react-router-dom';
import check from '../../../../assets/images/illustration/checkIcon.png';
import { Container, Row, Col, Button, Breadcrumb } from 'react-bootstrap'
import { Link, useParams } from 'react-router-dom'
import check from '../../../../assets/images/illustration/checkIcon.png'
// import { useValidation } from '../../utils/ValidationContext';
import useLevels from '../hooks/useLevels';
import { unSlugify } from '../../../../utils/Constant';
import useLevels from '../hooks/useLevels'
import { unSlugify } from '../../../../utils/Constant'
import Skeleton from 'react-loading-skeleton';
import Skeleton from 'react-loading-skeleton'
const Level = () => {
const { section, topic } = useParams();
const { section, topic } = useParams()
const { levels, lastCompletedLevel, loading, error, isLevelUnlocked, levelDesc } = useLevels(section, topic);
const { levels, loading, error, isLevelUnlocked, levelDesc } = useLevels(
section,
topic
)
if (loading) {
return (
<div className='row'>
<Skeleton containerClassName='w-100' className='w-100 mb-3 rounded-3' style={{height:"3vh"}} />
<Skeleton containerClassName='w-100' className='w-100 mb-3 rounded-4' style={{height:"25vh"}} />
<Skeleton containerClassName='w-100 d-flex flex-wrap justify-content-between' className='mb-3 rounded-4' count={4} inline={true} style={{width:"49%",height:"25vh"}} />
<div className="row">
<Skeleton
containerClassName="w-100"
className="w-100 mb-3 rounded-3"
style={{ height: '3vh' }}
/>
<Skeleton
containerClassName="w-100"
className="w-100 mb-3 rounded-4"
style={{ height: '25vh' }}
/>
<Skeleton
containerClassName="w-100 d-flex flex-wrap justify-content-between"
className="mb-3 rounded-4"
count={4}
inline={true}
style={{ width: '49%', height: '25vh' }}
/>
</div>
);
)
}
if (error) {
return <div>Error: {error.message}</div>;
return <div>Error: {error.message}</div>
}
return (
<Container className='level-page'>
<Container className="level-page">
<Row>
<Col className="d-flex align-items-center mb-3 breadcrumb-con">
<Button as={Link} variant='blue' className='btn-square-back' to={`/learning/module/${section}`}>
<Button
as={Link}
variant="blue"
className="btn-square-back"
to={`/learning/module/${section}`}
>
<i className="bi bi-arrow-90deg-left"></i>
</Button>
<Breadcrumb className='custom-breadcrumb'>
<Breadcrumb.Item href="/learning/module"><i className="bi bi-book me-1"></i>Learning</Breadcrumb.Item>
<Breadcrumb.Item href={`/learning/module/${section}`} className='text-capitalize'>{unSlugify(section)}</Breadcrumb.Item>
<Breadcrumb className="custom-breadcrumb">
<Breadcrumb.Item href="/learning/module">
<i className="bi bi-book me-1"></i>Learning
</Breadcrumb.Item>
<Breadcrumb.Item
href={`/learning/module/${section}`}
className="text-capitalize"
>
{unSlugify(section)}
</Breadcrumb.Item>
<Breadcrumb.Item active>{unSlugify(topic)}</Breadcrumb.Item>
</Breadcrumb>
</Col>
@ -47,34 +75,49 @@ const Level = () => {
) : (
<Row>
{levels.map((level, index) => (
<Col key={level.ID_LEVEL} xs={12} grant={level.ID_LEVEL} sm={level.IS_PRETEST === 1 ? 12 : 6} className='mb-3'>
<div className={`p-3 level-con ${isLevelUnlocked(index) ? 'bg-gd' : 'bg-light-grey'} rounded-4`}>
{(!isLevelUnlocked(index) && level.SCORE > 40) && <img src={check} alt="" className='check-icon' />}
<Col
key={level.ID_LEVEL}
xs={12}
grant={level.ID_LEVEL}
sm={level.IS_PRETEST === 1 ? 12 : 6}
className="mb-3"
>
<div
className={`p-3 level-con ${
isLevelUnlocked(index) ? 'bg-gd' : 'bg-light-grey'
} rounded-4`}
>
{!isLevelUnlocked(index) && level.SCORE > 40 && (
<img src={check} alt="" className="check-icon" />
)}
<div className="mb-3 w-100 d-flex justify-content-between">
{level.IS_PRETEST === 0 ? (
<div className="level-label">
<h4 className='m-0 lh-normal'>LEVEL</h4>
<h4 className="m-0 lh-normal">LEVEL</h4>
<span>{index}</span>
</div>
) : (
<div className="level-label">
<h4 className='m-0 lh-normal'>PRETEST</h4>
<h4 className="m-0 lh-normal">PRETEST</h4>
</div>
)}
<h5 className='m-0 text-white'>Score {level.SCORE === null ? 0 : level.SCORE}/100</h5>
<h5 className="m-0 text-white">
Score {level.SCORE === null ? 0 : level.SCORE}/100
</h5>
</div>
<p className='mb-3 text-white'>
{levelDesc[index]}
</p>
<p className="mb-3 text-white">{levelDesc[index]}</p>
{level.IS_PRETEST === 1 && level.SCORE !== null ? (
<div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
<Button
variant="success"
className="py-2 px-5 w-fit rounded-35"
as={Link}
to={`/learning/review/${level.ID_STUDENT_LEARNING}`}
>
Review
</div>
):(
!isLevelUnlocked(index) ? (
<div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
</Button>
) : !isLevelUnlocked(index) ? (
<div className="py-2 px-5 mt-auto w-fit bg-light rounded-35">
Not Allowed
</div>
) : (
@ -89,22 +132,26 @@ const Level = () => {
// Finished
// </div>
// )
<Button variant='warning' className='py-2 px-5 w-fit rounded-35'
as={Link} to={level.IS_PRETEST === 1 ? `/learning/module/${section}/${topic}/pretest/material` : `/learning/module/${section}/${topic}/level-${index}/material`}
<Button
variant="warning"
className="py-2 px-5 w-fit rounded-35"
as={Link}
to={
level.IS_PRETEST === 1
? `/learning/module/${section}/${topic}/pretest/material`
: `/learning/module/${section}/${topic}/level-${index}/material`
}
>
Learn Now
</Button>
)
)}
</div>
</Col>
))}
</Row>
)}
</Container>
);
};
)
}
export default Level;
export default Level

View File

@ -1,81 +1,81 @@
import axiosInstance from '../../../../utils/axiosInstance';
import axiosInstance from '../../../../utils/axiosInstance'
const getSoalNumber = (title) => {
const match = title.match(/\d+$/);
return match ? parseInt(match[0], 10) : 0;
};
// const getSoalNumber = (title) => {
// const match = title.match(/\d+$/);
// return match ? parseInt(match[0], 10) : 0;
// };
const getLevelId = async (topicId, levelName) => {
try {
const response = await axiosInstance.get(`/level/topic/${topicId}`);
const filteredData = response.data.data.levels.filter(item => item.NAME_LEVEL === levelName);
return filteredData[0].ID_LEVEL;
const response = await axiosInstance.get(`/level/topic/${topicId}`)
const filteredData = response.data.data.levels.filter(
item => item.NAME_LEVEL === levelName
)
return filteredData[0].ID_LEVEL
} catch (error) {
return [];
return []
}
}
};
const createStdLearning = async (data) => {
const createStdLearning = async data => {
try {
const response = await axiosInstance.post(`/stdLearning`, data);
return response.data;
const response = await axiosInstance.post(`/stdLearning`, data)
return response.data
} catch (error) {
console.error('Error creating std_learning:', error);
throw error;
console.error('Error creating std_learning:', error)
throw error
}
}
};
const fetchReview = async (stdLearning) => {
const fetchReview = async stdLearning => {
try {
const response = await axiosInstance.get(`/studentAnswers/${stdLearning}`);
const response = await axiosInstance.get(`/studentAnswers/${stdLearning}`)
return response.data;
return response.data
} catch (error) {
throw error;
console.error('Error fetching review:', error)
throw error
}
}
};
const sumbitAnswer = async (dataAnswer) => {
const submitAnswer = async dataAnswer => {
try {
const response = await axiosInstance.post(`/stdExercise`, dataAnswer);
return response.data;
const response = await axiosInstance.post(`/stdExercise`, dataAnswer)
return response.data
} catch (error) {
console.error('Error submit exercise:', error);
throw error;
console.error('Error submit exercise:', error)
throw error
}
}
};
const checkStdLearning = async (level) => {
const checkStdLearning = async level => {
try {
const response = await axiosInstance.get(`/stdLearning/level/${level}`);
return response.data.payload.ID_STUDENT_LEARNING;
const response = await axiosInstance.get(`/stdLearning/level/${level}`)
return response.data.payload.ID_STUDENT_LEARNING
} catch (error) {
// console.error('Error checking std_learning:', error);
// throw error;
return null;
return null
}
}
};
const getScore = async (stdLearning) => {
const getScore = async stdLearning => {
try {
const response = await axiosInstance.get(`/stdLearning/score/${stdLearning}`);
return response.data;
const response = await axiosInstance.get(
`/stdLearning/score/${stdLearning}`
)
return response.data
} catch (error) {
console.error('Error fetching result:', error);
throw error;
console.error('Error fetching result:', error)
throw error
}
}
};
export default {
getLevelId,
checkStdLearning,
createStdLearning,
fetchReview,
sumbitAnswer,
getScore
};
submitAnswer,
getScore,
}

View File

@ -1,15 +1,21 @@
import React from 'react';
import { useParams } from 'react-router-dom';
import { useReview } from '../hooks/useReview';
import { Container, Row, Col, ListGroup, Button, OverlayTrigger, Popover } from 'react-bootstrap';
import MultipleChoiceQuestion from './components/MultipleChoiceQuestion';
import TrueFalseQuestion from './components/TrueFalseQuestion';
import MatchingPairsQuestion from './components/MatchingPairsQuestion';
import Skeleton from 'react-loading-skeleton';
import { useParams } from 'react-router-dom'
import { useReview } from '../hooks/useReview'
import {
Container,
Row,
Col,
ListGroup,
Button,
OverlayTrigger,
Popover,
} from 'react-bootstrap'
import MultipleChoiceQuestion from './components/MultipleChoiceQuestion'
import TrueFalseQuestion from './components/TrueFalseQuestion'
import MatchingPairsQuestion from './components/MatchingPairsQuestion'
import Skeleton from 'react-loading-skeleton'
const Review = () => {
const { stdLearning } = useParams();
const { stdLearning } = useParams()
const {
questions,
currentQuestion,
@ -20,7 +26,7 @@ const Review = () => {
error,
nextQuestion,
prevQuestion,
} = useReview(stdLearning);
} = useReview(stdLearning)
const renderQuestion = () => {
switch (currentQuestion.QUESTION_TYPE) {
@ -31,7 +37,7 @@ const Review = () => {
studentAnswer={currentQuestion.ANSWER_STUDENT}
index={currentQuestionIndex}
/>
);
)
case 'TFQ':
return (
<TrueFalseQuestion
@ -39,7 +45,7 @@ const Review = () => {
studentAnswer={currentQuestion.ANSWER_STUDENT}
index={currentQuestionIndex}
/>
);
)
case 'MPQ':
return (
<MatchingPairsQuestion
@ -47,63 +53,101 @@ const Review = () => {
studentAnswer={currentQuestion.ANSWER_STUDENT}
index={currentQuestionIndex}
/>
);
)
default:
return <p>Unknown question type.</p>;
return <p>Unknown question type.</p>
}
}
};
const popover = (
<Popover id="popover-basic">
<Popover.Header as="h4">Tips</Popover.Header>
<Popover.Body className='p-2'>
<ul className='ps-3 m-0'>
<li>Click the <strong>left arrow</strong> key to go to the previous question</li>
<li>Click the <strong>right arrow</strong> key to go to the next question</li>
<Popover.Body className="p-2">
<ul className="ps-3 m-0">
<li>
Click the <strong>left arrow</strong> key to go to the previous
question
</li>
<li>
Click the <strong>right arrow</strong> key to go to the next
question
</li>
</ul>
</Popover.Body>
</Popover>
);
)
if (isLoading) {
return (
<div className="row">
<div className="col-2">
<Skeleton containerClassName='w-100' className='w-100 mb-1 rounded-3' count={6} style={{height:"4vh"}} />
<Skeleton
containerClassName="w-100"
className="w-100 mb-1 rounded-3"
count={6}
style={{ height: '4vh' }}
/>
</div>
<div className="col-10">
<Skeleton containerClassName='w-100' className='w-100 mb-1 rounded-3' style={{height:"5vh"}} />
<Skeleton containerClassName='w-100' className='w-50 mb-1 rounded-3' style={{height:"20vh"}} />
<Skeleton containerClassName='w-100' className='w-100 mb-1 rounded-3' style={{height:"30vh"}} />
<Skeleton
containerClassName="w-100"
className="w-100 mb-1 rounded-3"
style={{ height: '5vh' }}
/>
<Skeleton
containerClassName="w-100"
className="w-50 mb-1 rounded-3"
style={{ height: '20vh' }}
/>
<Skeleton
containerClassName="w-100"
className="w-100 mb-1 rounded-3"
style={{ height: '30vh' }}
/>
</div>
</div>
);
)
}
if (error) return <h1 className='text-center'>Exercise questions not yet available</h1>;
if (error)
return <h1 className="text-center">Exercise questions not yet available</h1>
return (
<Container fluid className='exercise-page'>
<Container fluid className="exercise-page">
<Row>
<Col sm={2}>
<div className='p-3 rounded-4 bg-white'>
<div className="p-3 rounded-4 bg-white">
<div className="mb-3 d-flex justify-content-between align-items-center">
<h4 className='mb-0 text-gd fw-bold'>Review</h4>
<OverlayTrigger trigger="click" placement="right" overlay={popover}>
<h4 className="mb-0 text-gd fw-bold">Review</h4>
<OverlayTrigger
trigger="click"
placement="right"
overlay={popover}
>
<i className=" bi bi-info-circle cursor-pointer text-secondary"></i>
</OverlayTrigger>
</div>
<ListGroup variant="flush" className='review-list'>
<ListGroup variant="flush" className="review-list">
{questions.map((q, index) => (
<ListGroup.Item
key={q.ID_ADMIN_EXERCISE}
active={index === currentQuestionIndex}
onClick={() => setCurrentQuestionIndex(index)}
className={`border-0 rounded-3 number-label fw-bold ${q.IS_CORRECT === 1 ? 'correct' : 'incorrect'}`}
className={`border-0 rounded-3 number-label fw-bold ${
q.IS_CORRECT === 1 ? 'correct' : 'incorrect'
}`}
style={{ cursor: 'pointer' }}
>
<i className={`me-2 bi bi-circle ${answers[index] !== null ? 'd-none' : 'd-block'}`}></i>
<i className={`me-2 bi bi-check2-circle ${answers[index] !== null ? 'd-block' : 'd-none'}`}></i>
<i
className={`me-2 bi bi-circle ${
answers[index] !== null ? 'd-none' : 'd-block'
}`}
></i>
<i
className={`me-2 bi bi-check2-circle ${
answers[index] !== null ? 'd-block' : 'd-none'
}`}
></i>
{index + 1}
</ListGroup.Item>
))}
@ -112,37 +156,38 @@ const Review = () => {
</Col>
<Col sm={10}>
<div className='p-4 rounded-4 bg-white'>
<div className="p-4 rounded-4 bg-white">
<div className="pb-4 d-flex justify-content-between align-items-center">
<Button
variant='outline-blue'
className={`rounded-35 ${currentQuestionIndex === 0 ? 'invisible' : 'visible'}`}
variant="outline-blue"
className={`rounded-35 ${
currentQuestionIndex === 0 ? 'invisible' : 'visible'
}`}
onClick={prevQuestion}
disabled={currentQuestionIndex === 0}
>
<i className="bi bi-arrow-left"></i>
</Button>
<h5 className='m-0'>{`Questions ${currentQuestionIndex + 1} of ${questions.length}`}</h5>
<h5 className="m-0">{`Questions ${currentQuestionIndex + 1} of ${
questions.length
}`}</h5>
<Button
variant="blue"
className={`rounded-35 ${currentQuestionIndex === questions.length - 1 ? 'd-none' : ''}`}
className={`rounded-35 ${
currentQuestionIndex === questions.length - 1 ? 'd-none' : ''
}`}
onClick={nextQuestion}
disabled={currentQuestionIndex === questions.length - 1}
>
Next Questions <i className="bi bi-arrow-right"></i>
</Button>
</div>
<div className='p-3 border rounded-3'>
{renderQuestion()}
</div>
<div className="p-3 border rounded-3">{renderQuestion()}</div>
</div>
</Col>
</Row>
</Container>
);
};
export default Review;
)
}
export default Review