Merge branch 'fix/review-pretest' into 'master'
fix(refactor): Level page review pretest navigator and Refactor history and... See merge request profile-image/kedaireka/polinema-adapative-learning/frontend-adaptive-learning!2
This commit is contained in:
commit
b2c76268f9
|
|
@ -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;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
try {
|
||||
const response = await axiosInstance.get(`/topic`)
|
||||
return response.data
|
||||
} catch (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);
|
||||
|
||||
return {message: response.data.message, payload: validHistory, status: response.data.statusCode,};
|
||||
try {
|
||||
const response = await axiosInstance.get(`/learningHistory`)
|
||||
const historyData = response.data.payload.history
|
||||
const validHistory = historyData.filter(
|
||||
history => history.STUDENT_FINISH !== null
|
||||
)
|
||||
|
||||
} 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) => {
|
||||
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 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
|
||||
)
|
||||
|
||||
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) => {
|
||||
try {
|
||||
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: []};
|
||||
}
|
||||
};
|
||||
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
|
||||
)
|
||||
|
||||
return {
|
||||
message: response.data.message,
|
||||
payload: validHistory,
|
||||
status: response.data.statusCode,
|
||||
}
|
||||
} catch (error) {
|
||||
return { payload: [] }
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
fetchTopic,
|
||||
fetchAllHistory,
|
||||
fetchHistoryBySection,
|
||||
fetchHistoryByTopic
|
||||
};
|
||||
fetchTopic,
|
||||
fetchAllHistory,
|
||||
fetchHistoryBySection,
|
||||
fetchHistoryByTopic,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,90 +1,148 @@
|
|||
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 {
|
||||
historyData,
|
||||
sectionData,
|
||||
loading,
|
||||
error,
|
||||
activeTab,
|
||||
topicsBySection,
|
||||
selectedTopic,
|
||||
setActiveTab,
|
||||
formatLocalDate,
|
||||
compareLevels,
|
||||
setSelectedTopic
|
||||
} = useHistories();
|
||||
const {
|
||||
historyData,
|
||||
sectionData,
|
||||
loading,
|
||||
error,
|
||||
activeTab,
|
||||
topicsBySection,
|
||||
selectedTopic,
|
||||
setActiveTab,
|
||||
formatLocalDate,
|
||||
compareLevels,
|
||||
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(''); }}>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant="pills" className="p-2 bg-white">
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey="all">All</Nav.Link>
|
||||
</Nav.Item>
|
||||
{sectionData.map((section, index) => (
|
||||
<Nav.Item key={index}>
|
||||
<Nav.Link eventKey={section}>{section}</Nav.Link>
|
||||
</Nav.Item>
|
||||
))}
|
||||
</Nav>
|
||||
</Col>
|
||||
</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.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}>
|
||||
{topic.NAME_TOPIC}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)}
|
||||
</Container>
|
||||
<Container fluid className='bg-white rounded rounded-4 p-4'>
|
||||
<Row xs={1}>
|
||||
{loading?(
|
||||
<Col>
|
||||
<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"}} />
|
||||
</div>
|
||||
<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>
|
||||
<Skeleton containerClassName="mb-2" className='mb-1 rounded-4' style={{width:"30vw", height:"5vw"}} />
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
) :
|
||||
historyData.length > 0 ? (
|
||||
historyData.map((history, index) => (
|
||||
<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'>
|
||||
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('')
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Col>
|
||||
<Nav variant="pills" className="p-2 bg-white">
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey="all">All</Nav.Link>
|
||||
</Nav.Item>
|
||||
{sectionData.map((section, index) => (
|
||||
<Nav.Item key={index}>
|
||||
<Nav.Link eventKey={section}>{section}</Nav.Link>
|
||||
</Nav.Item>
|
||||
))}
|
||||
</Nav>
|
||||
</Col>
|
||||
</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.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}
|
||||
>
|
||||
{topic.NAME_TOPIC}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)}
|
||||
</Container>
|
||||
<Container fluid className="bg-white rounded rounded-4 p-4">
|
||||
<Row xs={1}>
|
||||
{loading ? (
|
||||
<Col>
|
||||
<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' }}
|
||||
/>
|
||||
</div>
|
||||
<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>
|
||||
<Skeleton
|
||||
containerClassName="mb-2"
|
||||
className="mb-1 rounded-4"
|
||||
style={{ width: '30vw', height: '5vw' }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
) : historyData.length > 0 ? (
|
||||
historyData.map((history, index) => (
|
||||
<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>
|
||||
</Card.Title>
|
||||
|
|
@ -122,64 +180,85 @@ const ExerciseHistory = () => {
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</Breadcrumb>
|
||||
<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>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="d-flex flex-column justify-content-between align-items-end">
|
||||
{history.IS_PASS == 1?(
|
||||
<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'>
|
||||
<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'>
|
||||
<i className="bi bi-arrow-down me-1"></i>
|
||||
Go Down to {history.NEXT_LEVEL}
|
||||
</h5>
|
||||
) : (
|
||||
<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>
|
||||
</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>
|
||||
<img src={newBie} alt="" />
|
||||
<Button as={Link} to={'/learning/module'} variant='warning' className='mt-4 py-2 px-5 rounded-35'>Explore</Button>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<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>
|
||||
</Breadcrumb>
|
||||
<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>
|
||||
</h3>
|
||||
</div>
|
||||
<div className="d-flex flex-column justify-content-between align-items-end">
|
||||
{history.IS_PASS == 1 ? (
|
||||
<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">
|
||||
<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">
|
||||
<i className="bi bi-arrow-down me-1"></i>
|
||||
Go Down to {history.NEXT_LEVEL}
|
||||
</h5>
|
||||
) : (
|
||||
<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>
|
||||
</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>
|
||||
<img src={newBie} alt="" />
|
||||
<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
|
||||
|
|
|
|||
|
|
@ -1,110 +1,157 @@
|
|||
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>
|
||||
);
|
||||
}
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<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}`}>
|
||||
<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.Item active>{unSlugify(topic)}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{levels.length === 0 ? (
|
||||
<h1>Materi Belum Tersedia</h1>
|
||||
) : (
|
||||
<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' />}
|
||||
<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>
|
||||
<span>{index}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="level-label">
|
||||
<h4 className='m-0 lh-normal'>PRETEST</h4>
|
||||
</div>
|
||||
)}
|
||||
<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>
|
||||
|
||||
return (
|
||||
<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}`}
|
||||
>
|
||||
<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.Item active>{unSlugify(topic)}</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{level.IS_PRETEST === 1 && level.SCORE !== null ?(
|
||||
<div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
|
||||
Review
|
||||
</div>
|
||||
):(
|
||||
!isLevelUnlocked(index) ? (
|
||||
<div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
|
||||
Not Allowed
|
||||
</div>
|
||||
) : (
|
||||
// level.SCORE < 65 ?(
|
||||
// <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 className='py-2 px-5 bg-warning w-fit rounded-35'>
|
||||
// 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`}
|
||||
>
|
||||
Learn Now
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
{levels.length === 0 ? (
|
||||
<h1>Materi Belum Tersedia</h1>
|
||||
) : (
|
||||
<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" />
|
||||
)}
|
||||
<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>
|
||||
<span>{index}</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="level-label">
|
||||
<h4 className="m-0 lh-normal">PRETEST</h4>
|
||||
</div>
|
||||
)}
|
||||
<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>
|
||||
|
||||
{level.IS_PRETEST === 1 && level.SCORE !== null ? (
|
||||
<Button
|
||||
variant="success"
|
||||
className="py-2 px-5 w-fit rounded-35"
|
||||
as={Link}
|
||||
to={`/learning/review/${level.ID_STUDENT_LEARNING}`}
|
||||
>
|
||||
Review
|
||||
</Button>
|
||||
) : !isLevelUnlocked(index) ? (
|
||||
<div className="py-2 px-5 mt-auto w-fit bg-light rounded-35">
|
||||
Not Allowed
|
||||
</div>
|
||||
) : (
|
||||
// level.SCORE < 65 ?(
|
||||
// <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 className='py-2 px-5 bg-warning w-fit rounded-35'>
|
||||
// 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`
|
||||
}
|
||||
>
|
||||
Learn Now
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default Level;
|
||||
export default Level
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
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
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
const createStdLearning = async (data) => {
|
||||
try {
|
||||
const response = await axiosInstance.post(`/stdLearning`, data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error creating std_learning:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const createStdLearning = async data => {
|
||||
try {
|
||||
const response = await axiosInstance.post(`/stdLearning`, data)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Error creating std_learning:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const fetchReview = async (stdLearning) => {
|
||||
try {
|
||||
const response = await axiosInstance.get(`/studentAnswers/${stdLearning}`);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const fetchReview = async stdLearning => {
|
||||
try {
|
||||
const response = await axiosInstance.get(`/studentAnswers/${stdLearning}`)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Error fetching review:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const submitAnswer = async dataAnswer => {
|
||||
try {
|
||||
const response = await axiosInstance.post(`/stdExercise`, dataAnswer)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Error submit exercise:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const checkStdLearning = async level => {
|
||||
try {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
const sumbitAnswer = async (dataAnswer) => {
|
||||
try {
|
||||
const response = await axiosInstance.post(`/stdExercise`, dataAnswer);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error submit exercise:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const checkStdLearning = async (level) => {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const getScore = async (stdLearning) => {
|
||||
try {
|
||||
const response = await axiosInstance.get(`/stdLearning/score/${stdLearning}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching result:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getScore = async stdLearning => {
|
||||
try {
|
||||
const response = await axiosInstance.get(
|
||||
`/stdLearning/score/${stdLearning}`
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.error('Error fetching result:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
getLevelId,
|
||||
checkStdLearning,
|
||||
createStdLearning,
|
||||
fetchReview,
|
||||
|
||||
sumbitAnswer,
|
||||
getScore
|
||||
};
|
||||
getLevelId,
|
||||
checkStdLearning,
|
||||
createStdLearning,
|
||||
fetchReview,
|
||||
submitAnswer,
|
||||
getScore,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,148 +1,193 @@
|
|||
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 {
|
||||
questions,
|
||||
currentQuestion,
|
||||
currentQuestionIndex,
|
||||
setCurrentQuestionIndex,
|
||||
answers,
|
||||
isLoading,
|
||||
error,
|
||||
nextQuestion,
|
||||
prevQuestion,
|
||||
} = useReview(stdLearning);
|
||||
const { stdLearning } = useParams()
|
||||
const {
|
||||
questions,
|
||||
currentQuestion,
|
||||
currentQuestionIndex,
|
||||
setCurrentQuestionIndex,
|
||||
answers,
|
||||
isLoading,
|
||||
error,
|
||||
nextQuestion,
|
||||
prevQuestion,
|
||||
} = useReview(stdLearning)
|
||||
|
||||
const renderQuestion = () => {
|
||||
switch (currentQuestion.QUESTION_TYPE) {
|
||||
case 'MCQ':
|
||||
return (
|
||||
<MultipleChoiceQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
);
|
||||
case 'TFQ':
|
||||
return (
|
||||
<TrueFalseQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
);
|
||||
case 'MPQ':
|
||||
return (
|
||||
<MatchingPairsQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <p>Unknown question type.</p>;
|
||||
}
|
||||
};
|
||||
const renderQuestion = () => {
|
||||
switch (currentQuestion.QUESTION_TYPE) {
|
||||
case 'MCQ':
|
||||
return (
|
||||
<MultipleChoiceQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
)
|
||||
case 'TFQ':
|
||||
return (
|
||||
<TrueFalseQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
)
|
||||
case 'MPQ':
|
||||
return (
|
||||
<MatchingPairsQuestion
|
||||
question={currentQuestion}
|
||||
studentAnswer={currentQuestion.ANSWER_STUDENT}
|
||||
index={currentQuestionIndex}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
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>
|
||||
</ul>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
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>
|
||||
</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"}} />
|
||||
</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"}} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) return <h1 className='text-center'>Exercise questions not yet available</h1>;
|
||||
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' }}
|
||||
/>
|
||||
</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' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Container fluid className='exercise-page'>
|
||||
<Row>
|
||||
<Col sm={2}>
|
||||
<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}>
|
||||
<i className=" bi bi-info-circle cursor-pointer text-secondary"></i>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
<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'}`}
|
||||
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>
|
||||
{index+1}
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col sm={10}>
|
||||
<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'}`}
|
||||
onClick={prevQuestion}
|
||||
disabled={currentQuestionIndex === 0}
|
||||
>
|
||||
<i className="bi bi-arrow-left"></i>
|
||||
</Button>
|
||||
<h5 className='m-0'>{`Questions ${currentQuestionIndex + 1} of ${questions.length}`}</h5>
|
||||
<Button
|
||||
variant="blue"
|
||||
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>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default Review;
|
||||
if (error)
|
||||
return <h1 className="text-center">Exercise questions not yet available</h1>
|
||||
|
||||
return (
|
||||
<Container fluid className="exercise-page">
|
||||
<Row>
|
||||
<Col sm={2}>
|
||||
<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}
|
||||
>
|
||||
<i className=" bi bi-info-circle cursor-pointer text-secondary"></i>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
<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'
|
||||
}`}
|
||||
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>
|
||||
{index + 1}
|
||||
</ListGroup.Item>
|
||||
))}
|
||||
</ListGroup>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col sm={10}>
|
||||
<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'
|
||||
}`}
|
||||
onClick={prevQuestion}
|
||||
disabled={currentQuestionIndex === 0}
|
||||
>
|
||||
<i className="bi bi-arrow-left"></i>
|
||||
</Button>
|
||||
<h5 className="m-0">{`Questions ${currentQuestionIndex + 1} of ${
|
||||
questions.length
|
||||
}`}</h5>
|
||||
<Button
|
||||
variant="blue"
|
||||
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>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default Review
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user