feat: Implement assessment landing page flow

This commit is contained in:
Ruben Ramirez 2025-04-04 06:54:34 -05:00
parent ee3b337d5e
commit f71f305005
2 changed files with 391 additions and 202 deletions

View file

@ -7,23 +7,34 @@
(function($) { (function($) {
'use strict'; 'use strict';
$(function() { $(function() { // Document Ready
console.log('Assessment script loaded.'); console.log('Assessment script loaded.');
// Check if localized data is available // Check if localized data is available
if (typeof quiztech_assessment_vars === 'undefined') { if (typeof quiztech_assessment_vars === 'undefined') {
console.error('Quiztech Assessment Error: Localized variables not found.'); 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; return;
} }
var $prescreeningForm = $('#quiztech-prescreening-form'); // --- Cache jQuery Selectors ---
var $prescreeningSection = $('#quiztech-prescreening-section'); var $landingSection = $('#quiztech-landing-section');
var $assessmentSection = $('#quiztech-assessment-section'); var $prescreeningForm = $('#quiztech-prescreening-form'); // May not exist
var $submitButton = $prescreeningForm.find('button[type="submit"]'); var $prescreeningSection = $('#quiztech-prescreening-section'); // May not exist
var $preScreenFormMessages = $('<div class="form-messages"></div>').insertBefore($submitButton); // Area for messages 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);
}
// Assessment specific elements var $assessmentSection = $('#quiztech-assessment-section');
var $assessmentForm = $('#quiztech-assessment-form'); var $assessmentForm = $('#quiztech-assessment-form');
var $assessmentMessages = $assessmentForm.find('.form-messages'); // Placeholder in assessment form
var $questionsContainer = $('#quiztech-questions-container'); var $questionsContainer = $('#quiztech-questions-container');
var $questionContainers = $questionsContainer.find('.quiztech-question-container'); var $questionContainers = $questionsContainer.find('.quiztech-question-container');
var $timerDisplay = $('#quiztech-timer'); var $timerDisplay = $('#quiztech-timer');
@ -31,84 +42,185 @@
var $submitAssessmentButton = $('#quiztech-submit-assessment'); var $submitAssessmentButton = $('#quiztech-submit-assessment');
var $completionMessage = $('#quiztech-completion-message'); var $completionMessage = $('#quiztech-completion-message');
// State variables // --- State Variables ---
var currentQuestionIndex = 0; var currentQuestionIndex = 0;
var totalQuestions = $questionContainers.length; var totalQuestions = $questionContainers.length;
var timerInterval; var timerInterval;
var timerSeconds = 0; var timerSeconds = 0;
var isSubmittingPreScreen = false; // Prevent double clicks
// --- Pre-Screening Form Handling --- // --- Initial UI State ---
$prescreeningForm.on('submit', function(event) { $assessmentSection.hide(); // Ensure assessment is hidden initially
event.preventDefault(); // Stop traditional form submission $landingSection.show(); // Ensure landing is visible
// Use $preScreenFormMessages here // --- Helper Functions ---
$preScreenFormMessages.empty().removeClass('error success'); // Clear previous messages
$submitButton.prop('disabled', true).text('Submitting...'); // Disable button
var formData = $(this).serialize(); // Get form data /**
* 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();
}
// Add required AJAX parameters /**
formData += '&action=quiztech_submit_prescreening'; * Hides any messages in the target area.
formData += '&nonce=' + quiztech_assessment_vars.prescreening_nonce; * @param {jQuery} $targetArea - The jQuery object where the message should be hidden.
formData += '&invitation_id=' + quiztech_assessment_vars.invitation_id; */
function clearMessage($targetArea) {
if ($targetArea && $targetArea.length > 0) {
$targetArea.empty().hide();
}
}
$.ajax({ /**
type: 'POST', * Transitions the UI from the landing/pre-screening view to the assessment view.
url: quiztech_assessment_vars.ajax_url, */
data: formData, function startAssessmentUI() {
dataType: 'json', // Expect JSON response from server $landingSection.slideUp();
success: function(response) { $assessmentSection.slideDown();
if (response.success) { if (totalQuestions > 0) {
// Success! Hide pre-screening, show assessment startTimer();
$preScreenFormMessages.addClass('success').text(response.data.message || 'Success!'); // Show success message briefly & Use correct variable updateNavigationButtons();
$prescreeningSection.slideUp(); // Ensure only the first question is visible initially
$assessmentSection.slideDown(); $questionContainers.addClass('quiztech-question-hidden');
startTimer(); // Start the assessment timer $questionContainers.eq(0).removeClass('quiztech-question-hidden');
updateNavigationButtons(); // Show initial nav state } else {
// No need to re-enable button as the form is gone // 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 { } else {
// Handle WP JSON error $(this).css('border-color', ''); // Reset border color
$preScreenFormMessages.addClass('error').text(response.data.message || 'An error occurred.'); // Use correct variable
$submitButton.prop('disabled', false).text('Submit Pre-Screening & Start Assessment'); // Re-enable button
} }
}, });
error: function(jqXHR, textStatus, errorThrown) {
// Handle general AJAX error if (!isValid) {
console.error("AJAX Error:", textStatus, errorThrown); showMessage($landingMessages, 'Please fill out all required pre-screening questions.', true);
$preScreenFormMessages.addClass('error').text('A network error occurred. Please try again.'); // Use correct variable return; // Stop if validation fails
$submitButton.prop('disabled', false).text('Submit Pre-Screening & Start Assessment'); // Re-enable button
} }
});
// 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 --- // --- Assessment Answer Auto-Save ---
// var $assessmentForm = $('#quiztech-assessment-form'); // Already defined above // (Keep existing auto-save logic - it targets elements within $assessmentForm)
var autoSaveTimeout; // To debounce requests var autoSaveTimeout;
// Target input/textarea/select elements within the assessment form for auto-save
$assessmentForm.on('change blur', 'input, textarea, select', function() { $assessmentForm.on('change blur', 'input, textarea, select', function() {
clearTimeout(autoSaveTimeout); // Clear previous timeout if exists clearTimeout(autoSaveTimeout);
var $input = $(this); var $input = $(this);
var $questionGroup = $input.closest('.quiztech-question-container'); // Updated selector var $questionGroup = $input.closest('.quiztech-question-container');
var questionId = $questionGroup.data('question-id'); if ($questionGroup.length === 0) return; // Should not happen
var answer = $input.val();
// Add a small visual indicator within the question group 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'); var $indicator = $questionGroup.find('.save-indicator');
if ($indicator.length === 0) { if ($indicator.length === 0) {
$indicator = $('<span class="save-indicator" style="margin-left: 10px; font-size: 0.8em; color: grey;"></span>').appendTo($questionGroup.find('label:first')); $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...'); $indicator.text('Saving...').css('color', 'grey').show();
// Debounce the AJAX request slightly
autoSaveTimeout = setTimeout(function() { autoSaveTimeout = setTimeout(function() {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: quiztech_assessment_vars.ajax_url, url: quiztech_assessment_vars.ajax_url,
data: { data: {
action: 'quiztech_save_answer', action: 'quiztech_save_answer',
nonce: quiztech_assessment_vars.assessment_nonce, // Use the correct nonce nonce: quiztech_assessment_vars.assessment_nonce,
invitation_id: quiztech_assessment_vars.invitation_id, invitation_id: quiztech_assessment_vars.invitation_id,
question_id: questionId, question_id: questionId,
answer: answer answer: answer
@ -117,28 +229,52 @@
success: function(response) { success: function(response) {
if (response.success) { if (response.success) {
$indicator.text('Saved ✓').css('color', 'green'); $indicator.text('Saved ✓').css('color', 'green');
// Optionally fade out the indicator after a delay setTimeout(function() { $indicator.fadeOut(function() { $(this).remove(); }); }, 2000);
setTimeout(function() { $indicator.fadeOut().remove(); }, 2000);
} else { } else {
$indicator.text('Error!').css('color', 'red'); $indicator.text('Error!').css('color', 'red');
console.error("Auto-save error:", response.data.message); console.error("Auto-save error:", response.data.message);
// Consider more prominent error display if needed
} }
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function(jqXHR, textStatus, errorThrown) {
$indicator.text('Network Error!').css('color', 'red'); $indicator.text('Network Error!').css('color', 'red');
console.error("AJAX Error:", textStatus, errorThrown); console.error("AJAX Error (Auto-Save):", textStatus, errorThrown);
} }
}); });
}, 500); // Wait 500ms after the last change/blur before sending }, 750); // Slightly longer debounce
}); });
// --- Assessment Navigation --- // --- Assessment Navigation ---
$nextButton.on('click', function() { $nextButton.on('click', function() {
if (currentQuestionIndex < totalQuestions - 1) { 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 // Hide current, show next
$questionContainers.eq(currentQuestionIndex).addClass('quiztech-question-hidden'); $currentQuestion.addClass('quiztech-question-hidden');
currentQuestionIndex++; currentQuestionIndex++;
$questionContainers.eq(currentQuestionIndex).removeClass('quiztech-question-hidden'); $questionContainers.eq(currentQuestionIndex).removeClass('quiztech-question-hidden');
updateNavigationButtons(); updateNavigationButtons();
@ -146,6 +282,11 @@
}); });
function updateNavigationButtons() { function updateNavigationButtons() {
if (totalQuestions <= 0) {
$nextButton.hide();
$submitAssessmentButton.hide();
return;
}
if (currentQuestionIndex >= totalQuestions - 1) { if (currentQuestionIndex >= totalQuestions - 1) {
// Last question // Last question
$nextButton.hide(); $nextButton.hide();
@ -158,9 +299,9 @@
// --- Timer Functions --- // --- Timer Functions ---
function startTimer() { function startTimer() {
if (timerInterval) clearInterval(timerInterval); // Clear existing if any if (timerInterval) clearInterval(timerInterval);
timerSeconds = 0; // Reset timer timerSeconds = 0;
$timerDisplay.text(formatTime(timerSeconds)); // Initial display $timerDisplay.text(formatTime(timerSeconds)).show(); // Ensure visible
timerInterval = setInterval(function() { timerInterval = setInterval(function() {
timerSeconds++; timerSeconds++;
@ -168,57 +309,80 @@
}, 1000); }, 1000);
} }
function stopTimer() {
clearInterval(timerInterval);
}
function formatTime(totalSeconds) { function formatTime(totalSeconds) {
var hours = Math.floor(totalSeconds / 3600); var hours = Math.floor(totalSeconds / 3600);
var minutes = Math.floor((totalSeconds % 3600) / 60); var minutes = Math.floor((totalSeconds % 3600) / 60);
var seconds = totalSeconds % 60; var seconds = totalSeconds % 60;
// Pad with leading zeros
minutes = String(minutes).padStart(2, '0'); minutes = String(minutes).padStart(2, '0');
seconds = String(seconds).padStart(2, '0'); seconds = String(seconds).padStart(2, '0');
return (hours > 0 ? String(hours).padStart(2, '0') + ':' : '') + minutes + ':' + seconds; return (hours > 0 ? String(hours).padStart(2, '0') + ':' : '') + minutes + ':' + seconds;
} }
// --- Final Assessment Submission --- // --- Final Assessment Submission ---
// var $submitAssessmentButton = $('#quiztech-submit-assessment'); // Already defined above
var $assessmentFormMessages = $('<div class="form-messages"></div>').insertAfter($submitAssessmentButton); // Area for messages
$submitAssessmentButton.on('click', function(event) { $submitAssessmentButton.on('click', function(event) {
event.preventDefault(); // Stop traditional form submission (though AJAX auto-save handles data) 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?')) { if (!confirm('Are you sure you want to submit your assessment?')) {
return; // User cancelled return;
} }
$assessmentFormMessages.empty().removeClass('error success'); var $button = $(this);
$submitAssessmentButton.prop('disabled', true).text('Submitting...'); $button.prop('disabled', true).text('Submitting...');
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: quiztech_assessment_vars.ajax_url, url: quiztech_assessment_vars.ajax_url,
data: { data: {
action: 'quiztech_submit_assessment', action: 'quiztech_submit_assessment',
nonce: quiztech_assessment_vars.assessment_nonce, // Reuse assessment nonce nonce: quiztech_assessment_vars.assessment_nonce,
invitation_id: quiztech_assessment_vars.invitation_id invitation_id: quiztech_assessment_vars.invitation_id
}, },
dataType: 'json', dataType: 'json',
success: function(response) { success: function(response) {
if (response.success) { if (response.success) {
// Success! Display completion message and hide form/timer // Success! Display completion message and hide form/timer
// Use .html() to render potential basic HTML in the message stopTimer();
$assessmentForm.hide();
$timerDisplay.hide();
$completionMessage.html(response.data.completionMessage || 'Assessment Submitted Successfully!').show(); $completionMessage.html(response.data.completionMessage || 'Assessment Submitted Successfully!').show();
$assessmentForm.hide(); // Hide the form elements (questions, nav)
$timerDisplay.hide(); // Hide timer
clearInterval(timerInterval); // Stop timer
$assessmentFormMessages.remove(); // Remove the temporary message area if not needed
} else { } else {
$assessmentFormMessages.addClass('error').text(response.data.message || 'An error occurred during submission.'); showMessage($assessmentMessages, response.data.message || 'An error occurred during submission.', true);
$submitAssessmentButton.prop('disabled', false).text('Submit Assessment'); // Re-enable button $button.prop('disabled', false).text('Submit Assessment');
} }
}, },
error: function(jqXHR, textStatus, errorThrown) { error: function(jqXHR, textStatus, errorThrown) {
console.error("AJAX Error:", textStatus, errorThrown); console.error("AJAX Error (Submission):", textStatus, errorThrown);
$assessmentFormMessages.addClass('error').text('A network error occurred. Please try again.'); showMessage($assessmentMessages, 'A network error occurred. Please try again.', true);
$submitAssessmentButton.prop('disabled', false).text('Submit Assessment'); // Re-enable button $button.prop('disabled', false).text('Submit Assessment');
} }
}); });
}); });

View file

@ -18,169 +18,199 @@ if ( ! defined( 'ABSPATH' ) ) {
// Retrieve data passed from the handler // Retrieve data passed from the handler
$invitation_data = get_query_var( 'quiztech_invitation_data' ); $invitation_data = get_query_var( 'quiztech_invitation_data' );
$current_step = get_query_var( 'quiztech_current_step' ); $current_step = get_query_var( 'quiztech_current_step' ); // Note: This might be less relevant now with the landing page approach
$pre_screening_questions = get_query_var( 'quiztech_pre_screening_questions' ); $pre_screening_questions = get_query_var( 'quiztech_pre_screening_questions' );
// Basic security check - ensure we have invitation data // Basic security check - ensure we have invitation data
if ( ! $invitation_data || ! $current_step ) { if ( ! $invitation_data ) {
// Should not happen if loaded correctly by the handler, but good practice. // Should not happen if loaded correctly by the handler, but good practice.
wp_die( esc_html__( 'Could not load assessment data.', 'quiztech' ) ); wp_die( esc_html__( 'Could not load assessment data. Invalid invitation.', 'quiztech' ) );
} }
// Minimal HTML structure // Fetch associated Job and Assessment details
$job_post = get_post( $invitation_data->job_id );
$assessment_post = get_post( $invitation_data->assessment_id );
if ( ! $job_post || ! $assessment_post ) {
wp_die( esc_html__( 'Could not load associated job or assessment details.', 'quiztech' ) );
}
$job_title = get_the_title( $job_post );
$assessment_title = get_the_title( $assessment_post );
// Consider fetching job description/content if needed: $job_content = apply_filters('the_content', $job_post->post_content);
// Determine if pre-screening should be displayed
$show_pre_screening = ! empty( $pre_screening_questions ) && is_array( $pre_screening_questions ) && $invitation_data->status === 'sent';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html <?php language_attributes(); ?>> <html <?php language_attributes(); ?>>
<head> <head>
<meta charset="<?php bloginfo( 'charset' ); ?>"> <meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo esc_html( get_the_title( $invitation_data->assessment_id ) ?: __( 'Assessment', 'quiztech' ) ); ?></title> <title><?php echo esc_html( $assessment_title ?: __( 'Assessment Invitation', 'quiztech' ) ); ?></title>
<?php // wp_head(); // Consider if theme/WP styles are needed or if it should be isolated ?> <?php // wp_head(); // Consider if theme/WP styles are needed or if it should be isolated ?>
<style> <style>
/* Basic styling for isolation */ /* Basic styling for isolation */
body { font-family: sans-serif; padding: 20px; background-color: #f9f9f9; } body { font-family: sans-serif; padding: 20px; background-color: #f9f9f9; }
.assessment-container { background-color: #fff; padding: 30px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); max-width: 800px; margin: 20px auto; } .assessment-container { background-color: #fff; padding: 30px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); max-width: 800px; margin: 20px auto; }
h1, h2 { color: #333; } h1, h2, h3 { color: #333; }
.error { color: red; font-weight: bold; } .error { color: red; font-weight: bold; }
.step-info { margin-bottom: 20px; padding: 10px; background-color: #eef; border-left: 3px solid #aab; } .step-info { margin-bottom: 20px; padding: 10px; background-color: #eef; border-left: 3px solid #aab; }
.form-group { margin-bottom: 15px; } .form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: bold; } .form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
.form-group textarea { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px; min-height: 80px; } .form-group textarea { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px; min-height: 80px; }
button[type="submit"] { padding: 10px 20px; background-color: #0073aa; color: #fff; border: none; border-radius: 3px; cursor: pointer; } button { padding: 10px 20px; background-color: #0073aa; color: #fff; border: none; border-radius: 3px; cursor: pointer; font-size: 1em; }
button[type="submit"]:hover { background-color: #005a87; } button:hover { background-color: #005a87; }
button:disabled { background-color: #a0a5aa; cursor: not-allowed; }
#quiztech-begin-assessment { margin-top: 20px; }
.quiztech-timer { font-size: 1.2em; font-weight: bold; text-align: right; margin-bottom: 15px; padding: 5px; background-color: #f0f0f0; border-radius: 3px; } .quiztech-timer { font-size: 1.2em; font-weight: bold; text-align: right; margin-bottom: 15px; padding: 5px; background-color: #f0f0f0; border-radius: 3px; }
.quiztech-question-container { border: 1px solid #eee; padding: 20px; margin-bottom: 20px; } .quiztech-question-container { border: 1px solid #eee; padding: 20px; margin-bottom: 20px; }
.quiztech-question-hidden { display: none; } .quiztech-question-hidden { display: none; }
.quiztech-navigation { margin-top: 20px; text-align: right; } .quiztech-navigation { margin-top: 20px; text-align: right; }
.quiztech-navigation button { margin-left: 10px; padding: 10px 20px; background-color: #0073aa; color: #fff; border: none; border-radius: 3px; cursor: pointer; } .quiztech-navigation button { margin-left: 10px; }
.quiztech-navigation button:hover { background-color: #005a87; }
#quiztech-completion-message { margin-top: 20px; padding: 15px; background-color: #dff0d8; border: 1px solid #d6e9c6; color: #3c763d; border-radius: 4px; } #quiztech-completion-message { margin-top: 20px; padding: 15px; background-color: #dff0d8; border: 1px solid #d6e9c6; color: #3c763d; border-radius: 4px; }
/* Hide sections by default - JS will show them */
#quiztech-assessment-section { display: none; }
.form-messages { margin-top: 10px; padding: 10px; border-radius: 3px; }
.form-messages.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.form-messages.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
</style> </style>
</head> </head>
<body> <body>
<div class="assessment-container"> <div class="assessment-container">
<h1><?php esc_html_e( 'Assessment Invitation', 'quiztech' ); ?></h1> <!-- Landing Section -->
<div id="quiztech-landing-section">
<h1><?php esc_html_e( 'Assessment Invitation', 'quiztech' ); ?></h1>
<?php if ( 'pre_screening' === $current_step ) : ?> <h2><?php echo esc_html( $job_title ); ?></h2>
<div id="quiztech-prescreening-section"> <?php // Added ID for JS targeting ?> <h3><?php printf( esc_html__( 'Assessment: %s', 'quiztech' ), esc_html( $assessment_title ) ); ?></h3>
<?php // Optional: Display Job Content/Description ?>
<?php /*
if ( ! empty( $job_content ) ) {
echo '<div>' . wp_kses_post( $job_content ) . '</div>';
}
*/ ?>
<?php if ( $show_pre_screening ) : ?>
<div id="quiztech-prescreening-section">
<div class="step-info">
<h4><?php esc_html_e( 'Pre-Screening Questions', 'quiztech' ); ?></h4>
<p><?php esc_html_e( 'Please answer the following questions before starting the assessment.', 'quiztech' ); ?></p>
</div>
<form id="quiztech-prescreening-form" method="post" action=""> <?php // Action handled by AJAX ?>
<?php // Nonce is checked via AJAX ?>
<input type="hidden" name="action" value="quiztech_submit_prescreening">
<div class="form-messages" style="display: none;"></div> <?php // Placeholder for messages ?>
<?php foreach ( $pre_screening_questions as $index => $question ) : ?>
<div class="form-group">
<label for="pre_screen_answer_<?php echo esc_attr( $index ); ?>">
<?php echo esc_html( $question ); ?>
</label>
<textarea
id="pre_screen_answer_<?php echo esc_attr( $index ); ?>"
name="pre_screen_answer[<?php echo esc_attr( $index ); ?>]"
required
></textarea>
</div>
<?php endforeach; ?>
<?php // Submit button removed - handled by #quiztech-begin-assessment ?>
</form>
</div> <!-- #quiztech-prescreening-section -->
<?php endif; ?>
<button type="button" id="quiztech-begin-assessment"><?php esc_html_e( 'Begin Assessment', 'quiztech' ); ?></button>
</div> <!-- #quiztech-landing-section -->
<!-- Assessment Section (Initially Hidden) -->
<div id="quiztech-assessment-section">
<div class="step-info"> <div class="step-info">
<h2><?php esc_html_e( 'Step 1: Pre-Screening Questions', 'quiztech' ); ?></h2> <h2><?php printf( esc_html__( 'Assessment: %s', 'quiztech' ), esc_html( $assessment_title ) ); ?></h2>
<p><?php esc_html_e( 'Please answer the following questions before starting the assessment.', 'quiztech' ); ?></p> <?php // Optional: Add instructions here ?>
</div>
<form id="quiztech-prescreening-form" method="post" action=""> <?php // Action handled by AJAX ?>
<?php // Nonce is checked via AJAX, but good to have in form too for non-JS fallback (if implemented) ?>
<?php // wp_nonce_field( 'quiztech_submit_prescreening_' . $invitation_data->token, 'quiztech_prescreening_nonce' ); ?>
<?php // Token is passed via localized script vars ?>
<?php // <input type="hidden" name="quiztech_invitation_token" value="<?php echo esc_attr( $invitation_data->token ); ?>"> ?>
<input type="hidden" name="action" value="quiztech_submit_prescreening">
<?php if ( is_array( $pre_screening_questions ) && ! empty( $pre_screening_questions ) ) : ?>
<?php foreach ( $pre_screening_questions as $index => $question ) : ?>
<div class="form-group">
<label for="pre_screen_answer_<?php echo esc_attr( $index ); ?>">
<?php echo esc_html( $question ); ?>
</label>
<textarea
id="pre_screen_answer_<?php echo esc_attr( $index ); ?>"
name="pre_screen_answer[<?php echo esc_attr( $index ); ?>]"
required
></textarea>
</div>
<?php endforeach; ?>
<?php else : ?>
<p><?php esc_html_e( 'No pre-screening questions found, but expected.', 'quiztech' ); ?></p>
<?php // Optionally show the debug info if needed
// echo '<pre>Pre-screening Data: ' . esc_html( print_r( $pre_screening_questions, true ) ) . '</pre>';
?>
<?php endif; ?>
<?php // Render questions here - Removed, handled above ?>
<button type="submit"><?php esc_html_e( 'Submit Pre-Screening & Start Assessment', 'quiztech' ); ?></button>
</form>
</div> <!-- #quiztech-prescreening-section -->
<?php elseif ( 'assessment' === $current_step ) : ?>
<div id="quiztech-assessment-section" style="display: none;"> <?php // Added ID and initially hidden ?>
<div class="step-info">
<h2><?php esc_html_e( 'Step 2: Assessment', 'quiztech' ); ?></h2>
<p><?php printf( esc_html__( 'You are about to begin Assessment ID: %d', 'quiztech' ), absint( $invitation_data->assessment_id ) ); ?></p>
</div> </div>
<div id="quiztech-timer" class="quiztech-timer">00:00</div> <?php // Timer placeholder ?> <div id="quiztech-timer" class="quiztech-timer">00:00</div> <?php // Timer placeholder ?>
<?php <?php
$assessment_id = $invitation_data->assessment_id; $assessment_id = $invitation_data->assessment_id;
// Fetch question IDs associated with the assessment (assuming stored in 'question_ids' meta field) // Fetch question IDs associated with the assessment
$question_ids = get_post_meta( $assessment_id, '_quiztech_question_ids', true ); // Corrected meta key $question_ids = get_post_meta( $assessment_id, '_quiztech_question_ids', true );
if ( is_array( $question_ids ) && ! empty( $question_ids ) ) : if ( is_array( $question_ids ) && ! empty( $question_ids ) ) :
?> ?>
<form id="quiztech-assessment-form" method="post" action=""> <?php // Action handled by AJAX ?> <form id="quiztech-assessment-form" method="post" action=""> <?php // Action handled by AJAX ?>
<?php // Nonce is checked via AJAX, hidden inputs might not be needed if not submitting traditionally ?> <?php // Nonce is checked via AJAX ?>
<?php // wp_nonce_field( 'quiztech_submit_assessment_' . $invitation_data->token, 'quiztech_assessment_nonce' ); ?> <div class="form-messages" style="display: none;"></div> <?php // Placeholder for messages ?>
<?php // <input type="hidden" name="quiztech_invitation_token" value="<?php echo esc_attr( $invitation_data->token ); ?>"> ?>
<?php // <input type="hidden" name="quiztech_assessment_id" value="<?php echo esc_attr( $assessment_id ); ?>"> ?>
<?php // <input type="hidden" name="action" value="quiztech_submit_assessment"> ?>
<div id="quiztech-questions-container"> <div id="quiztech-questions-container">
<?php foreach ( $question_ids as $index => $question_id ) : ?>
<?php
$question_post = get_post( $question_id );
if ( ! $question_post || 'question' !== $question_post->post_type ) {
continue; // Skip if post not found or not a question
}
$question_title = get_the_title( $question_post );
$question_content = apply_filters('the_content', $question_post->post_content); // Get question content
$question_type = get_post_meta( $question_id, 'question_type', true );
$is_first_question = ( $index === 0 ); // Check if it's the first question
?>
<div class="quiztech-question-container <?php echo $is_first_question ? '' : 'quiztech-question-hidden'; ?>"
data-question-id="<?php echo esc_attr( $question_id ); ?>"
data-question-index="<?php echo esc_attr( $index ); ?>">
<?php foreach ( $question_ids as $index => $question_id ) : ?> <h4><?php printf( esc_html__( 'Question %d of %d', 'quiztech' ), $index + 1, count( $question_ids ) ); ?></h4>
<?php <div class="question-title"><strong><?php echo esc_html( $question_title ); ?></strong></div>
$question_post = get_post( $question_id ); <?php if ( ! empty( $question_content ) ) : ?>
if ( ! $question_post || 'question' !== $question_post->post_type ) { <div class="question-content">
continue; // Skip if post not found or not a question <?php echo wp_kses_post( $question_content ); ?>
} </div>
$question_title = get_the_title( $question_post ); <?php endif; ?>
$question_type = get_post_meta( $question_id, 'question_type', true );
?>
<div class="quiztech-question-container <?php echo $is_first_question ? '' : 'quiztech-question-hidden'; ?>"
data-question-id="<?php echo esc_attr( $question_id ); ?>"
data-question-index="<?php echo esc_attr( $index ); ?>">
<h4><?php printf( esc_html__( 'Question %d of %d', 'quiztech' ), $index + 1, count( $question_ids ) ); ?></h4> <div class="question-answer-area">
<label><strong><?php echo esc_html( $question_title ); ?></strong></label> <?php // Render input based on question type ?>
<?php switch ( $question_type ) :
case 'text': ?>
<textarea name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" required></textarea>
<?php break; ?>
<?php // Render input based on question type ?> <?php case 'multiple-choice': ?>
<?php switch ( $question_type ) : <?php
case 'text': ?> $choices = get_post_meta( $question_id, 'question_choices', true );
<textarea name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" required></textarea> if ( is_array( $choices ) && ! empty( $choices ) ) :
<?php break; ?> foreach ( $choices as $choice_index => $choice_text ) :
$choice_value = esc_attr( $choice_text ); // Use text as value for simplicity
$choice_id = 'choice_' . esc_attr( $question_id ) . '_' . esc_attr( $choice_index );
?>
<label for="<?php echo $choice_id; ?>">
<input type="radio"
id="<?php echo $choice_id; ?>"
name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]"
value="<?php echo $choice_value; ?>"
required>
<?php echo esc_html( $choice_text ); ?>
</label><br>
<?php endforeach; ?>
<?php else : ?>
<p class="error"><?php esc_html_e( 'Error: Choices not found for this question.', 'quiztech' ); ?></p>
<?php endif; ?>
<?php break; ?>
<?php case 'multiple-choice': ?> <?php case 'true-false': ?>
<?php <label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="true" required> True</label><br>
$choices = get_post_meta( $question_id, 'question_choices', true ); <label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="false"> False</label>
if ( is_array( $choices ) && ! empty( $choices ) ) : <?php break; ?>
foreach ( $choices as $choice_index => $choice_text ) :
$choice_value = esc_attr( $choice_text ); // Use text as value for simplicity
$choice_id = 'choice_' . esc_attr( $question_id ) . '_' . esc_attr( $choice_index );
?>
<label for="<?php echo $choice_id; ?>">
<input type="radio"
id="<?php echo $choice_id; ?>"
name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]"
value="<?php echo $choice_value; ?>"
required>
<?php echo esc_html( $choice_text ); ?>
</label><br>
<?php endforeach; ?>
<?php else : ?>
<p class="error"><?php esc_html_e( 'Error: Choices not found for this question.', 'quiztech' ); ?></p>
<?php endif; ?>
<?php break; ?>
<?php case 'true-false': ?> <?php default: ?>
<label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="true" required> True</label><br> <p class="error"><?php esc_html_e( 'Unsupported question type.', 'quiztech' ); ?></p>
<label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="false"> False</label> <?php endswitch; ?>
<?php break; ?> </div> <?php // .question-answer-area ?>
</div> <?php // .quiztech-question-container ?>
<?php default: ?> <?php endforeach; ?>
<p class="error"><?php esc_html_e( 'Unsupported question type.', 'quiztech' ); ?></p>
<?php endswitch; ?>
</div>
<?php // Removed <hr> as container provides separation ?>
<?php endforeach; ?>
</div> <!-- #quiztech-questions-container --> </div> <!-- #quiztech-questions-container -->
<div class="quiztech-navigation"> <div class="quiztech-navigation">
@ -201,13 +231,8 @@ if ( ! $invitation_data || ! $current_step ) {
// echo '<pre>Question IDs Data: ' . esc_html( print_r( $question_ids, true ) ) . '</pre>'; // echo '<pre>Question IDs Data: ' . esc_html( print_r( $question_ids, true ) ) . '</pre>';
endif; endif;
?> ?>
<?php // Removed the simple "Start Assessment" button, replaced by question rendering logic ?>
</div> <!-- #quiztech-assessment-section --> </div> <!-- #quiztech-assessment-section -->
<?php else : ?>
<p class="error"><?php esc_html_e( 'An unexpected error occurred. Invalid assessment step.', 'quiztech' ); ?></p>
<?php endif; ?>
</div> </div>
<?php // wp_footer(); // Consider if needed ?> <?php // wp_footer(); // Consider if needed ?>
</body> </body>