Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
WordPress Plugin v2.0.0: - Add Promo Banner widget (dark banner with deal text) - Add Horizontal Product Row widget (wide list format) - Add Category Card widget (image-based categories) - Add Compact Card widget (dense grid layout) - Add CannaiQAnalytics click tracking (tracks add_to_cart, product_view, promo_click, category_click events) - Register cannaiq-templates Elementor category - Fix branding: CannaiQAnalytics (not CannaIQAnalytics) Backend: - Add POST /api/analytics/click endpoint for WordPress plugin - Accepts API token auth, records to product_click_events table - Stores metadata: product_name, price, category, url, referrer 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
165 lines
6.7 KiB
JavaScript
165 lines
6.7 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 Add to Cart clicks
|
|
$(document).on('click', '.cannaiq-cart-button, .cannaiq-add-to-cart, .cannaiq-hr-add-btn, .cannaiq-cc-button, [class*="cannaiq"][href*="dutchie"], [class*="cannaiq"][href*="iheartjane"]', function(e) {
|
|
var $el = $(this);
|
|
var $card = $el.closest('[data-product-id], .cannaiq-product-card, .cannaiq-premium-card, .cannaiq-horizontal-row, .cannaiq-compact-card');
|
|
|
|
self.track('add_to_cart', {
|
|
product_id: $card.data('product-id') || $el.data('product-id'),
|
|
product_name: $card.data('product-name') || $el.data('product-name') || $card.find('.cannaiq-product-name, .cannaiq-premium-name, .cannaiq-hr-name, .cannaiq-cc-name').first().text().trim(),
|
|
product_price: $card.data('product-price') || $el.data('product-price'),
|
|
store_id: $card.data('store-id') || $el.data('store-id'),
|
|
category: $card.data('category') || $el.data('category'),
|
|
url: $el.attr('href')
|
|
});
|
|
});
|
|
|
|
// 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')
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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);
|