392 lines
No EOL
14 KiB
JavaScript
392 lines
No EOL
14 KiB
JavaScript
/**
|
|
* 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('<div class="error">A configuration error occurred. Please contact support.</div>');
|
|
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 = $('<div class="form-messages" style="display: none;"></div>').insertBefore($beginAssessmentButton);
|
|
} else if ($landingMessages.length === 0) {
|
|
// If no message area and no pre-screen form, add one anyway
|
|
$landingMessages = $('<div class="form-messages" style="display: none;"></div>').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 = $('<span class="save-indicator" style="margin-left: 10px; font-size: 0.8em; color: grey;"></span>').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); |