feat: Implement Credit Mgmt, View Results, and Question AJAX UIs
This commit is contained in:
parent
6942e7de77
commit
a1b87b559d
4 changed files with 511 additions and 44 deletions
222
functions.php
222
functions.php
|
|
@ -40,6 +40,8 @@ function quiztech_theme_enqueue_scripts() {
|
||||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||||
'add_job_nonce' => wp_create_nonce( 'quiztech_add_new_job_action' ),
|
'add_job_nonce' => wp_create_nonce( 'quiztech_add_new_job_action' ),
|
||||||
'send_invite_nonce' => wp_create_nonce( 'quiztech_send_job_invite_action' ),
|
'send_invite_nonce' => wp_create_nonce( 'quiztech_send_job_invite_action' ),
|
||||||
|
'save_question_nonce' => wp_create_nonce( 'quiztech_save_question_action' ), // Nonce for saving
|
||||||
|
'get_question_nonce' => wp_create_nonce( 'quiztech_get_question_action' ), // Nonce for fetching
|
||||||
'error_generic' => esc_html__( 'An error occurred. Please try again.', 'quiztech' ),
|
'error_generic' => esc_html__( 'An error occurred. Please try again.', 'quiztech' ),
|
||||||
'error_permissions' => esc_html__( 'You do not have permission to perform this action.', 'quiztech' ),
|
'error_permissions' => esc_html__( 'You do not have permission to perform this action.', 'quiztech' ),
|
||||||
'error_missing_assessment' => esc_html__( 'Error: No assessment is linked to this job.', 'quiztech' ),
|
'error_missing_assessment' => esc_html__( 'Error: No assessment is linked to this job.', 'quiztech' ),
|
||||||
|
|
@ -189,3 +191,223 @@ function quiztech_ajax_send_job_invite() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
add_action( 'wp_ajax_send_job_invite', 'quiztech_ajax_send_job_invite' );
|
add_action( 'wp_ajax_send_job_invite', 'quiztech_ajax_send_job_invite' );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the submission from the "Buy Credits" forms on the Manage Credits page.
|
||||||
|
*/
|
||||||
|
function quiztech_handle_credit_purchase_submission() {
|
||||||
|
// Only process if our action is set and we are on the frontend
|
||||||
|
if ( ! isset( $_POST['quiztech_action'] ) || $_POST['quiztech_action'] !== 'initiate_credit_purchase' || is_admin() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify nonce
|
||||||
|
if ( ! isset( $_POST['quiztech_buy_credits_nonce'] ) || ! wp_verify_nonce( $_POST['quiztech_buy_credits_nonce'], 'quiztech_buy_credits_action' ) ) {
|
||||||
|
// Nonce is invalid, redirect back with error
|
||||||
|
$redirect_url = add_query_arg( array(
|
||||||
|
'purchase_status' => 'error',
|
||||||
|
'message' => urlencode( __( 'Security check failed. Please try again.', 'quiztech' ) )
|
||||||
|
), get_permalink() ); // Assumes this runs when get_permalink() refers to the Manage Credits page
|
||||||
|
wp_safe_redirect( $redirect_url );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check capability (basic read for now)
|
||||||
|
if ( ! current_user_can( 'read' ) ) {
|
||||||
|
$redirect_url = add_query_arg( array(
|
||||||
|
'purchase_status' => 'error',
|
||||||
|
'message' => urlencode( __( 'You do not have permission to purchase credits.', 'quiztech' ) )
|
||||||
|
), get_permalink() );
|
||||||
|
wp_safe_redirect( $redirect_url );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize inputs
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
$package_id = isset( $_POST['package_id'] ) ? sanitize_key( $_POST['package_id'] ) : '';
|
||||||
|
$credit_amount = isset( $_POST['credit_amount'] ) ? absint( $_POST['credit_amount'] ) : 0;
|
||||||
|
$price = isset( $_POST['price'] ) ? sanitize_text_field( $_POST['price'] ) : '0.00';
|
||||||
|
$currency = isset( $_POST['currency'] ) ? sanitize_key( $_POST['currency'] ) : 'USD';
|
||||||
|
|
||||||
|
if ( $credit_amount <= 0 || empty($package_id) ) {
|
||||||
|
$redirect_url = add_query_arg( array(
|
||||||
|
'purchase_status' => 'error',
|
||||||
|
'message' => urlencode( __( 'Invalid credit package selected.', 'quiztech' ) )
|
||||||
|
), get_permalink() );
|
||||||
|
wp_safe_redirect( $redirect_url );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the plugin function exists and call it
|
||||||
|
if ( function_exists( 'quiztech_initiate_credit_purchase' ) ) {
|
||||||
|
$result = quiztech_initiate_credit_purchase( $user_id, $credit_amount, $price, $currency, $package_id );
|
||||||
|
|
||||||
|
// If the function returns an error, redirect back with the message
|
||||||
|
if ( is_wp_error( $result ) ) {
|
||||||
|
$redirect_url = add_query_arg( array(
|
||||||
|
'purchase_status' => 'error',
|
||||||
|
'message' => urlencode( $result->get_error_message() )
|
||||||
|
), get_permalink() );
|
||||||
|
wp_safe_redirect( $redirect_url );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
// If the function didn't return an error, it should have handled the redirect to the payment gateway.
|
||||||
|
// If it somehow didn't redirect AND didn't return an error, we might end up here.
|
||||||
|
// We could add a fallback redirect with 'initiated' status, but ideally the plugin handles it.
|
||||||
|
// For now, assume the plugin handles the success redirect or returns WP_Error.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Function doesn't exist - plugin likely inactive or issue
|
||||||
|
$redirect_url = add_query_arg( array(
|
||||||
|
'purchase_status' => 'error',
|
||||||
|
'message' => urlencode( __( 'Credit purchase functionality is currently unavailable.', 'quiztech' ) )
|
||||||
|
), get_permalink() );
|
||||||
|
wp_safe_redirect( $redirect_url );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX handler for fetching question data for editing.
|
||||||
|
*/
|
||||||
|
function quiztech_ajax_get_question() {
|
||||||
|
check_ajax_referer( 'quiztech_get_question_action', 'nonce' );
|
||||||
|
|
||||||
|
if ( ! current_user_can( 'edit_questions' ) ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$question_id = isset( $_POST['question_id'] ) ? absint( $_POST['question_id'] ) : 0;
|
||||||
|
|
||||||
|
if ( ! $question_id ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid question ID.', 'quiztech' ) ], 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = get_post( $question_id );
|
||||||
|
if ( ! $post || $post->post_type !== 'question' ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Question not found.', 'quiztech' ) ], 404 );
|
||||||
|
}
|
||||||
|
|
||||||
|
$question_type = get_post_meta( $question_id, '_quiztech_question_type', true );
|
||||||
|
$options_meta = get_post_meta( $question_id, '_quiztech_question_options', true ); // Expects array: [['text' => '...', 'correct' => bool], ...]
|
||||||
|
$terms = get_the_terms( $question_id, 'quiztech_category' );
|
||||||
|
$category_id = ! empty( $terms ) && ! is_wp_error( $terms ) ? $terms[0]->term_id : '';
|
||||||
|
|
||||||
|
// Format options back to string for textarea
|
||||||
|
$options_string = '';
|
||||||
|
if ( is_array( $options_meta ) ) {
|
||||||
|
foreach ( $options_meta as $option ) {
|
||||||
|
$options_string .= $option['text'] . ( $option['correct'] ? '*' : '' ) . "\n";
|
||||||
|
}
|
||||||
|
$options_string = trim( $options_string );
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_success( [
|
||||||
|
'id' => $question_id,
|
||||||
|
'title' => $post->post_title,
|
||||||
|
'content' => $post->post_content,
|
||||||
|
'type' => $question_type,
|
||||||
|
'options' => $options_string,
|
||||||
|
'category' => $category_id,
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
add_action( 'wp_ajax_quiztech_get_question', 'quiztech_ajax_get_question' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AJAX handler for saving (creating or updating) a question.
|
||||||
|
*/
|
||||||
|
function quiztech_ajax_save_question() {
|
||||||
|
check_ajax_referer( 'quiztech_save_question_action', 'nonce' );
|
||||||
|
|
||||||
|
if ( ! current_user_can( 'edit_questions' ) ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Insufficient permissions.', 'quiztech' ) ], 403 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize inputs
|
||||||
|
$question_id = isset( $_POST['question_id'] ) ? absint( $_POST['question_id'] ) : 0;
|
||||||
|
$title = isset( $_POST['question_title'] ) ? sanitize_text_field( wp_unslash( $_POST['question_title'] ) ) : '';
|
||||||
|
$content = isset( $_POST['question_content'] ) ? sanitize_textarea_field( wp_unslash( $_POST['question_content'] ) ) : '';
|
||||||
|
$type = isset( $_POST['question_type'] ) ? sanitize_key( $_POST['question_type'] ) : '';
|
||||||
|
$options_string = isset( $_POST['question_options'] ) ? sanitize_textarea_field( wp_unslash( $_POST['question_options'] ) ) : '';
|
||||||
|
$category_id = isset( $_POST['question_category'] ) ? absint( $_POST['question_category'] ) : 0;
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if ( empty( $title ) || empty( $content ) || empty( $type ) ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Title, Content, and Type are required.', 'quiztech' ) ], 400 );
|
||||||
|
}
|
||||||
|
// Validate type against known types
|
||||||
|
$valid_types = ['multiple-choice', 'true-false', 'short-answer']; // Keep in sync with template
|
||||||
|
if ( ! in_array( $type, $valid_types ) ) {
|
||||||
|
wp_send_json_error( [ 'message' => esc_html__( 'Invalid question type selected.', 'quiztech' ) ], 400 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare post data
|
||||||
|
$post_data = [
|
||||||
|
'post_title' => $title,
|
||||||
|
'post_content' => $content,
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'post_type' => 'question',
|
||||||
|
'post_author' => get_current_user_id(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if ( $question_id > 0 ) {
|
||||||
|
// Update existing post
|
||||||
|
$post_data['ID'] = $question_id;
|
||||||
|
$result = wp_update_post( $post_data, true );
|
||||||
|
} else {
|
||||||
|
// Insert new post
|
||||||
|
$result = wp_insert_post( $post_data, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( is_wp_error( $result ) ) {
|
||||||
|
wp_send_json_error( [ 'message' => $result->get_error_message() ], 500 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// If insert/update was successful, $result is the post ID
|
||||||
|
$new_or_updated_post_id = $result;
|
||||||
|
|
||||||
|
// --- Update Meta and Taxonomy ---
|
||||||
|
|
||||||
|
// Update Question Type meta
|
||||||
|
update_post_meta( $new_or_updated_post_id, '_quiztech_question_type', $type );
|
||||||
|
|
||||||
|
// Parse and Update Options meta (only for relevant types)
|
||||||
|
$options_meta = [];
|
||||||
|
if ( $type === 'multiple-choice' || $type === 'true-false' ) {
|
||||||
|
$lines = explode( "\n", $options_string );
|
||||||
|
foreach ( $lines as $line ) {
|
||||||
|
$line = trim( $line );
|
||||||
|
if ( ! empty( $line ) ) {
|
||||||
|
$is_correct = ( substr( $line, -1 ) === '*' );
|
||||||
|
$text = $is_correct ? trim( substr( $line, 0, -1 ) ) : $line;
|
||||||
|
if (!empty($text)) {
|
||||||
|
$options_meta[] = [ 'text' => $text, 'correct' => $is_correct ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_post_meta( $new_or_updated_post_id, '_quiztech_question_options', $options_meta );
|
||||||
|
|
||||||
|
// Set Category term
|
||||||
|
if ( $category_id > 0 ) {
|
||||||
|
wp_set_post_terms( $new_or_updated_post_id, [ $category_id ], 'quiztech_category', false ); // Replace existing terms
|
||||||
|
} else {
|
||||||
|
wp_set_post_terms( $new_or_updated_post_id, [], 'quiztech_category', false ); // Remove terms if none selected
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Prepare Success Response ---
|
||||||
|
// Optionally, generate HTML for the updated/new table row to send back
|
||||||
|
// For simplicity now, just send success and let JS reload or handle update
|
||||||
|
wp_send_json_success( [
|
||||||
|
'message' => $question_id > 0 ? esc_html__( 'Question updated successfully.', 'quiztech' ) : esc_html__( 'Question added successfully.', 'quiztech' ),
|
||||||
|
'question_id' => $new_or_updated_post_id,
|
||||||
|
// 'row_html' => '<tr>...</tr>' // TODO: Optionally generate row HTML
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
add_action( 'wp_ajax_quiztech_save_question', 'quiztech_ajax_save_question' );
|
||||||
|
|
||||||
|
add_action( 'init', 'quiztech_handle_credit_purchase_submission' ); // Hook into init
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Ensure the user is logged in and has the appropriate capability.
|
// Ensure the user is logged in and has the appropriate capability.
|
||||||
// Using 'manage_options' as a placeholder capability for Quiz Managers.
|
// Using basic 'read' capability for now, as specific credit management caps aren't defined.
|
||||||
// Replace with a more specific capability like 'manage_quiztech_credits' or similar if defined.
|
$required_capability = 'read';
|
||||||
if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
|
|
||||||
|
if ( ! is_user_logged_in() || ! current_user_can( $required_capability ) ) {
|
||||||
// Redirect to login page or show an error message.
|
// Redirect to login page or show an error message.
|
||||||
wp_safe_redirect( wp_login_url( get_permalink() ) );
|
wp_safe_redirect( wp_login_url( get_permalink() ) );
|
||||||
exit;
|
exit;
|
||||||
|
|
@ -18,6 +19,10 @@ if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
|
||||||
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
|
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get current user's credit balance
|
||||||
|
$user_id = get_current_user_id();
|
||||||
|
$current_balance = function_exists('quiztech_get_credit_balance') ? quiztech_get_credit_balance( $user_id ) : __( 'N/A (Function not found)', 'quiztech' );
|
||||||
|
|
||||||
get_header(); ?>
|
get_header(); ?>
|
||||||
|
|
||||||
<div id="primary" class="content-area">
|
<div id="primary" class="content-area">
|
||||||
|
|
@ -29,8 +34,60 @@ get_header(); ?>
|
||||||
</header><!-- .entry-header -->
|
</header><!-- .entry-header -->
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
<p><?php esc_html_e( 'Placeholder for Credit Management interface (view balance, purchase options).', 'quiztech' ); ?></p>
|
|
||||||
<!-- Add current balance display, credit package options, purchase button here -->
|
<h2><?php esc_html_e( 'Your Credit Balance', 'quiztech' ); ?></h2>
|
||||||
|
<p class="quiztech-credit-balance" style="font-size: 1.5em; font-weight: bold;">
|
||||||
|
<?php echo esc_html( $current_balance ); ?> <?php esc_html_e( 'Credits', 'quiztech' ); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2><?php esc_html_e( 'Purchase Credits', 'quiztech' ); ?></h2>
|
||||||
|
<p><?php esc_html_e( 'Select a package below to add credits to your account.', 'quiztech' ); ?></p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Define credit packages (could be moved to settings later)
|
||||||
|
$credit_packages = array(
|
||||||
|
'10_credits' => array( 'amount' => 10, 'price' => '10.00', 'currency' => 'USD' ),
|
||||||
|
'50_credits' => array( 'amount' => 50, 'price' => '45.00', 'currency' => 'USD' ),
|
||||||
|
'100_credits' => array( 'amount' => 100, 'price' => '80.00', 'currency' => 'USD' ),
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="quiztech-credit-packages" style="display: flex; gap: 20px; flex-wrap: wrap;">
|
||||||
|
<?php foreach ( $credit_packages as $package_id => $package ) : ?>
|
||||||
|
<div class="quiztech-credit-package" style="border: 1px solid #eee; padding: 15px; text-align: center;">
|
||||||
|
<h3><?php printf( esc_html__( '%d Credits', 'quiztech' ), intval( $package['amount'] ) ); ?></h3>
|
||||||
|
<p><?php printf( esc_html__( 'Price: %s %s', 'quiztech' ), esc_html( $package['price'] ), esc_html( $package['currency'] ) ); ?></p>
|
||||||
|
<form method="post" action="<?php echo esc_url( get_permalink() ); ?>">
|
||||||
|
<?php wp_nonce_field( 'quiztech_buy_credits_action', 'quiztech_buy_credits_nonce' ); ?>
|
||||||
|
<input type="hidden" name="quiztech_action" value="initiate_credit_purchase">
|
||||||
|
<input type="hidden" name="package_id" value="<?php echo esc_attr( $package_id ); ?>">
|
||||||
|
<input type="hidden" name="credit_amount" value="<?php echo esc_attr( $package['amount'] ); ?>">
|
||||||
|
<input type="hidden" name="price" value="<?php echo esc_attr( $package['price'] ); ?>">
|
||||||
|
<input type="hidden" name="currency" value="<?php echo esc_attr( $package['currency'] ); ?>">
|
||||||
|
<button type="submit" class="button button-primary"><?php esc_html_e( 'Buy Now', 'quiztech' ); ?></button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Display feedback messages if any (e.g., from the purchase initiation)
|
||||||
|
if ( isset( $_GET['purchase_status'] ) ) {
|
||||||
|
if ( $_GET['purchase_status'] === 'initiated' ) {
|
||||||
|
echo '<div class="notice notice-info is-dismissible"><p>' . esc_html__( 'Redirecting to payment gateway...', 'quiztech' ) . '</p></div>';
|
||||||
|
} elseif ( $_GET['purchase_status'] === 'error' ) {
|
||||||
|
$error_message = isset( $_GET['message'] ) ? sanitize_text_field( urldecode( $_GET['message'] ) ) : __( 'An unknown error occurred.', 'quiztech' );
|
||||||
|
echo '<div class="notice notice-error is-dismissible"><p>' . esc_html( $error_message ) . '</p></div>';
|
||||||
|
} elseif ( $_GET['purchase_status'] === 'success' ) {
|
||||||
|
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Credits successfully added!', 'quiztech' ) . '</p></div>';
|
||||||
|
} elseif ( $_GET['purchase_status'] === 'cancelled' ) {
|
||||||
|
echo '<div class="notice notice-warning is-dismissible"><p>' . esc_html__( 'Payment cancelled.', 'quiztech' ) . '</p></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
</div><!-- .entry-content -->
|
</div><!-- .entry-content -->
|
||||||
|
|
||||||
</article><!-- #post-<?php the_ID(); ?> -->
|
</article><!-- #post-<?php the_ID(); ?> -->
|
||||||
|
|
|
||||||
|
|
@ -146,23 +146,53 @@ get_header(); ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Basic JS to toggle form visibility and handle options field display
|
|
||||||
jQuery(document).ready(function($) {
|
jQuery(document).ready(function($) {
|
||||||
|
const formWrapper = $('#quiztech-add-edit-question-form');
|
||||||
|
const formElement = $('#question-form');
|
||||||
|
const formTitle = formWrapper.find('h3');
|
||||||
|
const questionIdField = $('#quiztech-question-id');
|
||||||
|
const cancelButton = $('#quiztech-cancel-edit-question-btn');
|
||||||
|
const submitButton = formElement.find('button[type="submit"]');
|
||||||
|
const tableBody = $('.wp-list-table tbody'); // Assuming table exists
|
||||||
|
|
||||||
|
// --- Helper: Reset and Hide Form ---
|
||||||
|
function resetAndHideForm() {
|
||||||
|
formElement[0].reset();
|
||||||
|
questionIdField.val('');
|
||||||
|
cancelButton.hide();
|
||||||
|
formTitle.text('<?php echo esc_js( __( 'Add New Question', 'quiztech' ) ); ?>');
|
||||||
|
formWrapper.slideUp();
|
||||||
|
submitButton.prop('disabled', false).text('<?php echo esc_js( __( 'Save Question', 'quiztech' ) ); ?>');
|
||||||
|
// Clear any previous messages
|
||||||
|
formWrapper.find('.notice').remove();
|
||||||
|
$('#quiztech-question-type').trigger('change'); // Reset options visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Helper: Display Form Message ---
|
||||||
|
function displayFormMessage(message, type = 'error') {
|
||||||
|
formWrapper.find('.notice').remove(); // Remove old messages
|
||||||
|
const noticeClass = type === 'success' ? 'notice-success' : 'notice-error';
|
||||||
|
// Insert message before the form element itself
|
||||||
|
formElement.before(`<div class="notice ${noticeClass} is-dismissible" style="margin-bottom: 10px; margin-top: 5px;"><p>${message}</p></div>`);
|
||||||
|
// Make dismissible work
|
||||||
|
$(document).on('click', '.notice.is-dismissible .notice-dismiss', function() {
|
||||||
|
$(this).closest('.notice').remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Add New Button ---
|
||||||
$('#quiztech-add-new-question-btn').on('click', function() {
|
$('#quiztech-add-new-question-btn').on('click', function() {
|
||||||
$('#quiztech-add-edit-question-form').slideToggle();
|
resetAndHideForm();
|
||||||
$('#question-form')[0].reset(); // Clear form
|
formWrapper.slideDown(); // Use slideDown instead of toggle for consistency
|
||||||
$('#quiztech-question-id').val(''); // Clear hidden ID
|
formTitle.text('<?php echo esc_js( __( 'Add New Question', 'quiztech' ) ); ?>');
|
||||||
$('#quiztech-cancel-edit-question-btn').hide();
|
|
||||||
$('#quiztech-add-edit-question-form h3').text('<?php echo esc_js( __( 'Add New Question', 'quiztech' ) ); ?>');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#quiztech-cancel-edit-question-btn').on('click', function() {
|
// --- Cancel Button ---
|
||||||
$('#quiztech-add-edit-question-form').slideUp();
|
cancelButton.on('click', function() {
|
||||||
$('#question-form')[0].reset();
|
resetAndHideForm();
|
||||||
$('#quiztech-question-id').val('');
|
|
||||||
$(this).hide();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --- Type Change (Show/Hide Options) ---
|
||||||
$('#quiztech-question-type').on('change', function() {
|
$('#quiztech-question-type').on('change', function() {
|
||||||
const type = $(this).val();
|
const type = $(this).val();
|
||||||
if (type === 'multiple-choice' || type === 'true-false') {
|
if (type === 'multiple-choice' || type === 'true-false') {
|
||||||
|
|
@ -170,39 +200,107 @@ get_header(); ?>
|
||||||
} else {
|
} else {
|
||||||
$('#quiztech-question-options-wrapper').hide();
|
$('#quiztech-question-options-wrapper').hide();
|
||||||
}
|
}
|
||||||
}).trigger('change'); // Trigger on load in case of editing
|
}).trigger('change');
|
||||||
|
|
||||||
// Placeholder for Edit button click (to populate form - needs AJAX)
|
// --- Edit Button Click ---
|
||||||
$('.quiztech-edit-question-btn').on('click', function() {
|
// Use event delegation for dynamically added rows later if needed
|
||||||
|
tableBody.on('click', '.quiztech-edit-question-btn', function() {
|
||||||
const questionId = $(this).data('question-id');
|
const questionId = $(this).data('question-id');
|
||||||
alert('Edit functionality for question ID ' + questionId + ' needs AJAX implementation.');
|
const button = $(this);
|
||||||
// TODO: AJAX call to get question data
|
button.prop('disabled', true).text('<?php echo esc_js( __( 'Loading...', 'quiztech' ) ); ?>');
|
||||||
// TODO: Populate form fields
|
formWrapper.find('.notice').remove(); // Clear previous messages
|
||||||
// TODO: Show form, change title, show cancel button
|
|
||||||
// $('#quiztech-question-id').val(questionId);
|
$.ajax({
|
||||||
// $('#quiztech-add-edit-question-form h3').text('<?php echo esc_js( __( 'Edit Question', 'quiztech' ) ); ?>');
|
url: quiztechThemeData.ajax_url,
|
||||||
// $('#quiztech-cancel-edit-question-btn').show();
|
type: 'POST',
|
||||||
// $('#quiztech-add-edit-question-form').slideDown();
|
data: {
|
||||||
|
action: 'quiztech_get_question',
|
||||||
|
nonce: quiztechThemeData.get_question_nonce,
|
||||||
|
question_id: questionId
|
||||||
|
},
|
||||||
|
dataType: 'json', // Expect JSON response
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
const data = response.data;
|
||||||
|
questionIdField.val(data.id);
|
||||||
|
$('#quiztech-question-title').val(data.title);
|
||||||
|
$('#quiztech-question-content').val(data.content);
|
||||||
|
$('#quiztech-question-type').val(data.type).trigger('change'); // Trigger change to show/hide options
|
||||||
|
$('#quiztech-question-options').val(data.options);
|
||||||
|
$('#quiztech-question-category').val(data.category);
|
||||||
|
|
||||||
|
formTitle.text('<?php echo esc_js( __( 'Edit Question', 'quiztech' ) ); ?>');
|
||||||
|
cancelButton.show();
|
||||||
|
formWrapper.slideDown();
|
||||||
|
// Scroll to form
|
||||||
|
$('html, body').animate({ scrollTop: formWrapper.offset().top - 50 }, 300);
|
||||||
|
} else {
|
||||||
|
alert(response.data.message || quiztechThemeData.error_generic);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error("AJAX Error:", textStatus, errorThrown, jqXHR.responseText);
|
||||||
|
alert(quiztechThemeData.error_generic);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
button.prop('disabled', false).text('<?php echo esc_js( __( 'Edit', 'quiztech' ) ); ?>');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Placeholder for Delete button click (needs AJAX)
|
// --- Delete Button Click --- (Needs AJAX implementation)
|
||||||
$('.quiztech-delete-question-btn').on('click', function() {
|
tableBody.on('click', '.quiztech-delete-question-btn', function() {
|
||||||
const questionId = $(this).data('question-id');
|
const questionId = $(this).data('question-id');
|
||||||
|
const button = $(this);
|
||||||
|
const row = button.closest('tr');
|
||||||
|
|
||||||
if (confirm('<?php echo esc_js( __( 'Are you sure you want to delete this question?', 'quiztech' ) ); ?>')) {
|
if (confirm('<?php echo esc_js( __( 'Are you sure you want to delete this question?', 'quiztech' ) ); ?>')) {
|
||||||
alert('Delete functionality for question ID ' + questionId + ' needs AJAX implementation.');
|
alert('Delete functionality for question ID ' + questionId + ' needs AJAX implementation (nonce, handler, etc.).');
|
||||||
// TODO: AJAX call to delete question
|
// TODO: AJAX call to delete question (needs nonce and handler)
|
||||||
// TODO: Remove row from table on success
|
// Example:
|
||||||
|
// $.ajax({
|
||||||
|
// url: quiztechThemeData.ajax_url,
|
||||||
|
// type: 'POST',
|
||||||
|
// data: { action: 'quiztech_delete_question', nonce: '...', question_id: questionId },
|
||||||
|
// success: function(response) { if(response.success) { row.fadeOut(300, function() { $(this).remove(); }); } else { alert(response.data.message); } },
|
||||||
|
// error: function() { alert(quiztechThemeData.error_generic); }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Placeholder for Form submission (needs AJAX)
|
// --- Form Submission ---
|
||||||
$('#question-form').on('submit', function(e) {
|
formElement.on('submit', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
alert('Form submission needs AJAX implementation to save/update the question.');
|
submitButton.prop('disabled', true).text('<?php echo esc_js( __( 'Saving...', 'quiztech' ) ); ?>');
|
||||||
// TODO: Gather form data
|
formWrapper.find('.notice').remove(); // Clear previous messages
|
||||||
// TODO: AJAX call to save/update question
|
|
||||||
// TODO: Update table or reload on success
|
const formData = $(this).serialize(); // Includes nonce and all fields
|
||||||
// TODO: Hide form
|
|
||||||
|
$.ajax({
|
||||||
|
url: quiztechThemeData.ajax_url,
|
||||||
|
type: 'POST',
|
||||||
|
data: formData + '&action=quiztech_save_question', // Add action parameter
|
||||||
|
dataType: 'json', // Expect JSON response
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
// Display success message within the form area before hiding/reloading
|
||||||
|
displayFormMessage(response.data.message, 'success');
|
||||||
|
// Option 1: Reload page after delay
|
||||||
|
setTimeout(function() { location.reload(); }, 1500);
|
||||||
|
// Option 2: Reset and hide form (if table update was dynamic)
|
||||||
|
// resetAndHideForm();
|
||||||
|
// TODO: Update table dynamically
|
||||||
|
} else {
|
||||||
|
displayFormMessage(response.data.message || quiztechThemeData.error_generic, 'error');
|
||||||
|
submitButton.prop('disabled', false).text('<?php echo esc_js( __( 'Save Question', 'quiztech' ) ); ?>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.error("AJAX Error:", textStatus, errorThrown, jqXHR.responseText);
|
||||||
|
displayFormMessage(quiztechThemeData.error_generic, 'error');
|
||||||
|
submitButton.prop('disabled', false).text('<?php echo esc_js( __( 'Save Question', 'quiztech' ) ); ?>');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Ensure the user is logged in and has the appropriate capability.
|
// Ensure the user is logged in and has the appropriate capability.
|
||||||
// Using 'manage_options' as a placeholder capability for Quiz Managers.
|
// Capability required to view results (tied to managing jobs).
|
||||||
// Replace with a more specific capability like 'view_quiztech_results' or similar if defined.
|
$required_capability = 'edit_jobs'; // Or a more specific 'view_results' capability if created
|
||||||
if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
|
|
||||||
|
if ( ! is_user_logged_in() || ! current_user_can( $required_capability ) ) {
|
||||||
// Redirect to login page or show an error message.
|
// Redirect to login page or show an error message.
|
||||||
wp_safe_redirect( wp_login_url( get_permalink() ) );
|
wp_safe_redirect( wp_login_url( get_permalink() ) );
|
||||||
exit;
|
exit;
|
||||||
|
|
@ -18,6 +19,26 @@ if ( ! is_user_logged_in() || ! current_user_can( 'manage_options' ) ) {
|
||||||
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
|
// wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'quiztech' ), 403 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Job ID from query parameter
|
||||||
|
$job_id = isset( $_GET['job_id'] ) ? absint( $_GET['job_id'] ) : 0;
|
||||||
|
$job_post = null;
|
||||||
|
$error_message = '';
|
||||||
|
|
||||||
|
if ( ! $job_id ) {
|
||||||
|
$error_message = __( 'No Job ID specified.', 'quiztech' );
|
||||||
|
} else {
|
||||||
|
$job_post = get_post( $job_id );
|
||||||
|
if ( ! $job_post || $job_post->post_type !== 'job' ) {
|
||||||
|
$error_message = __( 'Invalid Job ID specified.', 'quiztech' );
|
||||||
|
$job_post = null; // Ensure job_post is null if invalid
|
||||||
|
}
|
||||||
|
// Optional: Check if the current user owns the job or has higher privileges
|
||||||
|
// if ( $job_post && $job_post->post_author != get_current_user_id() && ! current_user_can('edit_others_jobs') ) {
|
||||||
|
// $error_message = __( 'You do not have permission to view results for this job.', 'quiztech' );
|
||||||
|
// $job_post = null;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
get_header(); ?>
|
get_header(); ?>
|
||||||
|
|
||||||
<div id="primary" class="content-area">
|
<div id="primary" class="content-area">
|
||||||
|
|
@ -29,8 +50,77 @@ get_header(); ?>
|
||||||
</header><!-- .entry-header -->
|
</header><!-- .entry-header -->
|
||||||
|
|
||||||
<div class="entry-content">
|
<div class="entry-content">
|
||||||
<p><?php esc_html_e( 'Placeholder for Assessment Results interface (list results, view details).', 'quiztech' ); ?></p>
|
|
||||||
<!-- Add results listing (likely filterable by Job), link to view individual user_evaluation details here -->
|
<?php if ( $error_message ) : ?>
|
||||||
|
<div class="notice notice-error"><p><?php echo esc_html( $error_message ); ?></p></div>
|
||||||
|
<p><a href="<?php echo esc_url( get_post_type_archive_link('job') ?: home_url('/manage-jobs/') ); ?>"><?php esc_html_e('« Back to Jobs', 'quiztech'); ?></a></p> <?php // Link back to job list ?>
|
||||||
|
<?php elseif ( $job_post ) : ?>
|
||||||
|
<h2><?php printf( esc_html__( 'Results for Job: %s', 'quiztech' ), esc_html( get_the_title( $job_post ) ) ); ?></h2>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$args = array(
|
||||||
|
'post_type' => 'user_evaluation',
|
||||||
|
'post_status' => 'any', // Show completed, in-progress, etc.
|
||||||
|
'posts_per_page' => -1, // Show all for this job
|
||||||
|
'meta_query' => array(
|
||||||
|
array(
|
||||||
|
'key' => '_quiztech_job_id', // Make sure this meta key is correct
|
||||||
|
'value' => $job_id,
|
||||||
|
'compare' => '=',
|
||||||
|
'type' => 'NUMERIC',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'orderby' => 'date', // Order by submission date
|
||||||
|
'order' => 'DESC',
|
||||||
|
);
|
||||||
|
$evaluations_query = new WP_Query( $args );
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ( $evaluations_query->have_posts() ) : ?>
|
||||||
|
<table class="wp-list-table widefat fixed striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><?php esc_html_e( 'Applicant Email', 'quiztech' ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Status', 'quiztech' ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Score', 'quiztech' ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Submitted', 'quiztech' ); ?></th>
|
||||||
|
<th><?php esc_html_e( 'Actions', 'quiztech' ); ?></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php while ( $evaluations_query->have_posts() ) : $evaluations_query->the_post(); ?>
|
||||||
|
<?php
|
||||||
|
$evaluation_id = get_the_ID();
|
||||||
|
$applicant_email = get_post_meta( $evaluation_id, '_quiztech_applicant_email', true ); // Make sure meta key is correct
|
||||||
|
$score = get_post_meta( $evaluation_id, '_quiztech_score', true ); // Make sure meta key is correct
|
||||||
|
$status = get_post_status_object( get_post_status( $evaluation_id ) );
|
||||||
|
$status_label = $status ? $status->label : get_post_status( $evaluation_id );
|
||||||
|
$edit_link = get_edit_post_link( $evaluation_id );
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo esc_html( $applicant_email ?: __( 'N/A', 'quiztech' ) ); ?></td>
|
||||||
|
<td><?php echo esc_html( $status_label ); ?></td>
|
||||||
|
<td><?php echo esc_html( $score ?: __( 'N/A', 'quiztech' ) ); ?></td>
|
||||||
|
<td><?php echo esc_html( get_the_date() ); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ( $edit_link ) : ?>
|
||||||
|
<a href="<?php echo esc_url( $edit_link ); ?>" target="_blank"><?php esc_html_e( 'View Details', 'quiztech' ); ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endwhile; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php wp_reset_postdata(); // Restore original Post Data ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<p><?php esc_html_e( 'No results found for this job yet.', 'quiztech' ); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php else : ?>
|
||||||
|
<?php // This case should be caught by $error_message check above, but as a fallback: ?>
|
||||||
|
<p><?php esc_html_e( 'Could not load job details.', 'quiztech' ); ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
</div><!-- .entry-content -->
|
</div><!-- .entry-content -->
|
||||||
|
|
||||||
</article><!-- #post-<?php the_ID(); ?> -->
|
</article><!-- #post-<?php the_ID(); ?> -->
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue