feat: Implement front-end assessment interaction AJAX flow
- Add AssessmentAjaxHandler class for AJAX requests. - Add assessment.js for front-end logic. - Implement pre-screening form submission via AJAX. - Implement answer auto-save via AJAX. - Implement final assessment submission via AJAX. - Update assessment-shell.php template for dynamic rendering and JS hooks. - Enqueue and localize assessment.js conditionally. Refs: assessment_interaction_plan.md Note: Includes TODOs for evaluation CPT handling, status updates, and sanitization.
This commit is contained in:
parent
d63aa8d409
commit
130b9eefb9
5 changed files with 394 additions and 9 deletions
163
public/js/assessment.js
Normal file
163
public/js/assessment.js
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
/**
|
||||||
|
* Quiztech Assessment Interaction Script
|
||||||
|
*
|
||||||
|
* Handles AJAX submissions for pre-screening, answer auto-save,
|
||||||
|
* and final assessment submission.
|
||||||
|
*/
|
||||||
|
(function($) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
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.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $prescreeningForm = $('#quiztech-prescreening-form');
|
||||||
|
var $prescreeningSection = $('#quiztech-prescreening-section');
|
||||||
|
var $assessmentSection = $('#quiztech-assessment-section');
|
||||||
|
var $submitButton = $prescreeningForm.find('button[type="submit"]');
|
||||||
|
var $formMessages = $('<div class="form-messages"></div>').insertBefore($submitButton); // Area for messages
|
||||||
|
|
||||||
|
// --- Pre-Screening Form Handling ---
|
||||||
|
$prescreeningForm.on('submit', function(event) {
|
||||||
|
event.preventDefault(); // Stop traditional form submission
|
||||||
|
|
||||||
|
$formMessages.empty().removeClass('error success'); // Clear previous messages
|
||||||
|
$submitButton.prop('disabled', true).text('Submitting...'); // Disable button
|
||||||
|
|
||||||
|
var formData = $(this).serialize(); // Get form data
|
||||||
|
|
||||||
|
// Add required AJAX parameters
|
||||||
|
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', // Expect JSON response from server
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Success! Hide pre-screening, show assessment
|
||||||
|
$formMessages.addClass('success').text(response.data.message || 'Success!'); // Show success message briefly
|
||||||
|
$prescreeningSection.slideUp();
|
||||||
|
$assessmentSection.slideDown();
|
||||||
|
// No need to re-enable button as the form is gone
|
||||||
|
} else {
|
||||||
|
// Handle WP JSON error
|
||||||
|
$formMessages.addClass('error').text(response.data.message || 'An error occurred.');
|
||||||
|
$submitButton.prop('disabled', false).text('Submit Pre-Screening & Start Assessment'); // Re-enable button
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
// Handle general AJAX error
|
||||||
|
console.error("AJAX Error:", textStatus, errorThrown);
|
||||||
|
$formMessages.addClass('error').text('A network error occurred. Please try again.');
|
||||||
|
$submitButton.prop('disabled', false).text('Submit Pre-Screening & Start Assessment'); // Re-enable button
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Assessment Answer Auto-Save ---
|
||||||
|
var $assessmentForm = $('#quiztech-assessment-form');
|
||||||
|
var autoSaveTimeout; // To debounce requests
|
||||||
|
|
||||||
|
// Target input/textarea/select elements within the assessment form for auto-save
|
||||||
|
$assessmentForm.on('change blur', 'input, textarea, select', function() {
|
||||||
|
clearTimeout(autoSaveTimeout); // Clear previous timeout if exists
|
||||||
|
|
||||||
|
var $input = $(this);
|
||||||
|
var $questionGroup = $input.closest('.question-group');
|
||||||
|
var questionId = $questionGroup.data('question-id');
|
||||||
|
var answer = $input.val();
|
||||||
|
|
||||||
|
// Add a small visual indicator within the question group
|
||||||
|
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('label:first'));
|
||||||
|
}
|
||||||
|
$indicator.text('Saving...');
|
||||||
|
|
||||||
|
// Debounce the AJAX request slightly
|
||||||
|
autoSaveTimeout = setTimeout(function() {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: quiztech_assessment_vars.ajax_url,
|
||||||
|
data: {
|
||||||
|
action: 'quiztech_save_answer',
|
||||||
|
nonce: quiztech_assessment_vars.assessment_nonce, // Use the correct 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');
|
||||||
|
// Optionally fade out the indicator after a delay
|
||||||
|
setTimeout(function() { $indicator.fadeOut().remove(); }, 2000);
|
||||||
|
} else {
|
||||||
|
$indicator.text('Error!').css('color', 'red');
|
||||||
|
console.error("Auto-save error:", response.data.message);
|
||||||
|
// Consider more prominent error display if needed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
$indicator.text('Network Error!').css('color', 'red');
|
||||||
|
console.error("AJAX Error:", textStatus, errorThrown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 500); // Wait 500ms after the last change/blur before sending
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// --- Final Assessment Submission ---
|
||||||
|
var $submitAssessmentButton = $('#quiztech-submit-assessment');
|
||||||
|
var $assessmentFormMessages = $('<div class="form-messages"></div>').insertAfter($submitAssessmentButton); // Area for messages
|
||||||
|
|
||||||
|
$submitAssessmentButton.on('click', function(event) {
|
||||||
|
event.preventDefault(); // Stop traditional form submission (though AJAX auto-save handles data)
|
||||||
|
|
||||||
|
if (!confirm('Are you sure you want to submit your assessment?')) {
|
||||||
|
return; // User cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
$assessmentFormMessages.empty().removeClass('error success');
|
||||||
|
$submitAssessmentButton.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, // Reuse assessment nonce
|
||||||
|
invitation_id: quiztech_assessment_vars.invitation_id
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Success! Display message and potentially hide the form/button
|
||||||
|
$assessmentFormMessages.addClass('success').text(response.data.message || 'Assessment Submitted Successfully!');
|
||||||
|
$assessmentForm.hide(); // Hide the form after successful submission
|
||||||
|
// Optionally redirect: window.location.href = response.data.redirect_url;
|
||||||
|
} else {
|
||||||
|
$assessmentFormMessages.addClass('error').text(response.data.message || 'An error occurred during submission.');
|
||||||
|
$submitAssessmentButton.prop('disabled', false).text('Submit Assessment'); // Re-enable button
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error("AJAX Error:", textStatus, errorThrown);
|
||||||
|
$assessmentFormMessages.addClass('error').text('A network error occurred. Please try again.');
|
||||||
|
$submitAssessmentButton.prop('disabled', false).text('Submit Assessment'); // Re-enable button
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}); // End document ready
|
||||||
|
|
||||||
|
})(jQuery);
|
||||||
|
|
@ -56,15 +56,17 @@ if ( ! $invitation_data || ! $current_step ) {
|
||||||
<h1><?php esc_html_e( 'Assessment Invitation', 'quiztech' ); ?></h1>
|
<h1><?php esc_html_e( 'Assessment Invitation', 'quiztech' ); ?></h1>
|
||||||
|
|
||||||
<?php if ( 'pre_screening' === $current_step ) : ?>
|
<?php if ( 'pre_screening' === $current_step ) : ?>
|
||||||
|
<div id="quiztech-prescreening-section"> <?php // Added ID for JS targeting ?>
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<h2><?php esc_html_e( 'Step 1: Pre-Screening Questions', 'quiztech' ); ?></h2>
|
<h2><?php esc_html_e( 'Step 1: Pre-Screening Questions', 'quiztech' ); ?></h2>
|
||||||
<p><?php esc_html_e( 'Please answer the following questions before starting the assessment.', 'quiztech' ); ?></p>
|
<p><?php esc_html_e( 'Please answer the following questions before starting the assessment.', 'quiztech' ); ?></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" action=""> <?php // TODO: Add action URL and nonce for processing submission ?>
|
<form id="quiztech-prescreening-form" method="post" action=""> <?php // Action handled by AJAX ?>
|
||||||
<?php wp_nonce_field( 'quiztech_submit_prescreening_' . $invitation_data->token, 'quiztech_prescreening_nonce' ); ?>
|
<?php // Nonce is checked via AJAX, but good to have in form too for non-JS fallback (if implemented) ?>
|
||||||
<input type="hidden" name="quiztech_invitation_token" value="<?php echo esc_attr( $invitation_data->token ); ?>">
|
<?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">
|
<input type="hidden" name="action" value="quiztech_submit_prescreening">
|
||||||
|
|
||||||
<?php if ( is_array( $pre_screening_questions ) && ! empty( $pre_screening_questions ) ) : ?>
|
<?php if ( is_array( $pre_screening_questions ) && ! empty( $pre_screening_questions ) ) : ?>
|
||||||
|
|
@ -90,9 +92,10 @@ if ( ! $invitation_data || ! $current_step ) {
|
||||||
<?php // Render questions here - Removed, handled above ?>
|
<?php // Render questions here - Removed, handled above ?>
|
||||||
<button type="submit"><?php esc_html_e( 'Submit Pre-Screening & Start Assessment', 'quiztech' ); ?></button>
|
<button type="submit"><?php esc_html_e( 'Submit Pre-Screening & Start Assessment', 'quiztech' ); ?></button>
|
||||||
</form>
|
</form>
|
||||||
|
</div> <!-- #quiztech-prescreening-section -->
|
||||||
|
|
||||||
<?php elseif ( 'assessment' === $current_step ) : ?>
|
<?php elseif ( 'assessment' === $current_step ) : ?>
|
||||||
|
<div id="quiztech-assessment-section" style="display: none;"> <?php // Added ID and initially hidden ?>
|
||||||
<div class="step-info">
|
<div class="step-info">
|
||||||
<h2><?php esc_html_e( 'Step 2: Assessment', 'quiztech' ); ?></h2>
|
<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>
|
<p><?php printf( esc_html__( 'You are about to begin Assessment ID: %d', 'quiztech' ), absint( $invitation_data->assessment_id ) ); ?></p>
|
||||||
|
|
@ -132,10 +135,25 @@ if ( ! $invitation_data || ! $current_step ) {
|
||||||
<?php break; ?>
|
<?php break; ?>
|
||||||
|
|
||||||
<?php case 'multiple-choice': ?>
|
<?php case 'multiple-choice': ?>
|
||||||
<?php // TODO: Fetch actual choices from post meta ?>
|
<?php
|
||||||
<label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="opt1" required> Option 1</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="opt2"> Option 2</label><br>
|
if ( is_array( $choices ) && ! empty( $choices ) ) :
|
||||||
<label><input type="radio" name="assessment_answer[<?php echo esc_attr( $question_id ); ?>]" value="opt3"> Option 3</label>
|
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 break; ?>
|
||||||
|
|
||||||
<?php case 'true-false': ?>
|
<?php case 'true-false': ?>
|
||||||
|
|
@ -160,6 +178,7 @@ if ( ! $invitation_data || ! $current_step ) {
|
||||||
endif;
|
endif;
|
||||||
?>
|
?>
|
||||||
<?php // Removed the simple "Start Assessment" button, replaced by question rendering logic ?>
|
<?php // Removed the simple "Start Assessment" button, replaced by question rendering logic ?>
|
||||||
|
</div> <!-- #quiztech-assessment-section -->
|
||||||
|
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<p class="error"><?php esc_html_e( 'An unexpected error occurred. Invalid assessment step.', 'quiztech' ); ?></p>
|
<p class="error"><?php esc_html_e( 'An unexpected error occurred. Invalid assessment step.', 'quiztech' ); ?></p>
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,10 @@ function quiztech_init() {
|
||||||
$frontend_handler = new \Quiztech\AssessmentPlatform\Includes\FrontendHandler();
|
$frontend_handler = new \Quiztech\AssessmentPlatform\Includes\FrontendHandler();
|
||||||
$frontend_handler->init_hooks();
|
$frontend_handler->init_hooks();
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize AJAX handler for assessment interactions
|
||||||
|
\Quiztech\AssessmentPlatform\Includes\Ajax\AssessmentAjaxHandler::init();
|
||||||
|
|
||||||
// TODO: Instantiate other core classes and call their init methods here
|
// TODO: Instantiate other core classes and call their init methods here
|
||||||
// e.g., Admin menu handler, AJAX handlers, Shortcode handlers etc.
|
// e.g., Admin menu handler, AJAX handlers, Shortcode handlers etc.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
177
src/Includes/Ajax/AssessmentAjaxHandler.php
Normal file
177
src/Includes/Ajax/AssessmentAjaxHandler.php
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Quiztech\\AssessmentPlatform\\Includes\\Ajax;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles AJAX requests related to the front-end assessment process.
|
||||||
|
*/
|
||||||
|
class AssessmentAjaxHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Registers AJAX hooks.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
add_action('wp_ajax_quiztech_submit_prescreening', [$this, 'handle_submit_prescreening']);
|
||||||
|
add_action('wp_ajax_quiztech_save_answer', [$this, 'handle_save_answer']);
|
||||||
|
add_action('wp_ajax_quiztech_submit_assessment', [$this, 'handle_submit_assessment']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the handler.
|
||||||
|
* Static method to instantiate the class and register hooks.
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
// AJAX handler methods will be added below:
|
||||||
|
// - handle_submit_prescreening()
|
||||||
|
// - handle_save_answer()
|
||||||
|
// - handle_submit_assessment()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the AJAX submission of the pre-screening form.
|
||||||
|
* Expects 'nonce', 'invitation_id', and 'pre_screen_answer' array in $_POST.
|
||||||
|
*/
|
||||||
|
public function handle_submit_prescreening() {
|
||||||
|
// 1. Verify Nonce
|
||||||
|
check_ajax_referer('quiztech_prescreening_nonce', 'nonce');
|
||||||
|
|
||||||
|
// 2. Get and Sanitize Core Data
|
||||||
|
$invitation_id = isset($_POST['invitation_id']) ? absint($_POST['invitation_id']) : 0;
|
||||||
|
if ( ! $invitation_id ) {
|
||||||
|
wp_send_json_error(['message' => __('Missing invitation ID.', 'quiztech')], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- TODO: Ensure user_evaluation CPT is created earlier (e.g., in FrontendHandler) ---
|
||||||
|
// For now, query for it based on invitation ID (assuming meta field 'quiztech_invitation_id' exists on evaluation)
|
||||||
|
$args = [
|
||||||
|
'post_type' => 'user_evaluation',
|
||||||
|
'post_status' => 'any', // Find it regardless of status
|
||||||
|
'meta_query' => [
|
||||||
|
[
|
||||||
|
'key' => 'quiztech_invitation_id', // Assumed meta key linking evaluation to invitation
|
||||||
|
'value' => $invitation_id,
|
||||||
|
'compare' => '=',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'posts_per_page' => 1,
|
||||||
|
'fields' => 'ids', // Only get the ID
|
||||||
|
];
|
||||||
|
$evaluation_posts = get_posts($args);
|
||||||
|
$evaluation_id = !empty($evaluation_posts) ? $evaluation_posts[0] : 0;
|
||||||
|
|
||||||
|
if ( ! $evaluation_id ) {
|
||||||
|
// If not found, something is wrong with the flow (should have been created earlier)
|
||||||
|
error_log("Quiztech AJAX Error: Could not find user_evaluation CPT for invitation ID: " . $invitation_id);
|
||||||
|
wp_send_json_error(['message' => __('Could not find associated evaluation record.', 'quiztech')], 404);
|
||||||
|
}
|
||||||
|
// --- End TODO section ---
|
||||||
|
|
||||||
|
// 3. Sanitize Submitted Answers
|
||||||
|
$submitted_answers = isset($_POST['pre_screen_answer']) && is_array($_POST['pre_screen_answer']) ? $_POST['pre_screen_answer'] : [];
|
||||||
|
$sanitized_answers = [];
|
||||||
|
foreach ($submitted_answers as $index => $answer) {
|
||||||
|
// Use sanitize_textarea_field as pre-screening questions are currently textareas
|
||||||
|
$sanitized_answers[sanitize_key($index)] = sanitize_textarea_field(wp_unslash($answer));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Save Answers (as user_evaluation CPT meta)
|
||||||
|
if (!empty($sanitized_answers)) {
|
||||||
|
update_post_meta($evaluation_id, 'quiztech_prescreening_answers', $sanitized_answers);
|
||||||
|
} else {
|
||||||
|
// Handle case where no answers were submitted? Or rely on form 'required' attribute?
|
||||||
|
// For now, proceed even if empty.
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Update Invitation Status
|
||||||
|
try {
|
||||||
|
$invitations = new \Quiztech\AssessmentPlatform\Includes\Invitations();
|
||||||
|
// TODO: Create the update_status method in Invitations class
|
||||||
|
$updated = $invitations->update_status($invitation_id, 'pre-screening-complete');
|
||||||
|
if (!$updated) {
|
||||||
|
error_log("Quiztech AJAX Error: Failed to update invitation status for ID: " . $invitation_id);
|
||||||
|
// Decide if this should be a user-facing error or just logged
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
error_log("Quiztech AJAX Error: Exception updating invitation status: " . $e->getMessage());
|
||||||
|
// Decide if this should be a user-facing error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 6. Send Response
|
||||||
|
wp_send_json_success(['message' => __('Pre-screening submitted successfully. Starting assessment...', 'quiztech')]);
|
||||||
|
|
||||||
|
// Ensure script execution stops
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the AJAX auto-save of a single assessment answer.
|
||||||
|
* Expects 'nonce', 'invitation_id', 'question_id', and 'answer' in $_POST.
|
||||||
|
*/
|
||||||
|
public function handle_save_answer() {
|
||||||
|
// 1. Verify Nonce
|
||||||
|
check_ajax_referer('quiztech_assessment_nonce', 'nonce');
|
||||||
|
|
||||||
|
// 2. Get and Sanitize Data
|
||||||
|
$invitation_id = isset($_POST['invitation_id']) ? absint($_POST['invitation_id']) : 0;
|
||||||
|
$question_id = isset($_POST['question_id']) ? absint($_POST['question_id']) : 0;
|
||||||
|
$answer = isset($_POST['answer']) ? wp_unslash($_POST['answer']) : ''; // Sanitize based on question type later
|
||||||
|
|
||||||
|
// TODO: Get the associated user_evaluation CPT ID based on invitation_id (similar to pre-screening handler)
|
||||||
|
$evaluation_id = 0; // Placeholder
|
||||||
|
|
||||||
|
if ( ! $invitation_id || ! $question_id || ! $evaluation_id ) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid request data for saving answer.', 'quiztech')], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fetch question type for $question_id to apply appropriate sanitization to $answer
|
||||||
|
// Example: if type is 'text', use sanitize_textarea_field; if 'multiple-choice', check against allowed values.
|
||||||
|
$sanitized_answer = sanitize_text_field($answer); // Basic sanitization for now
|
||||||
|
|
||||||
|
// 3. Save Answer (as user_evaluation CPT meta)
|
||||||
|
// Use a meta key structure like 'quiztech_answer_{question_id}' or store in a single array meta field.
|
||||||
|
// Using individual meta keys might be simpler for querying later if needed.
|
||||||
|
$meta_key = 'quiztech_answer_' . $question_id;
|
||||||
|
update_post_meta($evaluation_id, $meta_key, $sanitized_answer);
|
||||||
|
|
||||||
|
// 4. Send Response
|
||||||
|
wp_send_json_success(['message' => __('Answer saved.', 'quiztech')]);
|
||||||
|
|
||||||
|
// Ensure script execution stops
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the final AJAX submission of the assessment.
|
||||||
|
* Expects 'nonce' and 'invitation_id' in $_POST.
|
||||||
|
*/
|
||||||
|
public function handle_submit_assessment() {
|
||||||
|
// 1. Verify Nonce
|
||||||
|
check_ajax_referer('quiztech_assessment_nonce', 'nonce'); // Reuse assessment nonce
|
||||||
|
|
||||||
|
// 2. Get Data
|
||||||
|
$invitation_id = isset($_POST['invitation_id']) ? absint($_POST['invitation_id']) : 0;
|
||||||
|
|
||||||
|
// TODO: Get the associated user_evaluation CPT ID based on invitation_id (similar to other handlers)
|
||||||
|
$evaluation_id = 0; // Placeholder
|
||||||
|
|
||||||
|
if ( ! $invitation_id || ! $evaluation_id ) {
|
||||||
|
wp_send_json_error(['message' => __('Invalid request data for final submission.', 'quiztech')], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update Invitation Status
|
||||||
|
// TODO: Call Invitations->update_status($invitation_id, 'assessment-complete');
|
||||||
|
|
||||||
|
// 4. Update User Evaluation CPT Status
|
||||||
|
// TODO: Update post status of $evaluation_id to 'completed' using wp_update_post()
|
||||||
|
|
||||||
|
// 5. Send Response
|
||||||
|
// TODO: Consider adding a redirect URL or specific completion message to the response data
|
||||||
|
wp_send_json_success(['message' => __('Assessment submitted successfully!', 'quiztech')]);
|
||||||
|
|
||||||
|
// Ensure script execution stops
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,28 @@ class FrontendHandler {
|
||||||
// This template should be located within the plugin or theme.
|
// This template should be located within the plugin or theme.
|
||||||
$template_path = QUIZTECH_PLUGIN_DIR . 'public/templates/assessment-shell.php'; // Example path
|
$template_path = QUIZTECH_PLUGIN_DIR . 'public/templates/assessment-shell.php'; // Example path
|
||||||
|
|
||||||
|
|
||||||
|
// Enqueue and localize the assessment script
|
||||||
|
wp_enqueue_script(
|
||||||
|
'quiztech-assessment-script',
|
||||||
|
QUIZTECH_PLUGIN_URL . 'public/js/assessment.js',
|
||||||
|
['jquery'],
|
||||||
|
QUIZTECH_VERSION,
|
||||||
|
true // Load in footer
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_localize_script(
|
||||||
|
'quiztech-assessment-script',
|
||||||
|
'quiztech_assessment_vars',
|
||||||
|
[
|
||||||
|
'ajax_url' => admin_url('admin-ajax.php'),
|
||||||
|
'prescreening_nonce' => wp_create_nonce('quiztech_prescreening_nonce'),
|
||||||
|
'assessment_nonce' => wp_create_nonce('quiztech_assessment_nonce'),
|
||||||
|
'invitation_id' => $invitation_data->id, // Pass invitation ID for context
|
||||||
|
// Add other necessary vars here, e.g., evaluation ID if available
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
if ( file_exists( $template_path ) ) {
|
if ( file_exists( $template_path ) ) {
|
||||||
include( $template_path );
|
include( $template_path );
|
||||||
exit; // Important: Stop WordPress from loading the default theme template
|
exit; // Important: Stop WordPress from loading the default theme template
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue