Files
magdev af532b56eb Release version 1.1.7 - Enhanced user experience features
Added three new customer-facing features to improve product page interaction
and tier pricing functionality.

Features:
- Optional text labels for tier pricing (similar to package labels)
- Clickable tier pricing table rows to auto-populate quantity field
- Add to Cart button auto-disables when quantity is 0 or less

Enhanced User Experience:
- Tier pricing rows now clickable with cursor pointer and hover animation
- Clicking tier row sets quantity and smoothly scrolls to quantity field
- Add to Cart button shows disabled state with reduced opacity
- Tier labels display below quantity in italic gray text

Technical Changes:
- Added optional 'label' field to tier pricing admin meta box
- Updated tier save logic to include label field (sanitized)
- Enhanced tier pricing frontend template to display labels
- Added click handler for tier pricing rows in frontend.js
- Added updateAddToCartButton() function to manage button state
- CSS: .wc-tpp-tier-label styling for tier labels
- CSS: Clickable cursor and hover transform for tier rows
- CSS: Disabled button styling (.single_add_to_cart_button:disabled)

Updated Files:
- templates/admin/tier-row.twig (added label field)
- includes/class-wc-tpp-product-meta.php (save label, template update)
- templates/frontend/tier-pricing-table.twig (display labels)
- assets/js/frontend.js (tier row clicks, button disable logic)
- assets/css/frontend.css (tier label style, clickable rows, disabled button)
- wc-tier-and-package-prices.php (version 1.1.7)
- composer.json (version 1.1.7)
- CHANGELOG.md (v1.1.7 section)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-22 00:15:48 +01:00

250 lines
8.8 KiB
JavaScript

/**
* Frontend JavaScript for WooCommerce Tier and Package Prices
*/
(function($) {
'use strict';
$(document).ready(function() {
const $quantityInput = $('input.qty');
const $priceDisplay = $('.woocommerce-Price-amount.amount').first();
const $addToCartButton = $('.single_add_to_cart_button');
const isRestrictedMode = $('.wc-tpp-package-pricing-table').hasClass('wc-tpp-restricted-mode');
if ($quantityInput.length === 0 && !isRestrictedMode) {
return;
}
// Get tiers and packages data
const tiers = [];
const packages = [];
$('.wc-tpp-tier-pricing-table tbody tr').each(function() {
tiers.push({
minQty: parseInt($(this).data('min-qty')),
price: parseFloat($(this).data('price'))
});
});
$('.wc-tpp-package').each(function() {
packages.push({
qty: parseInt($(this).data('qty')),
price: parseFloat($(this).data('price'))
});
});
// Update price display based on quantity
function updatePriceDisplay() {
const quantity = parseInt($quantityInput.val()) || 1;
// Check for exact package match
let matchedPackage = null;
packages.forEach(function(pkg) {
if (pkg.qty === quantity) {
matchedPackage = pkg;
}
});
if (matchedPackage) {
const totalPrice = matchedPackage.price;
const unitPrice = totalPrice / quantity;
updatePrice(unitPrice, 'Package price: ' + formatPrice(totalPrice) + ' total');
highlightPackage(quantity);
return;
}
// Check for tier pricing
let applicableTier = null;
tiers.forEach(function(tier) {
if (quantity >= tier.minQty) {
applicableTier = tier;
}
});
if (applicableTier) {
updatePrice(applicableTier.price, 'Volume discount applied');
highlightTier(quantity);
removePackageHighlight();
} else {
removeAllHighlights();
}
}
// Update price in the product page
function updatePrice(price, message) {
if ($priceDisplay.length) {
const formattedPrice = formatPrice(price);
$priceDisplay.html(formattedPrice);
// Add message if provided
if (message) {
if (!$('.wc-tpp-price-message').length) {
$priceDisplay.after('<div class="wc-tpp-price-message"></div>');
}
$('.wc-tpp-price-message').html('<small>' + message + '</small>');
}
}
}
// Format price according to WooCommerce settings
function formatPrice(price) {
const decimals = wcTppData.price_decimals || 2;
const decimalSep = wcTppData.price_decimal_separator || '.';
const thousandSep = wcTppData.price_thousand_separator || ',';
const symbol = wcTppData.currency_symbol || '$';
const position = wcTppData.currency_position || 'left';
let priceStr = price.toFixed(decimals);
const parts = priceStr.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep);
priceStr = parts.join(decimalSep);
switch (position) {
case 'left':
return symbol + priceStr;
case 'right':
return priceStr + symbol;
case 'left_space':
return symbol + ' ' + priceStr;
case 'right_space':
return priceStr + ' ' + symbol;
default:
return symbol + priceStr;
}
}
// Highlight active tier
function highlightTier(quantity) {
$('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier');
let activeRow = null;
$('.wc-tpp-table tbody tr').each(function() {
const minQty = parseInt($(this).data('min-qty'));
if (quantity >= minQty) {
activeRow = $(this);
}
});
if (activeRow) {
activeRow.addClass('wc-tpp-active-tier');
}
}
// Highlight selected package
function highlightPackage(quantity) {
$('.wc-tpp-package').removeClass('wc-tpp-selected');
$('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier');
$('.wc-tpp-package').each(function() {
const pkgQty = parseInt($(this).data('qty'));
if (pkgQty === quantity) {
$(this).addClass('wc-tpp-selected');
}
});
}
// Remove package highlight
function removePackageHighlight() {
$('.wc-tpp-package').removeClass('wc-tpp-selected');
}
// Remove all highlights
function removeAllHighlights() {
$('.wc-tpp-package').removeClass('wc-tpp-selected');
$('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier');
$('.wc-tpp-price-message').remove();
}
// Toggle add to cart button state based on quantity
function updateAddToCartButton() {
const quantity = parseInt($quantityInput.val()) || 0;
if (quantity <= 0) {
$addToCartButton.prop('disabled', true).addClass('disabled');
} else {
$addToCartButton.prop('disabled', false).removeClass('disabled');
}
}
// Handle quantity input changes
$quantityInput.on('input change', function() {
updatePriceDisplay();
updateAddToCartButton();
});
// Handle tier pricing row clicks
$('.wc-tpp-tier-pricing-table tbody tr').on('click', function() {
const minQty = parseInt($(this).data('min-qty'));
if ($quantityInput.length > 0 && !isRestrictedMode) {
$quantityInput.val(minQty).trigger('change');
// Scroll to quantity input for better UX
$('html, body').animate({
scrollTop: $quantityInput.offset().top - 100
}, 300);
}
});
// Handle package selection
$('.wc-tpp-select-package').on('click', function(e) {
e.preventDefault();
const $package = $(this).closest('.wc-tpp-package');
const qty = parseInt($package.data('qty'));
if (isRestrictedMode) {
// In restricted mode, we need to set a hidden input or use data attribute
// since the quantity field is hidden
if ($quantityInput.length === 0) {
// Create a hidden quantity input if it doesn't exist
if ($('.qty-hidden-input').length === 0) {
$('.single_add_to_cart_button').before('<input type="hidden" name="quantity" class="qty qty-hidden-input" value="1" />');
}
$('.qty-hidden-input').val(qty);
} else {
$quantityInput.val(qty);
}
// Highlight selected package
$('.wc-tpp-package').removeClass('wc-tpp-selected');
$package.addClass('wc-tpp-selected');
// Update price display
const price = parseFloat($package.data('price'));
const unitPrice = price / qty;
updatePrice(unitPrice, 'Package price: ' + formatPrice(price) + ' total');
} else {
$quantityInput.val(qty).trigger('change');
}
// Scroll to add to cart button
$('html, body').animate({
scrollTop: $('.single_add_to_cart_button').offset().top - 100
}, 500);
});
// In restricted mode, prevent form submission if no package is selected
if (isRestrictedMode) {
$('form.cart').on('submit', function(e) {
const hasSelection = $('.wc-tpp-package.wc-tpp-selected').length > 0;
if (!hasSelection) {
e.preventDefault();
alert('Please select a package size before adding to cart.');
return false;
}
});
}
// Initial update
if (!isRestrictedMode) {
updatePriceDisplay();
}
// Initial button state check
if ($quantityInput.length > 0 && $addToCartButton.length > 0) {
updateAddToCartButton();
}
});
})(jQuery);