Prvi commit: Začetna verzija vtičnika Wheel of Fortune
This commit is contained in:
commit
0d28b08727
21 files changed
+13814
No files matched your search
@@ -0,0 +1,285 @@
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
|
||||
// Only run on our plugin's admin pages
|
||||
if (!$('.wheel-admin-page').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tab navigation
|
||||
$('.nav-tab').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('.nav-tab').removeClass('nav-tab-active');
|
||||
$(this).addClass('nav-tab-active');
|
||||
$('.wheel-tab-content').hide();
|
||||
$('#' + $(this).data('target')).show();
|
||||
});
|
||||
|
||||
// --- Modal Logic ---
|
||||
var modal = $('#edit-prize-modal');
|
||||
var span = modal.find('.close');
|
||||
|
||||
// Close modal
|
||||
span.on('click', function() {
|
||||
modal.hide();
|
||||
});
|
||||
$(window).on('click', function(event) {
|
||||
if ($(event.target).is(modal)) {
|
||||
modal.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// --- Add New Prize ---
|
||||
$('.add-new-prize').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Clear form for new prize
|
||||
$('#edit-prize-form')[0].reset();
|
||||
$('#edit-prize-id').val(''); // Clear prize ID for new prize
|
||||
|
||||
// Get wheel_id from the modal's data attribute
|
||||
var wheelId = $('#edit-prize-modal').data('wheel-id');
|
||||
$('#edit-wheel-id').val(wheelId);
|
||||
|
||||
console.log('Opening modal for new prize with wheel_id:', wheelId);
|
||||
|
||||
modal.show();
|
||||
});
|
||||
|
||||
// --- Edit Prize ---
|
||||
$('.edit-prize').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
var prizeId = $(this).data('id');
|
||||
|
||||
// AJAX call to get prize details
|
||||
$.post(ajaxurl, {
|
||||
action: 'wheel_get_prize_details',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
prize_id: prizeId
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
var prize = response.data;
|
||||
// Populate form
|
||||
$('#edit-prize-id').val(prize.id);
|
||||
$('#edit-wheel-id').val(prize.wheel_id); // wheel_id je že v formi
|
||||
$('#edit-prize-name').val(prize.name);
|
||||
$('#edit-prize-description').val(prize.description);
|
||||
$('#edit-prize-probability').val(prize.probability);
|
||||
$('#edit-prize-is-active').prop('checked', parseInt(prize.is_active, 10));
|
||||
$('#edit-prize-redemption-code').val(prize.redemption_code);
|
||||
$('#edit-prize-is-discount').prop('checked', parseInt(prize.is_discount, 10));
|
||||
$('#edit-prize-discount-value').val(prize.discount_value);
|
||||
$('#edit-prize-email-subject').val(prize.email_subject);
|
||||
$('#edit-prize-email-template').val(prize.email_template);
|
||||
|
||||
modal.show();
|
||||
} else {
|
||||
alert(response.data.message || 'An error occurred.');
|
||||
}
|
||||
}).fail(function() {
|
||||
alert('An error occurred while loading prize details.');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Save Prize (via AJAX) ---
|
||||
$('#edit-prize-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Pripravimo podatke iz forme
|
||||
const formData = {
|
||||
action: 'wheel_save_prize',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
wheel_id: $('#edit-wheel-id').val(), // **KLJUČNO: pošljemo wheel_id**
|
||||
prize_id: $('#edit-prize-id').val(),
|
||||
name: $('#edit-prize-name').val(),
|
||||
description: $('#edit-prize-description').val(),
|
||||
probability: $('#edit-prize-probability').val(),
|
||||
is_active: $('#edit-prize-is-active').is(':checked') ? 1 : 0,
|
||||
redemption_code: $('#edit-prize-redemption-code').val(),
|
||||
is_discount: $('#edit-prize-is-discount').is(':checked') ? 1 : 0,
|
||||
discount_value: $('#edit-prize-discount-value').val(),
|
||||
email_subject: $('#edit-prize-email-subject').val(),
|
||||
email_template: $('#edit-prize-email-template').val(),
|
||||
};
|
||||
|
||||
// Debug: preveri wheel_id
|
||||
console.log('Wheel ID from form:', formData.wheel_id);
|
||||
console.log('Form data:', formData);
|
||||
|
||||
// Validate form fields
|
||||
if (!formData.name) {
|
||||
alert('Prize name is required.');
|
||||
return;
|
||||
}
|
||||
if (isNaN(parseFloat(formData.probability)) || formData.probability < 0 || formData.probability > 1) {
|
||||
alert('Probability must be a number between 0 and 1.');
|
||||
return;
|
||||
}
|
||||
if (!formData.wheel_id || formData.wheel_id == 0) {
|
||||
alert('Wheel ID is missing. Please refresh the page and try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
// AJAX call to save the prize
|
||||
$.post(ajaxurl, formData, function(response) {
|
||||
console.log('Server response:', response);
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
modal.hide();
|
||||
location.reload(); // Refresh page to see changes
|
||||
} else {
|
||||
alert(response.data.message || 'An error occurred while saving.');
|
||||
}
|
||||
}).fail(function(xhr, status, error) {
|
||||
console.error('AJAX error:', {xhr: xhr, status: status, error: error});
|
||||
alert('A critical error occurred. Please try again.');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Delete Prize ---
|
||||
$('.delete-prize').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (confirm('Are you sure you want to delete this prize?')) {
|
||||
var prizeId = $(this).data('id');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'wheel_delete_prize',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
prize_id: prizeId
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
alert(response.data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message || 'An error occurred.');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// --- SETTINGS PAGE - TEST EMAIL ---
|
||||
if ($('#test-email-button').length) {
|
||||
$('#test-email-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const button = $(this);
|
||||
const resultDiv = $('#test-email-result');
|
||||
const recipientEmail = $('#test-email-recipient').val();
|
||||
const spinner = button.siblings('.spinner');
|
||||
|
||||
if (!recipientEmail) {
|
||||
alert(wheel_admin_i18n.enter_recipient_email || 'Please enter a recipient email address.');
|
||||
return;
|
||||
}
|
||||
|
||||
button.prop('disabled', true);
|
||||
spinner.addClass('is-active');
|
||||
resultDiv.slideUp().removeClass('notice notice-success notice-error is-dismissible').empty();
|
||||
|
||||
// Collect all settings from the form to test unsaved values
|
||||
const data = {
|
||||
action: 'wheel_test_email',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
recipient_email: recipientEmail,
|
||||
smtp_enabled: $('#wheel_smtp_enabled').is(':checked') ? '1' : '0',
|
||||
smtp_host: $('#wheel_smtp_host').val(),
|
||||
smtp_port: $('#wheel_smtp_port').val(),
|
||||
smtp_encryption: $('#wheel_smtp_encryption').val(),
|
||||
smtp_username: $('#wheel_smtp_username').val(),
|
||||
smtp_password: $('#wheel_smtp_password').val(),
|
||||
from_name: $('#wheel_email_from_name').val(),
|
||||
from_email: $('#wheel_email_from_email').val(),
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
method: 'POST',
|
||||
data: data,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
resultDiv.addClass('notice notice-success is-dismissible').html('<p>' + response.data.message + '</p>').slideDown();
|
||||
} else {
|
||||
const errorMessage = response.data.message || (wheel_admin_i18n.error_sending_email || 'An unknown error occurred.');
|
||||
resultDiv.addClass('notice notice-error is-dismissible').html('<p>' + errorMessage + '</p>').slideDown();
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
let errorMessage = wheel_admin_i18n.error_sending_email || 'A communication error occurred with the server.';
|
||||
if(xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
|
||||
errorMessage = xhr.responseJSON.data.message;
|
||||
}
|
||||
resultDiv.addClass('notice notice-error is-dismissible').html('<p>' + errorMessage + '</p>').slideDown();
|
||||
},
|
||||
complete: function() {
|
||||
button.prop('disabled', false);
|
||||
spinner.removeClass('is-active');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Dodajanje/brisanje produkta za spine
|
||||
$('#add-product-button').on('click', function() {
|
||||
const button = $(this);
|
||||
const productId = $('#new-product-id').val();
|
||||
const spins = $('#new-product-spins').val();
|
||||
const wheelId = button.data('wheel-id');
|
||||
|
||||
if (!productId || !spins) {
|
||||
alert('Prosim vnesite ID produkta in število spinov.');
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'ajax_update_wheel_product_spins',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
wheel_id: wheelId,
|
||||
product_id: productId,
|
||||
spins: spins
|
||||
},
|
||||
success: function(response) {
|
||||
if(response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Napaka: ' + response.data.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('Prišlo je do kritične napake.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.delete-product-button').on('click', function() {
|
||||
if(!confirm('Ste prepričani?')) return;
|
||||
|
||||
const button = $(this);
|
||||
const productId = button.data('product-id');
|
||||
const wheelId = button.data('wheel-id');
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'ajax_delete_wheel_product',
|
||||
_ajax_nonce: wheel_admin_nonce._ajax_nonce,
|
||||
wheel_id: wheelId,
|
||||
product_id: productId
|
||||
},
|
||||
success: function(response) {
|
||||
if(response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Napaka: ' + response.data.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('Prišlo je do kritične napake.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Wheel of Fortune - JavaScript for wheel functionality
|
||||
* Robust, smooth animation using requestAnimationFrame and easing functions.
|
||||
*/
|
||||
jQuery(document).ready(function($) {
|
||||
// Check if wheelSettings are available
|
||||
if (typeof wheelSettings === 'undefined') {
|
||||
console.error('Wheel of Fortune: Settings (wheelSettings) are not defined. Please check the plugin setup.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Elements
|
||||
const wheel = $('.wheel');
|
||||
const button = $('.wheel-button');
|
||||
const resultDiv = $('.wheel-result');
|
||||
const spinsCounter = $('.wheel-spins-counter span');
|
||||
|
||||
// Animation state
|
||||
let isSpinning = false;
|
||||
let animationFrameId = null;
|
||||
let accumulatedRotation = 0; // Total rotation is maintained between spins
|
||||
|
||||
// Animation settings
|
||||
const spinDuration = 8000; // 8 seconds for a more dramatic effect
|
||||
const spinRotations = 5; // Number of full rotations before stopping
|
||||
|
||||
/**
|
||||
* Easing function (cubic-bezier out) for natural deceleration
|
||||
* @param {number} t - Current time (0 to 1)
|
||||
* @returns {number} - Animation progress (0 to 1)
|
||||
*/
|
||||
function easeOutCubic(t) {
|
||||
return 1 - Math.pow(1 - t, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main animation loop
|
||||
* @param {number} startTime - Animation start time
|
||||
* @param {number} startRotation - Starting rotation angle
|
||||
* @param {number} rotationAmount - Total rotation to perform for this spin
|
||||
*/
|
||||
function animateWheel(startTime, startRotation, rotationAmount) {
|
||||
const currentTime = Date.now();
|
||||
const elapsedTime = currentTime - startTime;
|
||||
|
||||
if (elapsedTime >= spinDuration) {
|
||||
wheel.css('transform', `rotate(${startRotation + rotationAmount}deg)`);
|
||||
accumulatedRotation = startRotation + rotationAmount; // Update the final position
|
||||
finishSpin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate progress within the animation duration
|
||||
const timeProgress = elapsedTime / spinDuration;
|
||||
// Apply the easing function to determine the rotation progress
|
||||
const rotationProgress = easeOutCubic(timeProgress);
|
||||
|
||||
// Calculate the current rotation
|
||||
const newRotation = startRotation + (rotationAmount * rotationProgress);
|
||||
wheel.css('transform', `rotate(${newRotation}deg)`);
|
||||
|
||||
// Continue to the next frame
|
||||
animationFrameId = requestAnimationFrame(() => animateWheel(startTime, startRotation, rotationAmount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to execute after the spin animation is complete
|
||||
*/
|
||||
function finishSpin() {
|
||||
// Display the result with a slight delay for better effect
|
||||
setTimeout(() => {
|
||||
const prize = window.spinResult.prize;
|
||||
resultDiv.html(`<h3>Congratulations!</h3><p>You've won: <strong>${prize.name}</strong></p>`);
|
||||
resultDiv.addClass('win').fadeIn(300);
|
||||
|
||||
isSpinning = false;
|
||||
|
||||
// Re-enable the button if there are remaining spins
|
||||
if (window.spinResult.remaining_spins > 0) {
|
||||
button.prop('disabled', false);
|
||||
} else {
|
||||
button.prop('disabled', true);
|
||||
}
|
||||
}, 500); // 500ms pause before showing the result
|
||||
}
|
||||
|
||||
// Spin button click handler
|
||||
button.on('click', function() {
|
||||
if (isSpinning) return;
|
||||
|
||||
isSpinning = true;
|
||||
button.prop('disabled', true);
|
||||
resultDiv.hide().removeClass('win error');
|
||||
|
||||
$.ajax({
|
||||
url: wheelSettings.ajaxUrl,
|
||||
method: 'POST',
|
||||
data: {
|
||||
action: 'wheel_spin',
|
||||
nonce: wheelSettings.nonce
|
||||
},
|
||||
beforeSend: function(xhr) {
|
||||
xhr.setRequestHeader('X-WP-Nonce', wheelSettings.nonce);
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
window.spinResult = response;
|
||||
|
||||
// The target angle (0-360) for the wheel to stop at, sent by the server.
|
||||
const targetAngle = response.degree;
|
||||
|
||||
// Add several full rotations for a nice visual effect.
|
||||
const fullSpins = spinRotations * 360;
|
||||
|
||||
// Get the current angle of the wheel, normalized to 0-360 degrees.
|
||||
const currentAngle = accumulatedRotation % 360;
|
||||
|
||||
// Calculate the rotation needed to get from the current angle to the target angle.
|
||||
// We must always rotate forward (clockwise, positive rotation).
|
||||
let rotationToTarget = targetAngle - currentAngle;
|
||||
if (rotationToTarget < 0) {
|
||||
rotationToTarget += 360;
|
||||
}
|
||||
|
||||
// The total amount of rotation for this spin.
|
||||
const rotationAmount = fullSpins + rotationToTarget;
|
||||
|
||||
const startRotation = accumulatedRotation;
|
||||
|
||||
// Start the animation. The function will rotate the wheel by 'rotationAmount' degrees.
|
||||
animateWheel(Date.now(), startRotation, rotationAmount);
|
||||
|
||||
// Update the remaining spins counter on the screen.
|
||||
spinsCounter.text(response.remaining_spins);
|
||||
} else {
|
||||
// Display error from server
|
||||
resultDiv.html(`<p>${response.data.message || 'An error occurred.'}</p>`);
|
||||
resultDiv.addClass('error').fadeIn(300);
|
||||
isSpinning = false;
|
||||
button.prop('disabled', false);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
const errorMsg = xhr.responseJSON ? xhr.responseJSON.message : 'A communication error occurred.';
|
||||
resultDiv.html(`<p>${errorMsg}</p>`);
|
||||
resultDiv.addClass('error').fadeIn(300);
|
||||
isSpinning = false;
|
||||
button.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Initialization
|
||||
if (wheelSettings.remainingSpins <= 0) {
|
||||
button.prop('disabled', true);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user