/**
* 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);