/** * 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 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('
'); } $('.wc-tpp-price-message').html('' + message + ''); } } } // 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(); } // Handle quantity input changes $quantityInput.on('input change', function() { updatePriceDisplay(); }); // 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(''); } $('.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(); } }); })(jQuery);