diff --git a/api/exercise/submit.php b/api/exercise/submit.php index 971c37325ec4cf192b672e35624b7e1c378c09c3..4c8828ee9d3fa5c6bd6e324cc8f60293d3f5c462 100644 --- a/api/exercise/submit.php +++ b/api/exercise/submit.php @@ -1,5 +1,5 @@ <?php -function submitQuiz($exerciseId, $selectedOptions, $userId) +function submitQuiz($exerciseId, $selectedOptions, $userId, $isDone) { // rest submit exercise $pairs = array(); @@ -45,6 +45,9 @@ function submitQuiz($exerciseId, $selectedOptions, $userId) curl_close($ch); + if ($isDone) { + return $score; + } // rest add progress $ch = curl_init(); @@ -109,6 +112,8 @@ function submitQuiz($exerciseId, $selectedOptions, $userId) } curl_close($ch); + + return $score; } @@ -118,10 +123,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submitQuiz'])) { $isDone = $_POST['isDone']; $selectedOptions = isset($_POST['selectedOptions']) ? $_POST['selectedOptions'] : []; - if (!$isDone) { - submitQuiz($exerciseId, $selectedOptions, $userId); - } + $score = submitQuiz($exerciseId, $selectedOptions, $userId, $isDone); - header('Location: ../../exercise'); + header('Location: ../../exercise/result/' . $exerciseId . '?score=' . $score . '&isDone=' . $isDone); } ?> \ No newline at end of file diff --git a/app/controllers/Exercise.php b/app/controllers/Exercise.php index 92ce73c1558366de53217c9005dc2bf2d5a756c8..93374e9db6e54045c27564ddebfd36fc40eee13b 100644 --- a/app/controllers/Exercise.php +++ b/app/controllers/Exercise.php @@ -18,7 +18,7 @@ class Exercise extends Controller $response = curl_exec($ch); $exercise = json_decode($response, true); - $data["exercise"] = $exercise['result']; + $data["exercise"] = $exercise['result'] ?? []; // progress // $baseUrl = 'http://express:5000/progress/user/' . $data["user_id"]; @@ -28,7 +28,7 @@ class Exercise extends Controller $response = curl_exec($ch); $progress = json_decode($response, true); - $data["progress"] = $progress['result']; + $data["progress"] = $progress['result'] ?? []; $this->view('header/index', $data); $this->view('navbar/index'); @@ -55,7 +55,7 @@ class Exercise extends Controller $response = curl_exec($ch); $exercise = json_decode($response, true); - $data["exercise"] = $exercise['result']; + $data["exercise"] = $exercise['result'] ?? []; foreach ($data["exercise"] as $exercise) { if ($exercise["exercise_id"] === $data["exercise_id"]) { @@ -89,7 +89,7 @@ class Exercise extends Controller $response = curl_exec($ch); $question = json_decode($response, true); - $data["questions"] = $question['result']; + $data["questions"] = $question['result'] ?? []; // options foreach ($data["questions"] as &$question) { @@ -100,7 +100,7 @@ class Exercise extends Controller $response = curl_exec($ch); $option = json_decode($response, true); - $question["options"] = $option['result']; + $question["options"] = $option['result'] ?? []; curl_close($ch); } @@ -113,6 +113,75 @@ class Exercise extends Controller } } + public function result($exerciseId = null) + { + $this->validateSession(); + $this->validateParamExercise($exerciseId); + + // Result + if (isset($exerciseId) && !empty($exerciseId)) { + $data["pageTitle"] = "Test your knowledge!"; + $data["exercise_id"] = intval($exerciseId); + $data["user_id"] = $_SESSION['user_id']; + + $query = $this->getQuery(); + $data["score"] = -1; + + if (isset($query["score"]) && isset($query["isDone"])) { + $data["score"] = $query["score"]; + $data["isDone"] = $query["isDone"]; + } + + // user's gems + $baseUrl = 'http://soap:8080/service/gems'; + + $soapRequest = '<x:Envelope + xmlns:x="http://schemas.xmlsoap.org/soap/envelope/" + xmlns:ser="http://service.toco.org/"> + <x:Header/> + <x:Body> + <ser:getGems> + <user_id>' . $data["user_id"] . '</user_id> + </ser:getGems> + </x:Body> + </x:Envelope>'; + + $ch = curl_init($baseUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $soapRequest); + curl_setopt( + $ch, + CURLOPT_HTTPHEADER, + array( + 'Content-Type: text/xml', + 'SOAPAction: getGems', + 'X-api-key: toco_php' + ) + ); + + $response = curl_exec($ch); + + if (curl_errno($ch)) { + echo 'Curl error: ' . curl_error($ch); + } + + curl_close($ch); + $data['gems'] = $response; + + if (preg_match('/<return>(\d+)<\/return>/', $response, $matches)) { + $data['gems'] = (int) $matches[1]; + } else { + echo 'Error extracting numeric value from XML response.'; + } + + $this->view('header/index', $data); + $this->view('navbar/index'); + $this->view('result/index', $data); + $this->view('footer/index'); + } + } + public function validateParamExercise($exerciseId) { if (isset($exerciseId) && !empty($exerciseId)) { @@ -123,10 +192,12 @@ class Exercise extends Controller $response = curl_exec($ch); $valid = json_decode($response, true); - if ($valid['result'] === true) { + $valid = $valid['result'] ?? []; + + if ($valid === true) { return; } - + $this->show404(); exit(); } diff --git a/app/views/exercise/index.php b/app/views/exercise/index.php index 15d1ad14b255235b81a8c11b5c44638f9d259343..6210daf16eb10187efab577f3b6bbabead711deb 100644 --- a/app/views/exercise/index.php +++ b/app/views/exercise/index.php @@ -25,18 +25,7 @@ $selectedLanguageId = isset($_GET['language']) ? (int) $_GET['language'] : -1; <div class="card-container" id="exercise-container"> <?php foreach ($data['exercise'] as $exercise): ?> <div class="exercise-card"> - <?php - $isProgressExists = false; - foreach ($data["progress"] as $progress) { - if ($progress["exercise_id"] == $exercise['exercise_id']) { - $isProgressExists = true; - break; - } - } - ?> - <?php if (!$isProgressExists): ?> - <a href="/exercise/question/<?= $exercise['exercise_id'] ?>"> - <?php endif; ?> + <a href="/exercise/question/<?= $exercise['exercise_id'] ?>"> <div class="exercise-head"> <div class="content"> <h2 class="font-bold text-md"> @@ -66,9 +55,7 @@ $selectedLanguageId = isset($_GET['language']) ? (int) $_GET['language'] : -1; <?php endif; ?> <?php endforeach; ?> </div> - <?php if (!$isProgressExists): ?> - </a> - <?php endif; ?> + </a> </div> <?php endforeach; ?> </div> diff --git a/app/views/header/index.php b/app/views/header/index.php index c29fc91a7fca044367f83b640909201a91afe1e1..d56eb64b6b659ab2500b32cde36981597b8abf5d 100644 --- a/app/views/header/index.php +++ b/app/views/header/index.php @@ -29,5 +29,6 @@ <link rel="stylesheet" href="/public/css/video.css"> <link rel="stylesheet" href="/public/css/toast.css"> <link rel="stylesheet" href="/public/css/exercise.css"> + <link rel="stylesheet" href="/public/css/question.css"> </head> <body> \ No newline at end of file diff --git a/app/views/question/index.php b/app/views/question/index.php index bcbb87c58e31537f6db6d1617057e4c6e6ac5389..c079d5926453735d4b5899e77ef93f236f60916c 100644 --- a/app/views/question/index.php +++ b/app/views/question/index.php @@ -4,29 +4,43 @@ <?= $data['currentExercise']['exe_name'] ?> </h1> <form method="post" action="../../../api/exercise/submit.php"> - <div class="" id="question-container"> + <div class="question-content"> + <?php $questionNumber = 1; ?> <?php foreach ($data["questions"] as $question): ?> - <div> - <?= $question['question'] ?> - </div> - <?php foreach ($question["options"] as $option): ?> - <div> - <label> - <input type="radio" name="options[<?= $question['question_id'] ?>]" value="<?= $option['option_id'] ?>" - onclick="saveSelectedOption('<?= $question['question_id'] ?>', '<?= $option['option_id'] ?>')"> - <?= $option['option'] ?> - </label> + <div class="question-item"> + <h2 class="question-header"> + Question + <?= $questionNumber ?> + </h2> + <div class="question-text"> + <?= $question['question'] ?> </div> - <?php endforeach; ?> + <?php foreach ($question["options"] as $option): ?> + <div class="option"> + <input type="radio" name="options[<?= $question['question_id'] ?>]" + value="<?= $option['option_id'] ?>" + id="option_<?= $question['question_id'] ?>_<?= $option['option_id'] ?>" + onclick="saveSelectedOption('<?= $question['question_id'] ?>', '<?= $option['option_id'] ?>')"> + <label for="option_<?= $question['question_id'] ?>_<?= $option['option_id'] ?>" + class="option-label"> + <span class="radio-custom"></span> + <?= $option['option'] ?> + </label> + </div> + + <?php endforeach; ?> + <?php $questionNumber++; ?> + </div> <?php endforeach; ?> + <div class="" id="hidden"></div> + <input type="hidden" name="exerciseId" value="<?= $data['currentExercise']['exercise_id'] ?>"> + <input type="hidden" name="userId" value="<?= $data["user_id"] ?>"> + <input type="hidden" name="isDone" value="<?= $data["isDone"] ?>"> + <div class="button-container"> + <button class="primary-button submit-button" type="submit" name="submitQuiz">Submit</button> + </div> </div> - <div class="" id="hidden"></div> - <input type="hidden" name="exerciseId" value="<?= $data['currentExercise']['exercise_id'] ?>"> - <input type="hidden" name="userId" value="<?= $data["user_id"] ?>"> - <input type="hidden" name="isDone" value="<?= $data["isDone"] ?>"> - <button type="submit" name="submitQuiz">Submit</button> </form> - <div id="exercise-score"></div> </div> </div> @@ -42,8 +56,6 @@ listString += listString ? '.' : ''; - console.log(listString); - const hidden = document.getElementById('hidden'); hidden.innerHTML = ''; @@ -52,4 +64,4 @@ <input type="hidden" name="selectedOptions" value="${listString}"> `; } -</script> +</script> \ No newline at end of file diff --git a/app/views/result/index.php b/app/views/result/index.php new file mode 100644 index 0000000000000000000000000000000000000000..4a4e756d6469dad5d3db7a087b39e4f9b8f3f6e0 --- /dev/null +++ b/app/views/result/index.php @@ -0,0 +1,18 @@ +<div class="result"> + <div class="container result-container"> + <h1 class="font-bold text-xl text-blue-purple-gradient result-text"> + Your Score: <?= $data['score'] ?> + </h1> + <? if ($data['isDone'] == "") : ?> + <div class="font-bold text-xl text-blue-purple-gradient result-text"> + Gems earned: +<?= $data['score'] ?> + </div> + <div class="font-bold text-xl text-blue-purple-gradient result-text"> + Your total gems: <?= $data['gems'] ?> + </div> + <? endif; ?> + <div class="button-container"> + <a href="/exercise" class="distinct-button submit-button">Back to Exercise</a> + </div> + </div> +</div> \ No newline at end of file diff --git a/public/css/question.css b/public/css/question.css new file mode 100644 index 0000000000000000000000000000000000000000..cf17af65b2a2c8d44e81239cf46cce33aeb0baf8 --- /dev/null +++ b/public/css/question.css @@ -0,0 +1,139 @@ +.question { + width: 100%; + padding-top: 120px; + display: flex; + /* justify-content: center; */ + padding-bottom: 60px; + min-height: 100vh; +} + +.question .question-container { + display: flex; + flex-direction: column; + gap: 50px; +} + +.question .question-container h1 { + text-align: center; +} + +.question-content { + margin: 0 10vw; +} + +.question-content .question-item { + margin-bottom: 40px; +} + +.question-content .question-item .question-header { + color: #006CDB; + font-size: 18px; + margin-bottom: 5px; +} + +.question-content .question-item .question-text { + font-size: 21px; + color: var(--orange); + margin-bottom: 10px; +} + +.question-content .question-item .option .option-text { + font-size: 16px; + font-weight: 500; + margin-bottom: 7px; +} + +.option input[type="radio"] { + display: none; +} + +.option-label { + cursor: pointer; + display: inline-block; + position: relative; + padding-left: 35px; + margin-right: 15px; + font-size: 18px; +} + +.radio-custom { + position: absolute; + top: 50%; + left: 0; + transform: translateY(-50%); + width: 18px; + height: 18px; + border: 2px solid var(--orange); + border-radius: 50%; +} + +.option input[type="radio"]:checked + .option-label .radio-custom::after { + content: ""; + display: block; + width: 18px; + height: 18px; + background-color: var(--orange); + border-radius: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + transition: all 0.3s ease; +} + +.submit-button { + margin-top: 40px; + background: linear-gradient(45deg, #006CDB, #8535D4); + color: #FFFFFF; + padding: 12px 100px; + border-radius: 50px; + font-size: 16px; + letter-spacing: 1px; + cursor: pointer; + border: 0.5px solid #006CDB; + transition: all 0.3s ease 0s; +} + +.submit-button { + margin-top: 90px; + margin-bottom: 40px; + background: linear-gradient(45deg, #006CDB, #8535D4); + color: #FFFFFF; + padding: 12px 100px; + border-radius: 50px; + font-size: 16px; + letter-spacing: 1px; + cursor: pointer; + border: 0.5px solid #006CDB; + transition: all 0.3s ease 0s; +} + +.submit-button:hover { + transform: translateY(-4px); + opacity: 0.6; +} + +.submit-button:active { + transform: translateY(6px); +} + + +.result { + width: 100%; + padding-top: 120px; + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 60px; + min-height: 100vh; +} + +.result .result-container { + display: flex; + flex-direction: column; + gap: 50px; +} + +.result .result-text { + text-align: center; +} \ No newline at end of file diff --git a/public/js/question.js b/public/js/question.js deleted file mode 100644 index 254824b6746078fdea296be867830bb6002b5883..0000000000000000000000000000000000000000 --- a/public/js/question.js +++ /dev/null @@ -1,130 +0,0 @@ -fetchQuestions(0); - -let selectedOption = {}; - -function fetchQuestions(page) { - fetch(`http://localhost:5000/question/${exercise}`) - .then(response => response.json()) - .then(question => { - const questionContainer = document.getElementById('question-container'); - - questionContainer.innerHTML = ''; - - const questionList = document.createElement('div'); - questionList.classList.add('question-list'); - - questionList.innerHTML = ` - <div class="question-head"> - <div class="content" id="content"> - <h2>${question.result[page].question}</h2> - </div> - </div> - `; - - questionContainer.appendChild(questionList); - - fetch(`http://localhost:5000/option/${question.result[page].question_id}`) - .then(response => response.json()) - .then(options => { - const content = document.getElementById('content'); - const optionList = document.createElement('div'); - optionList.classList.add('option-list'); - - options.result.forEach(option => { - const optionItem = document.createElement('div'); - optionItem.innerHTML = ` - <label> - <input - type="radio" - name="options" - value="${option.option_id}" - ${selectedOption[question.result[page].question_id.toString()] === option.option_id ? 'checked' : ''} - onclick="saveSelectedOption(${question.result[page].question_id}, ${option.option_id})" - > - ${option.option} - </label> - `; - // console.log(option.option_id) - - optionList.appendChild(optionItem); - }); - - content.appendChild(optionList); - }); - }) - .catch(error => { - console.error('Error:', error); - }); -} - -const exercisePagination = document.getElementById('exercise-pagination'); -fetch(`http://localhost:5000/question/count/${exercise}`) - .then(response => response.json()) - .then(countData => { - const totalQuestions = countData.result; - const totalPages = totalQuestions; - - console.log('Total Questions:', totalQuestions); - - for (let page = 1; page <= totalPages; page++) { - const paginationCard = document.createElement('div'); - paginationCard.classList.add('pagination-card'); - - paginationCard.innerHTML = ` - <button class="pagination-button" onclick="fetchQuestions(${page - 1})"> - ${page} - </button> - `; - - exercisePagination.appendChild(paginationCard); - } - }) - .catch(error => { - console.error('Error:', error); - }); - -function saveSelectedOption(questionId, optionId) { - const questionKey = questionId.toString(); - selectedOption[questionKey] = optionId; -} - -function submitQuiz() { - const exerciseId = exercise; - - const submitData = {}; - - for (const questionId in selectedOption) { - if (selectedOption.hasOwnProperty(questionId)) { - const optionId = selectedOption[questionId]; - submitData[`question_${questionId}`] = optionId; - } - } - - fetch(`http://localhost:5000/exercise/result/${exerciseId}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(submitData), - }) - .then(response => response.json()) - .then(data => { - console.log('Quiz submitted successfully:', data); - const exerciseScore = document.getElementById('exercise-score'); - - const correct = Object.keys(data.correctResults); - const wrong = Object.keys(data.wrongResults); - const correctLength = correct.length; - const wrongLength = wrong.length; - const score = correctLength / (correctLength + wrongLength) * 100 - - exerciseScore.innerHTML = ''; - - exerciseScore.innerHTML = ` - ${score} - `; - }) - .catch(error => { - console.error('Error submitting quiz:', error); - }); -}