PHP /**
* Snippet Name: Add Free Shipping Progress Bar
* Snippet Author: coding-bunny.com
* Description: Adds a progress bar showing how much more is needed to qualify for free shipping.
* Version: 1.2.0
*/
if ( ! defined ( ' ABSPATH ' ) ) {
exit;
}
add_action ( ' init ' , ' cbfpb_init_hooks ' ) ;
function cbfpb_init_hooks () {
// Product page
add_action ( ' woocommerce_after_add_to_cart_button ' , ' cbfpb_display_product_bar ' , 25 ) ;
// Cart page
add_action ( ' woocommerce_cart_totals_after_order_total ' , ' cbfpb_display_cart_bar ' ) ;
// Checkout page
add_action ( ' woocommerce_review_order_after_order_total ' , ' cbfpb_display_checkout_bar ' ) ;
}
function cbfpb_format_price ( $ amount ) {
return wc_price ( $ amount ) ;
}
function cbfpb_currency_symbol () {
return html_entity_decode ( get_woocommerce_currency_symbol (), ENT_QUOTES , ' UTF-8 ' ) ;
}
function cbfpb_render_bar ( $ context = ' general ' ) {
if ( ! class_exists ( ' WooCommerce ' ) ) {
return;
}
$ free_shipping_threshold = cbfpb_minimum_amount () ;
if ( ! $ free_shipping_threshold ) {
return;
}
$ current_cart_total = 0 ;
if ( WC () -> cart ) {
$ current_cart_total = WC () -> cart -> get_subtotal () ;
}
$ remaining_amount = max ( 0 , $ free_shipping_threshold - $ current_cart_total ) ;
$ progress_percentage = min ( 100 , ( $ current_cart_total / $ free_shipping_threshold ) * 100 ) ;
?>
< div class= " free-shipping-unified <?php echo esc_attr( $ context ); ?>-context "
data - threshold = " <?php echo esc_attr( $ free_shipping_threshold ); ?> "
data - currency = " <?php echo esc_attr( cbfpb_currency_symbol() ); ?> "
data - currency - pos = " <?php echo esc_attr( get_option( 'woocommerce_currency_pos' ) ); ?> "
data - thousand - sep = " <?php echo esc_attr( wc_get_price_thousand_separator() ); ?> "
data - decimal - sep = " <?php echo esc_attr( wc_get_price_decimal_separator() ); ?> "
data - decimals = " <?php echo esc_attr( wc_get_price_decimals() ); ?> " >
<? php if ( $ remaining_amount > 0 ) : ?>
< p class= " shipping-message " >
<? php
printf (
esc_html__ ( ' Add %s more to get free shipping! ' , ' textdomain ' ),
wp_kses_post ( cbfpb_format_price ( $ remaining_amount ) )
) ;
?>
</ p >
<? php else : ?>
< p class= " shipping-message qualified " >
<? php esc_html_e ( ' Free shipping qualified ✓ ' , ' textdomain ' ) ; ?>
</ p >
<? php endif; ?>
< div class= " progress-track " >
< div class= " progress-fill " style = " width: <?php echo esc_attr( $ progress_percentage ); ?>%; " ></ div >
</ div >
<? php if ( $ context === ' checkout ' && $ remaining_amount == 0 ) : ?>
< p class= " shipping-bonus " >
<? php esc_html_e ( ' Free shipping will be applied at final step ' , ' textdomain ' ) ; ?>
</ p >
<? php endif; ?>
</ div >
<? php
}
function cbfpb_display_product_bar () {
cbfpb_render_bar ( ' product ' ) ;
}
function cbfpb_display_cart_bar () {
?>
< tr class= " free-shipping-progress-row " >
< td colspan = " 2 " class= " free-shipping-cell " >
<? php cbfpb_render_bar ( ' cart ' ) ; ?>
</ td >
</ tr >
<? php
}
function cbfpb_display_checkout_bar () {
?>
< tr class= " free-shipping-checkout-row " >
< td colspan = " 2 " class= " free-shipping-checkout-cell " >
<? php cbfpb_render_bar ( ' checkout ' ) ; ?>
</ td >
</ tr >
<? php
}
function cbfpb_minimum_amount () {
static $ cached_amount = null;
if ( $ cached_amount !== null ) {
return $ cached_amount ;
}
$ shipping_zones = WC_Shipping_Zones :: get_zones () ;
$ shipping_zones [] = array ( ' zone_id ' => 0 ) ;
foreach ( $ shipping_zones as $ zone ) {
$ zone_id = $ zone [ ' zone_id ' ] ;
$ shipping_zone = new WC_Shipping_Zone ( $ zone_id ) ;
$ shipping_methods = $ shipping_zone -> get_shipping_methods ( true ) ;
foreach ( $ shipping_methods as $ method ) {
if ( ' free_shipping ' === $ method -> id && ' yes ' === $ method -> enabled ) {
$ min_amount = $ method -> get_option ( ' min_amount ' ) ;
if ( ! empty ( $ min_amount ) && is_numeric ( $ min_amount ) ) {
$ cached_amount = ( float ) $ min_amount ;
return $ cached_amount ;
}
}
}
}
$ cached_amount = false;
return false;
}
add_action ( ' wp_head ' , ' cbfpb_styles ' ) ;
function cbfpb_styles () {
if ( ! is_product () && ! is_cart () && ! is_checkout () ) {
return;
}
?>
< style >
. free - shipping - unified {
margin: 0 ;
padding: 12 px ;
background: #f8f9fa;
border: 1 px solid #dee2e6;
border - radius : 6 px ;
font - size : 14 px ;
}
. free - shipping - unified . product - context {
margin: 0 ;
}
. free - shipping - unified . cart - context {
margin: 0 ;
}
. free - shipping - unified . checkout - context {
margin: 0 ;
}
. free - shipping - cell ,
. free - shipping - checkout - cell {
padding: 15 px 0 ! important ;
border - top : 1 px solid #e0e0e0;
}
. shipping - message {
margin: 0 0 8 px 0 ;
font - weight : 400 ;
text - align : center ;
font - size : 14 px ;
}
. shipping - message . qualified {
color: #27ae60;
}
. shipping - bonus {
margin: 8 px 0 0 0 ;
font - size : 12 px ;
color: #27ae60;
text - align : center ;
font - style : italic ;
}
. progress - track {
height: 6 px ;
background: #e0e0e0;
border - radius : 3 px ;
overflow: hidden ;
}
. progress - fill {
height: 100 %;
background: linear - gradient ( 90 deg , #27ae60 0%, #2ecc71 100%);
border - radius : 3 px ;
transition : width 0 . 3 s ease ;
}
. progress - fill . updating {
transition: width 0 . 5 s cubic - bezier ( 0 . 4 , 0 , 0 . 2 , 1 ) ;
}
. shipping - message . updating {
transition: color 0 . 3 s ease ;
}
. shipping - message . woocommerce - Price - amount {
font - weight : 700 ;
}
@ media ( max - width : 768 px ) {
. free - shipping - unified {
font - size : 13 px ;
padding: 10 px ;
}
. shipping - message {
font - size : 13 px ;
}
}
. woocommerce - cart . free - shipping - progress - row ,
. woocommerce - checkout . free - shipping - checkout - row {
border - top : 1 px solid #e0e0e0;
}
</ style >
<? php
}
add_action ( ' wp_ajax_cbfpb_cart_total ' , ' cbfpb_ajax_cart_total ' ) ;
add_action ( ' wp_ajax_nopriv_cbfpb_cart_total ' , ' cbfpb_ajax_cart_total ' ) ;
function cbfpb_ajax_cart_total () {
if ( ! isset ( $ _POST [ ' nonce ' ] ) || ! wp_verify_nonce ( sanitize_text_field ( wp_unslash ( $ _POST [ ' nonce ' ] ) ), ' cbfpb_nonce ' ) ) {
wp_die ( ' Security check failed ' ) ;
}
if ( ! class_exists ( ' WooCommerce ' ) || ! WC () -> cart ) {
wp_send_json_error ( ' WooCommerce not available ' ) ;
}
$ free_shipping_threshold = cbfpb_minimum_amount () ;
if ( ! $ free_shipping_threshold ) {
wp_send_json_error ( ' No free shipping available ' ) ;
}
$ current_cart_total = WC () -> cart -> get_subtotal () ;
$ remaining_amount = max ( 0 , $ free_shipping_threshold - $ current_cart_total ) ;
$ progress_percentage = min ( 100 , ( $ current_cart_total / $ free_shipping_threshold ) * 100 ) ;
$ response = array (
' remaining ' => $ remaining_amount ,
' remaining_formatted ' => cbfpb_format_price ( $ remaining_amount ),
' percentage ' => $ progress_percentage ,
' qualified ' => $ remaining_amount == 0 ,
' current_total ' => $ current_cart_total ,
' threshold ' => $ free_shipping_threshold
) ;
wp_send_json_success ( $ response ) ;
}
add_action ( ' wp_footer ' , ' cbfpb_realtime_script ' ) ;
function cbfpb_realtime_script () {
if ( ! is_product () && ! is_cart () && ! is_checkout () ) {
return;
}
?>
< script >
jQuery ( document ) . ready ( function ( $ ) {
var updateTimeout ;
function formatPrice ( amount , currency , currencyPos , thousandSep , decimalSep , decimals ) {
var formattedAmount = parseFloat ( amount ) . toFixed ( decimals ) ;
if ( thousandSep ) {
var parts = formattedAmount . split ( ' . ' ) ;
parts [ 0 ] = parts [ 0 ] . replace ( / \ B ( ?= ( \d { 3 }) + ( ?! \d)) / g , thousandSep) ;
formattedAmount = parts . join ( decimalSep ) ;
} else if ( decimalSep !== ' . ' ) {
formattedAmount = formattedAmount . replace ( ' . ' , decimalSep ) ;
}
switch ( currencyPos ) {
case ' left ' :
return currency + formattedAmount ;
case ' right ' :
return formattedAmount + currency ;
case ' left_space ' :
return currency + ' ' + formattedAmount ;
case ' right_space ' :
return formattedAmount + ' ' + currency ;
default:
return currency + formattedAmount ;
}
}
function updateProgressBarDisplay ( data ) {
var $ progressFill = $ ( ' .progress-fill ' ) ;
var $ shippingMessage = $ ( ' .shipping-message ' ) ;
var $ progressBar = $ ( ' .free-shipping-unified ' ) ;
$ progressFill . addClass ( ' updating ' ) ;
$ shippingMessage . addClass ( ' updating ' ) ;
var message ;
if ( data . qualified ) {
message = ' Free shipping qualified ✓ ' ;
} else {
if ( data . remaining_formatted ) {
message = ' Add ' + data . remaining_formatted + ' more to get free shipping! ' ;
} else {
var currency = $ progressBar . data ( ' currency ' ) ;
var currencyPos = $ progressBar . data ( ' currency-pos ' ) ;
var thousandSep = $ progressBar . data ( ' thousand-sep ' ) ;
var decimalSep = $ progressBar . data ( ' decimal-sep ' ) ;
var decimals = parseInt ( $ progressBar . data ( ' decimals ' )) ;
var formattedPrice = formatPrice ( data . remaining , currency , currencyPos , thousandSep , decimalSep , decimals ) ;
message = ' Add ' + formattedPrice + ' more to get free shipping! ' ;
}
}
$ shippingMessage . html ( message )
. toggleClass ( ' qualified ' , data . qualified ) ;
$ progressFill . css ( ' width ' , data . percentage + ' % ' ) ;
if ( $ ( ' .checkout-context ' ) . length && data . qualified ) {
if ( ! $ ( ' .shipping-bonus ' ) . length ) {
$ ( ' .checkout-context .progress-track ' ) . after (
' <p class="shipping-bonus">Free shipping will be applied at final step</p> '
) ;
}
} else {
$ ( ' .shipping-bonus ' ) . remove () ;
}
setTimeout ( function () {
$ progressFill . removeClass ( ' updating ' ) ;
$ shippingMessage . removeClass ( ' updating ' ) ;
}, 500 ) ;
}
function updateShippingProgress () {
$ . post ( ' <?php echo esc_url( admin_url( ' admin - ajax . php ' ) ); ?> ' , {
action: ' cbfpb_cart_total ' ,
nonce: ' <?php echo esc_js( wp_create_nonce( ' cbfpb_nonce ' ) ); ?> '
}, function ( response ) {
if ( response . success ) {
updateProgressBarDisplay ( response . data ) ;
}
}) ;
}
$ ( document . body ) . on ( ' added_to_cart ' , function ( event , fragments , cart_hash , button ) {
setTimeout ( updateShippingProgress , 100 ) ;
}) ;
$ ( document . body ) . on ( ' updated_wc_div ' , function () {
updateShippingProgress () ;
}) ;
$ ( document . body ) . on ( ' updated_cart_totals ' , function () {
updateShippingProgress () ;
}) ;
$ ( document . body ) . on ( ' updated_checkout ' , function () {
updateShippingProgress () ;
}) ;
$ ( document ) . on ( ' input change ' , ' input.qty ' , function () {
clearTimeout ( updateTimeout ) ;
updateTimeout = setTimeout ( function () {
updateShippingProgress () ;
}, 300 ) ;
}) ;
$ ( document ) . on ( ' click ' , ' a.remove ' , function () {
setTimeout ( updateShippingProgress , 800 ) ;
}) ;
$ ( ' form.variations_form ' ) . on ( ' found_variation ' , function () {
setTimeout ( updateShippingProgress , 100 ) ;
}) ;
if ( $ ( ' .product-context ' ) . length ) {
$ ( document ) . on ( ' input change ' , ' input[name="quantity"] ' , function () {
updateShippingProgress () ;
}) ;
}
setInterval ( updateShippingProgress , 30000 ) ;
setTimeout ( updateShippingProgress , 500 ) ;
}) ;
</ script >
<? php
}
?>