1/** 2 * Front-end interactions for the xchild/course-card block. 3 * Auto-enqueued by WordPress when the block is on the page (via block.json's viewScript). 4 * 5 * Behaviour: clicking the "Add" button on a course card routes through the 6 * same Echo3Cart pipeline used by the single product page (cart-integration.js), 7 * so the localStorage cart, WooCommerce session, and slide-out cart sidebar all 8 * stay in sync. 9 * 10 * This file is intentionally kept in sync with 11 * inc/blocks/courses-by-category/view.js â the only difference is the block 12 * namespace used in console.error messages. If the Echo3Cart API changes, 13 * update both files (or extract to a shared module). 14 */ 15( function() { 16 'use strict'; 17 18 const FEEDBACK_DURATION_MS = 1500; 19 20 function getProductMeta( btn ) { 21 const card = btn.closest( '.course-card' ); 22 let name = 'Course'; 23 let price = 0; 24 if ( card ) { 25 const titleEl = card.querySelector( '.course-title' ); 26 if ( titleEl ) { 27 name = titleEl.textContent.trim() || name; 28 } 29 const priceEl = card.querySelector( '.course-price' ); 30 if ( priceEl ) { 31 const parsed = parseFloat( priceEl.textContent.replace( /[^0-9.]/g, '' ) ); 32 if ( ! isNaN( parsed ) ) { 33 price = parsed; 34 } 35 } 36 } 37 return { name, price }; 38 } 39 40 document.addEventListener( 41 'click', 42 function( e ) { 43 const btn = e.target.closest( '.course-card-buy-btn' ); 44 if ( ! btn ) { 45 return; 46 } 47 48 // Always stop the parent <a class="course-card"> from navigating. 49 e.preventDefault(); 50 e.stopPropagation(); 51 52 if ( btn.disabled ) { 53 return; 54 } 55 56 const productId = parseInt( btn.dataset.productId, 10 ); 57 if ( ! productId ) { 58 return; 59 } 60 61 if ( ! window.Echo3Cart || typeof window.Echo3Cart.addItem !== 'function' ) { 62 // eslint-disable-next-line no-console 63 console.error( '[course-card] Echo3Cart not available' ); 64 return; 65 } 66 67 const originalText = btn.textContent; 68 btn.disabled = true; 69 btn.textContent = 'Addingâ¦'; 70 71 const meta = getProductMeta( btn ); 72 73 window.Echo3Cart.addItem( productId, null, meta.name, meta.price, 1, null ) 74 .then( function() { 75 btn.textContent = 'â Added'; 76 if ( window.Echo3CartSidebar && typeof window.Echo3CartSidebar.open === 'function' ) { 77 window.Echo3CartSidebar.open(); 78 } 79 setTimeout( function() { 80 btn.disabled = false; 81 btn.textContent = originalText; 82 }, FEEDBACK_DURATION_MS ); 83 } ) 84 .catch( function( err ) { 85 // eslint-disable-next-line no-console 86 console.error( '[course-card] add to cart failed:', err ); 87 btn.disabled = false; 88 btn.textContent = originalText; 89 } ); 90 }, 91 false, 92 ); 93}() );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.