Files
cannaiq/wordpress-plugin/widgets/card-template-category.php
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

253 lines
7.7 KiB
PHP

<?php
/**
* Elementor Category Card Widget
* Image-based category card display
*/
if (!defined('ABSPATH')) {
exit;
}
class CannaIQ_Card_Category_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'cannaiq_card_category';
}
public function get_title() {
return __('CannaiQ Category Card', 'cannaiq-menus');
}
public function get_icon() {
return 'eicon-image-box';
}
public function get_categories() {
return ['cannaiq-templates'];
}
public function get_keywords() {
return ['cannaiq', 'category', 'card', 'image'];
}
protected function register_controls() {
// Content Section
$this->start_controls_section(
'content_section',
[
'label' => __('Content', 'cannaiq-menus'),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$this->add_control(
'category_name',
[
'label' => __('Category Name', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => 'Flower',
'placeholder' => __('Category name...', 'cannaiq-menus'),
]
);
$this->add_control(
'category_image',
[
'label' => __('Category Image', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::MEDIA,
'default' => [
'url' => '',
],
]
);
$this->add_control(
'link_url',
[
'label' => __('Link URL', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::URL,
'placeholder' => __('/products?category=flower', 'cannaiq-menus'),
'default' => [
'url' => '#',
],
]
);
$this->add_control(
'show_product_count',
[
'label' => __('Show Product Count', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'return_value' => 'yes',
'default' => 'no',
]
);
$this->add_control(
'product_count',
[
'label' => __('Product Count', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::NUMBER,
'default' => 0,
'condition' => [
'show_product_count' => 'yes',
],
]
);
$this->end_controls_section();
// Style Section
$this->start_controls_section(
'style_section',
[
'label' => __('Style', 'cannaiq-menus'),
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'card_size',
[
'label' => __('Card Size', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'medium',
'options' => [
'small' => __('Small (120px)', 'cannaiq-menus'),
'medium' => __('Medium (160px)', 'cannaiq-menus'),
'large' => __('Large (200px)', 'cannaiq-menus'),
],
]
);
$this->add_control(
'card_background',
[
'label' => __('Card Background', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#ffffff',
]
);
$this->add_control(
'border_color',
[
'label' => __('Border Color', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#e5e7eb',
]
);
$this->add_control(
'hover_border_color',
[
'label' => __('Hover Border Color', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#22c55e',
]
);
$this->add_control(
'text_color',
[
'label' => __('Text Color', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::COLOR,
'default' => '#1f2937',
]
);
$this->add_control(
'border_radius',
[
'label' => __('Border Radius', 'cannaiq-menus'),
'type' => \Elementor\Controls_Manager::SLIDER,
'size_units' => ['px'],
'range' => [
'px' => [
'min' => 0,
'max' => 30,
],
],
'default' => [
'size' => 12,
],
]
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
$sizes = [
'small' => '120px',
'medium' => '160px',
'large' => '200px',
];
$size = $sizes[$settings['card_size']] ?? '160px';
$bg_color = $settings['card_background'];
$border_color = $settings['border_color'];
$hover_border = $settings['hover_border_color'];
$text_color = $settings['text_color'];
$radius = $settings['border_radius']['size'] . 'px';
$url = $settings['link_url']['url'] ?? '#';
$target = !empty($settings['link_url']['is_external']) ? '_blank' : '_self';
$widget_id = $this->get_id();
?>
<style>
#cannaiq-cat-<?php echo esc_attr($widget_id); ?>:hover {
border-color: <?php echo esc_attr($hover_border); ?> !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
</style>
<a href="<?php echo esc_url($url); ?>"
target="<?php echo esc_attr($target); ?>"
id="cannaiq-cat-<?php echo esc_attr($widget_id); ?>"
class="cannaiq-category-card"
style="
display: block;
width: <?php echo esc_attr($size); ?>;
background: <?php echo esc_attr($bg_color); ?>;
border: 2px solid <?php echo esc_attr($border_color); ?>;
border-radius: <?php echo esc_attr($radius); ?>;
padding: 16px;
text-align: center;
text-decoration: none;
transition: all 0.2s ease;
">
<div class="cannaiq-cat-name" style="
font-weight: 600;
font-size: 16px;
color: <?php echo esc_attr($text_color); ?>;
margin-bottom: 12px;
">
<?php echo esc_html($settings['category_name']); ?>
<?php if ($settings['show_product_count'] === 'yes' && $settings['product_count'] > 0): ?>
<span style="font-weight: 400; color: #6b7280; font-size: 14px;">
(<?php echo esc_html($settings['product_count']); ?>)
</span>
<?php endif; ?>
</div>
<?php if (!empty($settings['category_image']['url'])): ?>
<div class="cannaiq-cat-image">
<img src="<?php echo esc_url($settings['category_image']['url']); ?>"
alt="<?php echo esc_attr($settings['category_name']); ?>"
style="
max-width: 100%;
height: auto;
max-height: 80px;
object-fit: contain;
" />
</div>
<?php endif; ?>
</a>
<?php
}
}