/** * Quiztech Assessment Interaction Script * * Handles AJAX submissions for pre-screening, answer auto-save, * and final assessment submission. */ (function($) { 'use strict'; $(function() { // Document Ready console.log('Assessment script loaded.'); // Check if localized data is available if (typeof quiztech_assessment_vars === 'undefined') { console.error('Quiztech Assessment Error: Localized variables not found.'); // Optionally display an error message to the user $('.assessment-container').prepend('
A configuration error occurred. Please contact support.
'); return; } // --- Cache jQuery Selectors --- var $landingSection = $('#quiztech-landing-section'); var $prescreeningForm = $('#quiztech-prescreening-form'); // May not exist var $prescreeningSection = $('#quiztech-prescreening-section'); // May not exist var $beginAssessmentButton = $('#quiztech-begin-assessment'); var $landingMessages = $landingSection.find('.form-messages'); // Placeholder for messages in landing area if ($landingMessages.length === 0 && $prescreeningForm.length > 0) { // If message area doesn't exist in landing but pre-screen form does, add one near the button $landingMessages = $('').insertBefore($beginAssessmentButton); } else if ($landingMessages.length === 0) { // If no message area and no pre-screen form, add one anyway $landingMessages = $('').insertBefore($beginAssessmentButton); } var $assessmentSection = $('#quiztech-assessment-section'); var $assessmentForm = $('#quiztech-assessment-form'); var $assessmentMessages = $assessmentForm.find('.form-messages'); // Placeholder in assessment form var $questionsContainer = $('#quiztech-questions-container'); var $questionContainers = $questionsContainer.find('.quiztech-question-container'); var $timerDisplay = $('#quiztech-timer'); var $nextButton = $('#quiztech-next-question'); var $submitAssessmentButton = $('#quiztech-submit-assessment'); var $completionMessage = $('#quiztech-completion-message'); // --- State Variables --- var currentQuestionIndex = 0; var totalQuestions = $questionContainers.length; var timerInterval; var timerSeconds = 0; var isSubmittingPreScreen = false; // Prevent double clicks // --- Initial UI State --- $assessmentSection.hide(); // Ensure assessment is hidden initially $landingSection.show(); // Ensure landing is visible // --- Helper Functions --- /** * Displays messages (errors or success) * @param {jQuery} $targetArea - The jQuery object where the message should be displayed. * @param {string} message - The message text. * @param {boolean} isError - True for error styling, false for success. */ function showMessage($targetArea, message, isError) { if (!$targetArea || $targetArea.length === 0) { console.warn("Message area not found for:", message); return; } $targetArea.empty().removeClass('error success') .addClass(isError ? 'error' : 'success') .text(message) .show(); } /** * Hides any messages in the target area. * @param {jQuery} $targetArea - The jQuery object where the message should be hidden. */ function clearMessage($targetArea) { if ($targetArea && $targetArea.length > 0) { $targetArea.empty().hide(); } } /** * Transitions the UI from the landing/pre-screening view to the assessment view. */ function startAssessmentUI() { $landingSection.slideUp(); $assessmentSection.slideDown(); if (totalQuestions > 0) { startTimer(); updateNavigationButtons(); // Ensure only the first question is visible initially $questionContainers.addClass('quiztech-question-hidden'); $questionContainers.eq(0).removeClass('quiztech-question-hidden'); } else { // Handle case with no questions if needed showMessage($assessmentMessages, 'No questions found for this assessment.', true); $timerDisplay.hide(); $nextButton.hide(); $submitAssessmentButton.hide(); } } // --- Event Handlers --- // Handle "Begin Assessment" button click $beginAssessmentButton.on('click', function() { var $button = $(this); clearMessage($landingMessages); // Clear previous messages // Check if pre-screening form exists and is visible var needsPreScreening = $prescreeningForm.length > 0 && $prescreeningSection.is(':visible'); if (needsPreScreening) { // Validate pre-screening form before submitting var isValid = true; $prescreeningForm.find('[required]').each(function() { if (!$(this).val()) { isValid = false; $(this).css('border-color', 'red'); // Highlight missing fields } else { $(this).css('border-color', ''); // Reset border color } }); if (!isValid) { showMessage($landingMessages, 'Please fill out all required pre-screening questions.', true); return; // Stop if validation fails } // Submit pre-screening via AJAX if (isSubmittingPreScreen) return; // Prevent double submission isSubmittingPreScreen = true; $button.prop('disabled', true).text('Submitting...'); var formData = $prescreeningForm.serialize(); formData += '&action=quiztech_submit_prescreening'; formData += '&nonce=' + quiztech_assessment_vars.prescreening_nonce; formData += '&invitation_id=' + quiztech_assessment_vars.invitation_id; $.ajax({ type: 'POST', url: quiztech_assessment_vars.ajax_url, data: formData, dataType: 'json', success: function(response) { if (response.success) { // Pre-screening successful, start assessment UI startAssessmentUI(); } else { // Handle WP JSON error from pre-screening submission showMessage($landingMessages, response.data.message || 'An error occurred submitting pre-screening.', true); $button.prop('disabled', false).text('Begin Assessment'); } }, error: function(jqXHR, textStatus, errorThrown) { // Handle general AJAX error console.error("AJAX Error (Pre-Screening):", textStatus, errorThrown); showMessage($landingMessages, 'A network error occurred. Please try again.', true); $button.prop('disabled', false).text('Begin Assessment'); }, complete: function() { isSubmittingPreScreen = false; // Re-enable submission possibility } }); } else { // No pre-screening needed, directly start the assessment UI startAssessmentUI(); } }); // --- Assessment Answer Auto-Save --- // (Keep existing auto-save logic - it targets elements within $assessmentForm) var autoSaveTimeout; $assessmentForm.on('change blur', 'input, textarea, select', function() { clearTimeout(autoSaveTimeout); var $input = $(this); var $questionGroup = $input.closest('.quiztech-question-container'); if ($questionGroup.length === 0) return; // Should not happen var questionId = $questionGroup.data('question-id'); var answer = ''; // Handle different input types for getting the value if ($input.is(':radio')) { if ($input.is(':checked')) { answer = $input.val(); } else { // If a radio button is changed, but not this one, don't save yet. // The 'change' event on the *checked* radio will trigger the save. // However, handle the case where *no* radio in the group is checked (e.g., initial state or clearing) var groupName = $input.attr('name'); if ($assessmentForm.find('input[name="' + groupName + '"]:checked').length === 0) { answer = ''; // Or handle as needed - maybe save only on explicit next/submit? } else { return; // Let the checked radio handle the save } } } else if ($input.is(':checkbox')) { // Example for checkboxes (if added later) - might need array handling answer = $input.is(':checked') ? $input.val() : ''; // Simplistic, adjust if multiple checkboxes per question } else { answer = $input.val(); // For textarea, text input, select } // Add visual indicator var $indicator = $questionGroup.find('.save-indicator'); if ($indicator.length === 0) { $indicator = $('').appendTo($questionGroup.find('.question-answer-area')); } $indicator.text('Saving...').css('color', 'grey').show(); autoSaveTimeout = setTimeout(function() { $.ajax({ type: 'POST', url: quiztech_assessment_vars.ajax_url, data: { action: 'quiztech_save_answer', nonce: quiztech_assessment_vars.assessment_nonce, invitation_id: quiztech_assessment_vars.invitation_id, question_id: questionId, answer: answer }, dataType: 'json', success: function(response) { if (response.success) { $indicator.text('Saved ✓').css('color', 'green'); setTimeout(function() { $indicator.fadeOut(function() { $(this).remove(); }); }, 2000); } else { $indicator.text('Error!').css('color', 'red'); console.error("Auto-save error:", response.data.message); } }, error: function(jqXHR, textStatus, errorThrown) { $indicator.text('Network Error!').css('color', 'red'); console.error("AJAX Error (Auto-Save):", textStatus, errorThrown); } }); }, 750); // Slightly longer debounce }); // --- Assessment Navigation --- $nextButton.on('click', function() { if (currentQuestionIndex < totalQuestions - 1) { // Basic validation before proceeding (optional but good) var $currentQuestion = $questionContainers.eq(currentQuestionIndex); var $requiredInputs = $currentQuestion.find('[required]'); var isValid = true; $requiredInputs.each(function() { var $input = $(this); var value = ''; if ($input.is(':radio')) { var groupName = $input.attr('name'); value = $assessmentForm.find('input[name="' + groupName + '"]:checked').val(); } else { value = $input.val(); } if (!value) { isValid = false; // Maybe add a visual cue near the input? } }); if (!isValid) { showMessage($assessmentMessages, 'Please answer the current question before proceeding.', true); // Auto-hide message after a delay setTimeout(function() { clearMessage($assessmentMessages); }, 3000); return; } clearMessage($assessmentMessages); // Clear any previous error // Hide current, show next $currentQuestion.addClass('quiztech-question-hidden'); currentQuestionIndex++; $questionContainers.eq(currentQuestionIndex).removeClass('quiztech-question-hidden'); updateNavigationButtons(); } }); function updateNavigationButtons() { if (totalQuestions <= 0) { $nextButton.hide(); $submitAssessmentButton.hide(); return; } if (currentQuestionIndex >= totalQuestions - 1) { // Last question $nextButton.hide(); $submitAssessmentButton.show(); } else { $nextButton.show(); $submitAssessmentButton.hide(); } } // --- Timer Functions --- function startTimer() { if (timerInterval) clearInterval(timerInterval); timerSeconds = 0; $timerDisplay.text(formatTime(timerSeconds)).show(); // Ensure visible timerInterval = setInterval(function() { timerSeconds++; $timerDisplay.text(formatTime(timerSeconds)); }, 1000); } function stopTimer() { clearInterval(timerInterval); } function formatTime(totalSeconds) { var hours = Math.floor(totalSeconds / 3600); var minutes = Math.floor((totalSeconds % 3600) / 60); var seconds = totalSeconds % 60; minutes = String(minutes).padStart(2, '0'); seconds = String(seconds).padStart(2, '0'); return (hours > 0 ? String(hours).padStart(2, '0') + ':' : '') + minutes + ':' + seconds; } // --- Final Assessment Submission --- $submitAssessmentButton.on('click', function(event) { event.preventDefault(); // Final validation (optional, ensure last question answered) var $currentQuestion = $questionContainers.eq(currentQuestionIndex); var $requiredInputs = $currentQuestion.find('[required]'); var isValid = true; $requiredInputs.each(function() { var $input = $(this); var value = ''; if ($input.is(':radio')) { var groupName = $input.attr('name'); value = $assessmentForm.find('input[name="' + groupName + '"]:checked').val(); } else { value = $input.val(); } if (!value) { isValid = false; } }); if (!isValid) { showMessage($assessmentMessages, 'Please answer the final question before submitting.', true); setTimeout(function() { clearMessage($assessmentMessages); }, 3000); return; } clearMessage($assessmentMessages); if (!confirm('Are you sure you want to submit your assessment?')) { return; } var $button = $(this); $button.prop('disabled', true).text('Submitting...'); $.ajax({ type: 'POST', url: quiztech_assessment_vars.ajax_url, data: { action: 'quiztech_submit_assessment', nonce: quiztech_assessment_vars.assessment_nonce, invitation_id: quiztech_assessment_vars.invitation_id }, dataType: 'json', success: function(response) { if (response.success) { // Success! Display completion message and hide form/timer stopTimer(); $assessmentForm.hide(); $timerDisplay.hide(); $completionMessage.html(response.data.completionMessage || 'Assessment Submitted Successfully!').show(); } else { showMessage($assessmentMessages, response.data.message || 'An error occurred during submission.', true); $button.prop('disabled', false).text('Submit Assessment'); } }, error: function(jqXHR, textStatus, errorThrown) { console.error("AJAX Error (Submission):", textStatus, errorThrown); showMessage($assessmentMessages, 'A network error occurred. Please try again.', true); $button.prop('disabled', false).text('Submit Assessment'); } }); }); }); // End document ready })(jQuery);