From be9aba3355f09a4afbc3cc27a8e937cfa7d76594 Mon Sep 17 00:00:00 2001 From: Dimas Atmodjo Date: Sun, 3 Nov 2024 12:58:24 +0700 Subject: [PATCH] -u level & exercise hooks --- src/roles/user/UserRoutes.jsx | 4 - .../user/exercise/hooks/useExercises.jsx | 4 +- .../exercise/services/exerciseService.jsx | 1 - src/roles/user/exercise/views/Exercise.jsx | 2 +- .../exerciseCombine/hooks/useExercises.jsx | 119 ------- .../services/exerciseService.jsx | 72 ---- .../user/exerciseCombine/views/Exercise.jsx | 207 ----------- .../views/components/ExerciseMedia.jsx | 44 --- .../components/MatchingPairsQuestion.jsx | 227 ------------ .../components/MultipleChoiceQuestion.jsx | 64 ---- .../views/components/TrueFalseQuestion.jsx | 58 --- .../user/exerciseOld/hooks/useExercises.jsx | 248 ------------- .../exerciseOld/hooks/useMatchingPairs.jsx | 88 ----- .../user/exerciseOld/hooks/useResults.jsx | 93 ----- .../exerciseOld/services/exerciseService.jsx | 87 ----- src/roles/user/exerciseOld/views/Exercise.jsx | 331 ------------------ .../user/exerciseOld/views/ExerciseResult.jsx | 134 ------- .../views/components/MatchingPair.jsx | 139 -------- .../user/exerciseTest/hooks/useExercise.jsx | 95 ----- .../services/exerciseServices.jsx | 72 ---- .../user/exerciseTest/views/Exercise.jsx | 98 ------ .../views/MatchingPairsQuestion.jsx | 222 ------------ .../views/MultipleChoiceQuestion.jsx | 53 --- .../exerciseTest/views/TrueFalseQuestion.jsx | 45 --- src/roles/user/level/hooks/useLevels.jsx | 11 +- src/roles/user/level/views/Level.jsx | 9 - 26 files changed, 8 insertions(+), 2519 deletions(-) delete mode 100644 src/roles/user/exerciseCombine/hooks/useExercises.jsx delete mode 100644 src/roles/user/exerciseCombine/services/exerciseService.jsx delete mode 100644 src/roles/user/exerciseCombine/views/Exercise.jsx delete mode 100644 src/roles/user/exerciseCombine/views/components/ExerciseMedia.jsx delete mode 100644 src/roles/user/exerciseCombine/views/components/MatchingPairsQuestion.jsx delete mode 100644 src/roles/user/exerciseCombine/views/components/MultipleChoiceQuestion.jsx delete mode 100644 src/roles/user/exerciseCombine/views/components/TrueFalseQuestion.jsx delete mode 100644 src/roles/user/exerciseOld/hooks/useExercises.jsx delete mode 100644 src/roles/user/exerciseOld/hooks/useMatchingPairs.jsx delete mode 100644 src/roles/user/exerciseOld/hooks/useResults.jsx delete mode 100644 src/roles/user/exerciseOld/services/exerciseService.jsx delete mode 100644 src/roles/user/exerciseOld/views/Exercise.jsx delete mode 100644 src/roles/user/exerciseOld/views/ExerciseResult.jsx delete mode 100644 src/roles/user/exerciseOld/views/components/MatchingPair.jsx delete mode 100644 src/roles/user/exerciseTest/hooks/useExercise.jsx delete mode 100644 src/roles/user/exerciseTest/services/exerciseServices.jsx delete mode 100644 src/roles/user/exerciseTest/views/Exercise.jsx delete mode 100644 src/roles/user/exerciseTest/views/MatchingPairsQuestion.jsx delete mode 100644 src/roles/user/exerciseTest/views/MultipleChoiceQuestion.jsx delete mode 100644 src/roles/user/exerciseTest/views/TrueFalseQuestion.jsx diff --git a/src/roles/user/UserRoutes.jsx b/src/roles/user/UserRoutes.jsx index f0d6ac7..f1590c7 100644 --- a/src/roles/user/UserRoutes.jsx +++ b/src/roles/user/UserRoutes.jsx @@ -21,8 +21,6 @@ import { SlugProvider } from '../../utils/SlugContext'; import '../../assets/styles/user.css'; import useAuth from '../guest/auth/hooks/useAuth'; -import ExerciseTest from './exerciseCombine/views/Exercise'; - const UserRoutes = () => { const { logout } = useAuth(); return ( @@ -44,8 +42,6 @@ const UserRoutes = () => { } /> } /> } /> - - } /> diff --git a/src/roles/user/exercise/hooks/useExercises.jsx b/src/roles/user/exercise/hooks/useExercises.jsx index 35555fb..7ad526a 100644 --- a/src/roles/user/exercise/hooks/useExercises.jsx +++ b/src/roles/user/exercise/hooks/useExercises.jsx @@ -27,7 +27,7 @@ export const useExercises = (topic, level) => { const data = await exerciseService.fetchExercise(getLevelId); setQuestions(data); - if (localStorage.getItem(getLevelId)) { + if (localStorage.getItem(stdLearning.payload.ID_STUDENT_LEARNING)) { // const savedAnswers = JSON.parse(localStorage.getItem(getLevelId)); const savedAnswers = JSON.parse(localStorage.getItem(stdLearning.payload.ID_STUDENT_LEARNING)); setAnswers(savedAnswers); @@ -92,7 +92,7 @@ export const useExercises = (topic, level) => { }; const handleConfirmSubmit = async () => { - const answer = JSON.parse(localStorage.getItem(levelId)); + const answer = JSON.parse(localStorage.getItem(learningId)); try { const sendAnswer = await exerciseService.sumbitAnswer({ answers: answer }); } catch (error) { diff --git a/src/roles/user/exercise/services/exerciseService.jsx b/src/roles/user/exercise/services/exerciseService.jsx index a76ac61..81546e7 100644 --- a/src/roles/user/exercise/services/exerciseService.jsx +++ b/src/roles/user/exercise/services/exerciseService.jsx @@ -36,7 +36,6 @@ const fetchExercise = async (idLevel) => { return sortedData; } catch (error) { - console.error('Error fetching exercise data:', error); throw error; } }; diff --git a/src/roles/user/exercise/views/Exercise.jsx b/src/roles/user/exercise/views/Exercise.jsx index 5ed9d63..90cb23c 100644 --- a/src/roles/user/exercise/views/Exercise.jsx +++ b/src/roles/user/exercise/views/Exercise.jsx @@ -98,7 +98,7 @@ const Exercise = () => { ); } - if (error) return

Error loading questions.

; + if (error) return

Exercise questions not yet available

; return ( diff --git a/src/roles/user/exerciseCombine/hooks/useExercises.jsx b/src/roles/user/exerciseCombine/hooks/useExercises.jsx deleted file mode 100644 index 0e2d850..0000000 --- a/src/roles/user/exerciseCombine/hooks/useExercises.jsx +++ /dev/null @@ -1,119 +0,0 @@ -import { useState, useEffect } from 'react'; -import exerciseService from '../services/exerciseService'; -import { useSlugContext } from '../../../../utils/SlugContext'; -import { unSlugify } from '../../../../utils/Constant'; - -export const useExercise = (topic, level) => { - const [questions, setQuestions] = useState([]); - const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); - const [answers, setAnswers] = useState({}); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [levelId, setLevelId] = useState(null); - const [learningId, setLearningId] = useState(null); - const { topicSlugMap } = useSlugContext(); - - const fetchData = async () => { - try { - const getLevelId = await exerciseService.getLevelId(topicSlugMap[topic], unSlugify(level)); - setLevelId(getLevelId); - const stdLearning = await exerciseService.checkStdLearning(getLevelId); - if (stdLearning === null) { - const newLearningId = await exerciseService.createStdLearning({ ID_LEVEL: getLevelId }); - setLearningId(newLearningId); - }else{ - setLearningId(stdLearning); - } - - - const data = await exerciseService.fetchExercise(getLevelId); - setQuestions(data); - if (localStorage.getItem(getLevelId)) { - const savedAnswers = JSON.parse(localStorage.getItem(getLevelId)); - setAnswers(savedAnswers); - }else{ - setAnswers(new Array(data.length).fill(null)); - } - } catch (error) { - console.error("something wrong : ", error); - setError(error); - }finally{ - setIsLoading(false); - } - }; - - useEffect(() => { - fetchData(); - }, [topic, level]); - - const handleAnswer = (index, answer) => { - // const newAnswers = { ...answers, [index]: answer }; - const newAnswers = [...answers]; - newAnswers[index] = answer; - setAnswers(newAnswers); - localStorage.setItem(levelId, JSON.stringify(newAnswers)); - }; - - const handleMatchingPairs = (questionId, pairs) => { - const isComplete = pairs.every(pair => pair.left && pair.right); - if (isComplete) { - handleAnswer(questionId, pairs); - } - }; - - const nextQuestion = () => { - if (currentQuestionIndex < questions.length - 1) { - setCurrentQuestionIndex(currentQuestionIndex + 1); - } - }; - - const prevQuestion = () => { - if (currentQuestionIndex > 0) { - setCurrentQuestionIndex(currentQuestionIndex - 1); - } - }; - - useEffect(() => { - const handleKeyDown = (event) => { - if (event.key === 'ArrowLeft') { - prevQuestion(); - } else if (event.key === 'ArrowRight') { - nextQuestion(); - } - }; - - window.addEventListener('keydown', handleKeyDown); - - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [prevQuestion, nextQuestion]); - - const handleSubmit = () => { - const allAnswered = answers.every((answer) => answer !== null); - return allAnswered; - }; - - const handleConfirmSubmit = () => { - setShowModal(false); - navigate(`/learning/module/${section}/${topic}/${level}/result`, { - state: { answers } - }); - }; - - return { - questions, - currentQuestion: questions[currentQuestionIndex], - currentQuestionIndex, - setCurrentQuestionIndex, - answers, - isLoading, - error, - handleAnswer, - handleMatchingPairs, - nextQuestion, - prevQuestion, - handleSubmit, - handleConfirmSubmit - }; -}; diff --git a/src/roles/user/exerciseCombine/services/exerciseService.jsx b/src/roles/user/exerciseCombine/services/exerciseService.jsx deleted file mode 100644 index 9b4056b..0000000 --- a/src/roles/user/exerciseCombine/services/exerciseService.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import axios from 'axios'; -import { API_URL } from '../../../../utils/Constant'; - -const config = { - headers: { - Authorization: localStorage.getItem('token') - } -}; - -const getSoalNumber = (title) => { - const match = title.match(/\d+$/); // Mencari angka di akhir string - return match ? parseInt(match[0], 10) : 0; // Mengembalikan angka atau 0 jika tidak ditemukan -}; - -// Fungsi untuk cek apakah std_learning sudah ada -const checkStdLearning = async (level) => { - try { - const response = await axios.get(`${API_URL}/stdLearning/level/${level}`, config); - return response.data; - } catch (error) { - // console.error('Error checking std_learning:', error); - // throw error; - return null; - } -}; - -const getLevelId = async (topicId, levelName) => { - try { - const response = await axios.get(`${API_URL}/level/topic/${topicId}`, config); - const filteredData = response.data.data.levels.filter(item => item.NAME_LEVEL === levelName); - return filteredData[0].ID_LEVEL; - } catch (error) { - return []; - } -}; - -// Fungsi untuk membuat std_learning -const createStdLearning = async (data) => { - try { - const response = await axios.post(`${API_URL}/stdLearning`, data, config); - return response.data; - } catch (error) { - console.error('Error creating std_learning:', error); - throw error; - } -}; - -// Fungsi untuk mendapatkan data exercise -const fetchExercise = async (idLevel) => { - try { - const response = await axios.get(`${API_URL}/exercise/level/${idLevel}`, config); - // return response.data; - - const sortedData = response.data.payload.sort((a, b) => { - return getSoalNumber(a.TITLE) - getSoalNumber(b.TITLE); - }); - - return sortedData; - } catch (error) { - console.error('Error fetching exercise data:', error); - throw error; - } -}; - - - -export default { - getLevelId, - checkStdLearning, - createStdLearning, - fetchExercise -}; diff --git a/src/roles/user/exerciseCombine/views/Exercise.jsx b/src/roles/user/exerciseCombine/views/Exercise.jsx deleted file mode 100644 index 4357e48..0000000 --- a/src/roles/user/exerciseCombine/views/Exercise.jsx +++ /dev/null @@ -1,207 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useParams } from 'react-router-dom'; -import { useExercise } from '../hooks/useExercises'; -import { Container, Row, Col, ListGroup, Button, Modal, Alert, 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 ilustration from '../../../../assets/images/illustration/submitExercise.png'; - -const Exercise = () => { - const [showModal, setShowModal] = useState(false); - const [showAlert, setShowAlert] = useState(false); - const { topic, level } = useParams(); - const { - questions, - currentQuestion, - currentQuestionIndex, - setCurrentQuestionIndex, - answers, - isLoading, - error, - handleAnswer, - nextQuestion, - prevQuestion, - handleSubmit, - handleConfirmSubmit - } = useExercise(topic, level); - - const handleSubmitWrapper = () => { - if (handleSubmit()) { - setShowModal(true); - } else { - setShowAlert(true); - } - }; - const handleCloseModal = () => setShowModal(false); - - 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
  • -
-
-
- ); - - if (isLoading) { - return ( -
-
- -
-
- - - -
-
- ); - } - - if (error) return

Error loading questions.

; - - return ( - - - -
-
-

Pretest

- - - -
- - {questions.map((q, index) => ( - setCurrentQuestionIndex(index)} - className={`border-0 rounded-3 number-label ${answers[index] !== null ? 'answered fw-bold' : ''}`} - style={{ cursor: 'pointer' }} - > - - - {index+1} - - ))} - -
- - - -
-
- -
{`Questions ${currentQuestionIndex + 1} of ${questions.length}`}
- - -
-
- {renderQuestion()} -
-
- -
- - setShowAlert(false)} dismissible> - ATTENTION! - Please answer all questions before submitting. - - - {/* - - Confirmation - - -

Are you sure you want to submit your answer?

-
- - - - -
*/} - - - -

Proceed with Submission?

- -

Confirm submission? There's no going back.

-
- - -
-
-
-
- ); -}; - -export default Exercise; - - - diff --git a/src/roles/user/exerciseCombine/views/components/ExerciseMedia.jsx b/src/roles/user/exerciseCombine/views/components/ExerciseMedia.jsx deleted file mode 100644 index cdbe29e..0000000 --- a/src/roles/user/exerciseCombine/views/components/ExerciseMedia.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; - -const ExerciseMedia = ({ question, onAnswer, savedAnswer, index }) => { - const handleChange = (value) => { - onAnswer(index, value); - }; - - return ( -
-

{question.TITLE}

-

{question.QUESTION}

- {question.VIDEO && ( - - )} - {question.IMAGE && Question} - {question.AUDIO && } -
- -
handleChange('1')} - > - A - TRUE -
- -
handleChange('0')} - > - B - FALSE -
- -
-
- ); -}; - -export default ExerciseMedia; diff --git a/src/roles/user/exerciseCombine/views/components/MatchingPairsQuestion.jsx b/src/roles/user/exerciseCombine/views/components/MatchingPairsQuestion.jsx deleted file mode 100644 index 84eabd8..0000000 --- a/src/roles/user/exerciseCombine/views/components/MatchingPairsQuestion.jsx +++ /dev/null @@ -1,227 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -// const colors = ['#E9342D', '#FACC15', '#1FBC2F', '#0090FF', '#ED27D9']; -// const colors = ['#0090FF', '#FC6454', '#46E59A', '#FBD025', '#E355D5']; -const colors = ['#FC6454', '#FBD025', '#46E59A', '#0090FF','#E355D5']; - -const shuffleArray = (array) => { - return array - .map((value) => ({ value, sort: Math.random() })) - .sort((a, b) => a.sort - b.sort) - .map(({ value }) => value); -}; - -function arrayToString(arr) { - return arr.join(', '); -} - -function stringToArray(str) { - return str.split(', '); -} - -const MatchingPairsQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const [pairs, setPairs] = useState([]); - const [selectedLeft, setSelectedLeft] = useState(null); - const [selectedRight, setSelectedRight] = useState(null); - const [rightOptions, setRightOptions] = useState([]); - const [isComplete, setIsComplete] = useState(false); - const [isShuffled, setIsShuffled] = useState(false); - - useEffect(() => { - const handleClickOutside = (event) => { - if (!event.target.closest('.mp-choice')) { - setSelectedLeft(null); - setSelectedRight(null); - } - }; - - document.addEventListener('click', handleClickOutside); - - return () => { - document.removeEventListener('click', handleClickOutside); - }; - }, []); - - useEffect(() => { - const initialPairs = question.matchingPairs.map((pair) => ({ - left: pair.LEFT_PAIR, - right: '', - color: colors[question.matchingPairs.indexOf(pair) % colors.length], - })); - - if (savedAnswer) { - const arrSavedAnswer = stringToArray(savedAnswer); - const updatedPairs = initialPairs.map((pair, index) => ({ - ...pair, - right: arrSavedAnswer[index], - })); - setPairs(updatedPairs); - } else { - setPairs(initialPairs); - } - - if (!isShuffled) { - setIsShuffled(true); - setRightOptions(shuffleArray(question.matchingPairs.map((pair) => pair.RIGHT_PAIR))); - } - }, [question, savedAnswer]); - - const handleLeftClick = (index) => { - setSelectedLeft(index); - const status = pairs.findIndex(item => item.right === rightOptions[selectedRight]); - if (selectedRight !== null) { - makePair(index, selectedRight, status); - } - }; - - const handleRightClick = (index) => { - setSelectedRight(index); - const status = pairs.findIndex(item => item.right === rightOptions[index]); - if (selectedLeft !== null) { - makePair(selectedLeft, index, status); - } - }; - - const makePair = (leftIndex, rightIndex, changePair) => { - const newPairs = [...pairs]; - if (changePair > -1) { - setIsComplete(false); - pairs[changePair].right = ''; - } - - const selectedRightValue = rightOptions[rightIndex]; - newPairs[leftIndex].right = selectedRightValue; - setPairs(newPairs); - // console.log(newPairs); - - setSelectedLeft(null); - setSelectedRight(null); - - const allPairsMatched = newPairs.every((pair) => pair.right !== ''); - if (allPairsMatched && !isComplete) { - setIsComplete(true); - - const rightAnswers = newPairs.map((pair) => pair.right); - console.log(rightAnswers); - onAnswer(index, arrayToString(rightAnswers)); - } - }; - - return ( -
- {question.IMAGE !== null && ( -
- {/* */} -

{question.IMAGE}

-
- )} - {question.AUDIO !== null && ( -
- {/* */} -

{question.AUDIO}

-
- )} - {question.VIDEO !== null && ( - // -
-

{question.VIDEO}

-
- )} - -

{question.QUESTION}

-
- {/* Bagian kiri */} -
- {pairs.map((pair, index) => ( -
handleLeftClick(index)} - style={{ - display: 'flex', alignItems: 'center', cursor: 'pointer', - }} - > - - {index + 1} - - - {pair.left} - -
- ))} -
- - {/* Bagian kanan */} -
- {rightOptions.map((right, index) => ( -
handleRightClick(index)} - style={{ - display: 'flex', alignItems: 'center', cursor: 'pointer', - }} - > - pair.right === right) ? '#ffffff' : '#000000', - backgroundColor: - pairs.find((pair) => pair.right === right)?.color || - (selectedRight === index ? '#ccc' : '#ffffff'), - border: - pairs.find((pair) => pair.right === right)?.color || - (selectedRight === index ? '2px dashed #ffffff' : '1px solid #000000'), - }} - > - {index + 1} - - pair.right === right) ? '#ffffff' : '#000000'), - backgroundColor: - pairs.find((pair) => pair.right === right)?.color || - (selectedRight === index ? '#ccc' : '#ffffff'), - border: - pairs.find((pair) => pair.right === right)?.color || - (selectedRight === index ? '2px dashed #ffffff' : '1px solid #000000'), - }} - > - {right} - -
- ))} -
-
-
- ); -}; - -export default MatchingPairsQuestion; diff --git a/src/roles/user/exerciseCombine/views/components/MultipleChoiceQuestion.jsx b/src/roles/user/exerciseCombine/views/components/MultipleChoiceQuestion.jsx deleted file mode 100644 index c3dc15d..0000000 --- a/src/roles/user/exerciseCombine/views/components/MultipleChoiceQuestion.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; - -const MultipleChoiceQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const options = question.multipleChoices[0]; - - const handleChange = (value) => { - onAnswer(index, value); - }; - - function getOptionLetter(option) { - const match = option.match(/OPTION_(\w)/); - return match ? match[1] : null; - } - - return ( -
- {question.IMAGE !== null && ( -
- {/* */} -

{question.IMAGE}

-
- )} - {question.AUDIO !== null && ( -
- {/* */} -

{question.AUDIO}

-
- )} - {question.VIDEO !== null && ( - // -
-

{question.VIDEO}

-
- )} - -

{question.QUESTION}

-
- {['OPTION_A', 'OPTION_B', 'OPTION_C', 'OPTION_D', 'OPTION_E'].map( - (optionKey) => { - if (options[optionKey]) { - return ( -
handleChange(getOptionLetter(optionKey))} - > - {getOptionLetter(optionKey)} - {options[optionKey]} -
- ); - } - return null; - } - )} -
-
- ); -}; - -export default MultipleChoiceQuestion; diff --git a/src/roles/user/exerciseCombine/views/components/TrueFalseQuestion.jsx b/src/roles/user/exerciseCombine/views/components/TrueFalseQuestion.jsx deleted file mode 100644 index 3c957bf..0000000 --- a/src/roles/user/exerciseCombine/views/components/TrueFalseQuestion.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; - -const TrueFalseQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const handleChange = (value) => { - onAnswer(index, value); - }; - - return ( -
- {question.IMAGE !== null && ( -
- {/* */} -

{question.IMAGE}

-
- )} - {question.AUDIO !== null && ( -
- {/* */} -

{question.AUDIO}

-
- )} - {question.VIDEO !== null && ( - // -
-

{question.VIDEO}

-
- )} - -

{question.QUESTION}

-
- -
handleChange('1')} - > - A - TRUE -
- -
handleChange('0')} - > - B - FALSE -
- -
-
- ); -}; - -export default TrueFalseQuestion; diff --git a/src/roles/user/exerciseOld/hooks/useExercises.jsx b/src/roles/user/exerciseOld/hooks/useExercises.jsx deleted file mode 100644 index f0ec3ae..0000000 --- a/src/roles/user/exerciseOld/hooks/useExercises.jsx +++ /dev/null @@ -1,248 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import exerciseService from '../services/exerciseService'; -import { useSlugContext } from '../../../../utils/SlugContext'; -import { unSlugify } from '../../../../utils/Constant'; - -import Popover from 'react-bootstrap/Popover'; - -import image from '../../../../assets/images/illustration/material-image.png'; -import audio from '../../../../assets/audio/sample.mp3'; -import video from '../../../../assets/video/sample.mp4'; - -const basicQuestions = [ - { - id: 1, - question: "Which sentence is correct?", - options: ["She don't like apples.", "She doesn't like apples.", "She not likes apples.", "She don't likes apples."], - source: null, - correctAnswer: 1, - type: 'text' - }, - { - id: 2, - question: "Choose the correct form of the verb: 'He ___ to the store yesterday.'", - options: ["go", "went", "going", "goes"], - source: image, - correctAnswer: 1, - type: 'image' - }, - { - id: 3, - question: "Select the correct sentence:", - options: ["They was playing football.", "They were playing football.", "They playing football.", "They played football were."], - source: audio, - correctAnswer: 1, - type: 'audio' - }, - { - id: 4, - question: "Fill in the blank: 'I have ___ new car.'", - options: ["a", "an", "the", "some"], - source: video, - correctAnswer: 1, - type: 'video' - }, - { - id: 5, - question: "Which sentence is in the past perfect tense?", - options: ["I will have finished my homework.", "I had finished my homework.", "I finished my homework.", "I am finishing my homework."], - source: null, - correctAnswer: 1, - type: 'text' - }, - { - id: 6, - question: "Choose the correct pronoun: 'Neither of the boys lost ___ keys.'", - options: ["his", "their", "her", "its"], - source: null, - correctAnswer: 1, - type: 'text' - }, - { - id: 7, - question: "Identify the error in the sentence: 'She can sings very well.'", - options: ["sings", "can", "very", "well"], - source: null, - correctAnswer: 0, - type: 'text' - }, - { - id: 8, - question: "Select the correct sentence:", - options: ["There is many apples on the table.", "There are many apples on the table.", "There are much apples on the table.", "There is much apples on the table."], - source: null, - correctAnswer: 1, - type: 'text' - }, - { - id: 9, - question: "Choose the correct conditional form: 'If it rains, we ___ inside.'", - options: ["stay", "stays", "will stay", "stayed"], - source: null, - correctAnswer: 2, - type: 'text' - }, - { - id: 10, - question: "Fill in the blank: 'She ___ to the gym every day.'", - options: ["when", "going", "gone", "goes"], - source: null, - correctAnswer: 3, - type: 'text' - } -]; - -export const useExercises = ( topic, level ) => { - const [questions, setQuestions] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [answers, setAnswers] = useState([]); - const [currentQuestion, setCurrentQuestion] = useState(0); - const { topicSlugMap } = useSlugContext(); - const navigate = useNavigate(); - - useEffect(() => { - const checkingData = async () => { - let levelId =''; - try { - levelId = await exerciseService.getLevelId(topicSlugMap[topic], unSlugify(level)); - } catch (error) { - console.error("Failed to fetch questions:", error); - }finally{ - const datas = await exerciseService.latestStdLearning(topicSlugMap[topic], levelId); - if (datas === null) { - const createLearning = await exerciseService.createStdLearning({ ID_LEVEL: levelId }); - if (createLearning.data.statusCode === 201) { - fetchData(); - }else{ - checkingData(); - } - }else{ - fetchData(); - } - } - }; - - const fetchData = async () => { - try { - const levelId = await exerciseService.getLevelId(topicSlugMap[topic], unSlugify(level)); - const stdLearning = await exerciseService.latestStdLearning(topicSlugMap[topic], levelId); - if (stdLearning === null) { - await exerciseService.createStdLearning({ ID_LEVEL: levelId }); - } - - const datas = await exerciseService.fetchExercise(levelId); - console.log(datas); - - // const data = allQuestions[topic]?.[level] || []; - const data = basicQuestions || []; - // setQuestions(data); - - setQuestions(datas); - setAnswers(new Array(data.length).fill(null)); - } catch (error) { - console.error("something wrong : ", error); - setError(error); - }finally{ - setLoading(false); - } - }; - - // checkingData(); - fetchData(); - }, [topic, level]); - - useEffect(() => { - const savedAnswers = JSON.parse(localStorage.getItem('answers')); - if (savedAnswers) { - setAnswers(savedAnswers); - } - }, []); - - useEffect(() => { - if (answers.some(answer => answer !== null)) { - localStorage.setItem('answers', JSON.stringify(answers)); - console.log(localStorage.getItem('answers')); - } - }, [answers]); - - 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 */} -
    -
  • Click the left arrow key to go to the previous question
  • -
  • Click the right arrow key to go to the next question
  • -
-
-
- ); - - const handleAnswerSelect = (index) => { - const newAnswers = [...answers]; - newAnswers[currentQuestion] = index; - setAnswers(newAnswers); - }; - - const handleNextQuestion = () => { - setCurrentQuestion((prev) => (prev < questions.length - 1 ? prev + 1 : prev)); - }; - - const handlePrevQuestion = () => { - setCurrentQuestion((prev) => (prev > 0 ? prev - 1 : prev)); - }; - - useEffect(() => { - const handleKeyDown = (event) => { - if (event.key === 'ArrowLeft') { - handlePrevQuestion(); - } else if (event.key === 'ArrowRight') { - handleNextQuestion(); - } - }; - - window.addEventListener('keydown', handleKeyDown); - - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, [handlePrevQuestion, handleNextQuestion]); - - const handleSubmit = () => { - const allAnswered = answers.every((answer) => answer !== null); - return allAnswered; - }; - - const getCurrentQuestionData = () => { - return questions[currentQuestion] || {}; - }; - - const handleConfirmSubmit = () => { - setShowModal(false); - navigate(`/learning/module/${section}/${topic}/${level}/result`, { - state: { answers } - }); - }; - - return { - questions, - loading, - error, - answers, - currentQuestion, - popover, - setAnswers, - setCurrentQuestion, - handleAnswerSelect, - handleNextQuestion, - handlePrevQuestion, - handleSubmit, - getCurrentQuestionData, - handleConfirmSubmit - }; -}; - -export default useExercises; diff --git a/src/roles/user/exerciseOld/hooks/useMatchingPairs.jsx b/src/roles/user/exerciseOld/hooks/useMatchingPairs.jsx deleted file mode 100644 index 05b155a..0000000 --- a/src/roles/user/exerciseOld/hooks/useMatchingPairs.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -const useMatchingPairs = () => { - const COLORS = ['#E9342D', '#FACC15', '#1FBC2F', '#0090FF', '#ED27D9']; - const [rightShuffled, setRightShuffled] = useState([]); - const [selectedLeft, setSelectedLeft] = useState(null); - const [selectedRight, setSelectedRight] = useState(null); - const [matchedPairs, setMatchedPairs] = useState([]); - const [colorMap, setColorMap] = useState({}); - const leftItems = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5']; - const rightItems = ['Item A', 'Item B', 'Item C', 'Item D', 'Item E']; - const [shuffle, setShuffle] = useState(true); - - // Mengacak bagian kanan ketika komponen pertama kali dirender - useEffect(() => { - if (shuffle) { - shuffleRightItems(); - } - }, [rightItems]); - - // Fungsi untuk mengacak urutan bagian kanan - const shuffleRightItems = () => { - const shuffled = [...rightItems].sort(() => Math.random() - (leftItems.length/10)); - setRightShuffled(shuffled); - setShuffle(false); - }; - - // Fungsi untuk menangani klik pada bagian kiri - const handleLeftChoose = (index) => { - setSelectedLeft(index); - - if (selectedRight !== null) { - matchPair(index, selectedRight); - resetSelections(); - } - }; - - // Fungsi untuk menangani klik pada bagian kanan - const handleRightChoose = (index) => { - setSelectedRight(index); - - if (selectedLeft !== null) { - matchPair(selectedLeft, index); - resetSelections(); - } - console.log(matchedPairs); - }; - - // Fungsi untuk mencocokkan pasangan - const matchPair = (leftIndex, rightIndex) => { - if (!matchedPairs[leftIndex]) { - const newMatchedPairs = [...matchedPairs]; - newMatchedPairs[leftIndex] = rightShuffled[rightIndex]; - - const colorIndex = leftIndex % COLORS.length; - setColorMap((prevMap) => ({ - ...prevMap, - [leftIndex]: COLORS[colorIndex], - })); - - setMatchedPairs(newMatchedPairs); - } - }; - - // Fungsi untuk mereset seleksi setelah pencocokan - const resetSelections = () => { - setSelectedLeft(null); - setSelectedRight(null); - }; - - const resetMatchPair = () => { - setMatchedPairs([]); - setColorMap({}); - resetSelections(); - }; - - return ( - rightShuffled, - colorMap, - handleLeftChoose, - handleRightChoose, - matchPair, - resetSelections, - resetMatchPair - ); -}; - -export default useMatchingPairs; diff --git a/src/roles/user/exerciseOld/hooks/useResults.jsx b/src/roles/user/exerciseOld/hooks/useResults.jsx deleted file mode 100644 index 73043c2..0000000 --- a/src/roles/user/exerciseOld/hooks/useResults.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useParams } from 'react-router-dom'; -import useExercises from './useExercises'; - -// correct answer -// 1. b -// 2. b -// 3. b -// 4. b -// 5. b -// 6. b -// 7. a -// 8. b -// 9. c -//10. d - -const useResults = (answers) => { - const { level } = useParams(); - const [score, setScore] = useState(0); - const [nextLevel, setNextLevel] = useState(1); - const [savedLevel, setSavedLevel] = useState(JSON.parse(localStorage.getItem('savedLevels')) || []); - const [savedScore, setSavedScore] = useState(JSON.parse(localStorage.getItem('savedScores')) || []); - - const { questions } = useExercises(); - - const updateSavedScore = (scr) => { - const newSavedScore = [...savedScore]; - const index = parseInt(level.slice(-1) - 1); - newSavedScore[index] = scr; - setSavedScore(newSavedScore); - localStorage.setItem('savedScores', JSON.stringify(newSavedScore)); - }; - - const updateSavedLevel = (index, newValue) => { - const newSavedLevel = [...savedLevel]; - newSavedLevel[index] = newValue; - setSavedLevel(newSavedLevel); - localStorage.setItem('savedLevels', JSON.stringify(newSavedLevel)); - }; - - const unlockSavedLevel = () => { - const newSavedLevel = savedLevel.map(() => false); - setSavedLevel(newSavedLevel); - localStorage.setItem('savedLevels', JSON.stringify(newSavedLevel)); - }; - - useEffect(() => { - if (answers && questions) { - let totalScore = 0; - - answers.forEach((answer, index) => { - const question = questions[index]; - if (question) { - if (answer === question.correctAnswer) { - totalScore += 100 / questions.length; - } - } - }); - - setScore(totalScore); - updateSavedScore(totalScore); - if (totalScore >= 90 && level === 'level-5') { //all done - setNextLevel(0); - unlockSavedLevel(); - return; - } else if (totalScore >= 90 && level !== 'level-5') { - setNextLevel(5); - updateSavedLevel(4, false); - return; - } else if (totalScore >= 75) { - setNextLevel(4); - updateSavedLevel(3, false); - return; - } else if (totalScore >= 60) { - setNextLevel(3); - updateSavedLevel(2, false); - return; - } else if (totalScore >= 40) { - setNextLevel(2); - updateSavedLevel(1, false); - return; - } else { - setNextLevel(1); - } - - } - }, [answers, questions]); - - localStorage.removeItem('answers'); - return { score, nextLevel }; -}; - -export default useResults; diff --git a/src/roles/user/exerciseOld/services/exerciseService.jsx b/src/roles/user/exerciseOld/services/exerciseService.jsx deleted file mode 100644 index 5af9062..0000000 --- a/src/roles/user/exerciseOld/services/exerciseService.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import axios from 'axios'; -import { API_URL } from '../../../../utils/Constant'; - -const config = { - headers: { - Authorization: localStorage.getItem('token') - } -}; - -const getSoalNumber = (title) => { - const match = title.match(/\d+$/); // Mencari angka di akhir string - return match ? parseInt(match[0], 10) : 0; // Mengembalikan angka atau 0 jika tidak ditemukan -}; - -// Fungsi untuk cek apakah std_learning sudah ada -const latestStdLearning = async (topic, level) => { - try { - const response = await axios.get(`${API_URL}/stdLearning/level/${level}`, config); - return response.data; - } catch (error) { - // console.error('Error checking std_learning:', error); - // throw error; - return null; - } -}; - -const getLevelId = async (topicId, levelName) => { - try { - const response = await axios.get(`${API_URL}/level/topic/${topicId}`, config); - const filteredData = response.data.data.levels.filter(item => item.NAME_LEVEL === levelName); - return filteredData[0].ID_LEVEL; - } catch (error) { - return []; - } -}; - - - -// Fungsi untuk cek apakah std_learning sudah ada -const checkStdLearning = async (token) => { - try { - const response = await axios.get(`${API_URL}/std_learning`, config); - return response.data; - } catch (error) { - console.error('Error checking std_learning:', error); - throw error; - } -}; - -// Fungsi untuk membuat std_learning -const createStdLearning = async (data) => { - try { - const response = await axios.post(`${API_URL}/stdLearning`, data, config); - return response.data; - } catch (error) { - console.error('Error creating std_learning:', error); - throw error; - } -}; - -// Fungsi untuk mendapatkan data exercise -const fetchExercise = async (idLevel) => { - try { - const response = await axios.get(`${API_URL}/exercise/level/${idLevel}`, config); - // return response.data; - - const sortedData = response.data.payload.sort((a, b) => { - return getSoalNumber(a.TITLE) - getSoalNumber(b.TITLE); - }); - - return sortedData; - } catch (error) { - console.error('Error fetching exercise data:', error); - throw error; - } -}; - - - -export default { - latestStdLearning, - getLevelId, - - checkStdLearning, - createStdLearning, - fetchExercise -}; diff --git a/src/roles/user/exerciseOld/views/Exercise.jsx b/src/roles/user/exerciseOld/views/Exercise.jsx deleted file mode 100644 index 7ed9e8d..0000000 --- a/src/roles/user/exerciseOld/views/Exercise.jsx +++ /dev/null @@ -1,331 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Container, Row, Col, ListGroup, Button, Modal, Alert, OverlayTrigger, Popover } from 'react-bootstrap'; -import { useNavigate, useParams } from 'react-router-dom'; -import useExercises from '../hooks/useExercises'; -import ilustration from '../../../../assets/images/illustration/submitExercise.png'; -import Skeleton from 'react-loading-skeleton'; -import MatchingPairs from './components/MatchingPair'; - -const Exercise = () => { - const [showModal, setShowModal] = useState(false); - const [showAlert, setShowAlert] = useState(false); - - const [activeIndex, setActiveIndex] = useState(null); - - const { section, topic, level } = useParams(); - const navigate = useNavigate(); - - const { - questions, - loading, - error, - answers, - currentQuestion, - popover, - setAnswers, - setCurrentQuestion, - handleAnswerSelect, - handleNextQuestion, - handlePrevQuestion, - handleSubmit, - getCurrentQuestionData, - handleConfirmSubmit - } = useExercises( topic, level ); - - const handleSubmitWrapper = () => { - if (handleSubmit()) { - setShowModal(true); - } else { - setShowAlert(true); - } - }; - - // const handleConfirmSubmit = () => { - // setShowModal(false); - // navigate(`/learning/module/${section}/${topic}/${level}/result`, { - // state: { answers } - // }); - // }; - - const handleCloseModal = () => setShowModal(false); - - const currentQuestionData = getCurrentQuestionData(); - const questionId = currentQuestionData.id || ''; - - - const handleSelectLeft = (index) => { - setActiveIndex(index); - }; - - - - if (loading) { - return ( -
-
- -
-
- - - -
-
- ); - } - - return ( - - - -
-
-

Pretest

- - - -
- - {questions.map((q, index) => ( - setCurrentQuestion(index)} - className={`border-0 rounded-3 number-label ${answers[index] !== null ? 'answered fw-bold' : ''}`} - style={{ cursor: 'pointer' }} - > - - - {index+1} - - ))} - -
- - - -
-
- -
{`Questions ${currentQuestion + 1} of ${questions.length}`}
- - -
-
- {currentQuestionData.image && ( -
- question illustration -
- )} - {currentQuestionData.IMAGE !== null && ( -
- {/* */} -

{currentQuestionData.IMAGE}

-
- )} - {currentQuestionData.AUDIO !== null && ( -
- {/* */} - {/* */} -

{currentQuestionData.AUDIO}

-
- )} - {currentQuestionData.VIDEO !== null && ( - // -
-

{currentQuestionData.VIDEO}

-
- )} -

{currentQuestionData.QUESTION}

- {currentQuestionData.QUESTION_TYPE === "MCQ" && ( -
- {/* {currentQuestionData.options?.map((option, idx) => ( -
handleAnswerSelect(idx)} - > - {String.fromCharCode(65 + idx)} - {option} -
- ))} */} -
handleAnswerSelect(0)} - > - A - {currentQuestionData.multipleChoices[0].OPTION_A} -
-
handleAnswerSelect(1)} - > - B - {currentQuestionData.multipleChoices[0].OPTION_B} -
-
handleAnswerSelect(2)} - > - C - {currentQuestionData.multipleChoices[0].OPTION_C} -
-
handleAnswerSelect(3)} - > - D - {currentQuestionData.multipleChoices[0].OPTION_D} -
- {currentQuestionData.multipleChoices[0].OPTION_E && ( -
handleAnswerSelect(4)} - > - E - {currentQuestionData.multipleChoices[0].OPTION_E} -
- )} -
- )} - {currentQuestionData.QUESTION_TYPE === "TFQ" && ( -
-
handleAnswerSelect(0)} - > - A - TRUE -
-
handleAnswerSelect(1)} - > - B - FALSE -
-
- )} - {currentQuestionData.QUESTION_TYPE === "MPQ" &&( - //
- //
- // {currentQuestionData.matchingPairs?.map((option, idx) => ( - //
handleLeftChoose(idx)} - // > - // {idx + 1} - // {/* {idx + 1} */} - // {/* {option.LEFT_PAIR} */} - // {option.LEFT_PAIR} - //
- // ))} - //
- //
- // {currentQuestionData.matchingPairs?.map((option, idx) => ( - //
handleRightChoose(idx)} - // > - // {idx + 1} - // {/* {idx + 1} */} - // {/* {option.LEFT_PAIR} */} - // {option.RIGHT_PAIR} - //
- // ))} - //
- //
- - )} -
-
- -
- - setShowAlert(false)} dismissible> - ATTENTION! - Please answer all questions before submitting. - - - {/* - - Confirmation - - -

Are you sure you want to submit your answer?

-
- - - - -
*/} - - - -

Proceed with Submission?

- -

Confirm submission? There's no going back.

-
- - -
-
-
-
- ); -}; - -export default Exercise; - - - diff --git a/src/roles/user/exerciseOld/views/ExerciseResult.jsx b/src/roles/user/exerciseOld/views/ExerciseResult.jsx deleted file mode 100644 index 98361da..0000000 --- a/src/roles/user/exerciseOld/views/ExerciseResult.jsx +++ /dev/null @@ -1,134 +0,0 @@ -// import React from 'react'; -// import { Link, useParams, useLocation } from 'react-router-dom'; -// import { Container, Row, Col, Button } from 'react-bootstrap'; -// import staydown from '../../assets/images/user/resultStay.png' -// import jump from '../../assets/images/user/resultJump.png' -// import finish from '../../assets/images/user/resultFinish.png' - -// const ExerciseResult = () => { -// const { section, topic } = useParams(); -// const location = useLocation(); -// localStorage.removeItem('answers'); - -// return ( -// -// - -// {/* nilai sudah diatas KKM */} -// -//

Your Result!

-// / -//

-// You scored 60/100. Great job! You can jump to... -//

-//

LEVEL 3

-// -// - -// {/* jika nilai dibawah KKM */} -// -//

Your Result!

-// / -//

-// You scored 30/100. Good effort! To improve, you can focus on... -//

-//

LEVEL 3

-// -// - -// {/* jika nilai sudah bagus tetapi belum cukup untuk ke level selanjutnya */} -// -//

Your Result!

-// / -//

-// You scored 30/100. Learning is a journey.
-// Let's explore this level further to deepen your knowledge. -//

-//

LEVEL 2

-// -// - -// {/* jika yang telah dikerjakan adalah level 5 */} -// -//

Your Result!

-// / -//

-// Way to go! You conquered LEVEL 5 with a 90/100! You're a rock star! -//

-//

LEVEL 2

-// -// -//
-//
-// ); -// }; - -// export default ExerciseResult; - - - - -import React from 'react'; -import { Link, useParams, useLocation } from 'react-router-dom'; -import { Container, Row, Col, Button } from 'react-bootstrap'; -import staydown from '../../../../assets/images/illustration/resultStay.png'; -import jump from '../../../../assets/images/illustration/resultJump.png'; -import finish from '../../../../assets/images/illustration/resultFinish.png'; -import useResults from '../hooks/useResults'; - -const ExerciseResult = () => { - const { section, topic, level } = useParams(); - const location = useLocation(); - const { answers } = location.state || { answers: [] }; - - const { score, nextLevel } = useResults(answers); - - let resultImage; - let resultText; - let resultColor; - - if (nextLevel === 0) { - resultImage = finish; - resultColor = 'text-blue'; - resultText = ( - <> Way to go! You conquered LEVEL 5 with a {score}/100! You're a rock star! - ); - } else if (score >= 60) { - resultImage = jump; - resultColor = 'text-blue'; - resultText = ( - <> You scored {score}/100. Great job! You can jump to... - ); - } else if (score >= 40) { - resultImage = staydown; - resultColor = ''; - resultText = ( - <> You scored {score}/100. Learning is a journey.
Let's explore this level further to deepen your knowledge. - ); - } else { - resultImage = staydown; - resultColor = 'text-red'; - resultText = ( - <> You scored {score}/100. Good effort! To improve, you can focus on... - ); - } - - return ( - - - -

Your Result!

- / -

- {resultText} -

-

{nextLevel === 0 ? '' : `LEVEL ${nextLevel}`}

- - -
-
- ); -}; - -export default ExerciseResult; - diff --git a/src/roles/user/exerciseOld/views/components/MatchingPair.jsx b/src/roles/user/exerciseOld/views/components/MatchingPair.jsx deleted file mode 100644 index 29bb458..0000000 --- a/src/roles/user/exerciseOld/views/components/MatchingPair.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -const COLORS = ['#E9342D', '#FACC15', '#1FBC2F', '#0090FF', '#ED27D9']; - -const MatchingPairs = ( stateData ) => { - const [selectedLeft, setSelectedLeft] = useState(null); - const [selectedRight, setSelectedRight] = useState(null); - const [matchedPairs, setMatchedPairs] = useState([]); - const [colorMap, setColorMap] = useState({}); - const [leftItems, setLeftPairs] = useState([]); - const [rightItems, setRightPairs] = useState([]); - - - useEffect(() => { - console.log(stateData); - const pairItem = stateData.pairData; - const left = pairItem.map(pair => pair.LEFT_PAIR); - const right = pairItem.map(pair => pair.RIGHT_PAIR); - const shuffledRight = [...right].sort(() => Math.random() - (left.length/10)); - - setLeftPairs(left); - setRightPairs(shuffledRight); - }, []); - - - // Fungsi untuk menangani klik pada bagian kiri - const handleLeftClick = (index) => { - setSelectedLeft(index); - - if (selectedRight !== null) { - matchPair(index, selectedRight); - resetSelections(); - } - }; - - // Fungsi untuk menangani klik pada bagian kanan - const handleRightClick = (index) => { - setSelectedRight(index); - - if (selectedLeft !== null) { - matchPair(selectedLeft, index); - resetSelections(); - } - console.log(selectedRight); - }; - - // Fungsi untuk mencocokkan pasangan - const matchPair = (leftIndex, rightIndex) => { - if (!matchedPairs[leftIndex]) { - const newMatchedPairs = [...matchedPairs]; - newMatchedPairs[leftIndex] = rightItems[rightIndex]; - - const colorIndex = leftIndex % COLORS.length; - setColorMap((prevMap) => ({ - ...prevMap, - [leftIndex]: COLORS[colorIndex], - })); - - setMatchedPairs(newMatchedPairs); - } - }; - - // Fungsi untuk mereset seleksi setelah pencocokan - const resetSelections = () => { - setSelectedLeft(null); - setSelectedRight(null); - }; - - const resetAnswers = () => { - setMatchedPairs([]); - setColorMap({}); - resetSelections(); - }; - - const isAllMatched = () => { - console.log('gatau'); - return matchedPairs.length === leftItems.length && matchedPairs.every((pair) => pair !== undefined && pair !== null); - }; - - return ( -
-
- {/* Bagian Kiri */} -
-

Left Items

-
    - {leftItems.map((item, index) => ( -
  • handleLeftClick(index)} - style={{ - cursor: 'pointer', - backgroundColor: colorMap[index] || 'transparent', - padding: '10px', - marginBottom: '5px', - border: selectedLeft === index ? '2px solid black' : '2px solid #fff', - }} - > - {item} -
  • - ))} -
-
- - {/* Bagian Kanan */} -
-

Right Items (Shuffled)

-
    - {rightItems.map((item, index) => ( -
  • handleRightClick(index)} - style={{ - cursor: 'pointer', - backgroundColor: matchedPairs.includes(item) ? colorMap[matchedPairs.indexOf(item)] : 'transparent', - padding: '10px', - marginBottom: '5px', - border: selectedRight === index ? '2px solid black' : '2px solid #fff', - }} - > - {item} -
  • - ))} -
-
-
- {/*
- {isAllMatched() ? ( -
Semua pasangan sudah terjawab!
- ) : ( -
Belum semua pasangan terjawab.
- )} -
*/} - -
- ); -}; - -export default MatchingPairs; diff --git a/src/roles/user/exerciseTest/hooks/useExercise.jsx b/src/roles/user/exerciseTest/hooks/useExercise.jsx deleted file mode 100644 index ddf110f..0000000 --- a/src/roles/user/exerciseTest/hooks/useExercise.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useState, useEffect } from 'react'; -import exerciseService from '../services/exerciseServices'; -import { useSlugContext } from '../../../../utils/SlugContext'; -import { unSlugify } from '../../../../utils/Constant'; - -export const useExercise = (topic, level) => { - const [questions, setQuestions] = useState([]); - const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); - const [answers, setAnswers] = useState({}); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [levelId, setLevelId] = useState(null); - const [learningId, setLearningId] = useState(null); - const { topicSlugMap } = useSlugContext(); - - const fetchData = async () => { - try { - const getLevelId = await exerciseService.getLevelId(topicSlugMap[topic], unSlugify(level)); - setLevelId(getLevelId); - const stdLearning = await exerciseService.checkStdLearning(getLevelId); - if (stdLearning === null) { - const newLearningId = await exerciseService.createStdLearning({ ID_LEVEL: getLevelId }); - setLearningId(newLearningId); - }else{ - setLearningId(stdLearning); - } - - - const data = await exerciseService.fetchExercise(getLevelId); - setQuestions(data); - if (localStorage.getItem(getLevelId)) { - const savedAnswers = JSON.parse(localStorage.getItem(getLevelId)); - setAnswers(savedAnswers); - }else{ - setAnswers(new Array(data.length).fill(null)); - } - } catch (error) { - console.error("something wrong : ", error); - setError(error); - }finally{ - setIsLoading(false); - } - }; - - // Fetch data soal dari API saat komponen dimount - useEffect(() => { - fetchData(); - }, [topic, level]); - - // Fungsi untuk menangani jawaban user dan menyimpannya ke localStorage - const handleAnswer = (index, answer) => { - // const newAnswers = { ...answers, [index]: answer }; - const newAnswers = [...answers]; - newAnswers[index] = answer; - setAnswers(newAnswers); - console.log(newAnswers); - localStorage.setItem(levelId, JSON.stringify(newAnswers)); - }; - - // Fungsi untuk menyimpan pasangan jawaban Matching Pair ketika sudah lengkap - const handleMatchingPairs = (questionId, pairs) => { - const isComplete = pairs.every(pair => pair.left && pair.right); // Pastikan semua pasangan sudah lengkap - if (isComplete) { - handleAnswer(questionId, pairs); // Simpan jawaban jika sudah lengkap - } - }; - - // Fungsi untuk pindah ke soal selanjutnya - const nextQuestion = () => { - if (currentQuestionIndex < questions.length - 1) { - setCurrentQuestionIndex(currentQuestionIndex + 1); - } - }; - - // Fungsi untuk kembali ke soal sebelumnya - const prevQuestion = () => { - if (currentQuestionIndex > 0) { - setCurrentQuestionIndex(currentQuestionIndex - 1); - } - }; - - return { - questions, - currentQuestion: questions[currentQuestionIndex], - currentQuestionIndex, - setCurrentQuestionIndex, - answers, - isLoading, - error, - handleAnswer, - handleMatchingPairs, - nextQuestion, - prevQuestion, - }; -}; diff --git a/src/roles/user/exerciseTest/services/exerciseServices.jsx b/src/roles/user/exerciseTest/services/exerciseServices.jsx deleted file mode 100644 index 9b4056b..0000000 --- a/src/roles/user/exerciseTest/services/exerciseServices.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import axios from 'axios'; -import { API_URL } from '../../../../utils/Constant'; - -const config = { - headers: { - Authorization: localStorage.getItem('token') - } -}; - -const getSoalNumber = (title) => { - const match = title.match(/\d+$/); // Mencari angka di akhir string - return match ? parseInt(match[0], 10) : 0; // Mengembalikan angka atau 0 jika tidak ditemukan -}; - -// Fungsi untuk cek apakah std_learning sudah ada -const checkStdLearning = async (level) => { - try { - const response = await axios.get(`${API_URL}/stdLearning/level/${level}`, config); - return response.data; - } catch (error) { - // console.error('Error checking std_learning:', error); - // throw error; - return null; - } -}; - -const getLevelId = async (topicId, levelName) => { - try { - const response = await axios.get(`${API_URL}/level/topic/${topicId}`, config); - const filteredData = response.data.data.levels.filter(item => item.NAME_LEVEL === levelName); - return filteredData[0].ID_LEVEL; - } catch (error) { - return []; - } -}; - -// Fungsi untuk membuat std_learning -const createStdLearning = async (data) => { - try { - const response = await axios.post(`${API_URL}/stdLearning`, data, config); - return response.data; - } catch (error) { - console.error('Error creating std_learning:', error); - throw error; - } -}; - -// Fungsi untuk mendapatkan data exercise -const fetchExercise = async (idLevel) => { - try { - const response = await axios.get(`${API_URL}/exercise/level/${idLevel}`, config); - // return response.data; - - const sortedData = response.data.payload.sort((a, b) => { - return getSoalNumber(a.TITLE) - getSoalNumber(b.TITLE); - }); - - return sortedData; - } catch (error) { - console.error('Error fetching exercise data:', error); - throw error; - } -}; - - - -export default { - getLevelId, - checkStdLearning, - createStdLearning, - fetchExercise -}; diff --git a/src/roles/user/exerciseTest/views/Exercise.jsx b/src/roles/user/exerciseTest/views/Exercise.jsx deleted file mode 100644 index 216bf56..0000000 --- a/src/roles/user/exerciseTest/views/Exercise.jsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import { useExercise } from '../hooks/useExercise'; -import MultipleChoiceQuestion from './MultipleChoiceQuestion'; -import TrueFalseQuestion from './TrueFalseQuestion'; -import MatchingPairsQuestion from './MatchingPairsQuestion'; -import { useParams } from 'react-router-dom'; - -const Exercise = () => { - const { topic, level } = useParams(); - const { - questions, - currentQuestion, - currentQuestionIndex, - setCurrentQuestionIndex, - answers, - isLoading, - error, - handleAnswer, - nextQuestion, - prevQuestion, - } = useExercise(topic, level); - - if (isLoading) return

Loading...

; - if (error) return

Error loading questions.

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

Unknown question type.

; - } - }; - - return ( -
-

Exercise

-
- {/* List nomor soal */} -
- {questions.map((q, index) => ( - - ))} -
- {/* Soal saat ini */} -
{renderQuestion()}
- {/* Navigasi Next dan Previous */} -
- - -
-
-
- ); -}; - -export default Exercise; diff --git a/src/roles/user/exerciseTest/views/MatchingPairsQuestion.jsx b/src/roles/user/exerciseTest/views/MatchingPairsQuestion.jsx deleted file mode 100644 index 13069bf..0000000 --- a/src/roles/user/exerciseTest/views/MatchingPairsQuestion.jsx +++ /dev/null @@ -1,222 +0,0 @@ -// import React, { useState, useEffect } from 'react'; - -// const MatchingPairsQuestion = ({ question, onAnswer, savedAnswer, index }) => { -// const [pairs, setPairs] = useState([]); - -// useEffect(() => { -// const initialPairs = question.matchingPairs.map((pair) => ({ -// left: pair.LEFT_PAIR, -// right: '', -// })); -// if (savedAnswer) { -// setPairs(savedAnswer); -// } else { -// setPairs(initialPairs); -// } -// }, [question, savedAnswer]); - -// const rightOptions = question.matchingPairs.map((pair) => pair.RIGHT_PAIR); - -// const handleSelect = (leftIndex, value) => { -// const newPairs = [...pairs]; -// newPairs[leftIndex].right = value; -// setPairs(newPairs); - -// const isComplete = newPairs.every((pair) => pair.right !== ''); -// if (isComplete) { -// onAnswer(index , newPairs); -// } -// }; - -// return ( -//
-//

{question.TITLE}

-//

{question.QUESTION}

-// {question.VIDEO && ( -// -// )} -// {question.IMAGE && Question} -// {question.AUDIO && } -//
-// {pairs.map((pair, index) => ( -//
-//
{pair.left}
-// -//
-// ))} -//
-//
-// ); -// }; - -// export default MatchingPairsQuestion; - - - - - -// components/MatchingPairsQuestion.js -import React, { useState, useEffect } from 'react'; - -const colors = ['#E9342D', '#FACC15', '#1FBC2F', '#0090FF', '#ED27D9']; - -const shuffleArray = (array) => { - return array - .map((value) => ({ value, sort: Math.random() })) - .sort((a, b) => a.sort - b.sort) - .map(({ value }) => value); -}; - -function arrayToString(arr) { - return arr.join(', '); -} - -function stringToArray(str) { - return str.split(', '); -} - -const MatchingPairsQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const [pairs, setPairs] = useState([]); - const [selectedLeft, setSelectedLeft] = useState(null); - const [selectedRight, setSelectedRight] = useState(null); - const [rightOptions, setRightOptions] = useState([]); - const [isComplete, setIsComplete] = useState(false); - const [isShuffled, setIsShuffled] = useState(false); - - useEffect(() => { - const initialPairs = question.matchingPairs.map((pair) => ({ - left: pair.LEFT_PAIR, - right: '', - color: colors[question.matchingPairs.indexOf(pair) % colors.length], - })); - - if (savedAnswer) { - const arrSavedAnswer = stringToArray(savedAnswer); - const updatedPairs = initialPairs.map((pair, index) => ({ - ...pair, - right: arrSavedAnswer[index], - })); - setPairs(updatedPairs); - } else { - setPairs(initialPairs); - } - - if (!isShuffled) { - setIsShuffled(true); - setRightOptions(shuffleArray(question.matchingPairs.map((pair) => pair.RIGHT_PAIR))); - } - }, [question, savedAnswer]); - - const handleLeftClick = (index) => { - setSelectedLeft(index); - const status = pairs.findIndex(item => item.right === rightOptions[selectedRight]); - if (selectedRight !== null) { - makePair(index, selectedRight, status); - } - }; - - const handleRightClick = (index) => { - setSelectedRight(index); - const status = pairs.findIndex(item => item.right === rightOptions[index]); - if (selectedLeft !== null) { - makePair(selectedLeft, index, status); - } - }; - - const makePair = (leftIndex, rightIndex, changePair) => { - const newPairs = [...pairs]; - if (changePair > -1) { - setIsComplete(false); - pairs[changePair].right = ''; - } - - const selectedRightValue = rightOptions[rightIndex]; - newPairs[leftIndex].right = selectedRightValue; - setPairs(newPairs); - // console.log(newPairs); - - setSelectedLeft(null); - setSelectedRight(null); - - const allPairsMatched = newPairs.every((pair) => pair.right !== ''); - if (allPairsMatched && !isComplete) { - setIsComplete(true); - - const rightAnswers = newPairs.map((pair) => pair.right); - console.log(rightAnswers); - onAnswer(index, arrayToString(rightAnswers)); - } - }; - - return ( -
-

{question.TITLE}

-

{question.QUESTION}

- {question.VIDEO && ( - - )} - {question.IMAGE && Question} - {question.AUDIO && } - -
- {/* Bagian kiri */} -
- {pairs.map((pair, index) => ( -
handleLeftClick(index)} - style={{ - padding: '10px', - margin: '10px', - cursor: 'pointer', - backgroundColor: selectedLeft === index ? '#ccc' : pair.color, - color: 'white', - borderRadius: '5px', - }} - > - {pair.left} -
- ))} -
- - {/* Bagian kanan */} -
- {rightOptions.map((right, index) => ( -
handleRightClick(index)} - style={{ - padding: '10px', - margin: '10px', - cursor: 'pointer', - backgroundColor: - pairs.find((pair) => pair.right === right)?.color || - (selectedRight === index ? '#ccc' : '#f0f0f0'), - color: 'white', - borderRadius: '5px', - }} - > - {right} -
- ))} -
-
-
- ); -}; - -export default MatchingPairsQuestion; diff --git a/src/roles/user/exerciseTest/views/MultipleChoiceQuestion.jsx b/src/roles/user/exerciseTest/views/MultipleChoiceQuestion.jsx deleted file mode 100644 index 2ba614e..0000000 --- a/src/roles/user/exerciseTest/views/MultipleChoiceQuestion.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; - -const MultipleChoiceQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const options = question.multipleChoices[0]; - - const handleChange = (e) => { - onAnswer(index, e.target.value); - }; - - function getOptionLetter(option) { - const match = option.match(/OPTION_(\w)/); - return match ? match[1] : null; - } - - return ( -
-

{question.TITLE}

-

{question.QUESTION}

- {question.VIDEO && ( - - )} - {question.IMAGE && Question} - {question.AUDIO && } -
- {['OPTION_A', 'OPTION_B', 'OPTION_C', 'OPTION_D', 'OPTION_E'].map( - (optionKey) => { - if (options[optionKey]) { - return ( -
- -
- ); - } - return null; - } - )} -
-
- ); -}; - -export default MultipleChoiceQuestion; diff --git a/src/roles/user/exerciseTest/views/TrueFalseQuestion.jsx b/src/roles/user/exerciseTest/views/TrueFalseQuestion.jsx deleted file mode 100644 index 4bba64b..0000000 --- a/src/roles/user/exerciseTest/views/TrueFalseQuestion.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; - -const TrueFalseQuestion = ({ question, onAnswer, savedAnswer, index }) => { - const handleChange = (e) => { - onAnswer(index, e.target.value); - }; - - return ( -
-

{question.TITLE}

-

{question.QUESTION}

- {question.VIDEO && ( - - )} - {question.IMAGE && Question} - {question.AUDIO && } -
- - -
-
- ); -}; - -export default TrueFalseQuestion; diff --git a/src/roles/user/level/hooks/useLevels.jsx b/src/roles/user/level/hooks/useLevels.jsx index 766ebce..64dea61 100644 --- a/src/roles/user/level/hooks/useLevels.jsx +++ b/src/roles/user/level/hooks/useLevels.jsx @@ -57,13 +57,12 @@ const useLevels = (section, topic) => { fetchData(); }, [section, topic]); - const isLevelUnlocked = (levelId) => { - if (levelId !== 0) { - if (!lastCompletedLevel){ - return false; + const isLevelUnlocked = (levelIndex) => { + if (levelIndex !== 0) { + if (levels[levelIndex].CONTENT) { + return true; }else{ - const maxUnlock = extractLevelNumber(lastCompletedLevel.NAME_LEVEL); - return levelId <= maxUnlock; + return false; } }else{ return true; diff --git a/src/roles/user/level/views/Level.jsx b/src/roles/user/level/views/Level.jsx index 9e3d518..ef8e4ce 100644 --- a/src/roles/user/level/views/Level.jsx +++ b/src/roles/user/level/views/Level.jsx @@ -10,15 +10,6 @@ import Skeleton from 'react-loading-skeleton'; const Level = () => { const { section, topic } = useParams(); - // const { isValidSection, isValidTopic } = useValidation(); - - // if (!isValidSection(section)) { - // return ; - // } else { - // if (!isValidTopic(topic)) { - // return ; - // } - // } const { levels, lastCompletedLevel, loading, error, isLevelUnlocked, levelDesc } = useLevels(section, topic);