Files
cannaiq/wordpress-plugin/assets/js/cannaiq-menus.js
Kelly a2324214e9
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: Enhanced cart button tracking and automatic outbound click tracking
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>
2025-12-17 15:25:15 -07:00

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);