All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Plugin: - Enhanced [cannaiq_cart_button] shortcode with full tracking data - Includes product_id, name, price, category, on_special, store_id - Added size, full-width, and custom class options - Added [cannaiq_product_wrapper] shortcode for custom buttons - Auto-track ALL outbound clicks to menu providers: - dutchie.com, iheartjane.com, jane.com, treez.io, weedmaps.com, leafly.com API: - Added menu_url to /products response - Added menu_url to /specials response Docs: - Updated Elementor guide with button tracking options - Documented shortcode attributes and examples 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
197 lines
8.6 KiB
JavaScript
197 lines
8.6 KiB
JavaScript
/**
|
|
* CannaIQ Menus - WordPress Plugin JavaScript
|
|
* v2.0.0
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
/**
|
|
* Click Analytics Tracker
|
|
*/
|
|
var CannaiQAnalytics = {
|
|
/**
|
|
* Track a click event
|
|
* @param {string} eventType - Type of event (add_to_cart, product_view, promo_click, etc)
|
|
* @param {object} data - Event data (product_id, store_id, product_name, etc)
|
|
*/
|
|
track: function(eventType, data) {
|
|
if (!window.cannaiqAnalytics || !window.cannaiqAnalytics.enabled) {
|
|
return;
|
|
}
|
|
|
|
var payload = {
|
|
event_type: eventType,
|
|
store_id: data.store_id || window.cannaiqAnalytics.store_id,
|
|
product_id: data.product_id || null,
|
|
product_name: data.product_name || null,
|
|
product_price: data.product_price || null,
|
|
category: data.category || null,
|
|
url: data.url || window.location.href,
|
|
referrer: document.referrer || null,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
|
|
// Send to analytics endpoint
|
|
$.ajax({
|
|
url: window.cannaiqAnalytics.api_url + '/analytics/click',
|
|
method: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(payload),
|
|
headers: {
|
|
'Authorization': 'Bearer ' + window.cannaiqAnalytics.api_token
|
|
},
|
|
// Fire and forget - don't block user interaction
|
|
async: true
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Initialize click tracking on all CannaiQ elements
|
|
*/
|
|
init: function() {
|
|
var self = this;
|
|
|
|
// Track ALL outbound clicks to dispensary menus (Dutchie, iHeartJane, Treez, etc.)
|
|
// This catches any link leaving the site to a menu provider
|
|
$(document).on('click', 'a[href*="dutchie.com"], a[href*="iheartjane.com"], a[href*="jane.com"], a[href*="treez.io"], a[href*="weedmaps.com"], a[href*="leafly.com"], .cannaiq-cart-button, .cannaiq-add-to-cart', function(e) {
|
|
var $el = $(this);
|
|
var $card = $el.closest('[data-product-id], .cannaiq-product-card, .cannaiq-premium-card, .cannaiq-horizontal-row, .cannaiq-compact-card, .cannaiq-product-item');
|
|
|
|
// Get product data from element or parent
|
|
var productId = $el.data('product-id') || $card.data('product-id') || '';
|
|
var productName = $el.data('product-name') || $card.data('product-name') || $card.find('.cannaiq-product-name, .cannaiq-premium-name, .cannaiq-hr-name, .cannaiq-cc-name, [data-product-name]').first().text().trim() || '';
|
|
var productPrice = $el.data('product-price') || $card.data('product-price') || '';
|
|
var storeId = $el.data('store-id') || $card.data('store-id') || '';
|
|
var category = $el.data('category') || $card.data('category') || '';
|
|
var onSpecial = $el.data('on-special') || $card.data('on-special') || false;
|
|
var destinationUrl = $el.attr('href');
|
|
|
|
self.track('add_to_cart', {
|
|
product_id: productId,
|
|
product_name: productName,
|
|
product_price: productPrice,
|
|
store_id: storeId,
|
|
category: category,
|
|
on_special: onSpecial,
|
|
url: destinationUrl
|
|
});
|
|
});
|
|
|
|
// Track product card clicks (view intent)
|
|
$(document).on('click', '.cannaiq-product-card, .cannaiq-premium-card, .cannaiq-special-card', function(e) {
|
|
// Don't double-track if clicking the cart button
|
|
if ($(e.target).closest('.cannaiq-cart-button, .cannaiq-add-to-cart').length) {
|
|
return;
|
|
}
|
|
|
|
var $card = $(this);
|
|
self.track('product_view', {
|
|
product_id: $card.data('product-id'),
|
|
product_name: $card.data('product-name') || $card.find('.cannaiq-product-name, .cannaiq-premium-name').first().text().trim(),
|
|
product_price: $card.data('product-price'),
|
|
store_id: $card.data('store-id'),
|
|
category: $card.data('category')
|
|
});
|
|
});
|
|
|
|
// Track promo banner clicks
|
|
$(document).on('click', '.cannaiq-promo-banner .cannaiq-promo-button, .cannaiq-promo-banner', function(e) {
|
|
var $banner = $(this).closest('.cannaiq-promo-banner');
|
|
self.track('promo_click', {
|
|
store_id: $banner.data('store-id'),
|
|
promo_headline: $banner.find('.cannaiq-promo-headline').text().trim(),
|
|
url: $(this).attr('href') || $banner.find('a').first().attr('href')
|
|
});
|
|
});
|
|
|
|
// Track category clicks
|
|
$(document).on('click', '.cannaiq-category-card, .cannaiq-category-item', function(e) {
|
|
var $cat = $(this);
|
|
self.track('category_click', {
|
|
store_id: $cat.data('store-id'),
|
|
category: $cat.data('category') || $cat.find('.cannaiq-cat-name, .cannaiq-category-name').first().text().trim(),
|
|
url: $cat.attr('href')
|
|
});
|
|
});
|
|
|
|
// Generic tracking via data-cannaiq-track attribute
|
|
// Usage: <a href="..." data-cannaiq-track="shop_now">Shop Now</a>
|
|
// Optional: data-cannaiq-track-category, data-cannaiq-track-label, data-cannaiq-track-value
|
|
$(document).on('click', '[data-cannaiq-track]', function(e) {
|
|
var $el = $(this);
|
|
var trackName = $el.data('cannaiq-track');
|
|
|
|
// Don't double-track elements that are already handled above
|
|
if ($el.is('.cannaiq-cart-button, .cannaiq-add-to-cart, .cannaiq-product-card, .cannaiq-premium-card, .cannaiq-special-card, .cannaiq-category-card, .cannaiq-promo-banner')) {
|
|
return;
|
|
}
|
|
|
|
self.track(trackName, {
|
|
store_id: $el.data('store-id') || $el.closest('[data-store-id]').data('store-id'),
|
|
category: $el.data('cannaiq-track-category') || null,
|
|
label: $el.data('cannaiq-track-label') || $el.text().trim().substring(0, 100),
|
|
value: $el.data('cannaiq-track-value') || null,
|
|
url: $el.attr('href') || null
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Initialize plugin
|
|
*/
|
|
$(document).ready(function() {
|
|
// Initialize analytics tracking
|
|
CannaiQAnalytics.init();
|
|
|
|
// Lazy load images
|
|
if ('IntersectionObserver' in window) {
|
|
const imageObserver = new IntersectionObserver((entries, observer) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const img = entry.target;
|
|
if (img.dataset.src) {
|
|
img.src = img.dataset.src;
|
|
img.removeAttribute('data-src');
|
|
observer.unobserve(img);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.cannaiq-product-image img[data-src]').forEach(img => {
|
|
imageObserver.observe(img);
|
|
});
|
|
}
|
|
|
|
// Add animation to product cards on scroll
|
|
if ('IntersectionObserver' in window) {
|
|
const cardObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.style.opacity = '0';
|
|
entry.target.style.transform = 'translateY(20px)';
|
|
setTimeout(() => {
|
|
entry.target.style.transition = 'opacity 0.5s, transform 0.5s';
|
|
entry.target.style.opacity = '1';
|
|
entry.target.style.transform = 'translateY(0)';
|
|
}, 10);
|
|
cardObserver.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, {
|
|
threshold: 0.1
|
|
});
|
|
|
|
document.querySelectorAll('.cannaiq-product-card, .cannaiq-premium-card, .cannaiq-compact-card').forEach(card => {
|
|
cardObserver.observe(card);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Expose for external use
|
|
window.CannaiQAnalytics = CannaiQAnalytics;
|
|
|
|
})(jQuery);
|