From f44fe0a9b940594530265ed401142ed9347d98bb Mon Sep 17 00:00:00 2001 From: Resh Date: Wed, 11 Dec 2024 10:44:47 +0700 Subject: [PATCH] fix(refactor): Level page review pretest navigator and Refactor history and review services for improved error handling and code consistency --- .../user/history/services/historyService.jsx | 109 +++--- .../user/history/views/ExerciseHistory.jsx | 363 +++++++++++------- src/roles/user/level/views/Level.jsx | 245 +++++++----- .../user/review/services/reviewService.jsx | 140 +++---- src/roles/user/review/views/Review.jsx | 323 +++++++++------- 5 files changed, 686 insertions(+), 494 deletions(-) diff --git a/src/roles/user/history/services/historyService.jsx b/src/roles/user/history/services/historyService.jsx index 8ee7044..4c469d4 100644 --- a/src/roles/user/history/services/historyService.jsx +++ b/src/roles/user/history/services/historyService.jsx @@ -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 -}; \ No newline at end of file + fetchTopic, + fetchAllHistory, + fetchHistoryBySection, + fetchHistoryByTopic, +} diff --git a/src/roles/user/history/views/ExerciseHistory.jsx b/src/roles/user/history/views/ExerciseHistory.jsx index 3e71541..40cf4ca 100644 --- a/src/roles/user/history/views/ExerciseHistory.jsx +++ b/src/roles/user/history/views/ExerciseHistory.jsx @@ -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

{error}

; + if (error) return

{error}

- const topics = activeTab !== 'all' ? topicsBySection[activeTab] || []: []; + const topics = activeTab !== 'all' ? topicsBySection[activeTab] || [] : [] - return ( -
-

Your Exercise History!

-

Track your progress with a personalized overview of all your workouts.

- - { setActiveTab(k); setSelectedTopic(''); }}> - - - - - - - {topics.length > 0 && ( - setSelectedTopic(topicId)}> - - {selectedTopic ? topics.find(t => t.ID_TOPIC === selectedTopic)?.NAME_TOPIC : 'Select a Topic'} - - - {topics.map((topic, index) => ( - - {topic.NAME_TOPIC} - - ))} - - - )} - - - - {loading?( - - -
- - -
- -
-
- - -
- -
-
- - ) : - historyData.length > 0 ? ( - historyData.map((history, index) => ( - - - {/* + return ( +
+

Your Exercise History!

+

+ Track your progress with a personalized overview of all your workouts. +

+ + { + setActiveTab(k) + setSelectedTopic('') + }} + > + + + + + + + {topics.length > 0 && ( + setSelectedTopic(topicId)} + > + + {selectedTopic + ? topics.find(t => t.ID_TOPIC === selectedTopic)?.NAME_TOPIC + : 'Select a Topic'} + + + {topics.map((topic, index) => ( + + {topic.NAME_TOPIC} + + ))} + + + )} + + + + {loading ? ( + + +
+ + +
+ +
+
+ + +
+ +
+
+ + ) : historyData.length > 0 ? ( + historyData.map((history, index) => ( + + + {/*

{history.CURRENT_LEVEL}

@@ -122,64 +180,85 @@ const ExerciseHistory = () => {
*/} - -
-

{history.CURRENT_LEVEL}

- - {history.SECTION_NAME} - {history.TOPIC_NAME} - -

Submission: {formatLocalDate(history.STUDENT_FINISH)}

-

- - {history.SCORE}/100 -

-
-
- {history.IS_PASS == 1?( -
- - Topic Finished! -
- ):( - compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'jump' ?( -
- - Jump to {history.NEXT_LEVEL} -
- ) : ( - compareLevels(history.CURRENT_LEVEL, history.NEXT_LEVEL) === 'down' ?( -
- - Go Down to {history.NEXT_LEVEL} -
- ) : ( -
- - Stay in {history.NEXT_LEVEL} -
- ) - ) - )} - Review -
-
- - - )) - ) : ( - -

Still new?

-

Begin your journey!

- - - - ) - } - - - - ); -}; + +
+

{history.CURRENT_LEVEL}

+ + + {history.SECTION_NAME} + + + {history.TOPIC_NAME} + + +

+ Submission: {formatLocalDate(history.STUDENT_FINISH)} +

+

+ + + {history.SCORE}/100 + +

+
+
+ {history.IS_PASS == 1 ? ( +
+ + Topic Finished! +
+ ) : compareLevels( + history.CURRENT_LEVEL, + history.NEXT_LEVEL + ) === 'jump' ? ( +
+ + Jump to {history.NEXT_LEVEL} +
+ ) : compareLevels( + history.CURRENT_LEVEL, + history.NEXT_LEVEL + ) === 'down' ? ( +
+ + Go Down to {history.NEXT_LEVEL} +
+ ) : ( +
+ + Stay in {history.NEXT_LEVEL} +
+ )} + + Review + +
+
+ + + )) + ) : ( + +

Still new?

+

Begin your journey!

+ + + + )} + + + + ) +} -export default ExerciseHistory; +export default ExerciseHistory diff --git a/src/roles/user/level/views/Level.jsx b/src/roles/user/level/views/Level.jsx index dc925d7..9dd6143 100644 --- a/src/roles/user/level/views/Level.jsx +++ b/src/roles/user/level/views/Level.jsx @@ -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 ( -
- - - -
- ); - } + if (loading) { + return ( +
+ + + +
+ ) + } - if (error) { - return
Error: {error.message}
; - } + if (error) { + return
Error: {error.message}
+ } - return ( - - - - - - Learning - {unSlugify(section)} - {unSlugify(topic)} - - - - - {levels.length === 0 ? ( -

Materi Belum Tersedia

- ) : ( - - {levels.map((level, index) => ( - -
- {(!isLevelUnlocked(index) && level.SCORE > 40) && } -
- {level.IS_PRETEST === 0 ?( -
-

LEVEL

- {index} -
- ) : ( -
-

PRETEST

-
- )} -
Score {level.SCORE === null ? 0 : level.SCORE}/100
-
-

- {levelDesc[index]} -

- + return ( + + + + + + + Learning + + + {unSlugify(section)} + + {unSlugify(topic)} + + + - {level.IS_PRETEST === 1 && level.SCORE !== null ?( -
- Review -
- ):( - !isLevelUnlocked(index) ? ( -
- Not Allowed -
- ) : ( - // level.SCORE < 65 ?( - // - // ) : ( - //
- // Finished - //
- // ) - - ) - )} + {levels.length === 0 ? ( +

Materi Belum Tersedia

+ ) : ( + + {levels.map((level, index) => ( + +
+ {!isLevelUnlocked(index) && level.SCORE > 40 && ( + + )} +
+ {level.IS_PRETEST === 0 ? ( +
+

LEVEL

+ {index} +
+ ) : ( +
+

PRETEST

+
+ )} +
+ Score {level.SCORE === null ? 0 : level.SCORE}/100 +
+
+

{levelDesc[index]}

+ {level.IS_PRETEST === 1 && level.SCORE !== null ? ( + + ) : !isLevelUnlocked(index) ? ( +
+ Not Allowed +
+ ) : ( + // level.SCORE < 65 ?( + // + // ) : ( + //
+ // Finished + //
+ // ) + + )} +
+ + ))} +
+ )} +
+ ) +} -
- - ))} -
- )} -
- ); -}; - -export default Level; \ No newline at end of file +export default Level diff --git a/src/roles/user/review/services/reviewService.jsx b/src/roles/user/review/services/reviewService.jsx index e4bc055..6812076 100644 --- a/src/roles/user/review/services/reviewService.jsx +++ b/src/roles/user/review/services/reviewService.jsx @@ -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, +} diff --git a/src/roles/user/review/views/Review.jsx b/src/roles/user/review/views/Review.jsx index a170323..bc6516a 100644 --- a/src/roles/user/review/views/Review.jsx +++ b/src/roles/user/review/views/Review.jsx @@ -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 ( - - ); - case 'TFQ': - return ( - - ); - case 'MPQ': - return ( - - ); - default: - return

Unknown question type.

; - } - }; + const renderQuestion = () => { + switch (currentQuestion.QUESTION_TYPE) { + case 'MCQ': + return ( + + ) + case 'TFQ': + return ( + + ) + case 'MPQ': + return ( + + ) + default: + return

Unknown question type.

+ } + } - const popover = ( - - Tips - -
    -
  • Click the left arrow key to go to the previous question
  • -
  • Click the right arrow key to go to the next question
  • -
-
-
- ); + const popover = ( + + Tips + +
    +
  • + Click the left arrow key to go to the previous + question +
  • +
  • + Click the right arrow key to go to the next + question +
  • +
+
+
+ ) - if (isLoading) { - return ( -
-
- -
-
- - - -
-
- ); - } - - if (error) return

Exercise questions not yet available

; + if (isLoading) { + return ( +
+
+ +
+
+ + + +
+
+ ) + } - return ( - - - -
-
-

Review

- - - -
- - {questions.map((q, index) => ( - setCurrentQuestionIndex(index)} - className={`border-0 rounded-3 number-label fw-bold ${q.IS_CORRECT === 1 ? 'correct' : 'incorrect'}`} - style={{ cursor: 'pointer' }} - > - - - {index+1} - - ))} - -
- - - -
-
- -
{`Questions ${currentQuestionIndex + 1} of ${questions.length}`}
- -
-
- {renderQuestion()} -
-
- -
-
- ); -}; - -export default Review; + if (error) + return

Exercise questions not yet available

+ return ( + + + +
+
+

Review

+ + + +
+ + {questions.map((q, index) => ( + setCurrentQuestionIndex(index)} + className={`border-0 rounded-3 number-label fw-bold ${ + q.IS_CORRECT === 1 ? 'correct' : 'incorrect' + }`} + style={{ cursor: 'pointer' }} + > + + + {index + 1} + + ))} + +
+ + +
+
+ +
{`Questions ${currentQuestionIndex + 1} of ${ + questions.length + }`}
+ +
+
{renderQuestion()}
+
+ +
+
+ ) +} +export default Review