PageSourceSearch

https://echo-3.co.uk/wp-content/themes/x-child/inc/blocks/course-card/view.js?ver=1790185488

js echo-3.co.uk collected 2026-10-03 21:55:21 UTC 2,673 bytes, 93 lines download raw bytes

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.