From 948a732dd5bdb409ce04a095459fa84d90361285 Mon Sep 17 00:00:00 2001 From: Kelly Date: Mon, 8 Dec 2025 00:24:43 -0700 Subject: [PATCH] feat: Rename WordPress plugin to CannaIQ Menus v1.5.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename plugin from Crawlsy Menus to CannaIQ Menus - Update version to 1.5.3 - Update text domain to cannaiq-menus - Rename all CSS classes from crawlsy-* to cannaiq-* - Update shortcodes to [cannaiq_products] and [cannaiq_product] - Add backward compatibility for legacy shortcodes - Update download links on Home and LandingPage - Fix health panel Redis timeout issue - Add clear error message when backend not running 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../public/downloads/cannaiq-menus-1.5.3.zip | Bin 0 -> 13515 bytes cannaiq/src/pages/Home.tsx | 4 +- cannaiq/src/pages/LandingPage.tsx | 12 +- findadispo/frontend/.env.example | 2 +- wordpress-plugin/assets/css/cannaiq-menus.css | 314 ++++++++++++++ wordpress-plugin/assets/js/cannaiq-menus.js | 58 +++ wordpress-plugin/build-plugin.sh | 23 +- wordpress-plugin/cannaiq-menus.php | 402 ++++++++++++++++++ wordpress-plugin/templates/product-grid.php | 47 +- wordpress-plugin/templates/single-product.php | 87 ++-- wordpress-plugin/widgets/product-grid.php | 131 +++--- wordpress-plugin/widgets/single-product.php | 171 ++++---- 12 files changed, 1010 insertions(+), 241 deletions(-) create mode 100644 backend/public/downloads/cannaiq-menus-1.5.3.zip create mode 100644 wordpress-plugin/assets/css/cannaiq-menus.css create mode 100644 wordpress-plugin/assets/js/cannaiq-menus.js create mode 100644 wordpress-plugin/cannaiq-menus.php diff --git a/backend/public/downloads/cannaiq-menus-1.5.3.zip b/backend/public/downloads/cannaiq-menus-1.5.3.zip new file mode 100644 index 0000000000000000000000000000000000000000..f605c0d6c7fef7408cf8a57ed43325bd5e6028e3 GIT binary patch literal 13515 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 @@ -497,7 +497,7 @@ export function Home() { diff --git a/cannaiq/src/pages/LandingPage.tsx b/cannaiq/src/pages/LandingPage.tsx index 26607a05..36b6c388 100644 --- a/cannaiq/src/pages/LandingPage.tsx +++ b/cannaiq/src/pages/LandingPage.tsx @@ -23,7 +23,7 @@ export default function LandingPage() { Sign In - + Download WordPress Plugin @@ -74,20 +74,20 @@ export default function LandingPage() {

WordPress Integration

- Display your dispensary menu directly on your WordPress site with our Crawlsy Menus plugin. + Display your dispensary menu directly on your WordPress site with our CannaIQ Menus plugin. Features Elementor widgets and shortcode support for maximum flexibility.

// Display products grid
-
[crawlsy_products limit="12" columns="3"]
+
[cannaiq_products limit="12" columns="3"]
// Display single product
-
[crawlsy_product id="123"]
+
[cannaiq_product id="123"]
- Download Crawlsy Menus v1.3.0 + Download CannaIQ Menus v1.5.3
diff --git a/findadispo/frontend/.env.example b/findadispo/frontend/.env.example index 48001a73..3e620216 100644 --- a/findadispo/frontend/.env.example +++ b/findadispo/frontend/.env.example @@ -3,7 +3,7 @@ # API URL for dispensary data endpoints (public API) # Local development: http://localhost:3010 -# Production: https://dispos.crawlsy.com (or your production API URL) +# Production: https://cannaiq.co (or your production API URL) REACT_APP_DATA_API_URL=http://localhost:3010 # API Key for accessing the /api/v1/* endpoints diff --git a/wordpress-plugin/assets/css/cannaiq-menus.css b/wordpress-plugin/assets/css/cannaiq-menus.css new file mode 100644 index 00000000..6768181b --- /dev/null +++ b/wordpress-plugin/assets/css/cannaiq-menus.css @@ -0,0 +1,314 @@ +/** + * CannaIQ Menus - WordPress Plugin Styles + * v1.5.3 + */ + +/* Product Grid */ +.cannaiq-product-grid { + display: grid; + gap: 24px; + margin: 20px 0; +} + +.cannaiq-grid-cols-2 { + grid-template-columns: repeat(2, 1fr); +} + +.cannaiq-grid-cols-3 { + grid-template-columns: repeat(3, 1fr); +} + +.cannaiq-grid-cols-4 { + grid-template-columns: repeat(4, 1fr); +} + +.cannaiq-grid-cols-6 { + grid-template-columns: repeat(6, 1fr); +} + +@media (max-width: 1024px) { + .cannaiq-grid-cols-4, + .cannaiq-grid-cols-6 { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (max-width: 768px) { + .cannaiq-grid-cols-3, + .cannaiq-grid-cols-4, + .cannaiq-grid-cols-6 { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 480px) { + .cannaiq-product-grid { + grid-template-columns: 1fr; + } +} + +/* Product Card */ +.cannaiq-product-card { + background: #fff; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.2s, box-shadow 0.2s; + display: flex; + flex-direction: column; +} + +.cannaiq-product-card:hover { + transform: translateY(-4px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); +} + +.cannaiq-product-image { + width: 100%; + aspect-ratio: 1; + overflow: hidden; + background: #f5f5f5; +} + +.cannaiq-product-image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.cannaiq-product-content { + padding: 16px; + flex: 1; + display: flex; + flex-direction: column; +} + +.cannaiq-product-title { + font-size: 18px; + font-weight: 600; + margin: 0 0 8px 0; + color: #333; + line-height: 1.4; +} + +.cannaiq-product-brand { + font-size: 14px; + color: #666; + margin-bottom: 8px; +} + +.cannaiq-product-description { + font-size: 14px; + color: #666; + line-height: 1.6; + margin: 0 0 12px 0; +} + +.cannaiq-product-meta { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin: 12px 0; +} + +.cannaiq-meta-item { + font-size: 13px; + padding: 4px 10px; + border-radius: 4px; + background: #f0f0f0; +} + +.cannaiq-meta-item strong { + color: #333; +} + +.cannaiq-thc { + background: #e8f5e9; + color: #2e7d32; +} + +.cannaiq-cbd { + background: #e3f2fd; + color: #1565c0; +} + +.cannaiq-product-price { + font-size: 20px; + font-weight: 700; + color: #2e7d32; + margin-top: auto; + padding-top: 12px; +} + +.cannaiq-price-sale { + color: #c62828; +} + +.cannaiq-price-regular.cannaiq-strikethrough { + text-decoration: line-through; + color: #999; + font-size: 16px; + margin-left: 8px; +} + +.cannaiq-out-of-stock { + display: inline-block; + padding: 6px 12px; + background: #ffebee; + color: #c62828; + font-size: 13px; + font-weight: 600; + border-radius: 4px; + margin-top: 8px; +} + +/* Single Product */ +.cannaiq-single-product { + background: #fff; + border-radius: 8px; + padding: 24px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + margin: 20px 0; +} + +.cannaiq-layout-horizontal { + display: grid; + grid-template-columns: 400px 1fr; + gap: 32px; +} + +.cannaiq-layout-vertical { + display: flex; + flex-direction: column; + gap: 24px; +} + +@media (max-width: 768px) { + .cannaiq-layout-horizontal { + grid-template-columns: 1fr; + } +} + +.cannaiq-single-product-image { + width: 100%; + aspect-ratio: 1; + overflow: hidden; + border-radius: 8px; + background: #f5f5f5; +} + +.cannaiq-single-product-image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.cannaiq-single-product-details { + display: flex; + flex-direction: column; + gap: 16px; +} + +.cannaiq-single-product-title { + font-size: 28px; + font-weight: 700; + margin: 0; + color: #333; + line-height: 1.3; +} + +.cannaiq-single-product-brand { + font-size: 16px; + color: #666; +} + +.cannaiq-single-product-brand strong { + color: #333; +} + +.cannaiq-single-product-description { + font-size: 16px; + line-height: 1.6; + color: #555; +} + +.cannaiq-single-product-info { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + padding: 16px; + background: #f8f8f8; + border-radius: 8px; +} + +.cannaiq-info-item { + display: flex; + flex-direction: column; + gap: 4px; +} + +.cannaiq-info-item strong { + font-size: 14px; + color: #333; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.cannaiq-info-item span { + font-size: 16px; + color: #555; +} + +.cannaiq-info-item.cannaiq-terpenes span, +.cannaiq-info-item.cannaiq-effects span, +.cannaiq-info-item.cannaiq-flavors span { + font-size: 14px; +} + +.cannaiq-single-product-price { + font-size: 32px; + font-weight: 700; + color: #2e7d32; +} + +/* Loading State */ +.cannaiq-loading { + text-align: center; + padding: 40px; + color: #666; +} + +.cannaiq-loading:after { + content: "..."; + animation: cannaiq-dots 1.5s steps(4, end) infinite; +} + +@keyframes cannaiq-dots { + 0%, 20% { + color: rgba(0, 0, 0, 0); + text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), + 0.5em 0 0 rgba(0, 0, 0, 0); + } + 40% { + color: #666; + text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), + 0.5em 0 0 rgba(0, 0, 0, 0); + } + 60% { + text-shadow: 0.25em 0 0 #666, + 0.5em 0 0 rgba(0, 0, 0, 0); + } + 80%, 100% { + text-shadow: 0.25em 0 0 #666, + 0.5em 0 0 #666; + } +} + +/* Error State */ +.cannaiq-error { + padding: 16px; + background: #ffebee; + color: #c62828; + border-radius: 4px; + border-left: 4px solid #c62828; +} diff --git a/wordpress-plugin/assets/js/cannaiq-menus.js b/wordpress-plugin/assets/js/cannaiq-menus.js new file mode 100644 index 00000000..a4990a70 --- /dev/null +++ b/wordpress-plugin/assets/js/cannaiq-menus.js @@ -0,0 +1,58 @@ +/** + * CannaIQ Menus - WordPress Plugin JavaScript + * v1.5.3 + */ + +(function($) { + 'use strict'; + + /** + * Initialize plugin + */ + $(document).ready(function() { + // 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').forEach(card => { + cardObserver.observe(card); + }); + } + }); + +})(jQuery); diff --git a/wordpress-plugin/build-plugin.sh b/wordpress-plugin/build-plugin.sh index 1bdf7b00..780992c8 100755 --- a/wordpress-plugin/build-plugin.sh +++ b/wordpress-plugin/build-plugin.sh @@ -1,23 +1,23 @@ #!/bin/bash # WordPress Plugin Build Script -# Builds the plugin zip with the correct naming convention: cb-wpmenu-{version}.zip +# Builds the plugin zip with the correct naming convention: cannaiq-menus-{version}.zip set -e # Get the version from the main plugin file -VERSION=$(grep -oP "Version:\s*\K[0-9.]+" crawlsy-menus.php) +VERSION=$(grep -oP "Version:\s*\K[0-9.]+" cannaiq-menus.php) if [ -z "$VERSION" ]; then - echo "Error: Could not extract version from crawlsy-menus.php" + echo "Error: Could not extract version from cannaiq-menus.php" exit 1 fi # Define paths PLUGIN_DIR="$(cd "$(dirname "$0")" && pwd)" OUTPUT_DIR="${PLUGIN_DIR}/../backend/public/downloads" -OUTPUT_FILE="cb-wpmenu-${VERSION}.zip" +OUTPUT_FILE="cannaiq-menus-${VERSION}.zip" -echo "Building WordPress plugin..." +echo "Building CannaIQ Menus WordPress plugin..." echo " Version: ${VERSION}" echo " Output: ${OUTPUT_DIR}/${OUTPUT_FILE}" @@ -27,13 +27,18 @@ mkdir -p "${OUTPUT_DIR}" # Create the zip file (from the plugin directory) cd "${PLUGIN_DIR}" rm -f "${OUTPUT_DIR}/${OUTPUT_FILE}" -zip -r "${OUTPUT_DIR}/${OUTPUT_FILE}" . -x "*.git*" -x "build-plugin.sh" + +# Exclude old/legacy files and build script +zip -r "${OUTPUT_DIR}/${OUTPUT_FILE}" . \ + -x "*.git*" \ + -x "build-plugin.sh" \ + -x "crawlsy-menus.php" \ + -x "assets/css/crawlsy-menus.css" \ + -x "assets/js/crawlsy-menus.js" echo "" echo "Build complete!" echo " File: ${OUTPUT_DIR}/${OUTPUT_FILE}" echo " Size: $(ls -lh "${OUTPUT_DIR}/${OUTPUT_FILE}" | awk '{print $5}')" echo "" -echo "IMPORTANT: Update frontend/src/pages/LandingPage.tsx with the new version:" -echo " href=\"/downloads/cb-wpmenu-${VERSION}.zip\"" -echo " Download Plugin v${VERSION}" +echo "Download URL: https://cannaiq.co/downloads/cannaiq-menus-${VERSION}.zip" diff --git a/wordpress-plugin/cannaiq-menus.php b/wordpress-plugin/cannaiq-menus.php new file mode 100644 index 00000000..ebbaa4b2 --- /dev/null +++ b/wordpress-plugin/cannaiq-menus.php @@ -0,0 +1,402 @@ +register(new \CannaIQ_Menus_Product_Grid_Widget()); + $widgets_manager->register(new \CannaIQ_Menus_Single_Product_Widget()); + } + + /** + * Enqueue Scripts and Styles + */ + public function enqueue_scripts() { + wp_enqueue_style( + 'cannaiq-menus-styles', + CANNAIQ_MENUS_PLUGIN_URL . 'assets/css/cannaiq-menus.css', + [], + CANNAIQ_MENUS_VERSION + ); + + wp_enqueue_script( + 'cannaiq-menus-script', + CANNAIQ_MENUS_PLUGIN_URL . 'assets/js/cannaiq-menus.js', + ['jquery'], + CANNAIQ_MENUS_VERSION, + true + ); + } + + /** + * Add Admin Menu + */ + public function add_admin_menu() { + add_menu_page( + 'CannaIQ Menus', + 'CannaIQ Menus', + 'manage_options', + 'cannaiq-menus', + [$this, 'admin_page'], + 'dashicons-products', + 30 + ); + } + + /** + * Register Plugin Settings + */ + public function register_settings() { + register_setting('cannaiq_menus_settings', 'cannaiq_api_token'); + + // Migrate old settings if they exist + $old_crawlsy_token = get_option('crawlsy_api_token'); + $old_dutchie_token = get_option('dutchie_api_token'); + + if (!get_option('cannaiq_api_token')) { + if ($old_crawlsy_token) { + update_option('cannaiq_api_token', $old_crawlsy_token); + } elseif ($old_dutchie_token) { + update_option('cannaiq_api_token', $old_dutchie_token); + } + } + } + + /** + * Admin Page + */ + public function admin_page() { + ?> +
+

CannaIQ Menus Settings

+

Version by CannaIQ

+

Display real-time cannabis menus with data updated daily from CannaIQ.

+ +
+ + + + + + + + +
+ +

Your authentication token from the CannaIQ admin dashboard. The token includes your store configuration.

+
+ + +
+ +
+ +

Test Connection

+ +
+ + + +
+ +

Usage

+

Shortcodes

+ + + + + + + + + + + + + + + + + +
ShortcodeDescription
[cannaiq_products]Display a grid of products. Options: category_id, limit, columns, in_stock
[cannaiq_product id="123"]Display a single product by ID
+ +

Elementor Widgets

+

If you have Elementor installed, you can use the CannaIQ widgets:

+
    +
  • CannaIQ Product Grid - Display a grid of products with filtering options
  • +
  • CannaIQ Single Product - Display a single product card
  • +
+
+ '', + 'limit' => 12, + 'columns' => 3, + 'in_stock' => 'true' + ], $atts); + + $products = $this->fetch_products($atts); + + if (!$products) { + return '

No products found.

'; + } + + ob_start(); + include CANNAIQ_MENUS_PLUGIN_DIR . 'templates/product-grid.php'; + return ob_get_clean(); + } + + /** + * Single Product Shortcode + */ + public function single_product_shortcode($atts) { + $atts = shortcode_atts([ + 'id' => 0 + ], $atts); + + if (!$atts['id']) { + return '

Product ID required.

'; + } + + $product = $this->fetch_product($atts['id']); + + if (!$product) { + return '

Product not found.

'; + } + + ob_start(); + include CANNAIQ_MENUS_PLUGIN_DIR . 'templates/single-product.php'; + return ob_get_clean(); + } + + /** + * Fetch Products from API + */ + public function fetch_products($args = []) { + $api_token = get_option('cannaiq_api_token'); + + if (!$api_token) { + return false; + } + + $query_args = http_build_query($args); + $url = CANNAIQ_MENUS_API_URL . '/products?' . $query_args; + + $response = wp_remote_get($url, [ + 'headers' => [ + 'X-API-Key' => $api_token + ], + 'timeout' => 30 + ]); + + if (is_wp_error($response)) { + return false; + } + + $body = wp_remote_retrieve_body($response); + $data = json_decode($body, true); + + return $data['products'] ?? false; + } + + /** + * Fetch Single Product from API + */ + public function fetch_product($id) { + $api_token = get_option('cannaiq_api_token'); + + if (!$api_token) { + return false; + } + + $url = CANNAIQ_MENUS_API_URL . '/products/' . intval($id); + + $response = wp_remote_get($url, [ + 'headers' => [ + 'X-API-Key' => $api_token + ], + 'timeout' => 30 + ]); + + if (is_wp_error($response)) { + return false; + } + + $body = wp_remote_retrieve_body($response); + $data = json_decode($body, true); + + return $data['product'] ?? false; + } +} + +// Initialize Plugin +function cannaiq_menus() { + return CannaIQ_Menus_Plugin::instance(); +} + +cannaiq_menus(); diff --git a/wordpress-plugin/templates/product-grid.php b/wordpress-plugin/templates/product-grid.php index a9b2f178..78b6d9fe 100644 --- a/wordpress-plugin/templates/product-grid.php +++ b/wordpress-plugin/templates/product-grid.php @@ -10,62 +10,67 @@ if (!defined('ABSPATH')) { $columns = isset($atts['columns']) ? $atts['columns'] : 3; ?> -
+
-
onclick="window.open('', '_blank')" style="cursor: ;"> -
+
<?php echo esc_attr($product['name']); ?>
-
-

+
+

-
+
-

+

-
- - - THC: +
+ + + THC: % - - - CBD: + + + CBD: %
- -
- $ + +
+ + $ + $ + + $ +
-
- +
+
diff --git a/wordpress-plugin/templates/single-product.php b/wordpress-plugin/templates/single-product.php index bc92e22c..40024844 100644 --- a/wordpress-plugin/templates/single-product.php +++ b/wordpress-plugin/templates/single-product.php @@ -6,92 +6,77 @@ if (!defined('ABSPATH')) { exit; } + +$image_url = $product['image_url'] ?? ''; ?> -
- -
- + +
+ <?php echo esc_attr($product['name']); ?>
-
-

+
+

-
- +
+
-
+
-
- -
- - +
+ +
+ + %
- -
- - + +
+ + %
- -
- - - + +
+ + + - - -
- - - -
- - - - - - -
- - - -
- - - - - +
- -
- $ + +
+ + $ + $ + + $ +
-
- +
+
diff --git a/wordpress-plugin/widgets/product-grid.php b/wordpress-plugin/widgets/product-grid.php index 84499c9d..e92f4ad3 100644 --- a/wordpress-plugin/widgets/product-grid.php +++ b/wordpress-plugin/widgets/product-grid.php @@ -7,14 +7,14 @@ if (!defined('ABSPATH')) { exit; } -class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { +class CannaIQ_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { public function get_name() { - return 'crawlsy_product_grid'; + return 'cannaiq_product_grid'; } public function get_title() { - return __('Crawlsy Product Grid', 'crawlsy-menus'); + return __('CannaIQ Product Grid', 'cannaiq-menus'); } public function get_icon() { @@ -31,7 +31,7 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'content_section', [ - 'label' => __('Content', 'crawlsy-menus'), + 'label' => __('Content', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); @@ -39,9 +39,9 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'store_id', [ - 'label' => __('Store ID', 'crawlsy-menus'), + 'label' => __('Store ID', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::NUMBER, - 'default' => get_option('crawlsy_default_store_id', 1), + 'default' => get_option('cannaiq_default_store_id', 1), 'min' => 1, ] ); @@ -49,17 +49,17 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'category_id', [ - 'label' => __('Category ID', 'crawlsy-menus'), + 'label' => __('Category ID', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::NUMBER, 'default' => '', - 'description' => __('Leave empty to show all categories', 'crawlsy-menus'), + 'description' => __('Leave empty to show all categories', 'cannaiq-menus'), ] ); $this->add_control( 'limit', [ - 'label' => __('Number of Products', 'crawlsy-menus'), + 'label' => __('Number of Products', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::NUMBER, 'default' => 12, 'min' => 1, @@ -70,14 +70,14 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'columns', [ - 'label' => __('Columns', 'crawlsy-menus'), + 'label' => __('Columns', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SELECT, 'default' => '3', 'options' => [ - '2' => __('2 Columns', 'crawlsy-menus'), - '3' => __('3 Columns', 'crawlsy-menus'), - '4' => __('4 Columns', 'crawlsy-menus'), - '6' => __('6 Columns', 'crawlsy-menus'), + '2' => __('2 Columns', 'cannaiq-menus'), + '3' => __('3 Columns', 'cannaiq-menus'), + '4' => __('4 Columns', 'cannaiq-menus'), + '6' => __('6 Columns', 'cannaiq-menus'), ], ] ); @@ -85,10 +85,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'in_stock_only', [ - 'label' => __('In Stock Only', 'crawlsy-menus'), + 'label' => __('In Stock Only', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -97,10 +97,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'search', [ - 'label' => __('Search Filter', 'crawlsy-menus'), + 'label' => __('Search Filter', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', - 'description' => __('Filter products by name', 'crawlsy-menus'), + 'description' => __('Filter products by name', 'cannaiq-menus'), ] ); @@ -110,7 +110,7 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'display_section', [ - 'label' => __('Display Options', 'crawlsy-menus'), + 'label' => __('Display Options', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); @@ -118,10 +118,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_image', [ - 'label' => __('Show Image', 'crawlsy-menus'), + 'label' => __('Show Image', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -130,10 +130,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_price', [ - 'label' => __('Show Price', 'crawlsy-menus'), + 'label' => __('Show Price', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -142,10 +142,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_thc', [ - 'label' => __('Show THC', 'crawlsy-menus'), + 'label' => __('Show THC', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -154,10 +154,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_cbd', [ - 'label' => __('Show CBD', 'crawlsy-menus'), + 'label' => __('Show CBD', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -166,10 +166,10 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_description', [ - 'label' => __('Show Description', 'crawlsy-menus'), + 'label' => __('Show Description', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'no', ] @@ -181,7 +181,7 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'style_section', [ - 'label' => __('Style', 'crawlsy-menus'), + 'label' => __('Style', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ] ); @@ -189,11 +189,11 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'card_background', [ - 'label' => __('Card Background', 'crawlsy-menus'), + 'label' => __('Card Background', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#ffffff', 'selectors' => [ - '{{WRAPPER}} .crawlsy-product-card' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .cannaiq-product-card' => 'background-color: {{VALUE}};', ], ] ); @@ -201,7 +201,7 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'card_border_radius', [ - 'label' => __('Border Radius', 'crawlsy-menus'), + 'label' => __('Border Radius', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SLIDER, 'size_units' => ['px'], 'range' => [ @@ -214,7 +214,7 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { 'size' => 8, ], 'selectors' => [ - '{{WRAPPER}} .crawlsy-product-card' => 'border-radius: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .cannaiq-product-card' => 'border-radius: {{SIZE}}{{UNIT}};', ], ] ); @@ -222,11 +222,11 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $this->add_control( 'text_color', [ - 'label' => __('Text Color', 'crawlsy-menus'), + 'label' => __('Text Color', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#333333', 'selectors' => [ - '{{WRAPPER}} .crawlsy-product-card' => 'color: {{VALUE}};', + '{{WRAPPER}} .cannaiq-product-card' => 'color: {{VALUE}};', ], ] ); @@ -251,66 +251,71 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base { $args['search'] = $settings['search']; } - $plugin = Crawlsy_Menus_Plugin::instance(); + $plugin = CannaIQ_Menus_Plugin::instance(); $products = $plugin->fetch_products($args); if (!$products) { - echo '

' . __('No products found.', 'crawlsy-menus') . '

'; + echo '

' . __('No products found.', 'cannaiq-menus') . '

'; return; } $columns = $settings['columns']; ?> -
+
-
onclick="window.open('', '_blank')" style="cursor: ;"> -
+
<?php echo esc_attr($product['name']); ?>
-
-

+
+

-

+

-
- - - THC: +
+ + + THC: % - - - CBD: + + + CBD: %
- -
- $ + +
+ + $ + $ + + $ +
-
- +
+
diff --git a/wordpress-plugin/widgets/single-product.php b/wordpress-plugin/widgets/single-product.php index 9d7bcaeb..4a23e8d4 100644 --- a/wordpress-plugin/widgets/single-product.php +++ b/wordpress-plugin/widgets/single-product.php @@ -7,14 +7,14 @@ if (!defined('ABSPATH')) { exit; } -class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { +class CannaIQ_Menus_Single_Product_Widget extends \Elementor\Widget_Base { public function get_name() { - return 'crawlsy_single_product'; + return 'cannaiq_single_product'; } public function get_title() { - return __('Crawlsy Single Product', 'crawlsy-menus'); + return __('CannaIQ Single Product', 'cannaiq-menus'); } public function get_icon() { @@ -31,9 +31,9 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { * @return array Associative array of product_id => product_name */ protected function get_products_for_select() { - $options = ['' => __('-- Select a Product --', 'crawlsy-menus')]; + $options = ['' => __('-- Select a Product --', 'cannaiq-menus')]; - $plugin = Crawlsy_Menus_Plugin::instance(); + $plugin = CannaIQ_Menus_Plugin::instance(); $products = $plugin->fetch_products(['limit' => 500]); if ($products && is_array($products)) { @@ -58,7 +58,7 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'content_section', [ - 'label' => __('Content', 'crawlsy-menus'), + 'label' => __('Content', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); @@ -69,22 +69,22 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'product_id', [ - 'label' => __('Select Product', 'crawlsy-menus'), + 'label' => __('Select Product', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SELECT2, 'options' => $products_options, 'default' => '', 'label_block' => true, - 'description' => __('Search and select a product to display', 'crawlsy-menus'), + 'description' => __('Search and select a product to display', 'cannaiq-menus'), ] ); $this->add_control( 'product_id_manual', [ - 'label' => __('Or Enter Product ID', 'crawlsy-menus'), + 'label' => __('Or Enter Product ID', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::NUMBER, 'default' => '', - 'description' => __('Manually enter a product ID if not found in dropdown', 'crawlsy-menus'), + 'description' => __('Manually enter a product ID if not found in dropdown', 'cannaiq-menus'), 'condition' => [ 'product_id' => '', ], @@ -94,12 +94,12 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'layout', [ - 'label' => __('Layout', 'crawlsy-menus'), + 'label' => __('Layout', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SELECT, 'default' => 'horizontal', 'options' => [ - 'horizontal' => __('Horizontal', 'crawlsy-menus'), - 'vertical' => __('Vertical', 'crawlsy-menus'), + 'horizontal' => __('Horizontal', 'cannaiq-menus'), + 'vertical' => __('Vertical', 'cannaiq-menus'), ], ] ); @@ -110,7 +110,7 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'display_section', [ - 'label' => __('Display Options', 'crawlsy-menus'), + 'label' => __('Display Options', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); @@ -118,10 +118,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_image', [ - 'label' => __('Show Image', 'crawlsy-menus'), + 'label' => __('Show Image', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -130,10 +130,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_price', [ - 'label' => __('Show Price', 'crawlsy-menus'), + 'label' => __('Show Price', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -142,10 +142,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_description', [ - 'label' => __('Show Description', 'crawlsy-menus'), + 'label' => __('Show Description', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -154,10 +154,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_thc', [ - 'label' => __('Show THC', 'crawlsy-menus'), + 'label' => __('Show THC', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -166,10 +166,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_cbd', [ - 'label' => __('Show CBD', 'crawlsy-menus'), + 'label' => __('Show CBD', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -178,10 +178,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_terpenes', [ - 'label' => __('Show Terpenes', 'crawlsy-menus'), + 'label' => __('Show Terpenes', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -190,10 +190,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_effects', [ - 'label' => __('Show Effects', 'crawlsy-menus'), + 'label' => __('Show Effects', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -202,10 +202,10 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'show_brand', [ - 'label' => __('Show Brand', 'crawlsy-menus'), + 'label' => __('Show Brand', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SWITCHER, - 'label_on' => __('Yes', 'crawlsy-menus'), - 'label_off' => __('No', 'crawlsy-menus'), + 'label_on' => __('Yes', 'cannaiq-menus'), + 'label_off' => __('No', 'cannaiq-menus'), 'return_value' => 'yes', 'default' => 'yes', ] @@ -217,7 +217,7 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->start_controls_section( 'style_section', [ - 'label' => __('Style', 'crawlsy-menus'), + 'label' => __('Style', 'cannaiq-menus'), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ] ); @@ -225,11 +225,11 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'background_color', [ - 'label' => __('Background Color', 'crawlsy-menus'), + 'label' => __('Background Color', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#ffffff', 'selectors' => [ - '{{WRAPPER}} .crawlsy-single-product' => 'background-color: {{VALUE}};', + '{{WRAPPER}} .cannaiq-single-product' => 'background-color: {{VALUE}};', ], ] ); @@ -237,7 +237,7 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'border_radius', [ - 'label' => __('Border Radius', 'crawlsy-menus'), + 'label' => __('Border Radius', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::SLIDER, 'size_units' => ['px'], 'range' => [ @@ -250,7 +250,7 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { 'size' => 8, ], 'selectors' => [ - '{{WRAPPER}} .crawlsy-single-product' => 'border-radius: {{SIZE}}{{UNIT}};', + '{{WRAPPER}} .cannaiq-single-product' => 'border-radius: {{SIZE}}{{UNIT}};', ], ] ); @@ -258,11 +258,11 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $this->add_control( 'text_color', [ - 'label' => __('Text Color', 'crawlsy-menus'), + 'label' => __('Text Color', 'cannaiq-menus'), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#333333', 'selectors' => [ - '{{WRAPPER}} .crawlsy-single-product' => 'color: {{VALUE}};', + '{{WRAPPER}} .cannaiq-single-product' => 'color: {{VALUE}};', ], ] ); @@ -277,93 +277,88 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base { $product_id = !empty($settings['product_id']) ? $settings['product_id'] : $settings['product_id_manual']; if (empty($product_id)) { - echo '

' . __('Please select or enter a product ID.', 'crawlsy-menus') . '

'; + echo '

' . __('Please select or enter a product ID.', 'cannaiq-menus') . '

'; return; } - $plugin = Crawlsy_Menus_Plugin::instance(); + $plugin = CannaIQ_Menus_Plugin::instance(); $product = $plugin->fetch_product($product_id); if (!$product) { - echo '

' . __('Product not found.', 'crawlsy-menus') . '

'; + echo '

' . __('Product not found.', 'cannaiq-menus') . '

'; return; } $layout = $settings['layout']; + $image_url = $product['image_url'] ?? ''; ?> -
- -
- + +
+ <?php echo esc_attr($product['name']); ?>
-
-

+
+

-
- +
+
-
+
-
- -
- - +
+ +
+ + %
- -
- - + +
+ + %
- -
- - - + +
+ + + - - -
- - - -
- - - - - +
- -
- $ + +
+ + $ + $ + + $ +
-
- +
+