Files
cannaiq/wordpress-plugin/assets/js/cannaiq-menus.js
Kelly 0b4ed48d2f
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
feat: Add premade card templates and click analytics
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>
2025-12-17 02:03:28 -07:00

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