fixing user bug

This commit is contained in:
Dimas Atmodjo 2024-12-05 15:31:12 +07:00
parent b661043755
commit 7e31c5414d
12 changed files with 177 additions and 85 deletions

View File

@ -99,6 +99,10 @@ body {
color: #E9342D; color: #E9342D;
} }
.text-green{
color: #00BC65;
}
.text-muted-50{ .text-muted-50{
color: #A3A3A3; color: #A3A3A3;
} }
@ -267,6 +271,15 @@ body {
color: #ffffff!important; color: #ffffff!important;
} }
.btn-nope{
background-color: #00000000!important;
box-shadow: none!important;
}
.btn-nope:hover{
background-color: #00000007!important;
}
.btn-nav{ .btn-nav{
width: 140px; width: 140px;
} }
@ -358,6 +371,29 @@ body {
font-size: 24px; font-size: 24px;
} }
.drop-zone {
cursor: pointer;
background-color: #00000000;
width: 100%;
height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 2px dashed #dee2e6;
transition: border-color 0.3s ease-in-out;
}
.drop-zone:not(.active):hover{
background-color: #00000007!important;
}
.drop-zone.dragging,
.drop-zone.active {
background-color: #0cb61518;
border: 2px dashed #00BC65;
}
@media (max-width: 992px) { @media (max-width: 992px) {
.ilustration-img{ .ilustration-img{

View File

@ -13,6 +13,7 @@ import Level from './level/views/Level';
import Exercise from './exercise/views/Exercise'; import Exercise from './exercise/views/Exercise';
import ExerciseResult from './exercise/views/ExerciseResult'; import ExerciseResult from './exercise/views/ExerciseResult';
import ExerciseHistory from './history/views/ExerciseHistory'; import ExerciseHistory from './history/views/ExerciseHistory';
import Review from './review/views/Review';
import Material from './material/views/Material'; import Material from './material/views/Material';
import ProtectedRoute from '../../utils/ProtectedRoute'; import ProtectedRoute from '../../utils/ProtectedRoute';
@ -35,6 +36,7 @@ const UserRoutes = () => {
<Route path="module" element={<Learning />} /> <Route path="module" element={<Learning />} />
<Route path="setting" element={<Setting />} /> <Route path="setting" element={<Setting />} />
<Route path="history" element={<ExerciseHistory />} /> <Route path="history" element={<ExerciseHistory />} />
<Route path="review/:stdLearning" element={<Review />} />
<Route path="module/:section" element={<Topic />} /> <Route path="module/:section" element={<Topic />} />
<Route path="module/:section/:topic" element={<Level />} /> <Route path="module/:section/:topic" element={<Level />} />

View File

@ -98,7 +98,7 @@ export const useExercises = (topic, level) => {
} catch (error) { } catch (error) {
console.error(error); console.error(error);
}finally{ }finally{
navigate(`/learning/module/${section}/${topic}/${level}/result`); // navigate(`/learning/module/${section}/${topic}/${level}/result`);
} }
}; };

View File

@ -106,7 +106,7 @@ const Exercise = () => {
<Col sm={2}> <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"> <div className="mb-3 d-flex justify-content-between align-items-center">
<h4 className='mb-0 text-gd fw-bold'>Pretest</h4> <h4 className='mb-0 text-gd fw-bold'>Exercise</h4>
<OverlayTrigger trigger="click" placement="right" overlay={popover}> <OverlayTrigger trigger="click" placement="right" overlay={popover}>
<i className=" bi bi-info-circle cursor-pointer text-secondary"></i> <i className=" bi bi-info-circle cursor-pointer text-secondary"></i>
</OverlayTrigger> </OverlayTrigger>

View File

@ -96,7 +96,6 @@ const MatchingPairsQuestion = ({ question, onAnswer, studentAnswer, index }) =>
const selectedRightValue = rightOptions[rightIndex]; const selectedRightValue = rightOptions[rightIndex];
newPairs[leftIndex].right = selectedRightValue; newPairs[leftIndex].right = selectedRightValue;
setPairs(newPairs); setPairs(newPairs);
// console.log(newPairs);
setSelectedLeft(null); setSelectedLeft(null);
setSelectedRight(null); setSelectedRight(null);
@ -104,9 +103,15 @@ const MatchingPairsQuestion = ({ question, onAnswer, studentAnswer, index }) =>
const allPairsMatched = newPairs.every((pair) => pair.right !== ''); const allPairsMatched = newPairs.every((pair) => pair.right !== '');
if (allPairsMatched && !isComplete) { if (allPairsMatched && !isComplete) {
setIsComplete(true); setIsComplete(true);
const rightAnswers = newPairs.map((pair) => pair.right); const rightAnswers = newPairs.map((pair) => pair.right);
onAnswer(index, arrayToString(rightAnswers), question.ID_ADMIN_EXERCISE);
const stringPairs = newPairs
.map(pair => `${pair.left}>${pair.right}`)
.join("| ");
// onAnswer(index, arrayToString(rightAnswers), question.ID_ADMIN_EXERCISE);
onAnswer(index, stringPairs, question.ID_ADMIN_EXERCISE);
} }
}; };

View File

@ -58,6 +58,7 @@ const useHistories = () => {
} else { } else {
data = await serviceHistory.fetchHistoryBySection(sectionSlugMap[activeTab]); data = await serviceHistory.fetchHistoryBySection(sectionSlugMap[activeTab]);
} }
console.log(data.payload);
setHistoryData(data.payload); setHistoryData(data.payload);
} catch (error) { } catch (error) {
if (error.status == 404) { if (error.status == 404) {

View File

@ -25,10 +25,10 @@ const fetchAllHistory = async () => {
const fetchHistoryBySection = async (section) => { const fetchHistoryBySection = async (section) => {
try { try {
const response = await axiosInstance.get(`/learningHistory/section/${section}`); const response = await axiosInstance.get(`/learningHistory/section/${section}`);
const historyData = response.data.payload; const historyData = response.data.payload.history;
const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null); const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null);
return {message: response.data.message, payload: validHistory, status: response.data.statusCode,}; return {message: response.data.message, payload: validHistory, status: response.data.statusCode};
} catch (error) { } catch (error) {
return {payload: []}; return {payload: []};
} }
@ -37,7 +37,7 @@ const fetchHistoryBySection = async (section) => {
const fetchHistoryByTopic = async (topic) => { const fetchHistoryByTopic = async (topic) => {
try { try {
const response = await axiosInstance.get(`/learningHistory/topic/${topic}`); const response = await axiosInstance.get(`/learningHistory/topic/${topic}`);
const historyData = response.data.payload; const historyData = response.data.payload.history;
const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null); const validHistory = historyData.filter(history => history.STUDENT_FINISH !== null);
return {message: response.data.message, payload: validHistory, status: response.data.statusCode,}; return {message: response.data.message, payload: validHistory, status: response.data.statusCode,};

View File

@ -28,23 +28,21 @@ const ExerciseHistory = () => {
<div className='exercise-history'> <div className='exercise-history'>
<h3 className='fw-bold text-blue'>Your Exercise History!</h3> <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> <p className='text-secondary mb-4'>Track your progress with a personalized overview of all your workouts.</p>
<Container className='mb-4 d-flex align-items-center bg-white rounded rounded-4'> <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(''); }}> <Tab.Container id="left-tabs-example" defaultActiveKey="all" onSelect={(k) => { setActiveTab(k); setSelectedTopic(''); }}>
<Row> <Row>
<Col> <Col>
{/* <h3 className='fw-bold text-blue'>Your Exercise History!</h3> <Nav variant="pills" className="p-2 bg-white">
<p className='text-secondary mb-4'>Track your progress with a personalized overview of all your workouts.</p> */} <Nav.Item>
<Nav variant="pills" className="p-2 bg-white"> <Nav.Link eventKey="all">All</Nav.Link>
<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.Item>
))} {sectionData.map((section, index) => (
</Nav> <Nav.Item key={index}>
</Col> <Nav.Link eventKey={section}>{section}</Nav.Link>
</Nav.Item>
))}
</Nav>
</Col>
</Row> </Row>
</Tab.Container> </Tab.Container>
{topics.length > 0 && ( {topics.length > 0 && (
@ -86,40 +84,84 @@ const ExerciseHistory = () => {
historyData.map((history, index) => ( historyData.map((history, index) => (
<Col key={index} className='mb-3'> <Col key={index} className='mb-3'>
<Card className='flex-row p-3 border-primary rounded-4'> <Card className='flex-row p-3 border-primary rounded-4'>
<Card.Body className='p-0 d-flex flex-column justify-content-between'> {/* <Card.Body className='p-0 d-flex flex-column justify-content-between'>
<Card.Title className='mb-1 w-100 d-flex justify-content-between'> <Card.Title className='mb-1 w-100 d-flex justify-content-between'>
<h4 className='mb-0'>Exercise</h4> <h4 className='mb-0'>{history.CURRENT_LEVEL}</h4>
<p className='mb-0 submit-time'>Submission: {formatLocalDate(history.STUDENT_FINISH)}</p>
</Card.Title> </Card.Title>
<Breadcrumb className='custom-breadcrumb'> <Breadcrumb className='custom-breadcrumb'>
<Breadcrumb.Item>{history.SECTION_NAME}</Breadcrumb.Item> <Breadcrumb.Item>{history.SECTION_NAME}</Breadcrumb.Item>
<Breadcrumb.Item>{history.TOPIC_NAME}</Breadcrumb.Item> <Breadcrumb.Item active>{history.TOPIC_NAME}</Breadcrumb.Item>
<Breadcrumb.Item active>{history.CURRENT_LEVEL}</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<p className='mt-2 mb-0 submit-time'>Submission: {formatLocalDate(history.STUDENT_FINISH)}</p>
<div className="w-100 d-flex align-items-center mt-2"> <div className="w-100 d-flex align-items-center mt-2">
<h3 className='m-0'> <h3 className='m-0'>
<i className="bi bi-trophy-fill text-warning"></i> <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> </h3>
{compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'jump' ?( <div className="ms-auto">
<div className='bg-warning ms-auto py-3 px-5 rounded-35'> {compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'jump' ?(
<i className="bi bi-arrow-up me-1"></i> <div className='bg-warning py-3 px-5 rounded-35'>
Jump to Level {history.NEXT_LEVEL} <i className="bi bi-arrow-up me-1"></i>
</div> Jump to Level {history.NEXT_LEVEL}
</div>
) : (
compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'down' ?(
<div className='bg-red text-white ms-auto py-3 px-5 rounded-35'>
<i className="bi bi-arrow-down me-1"></i>
Go Down to Level {history.NEXT_LEVEL}
</div>
) : ( ) : (
<div className='bg-blue text-white ms-auto py-3 px-5 rounded-35'> compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'down' ?(
<i className="bi bi-arrow-repeat me-1"></i> <div className='bg-red text-white py-3 px-5 rounded-35'>
Stay in Level {history.NEXT_LEVEL} <i className="bi bi-arrow-down me-1"></i>
</div> Go Down to Level {history.NEXT_LEVEL}
</div>
) : (
<div className='bg-blue text-white py-3 px-5 rounded-35'>
<i className="bi bi-arrow-repeat me-1"></i>
Stay in Level {history.NEXT_LEVEL}
</div>
)
)}
<Link to={`/review/${history.ID_STUDENT_LEARNING}`} className='btn btn-blue'>Review</Link>
</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> </div>
</Card.Body> </Card.Body>
</Card> </Card>

View File

@ -59,7 +59,8 @@ const useLevels = (section, topic) => {
const isLevelUnlocked = (levelIndex) => { const isLevelUnlocked = (levelIndex) => {
if (levelIndex !== 0) { if (levelIndex !== 0) {
if (levels[levelIndex].CONTENT) { console.log(levels[levelIndex]);
if ("CONTENT" in levels[levelIndex]) {
return true; return true;
}else{ }else{
return false; return false;

View File

@ -66,28 +66,38 @@ const Level = () => {
<p className='mb-3 text-white'> <p className='mb-3 text-white'>
{levelDesc[index]} {levelDesc[index]}
</p> </p>
{!isLevelUnlocked(index) ? (
{level.IS_PRETEST === 1 && level.SCORE !== null ?(
<div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'> <div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
Not Allowed Review
</div> </div>
) : ( ):(
// level.SCORE < 65 ?( !isLevelUnlocked(index) ? (
// <Button variant='warning' className='py-2 px-5 w-fit rounded-35' <div className='py-2 px-5 mt-auto w-fit bg-light rounded-35'>
// as={Link} to={level.IS_PRETEST === 1 ? `/learning/module/${section}/${topic}/pretest/material` : `/learning/module/${section}/${topic}/level-${index}/material`} Not Allowed
// > </div>
// Learn Now ) : (
// </Button> // level.SCORE < 65 ?(
// ) : ( // <Button variant='warning' className='py-2 px-5 w-fit rounded-35'
// <div className='py-2 px-5 bg-warning w-fit rounded-35'> // as={Link} to={level.IS_PRETEST === 1 ? `/learning/module/${section}/${topic}/pretest/material` : `/learning/module/${section}/${topic}/level-${index}/material`}
// Finished // >
// </div> // Learn Now
// ) // </Button>
<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`} // <div className='py-2 px-5 bg-warning w-fit rounded-35'>
> // Finished
Learn Now // </div>
</Button> // )
<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> </div>
</Col> </Col>
))} ))}

View File

@ -1,5 +1,4 @@
import axios from 'axios'; import axiosInstance from '../../../../utils/axiosInstance';
import { API_URL } from '../../../../utils/Constant';
const config = { const config = {
headers: { headers: {
@ -9,7 +8,7 @@ const config = {
const fetchProfile = async () => { const fetchProfile = async () => {
try { try {
const response = await axios.get(`${API_URL}/getMe`, config); const response = await axiosInstance.get(`/getMe`, config);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;
@ -17,16 +16,8 @@ const fetchProfile = async () => {
}; };
const updateProfile = async (id, formData) => { const updateProfile = async (id, formData) => {
const cfg = {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: localStorage.getItem('token')
},
};
try { try {
const response = await axios.put(`${API_URL}/user/update/${id}`, formData, cfg); const response = await axiosInstance.put(`/user/update/${id}`, formData);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;
@ -34,14 +25,8 @@ const updateProfile = async (id, formData) => {
}; };
const updatePassword = async (userId, passwordData) => { const updatePassword = async (userId, passwordData) => {
const cfg = {
headers: {
Authorization: localStorage.getItem('token'),
'Content-Type': 'application/json',
},
};
try { try {
const response = await axios.put(`${API_URL}/user/update/password/${userId}`, passwordData, cfg); const response = await axiosInstance.put(`/user/update/password/${userId}`, passwordData);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -1,6 +1,10 @@
export const API_URL = 'http://54.173.167.62/api'; export const API_URL = 'http://54.173.167.62/api';
export const MEDIA_URL = 'http://54.173.167.62/api/uploads'; export const MEDIA_URL = 'http://54.173.167.62/api/uploads';
export let headerSection = 'section';
export let headerTopic = 'topic';
export let headerLevel = 'level';
export const slugify = (text) => { export const slugify = (text) => {
if (!text) { if (!text) {
return ''; return '';
@ -25,4 +29,10 @@ export const unSlugify = (text) => {
.toLowerCase() .toLowerCase()
.replace(/-/g, ' ') .replace(/-/g, ' ')
.replace(/\b\w/g, (char) => char.toUpperCase()); .replace(/\b\w/g, (char) => char.toUpperCase());
}; };
export const setReviewHeader = (section, topic, level) => {
headerSection = section;
headerTopic = topic;
headerLevel = level;
}