feat: WordPress plugin v2.0.0 - modular component library
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Add 14 new shortcodes for building custom product cards - Add visual builder guide with Hot Lava example in admin page - Add comprehensive component documentation - Update branding to "CannaiQ" throughout - Include layout shortcodes: specials, brands, categories - Include component shortcodes: discount_badge, strain_badge, thc, cbd, effects, price, cart_button, stock, terpenes - Add build steps and instructions for assembling cards 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -69,9 +69,23 @@ class CannaIQ_Menus_Plugin {
|
||||
require_once CANNAIQ_MENUS_PLUGIN_DIR . 'widgets/dynamic-tags-extended.php';
|
||||
}
|
||||
|
||||
// Register shortcodes - primary CannaIQ shortcodes
|
||||
// Register shortcodes - primary CannaiQ shortcodes
|
||||
add_shortcode('cannaiq_products', [$this, 'products_shortcode']);
|
||||
add_shortcode('cannaiq_product', [$this, 'single_product_shortcode']);
|
||||
add_shortcode('cannaiq_specials', [$this, 'specials_shortcode']);
|
||||
add_shortcode('cannaiq_brands', [$this, 'brands_shortcode']);
|
||||
add_shortcode('cannaiq_categories', [$this, 'categories_shortcode']);
|
||||
|
||||
// Component shortcodes (v2.0)
|
||||
add_shortcode('cannaiq_discount_badge', [$this, 'discount_badge_shortcode']);
|
||||
add_shortcode('cannaiq_strain_badge', [$this, 'strain_badge_shortcode']);
|
||||
add_shortcode('cannaiq_thc', [$this, 'thc_shortcode']);
|
||||
add_shortcode('cannaiq_cbd', [$this, 'cbd_shortcode']);
|
||||
add_shortcode('cannaiq_effects', [$this, 'effects_shortcode']);
|
||||
add_shortcode('cannaiq_price', [$this, 'price_shortcode']);
|
||||
add_shortcode('cannaiq_cart_button', [$this, 'cart_button_shortcode']);
|
||||
add_shortcode('cannaiq_stock', [$this, 'stock_shortcode']);
|
||||
add_shortcode('cannaiq_terpenes', [$this, 'terpenes_shortcode']);
|
||||
|
||||
// DEPRECATED: Legacy shortcode alias for backward compatibility only
|
||||
add_shortcode('crawlsy_products', [$this, 'products_shortcode']);
|
||||
@@ -320,8 +334,9 @@ class CannaIQ_Menus_Plugin {
|
||||
<hr />
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<h3>Shortcodes</h3>
|
||||
<table class="widefat" style="max-width: 800px;">
|
||||
<table class="widefat" style="max-width: 900px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shortcode</th>
|
||||
@@ -331,21 +346,175 @@ class CannaIQ_Menus_Plugin {
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>[cannaiq_products]</code></td>
|
||||
<td>Display a grid of products. Options: <code>category_id</code>, <code>limit</code>, <code>columns</code>, <code>in_stock</code></td>
|
||||
<td>Product grid. Options: <code>category</code>, <code>brand</code>, <code>limit</code>, <code>columns</code>, <code>in_stock</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_product id="123"]</code></td>
|
||||
<td>Display a single product by ID</td>
|
||||
<td>Single product by ID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_specials]</code></td>
|
||||
<td>Products on sale. Options: <code>limit</code>, <code>columns</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_brands]</code></td>
|
||||
<td>Brand grid. Options: <code>limit</code>, <code>columns</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_categories]</code></td>
|
||||
<td>Category list. Options: <code>style</code> (list|grid)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Elementor Widgets</h3>
|
||||
<p>If you have Elementor installed, you can use the CannaiQ widgets:</p>
|
||||
<h4 style="margin-top: 20px;">Component Shortcodes <span style="color: #666; font-weight: normal;">(use inside product context)</span></h4>
|
||||
<table class="widefat" style="max-width: 900px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shortcode</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>[cannaiq_discount_badge]</code></td>
|
||||
<td>Discount ribbon/pill. Options: <code>style</code> (ribbon|pill|text)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_strain_badge]</code></td>
|
||||
<td>Sativa/Indica/Hybrid badge. Options: <code>style</code> (pill|text)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_thc]</code></td>
|
||||
<td>THC percentage. Options: <code>style</code> (meter|badge|pill|text)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_cbd]</code></td>
|
||||
<td>CBD percentage. Options: <code>style</code> (meter|badge|pill|text)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_effects]</code></td>
|
||||
<td>Effect chips with icons. Options: <code>limit</code>, <code>icons</code> (yes|no)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_price]</code></td>
|
||||
<td>Price display. Options: <code>show_original</code> (yes|no), <code>show_weight</code> (yes|no)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_cart_button]</code></td>
|
||||
<td>Add to cart button. Options: <code>text</code>, <code>style</code> (solid|outline)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_stock]</code></td>
|
||||
<td>Stock status. Options: <code>style</code> (badge|text|dot)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>[cannaiq_terpenes]</code></td>
|
||||
<td>Terpene profile. Options: <code>limit</code>, <code>style</code> (chips|list|text)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 style="margin-top: 30px;">Elementor Widgets</h3>
|
||||
<p>With Elementor installed, find CannaiQ widgets in the editor:</p>
|
||||
|
||||
<h4>Layout Widgets</h4>
|
||||
<ul style="list-style: disc; margin-left: 20px;">
|
||||
<li><strong>CannaiQ Product Grid</strong> - Display a grid of products with filtering options</li>
|
||||
<li><strong>CannaiQ Single Product</strong> - Display a single product card</li>
|
||||
<li><strong>Product Grid</strong> - Filterable product grid</li>
|
||||
<li><strong>Product Loop</strong> - Custom loop for building cards</li>
|
||||
<li><strong>Single Product</strong> - Display one product</li>
|
||||
<li><strong>Brand Grid</strong> - Display brands</li>
|
||||
<li><strong>Category List</strong> - Display categories</li>
|
||||
<li><strong>Specials/Deals</strong> - Products on sale</li>
|
||||
</ul>
|
||||
|
||||
<h4>Component Widgets <span style="color: #666; font-weight: normal;">(v2.0)</span></h4>
|
||||
<ul style="list-style: disc; margin-left: 20px;">
|
||||
<li><strong>Discount Ribbon</strong> - Sale percentage badge</li>
|
||||
<li><strong>Strain Badge</strong> - Sativa/Indica/Hybrid pill</li>
|
||||
<li><strong>THC/CBD Meter</strong> - Potency display</li>
|
||||
<li><strong>Effects Display</strong> - Effect chips with icons</li>
|
||||
<li><strong>Price Block</strong> - Price with sale formatting</li>
|
||||
<li><strong>Cart Button</strong> - Styled CTA button</li>
|
||||
<li><strong>Stock Indicator</strong> - Availability badge</li>
|
||||
<li><strong>Product Image + Badges</strong> - Image with overlays</li>
|
||||
</ul>
|
||||
|
||||
<h4>Card Templates <span style="color: #666; font-weight: normal;">(v2.0)</span></h4>
|
||||
<ul style="list-style: disc; margin-left: 20px;">
|
||||
<li><strong>Premium Product Card</strong> - Ready-to-use card with all components</li>
|
||||
</ul>
|
||||
|
||||
<h3 style="margin-top: 30px;">Dynamic Tags</h3>
|
||||
<p>In Elementor, use dynamic tags to insert product data into any widget. Look for "CannaiQ Product" in the dynamic tags menu.</p>
|
||||
|
||||
<hr style="margin: 30px 0;" />
|
||||
|
||||
<h2>How to Build a Product Card</h2>
|
||||
<p>Use the modular components to build custom product cards. Here's an example layout:</p>
|
||||
|
||||
<div style="display: flex; gap: 30px; flex-wrap: wrap; margin-top: 20px;">
|
||||
<!-- Visual Example -->
|
||||
<div style="background: #fff; border: 2px solid #ddd; border-radius: 12px; width: 300px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
||||
<!-- Image area -->
|
||||
<div style="background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%); height: 200px; position: relative;">
|
||||
<span style="position: absolute; top: 0; left: 0; background: #ef4444; color: white; padding: 4px 12px; font-size: 12px; font-weight: bold; border-bottom-right-radius: 8px;">67% OFF</span>
|
||||
<div style="position: absolute; bottom: 8px; left: 8px; display: flex; gap: 4px;">
|
||||
<span style="background: #22c55e; color: white; padding: 2px 8px; border-radius: 999px; font-size: 10px; font-weight: bold;">SATIVA</span>
|
||||
<span style="background: #1f2937; color: white; padding: 2px 8px; border-radius: 999px; font-size: 10px; font-weight: bold;">24.5% THC</span>
|
||||
</div>
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #999; font-size: 48px;">🌿</div>
|
||||
</div>
|
||||
<!-- Body -->
|
||||
<div style="padding: 16px;">
|
||||
<h4 style="margin: 0 0 4px 0; font-size: 18px;">Hot Lava</h4>
|
||||
<p style="margin: 0 0 12px 0; color: #666; font-size: 14px;">by TruInfusion</p>
|
||||
<div style="display: flex; gap: 6px; margin-bottom: 12px;">
|
||||
<span style="background: #fef3c7; border: 1px solid #fcd34d; padding: 2px 8px; border-radius: 999px; font-size: 11px;">😴 Sleepy</span>
|
||||
<span style="background: #dbeafe; border: 1px solid #93c5fd; padding: 2px 8px; border-radius: 999px; font-size: 11px;">😌 Relaxed</span>
|
||||
<span style="background: #fce7f3; border: 1px solid #f9a8d4; padding: 2px 8px; border-radius: 999px; font-size: 11px;">😊 Happy</span>
|
||||
</div>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<span style="color: #999; font-size: 14px;">1/8 oz</span>
|
||||
<span style="color: #999; text-decoration: line-through; margin-left: 8px;">$45.00</span>
|
||||
<span style="color: #dc2626; font-weight: bold; font-size: 18px; margin-left: 8px;">$15.00</span>
|
||||
</div>
|
||||
<div style="background: #1f2937; color: white; text-align: center; padding: 10px; border-radius: 6px; font-weight: bold; font-size: 14px;">ADD TO CART →</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Component Labels -->
|
||||
<div style="flex: 1; min-width: 300px;">
|
||||
<h4 style="margin-top: 0;">Components Used:</h4>
|
||||
<table class="widefat" style="max-width: 400px;">
|
||||
<tr><td style="width: 40%;"><strong>Discount Ribbon</strong></td><td>Top-left corner badge</td></tr>
|
||||
<tr><td><strong>Product Image</strong></td><td>With badge overlays</td></tr>
|
||||
<tr><td><strong>Strain Badge</strong></td><td>Green Sativa pill</td></tr>
|
||||
<tr><td><strong>THC Badge</strong></td><td>Dark potency pill</td></tr>
|
||||
<tr><td><strong>Product Name</strong></td><td>Dynamic tag</td></tr>
|
||||
<tr><td><strong>Brand Name</strong></td><td>Dynamic tag</td></tr>
|
||||
<tr><td><strong>Effects Display</strong></td><td>Colored chips with icons</td></tr>
|
||||
<tr><td><strong>Price Block</strong></td><td>Weight + strikethrough + sale</td></tr>
|
||||
<tr><td><strong>Cart Button</strong></td><td>Links to dispensary menu</td></tr>
|
||||
</table>
|
||||
|
||||
<h4 style="margin-top: 20px;">Build Steps:</h4>
|
||||
<ol style="margin-left: 20px; line-height: 1.8;">
|
||||
<li>Add a <strong>Product Loop</strong> widget</li>
|
||||
<li>Inside the loop, add a container</li>
|
||||
<li>Add <strong>Product Image + Badges</strong> widget</li>
|
||||
<li>Add heading with <strong>Product Name</strong> dynamic tag</li>
|
||||
<li>Add text with <strong>Brand Name</strong> dynamic tag</li>
|
||||
<li>Add <strong>Effects Display</strong> widget</li>
|
||||
<li>Add <strong>Price Block</strong> widget</li>
|
||||
<li>Add <strong>Cart Button</strong> widget</li>
|
||||
</ol>
|
||||
|
||||
<p style="margin-top: 20px; padding: 12px; background: #e7f3ff; border-left: 4px solid #2196f3; border-radius: 4px;">
|
||||
<strong>Tip:</strong> Use the <strong>Premium Product Card</strong> template widget for a ready-to-use version of this layout!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -395,6 +564,321 @@ class CannaIQ_Menus_Plugin {
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specials Shortcode
|
||||
*/
|
||||
public function specials_shortcode($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'limit' => 12,
|
||||
'columns' => 3
|
||||
], $atts);
|
||||
|
||||
$products = $this->fetch_specials($atts);
|
||||
|
||||
if (!$products) {
|
||||
return '<p>No specials found.</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include CANNAIQ_MENUS_PLUGIN_DIR . 'templates/product-grid.php';
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands Shortcode
|
||||
*/
|
||||
public function brands_shortcode($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'limit' => 20,
|
||||
'columns' => 4
|
||||
], $atts);
|
||||
|
||||
$brands = $this->fetch_brands($atts);
|
||||
|
||||
if (!$brands) {
|
||||
return '<p>No brands found.</p>';
|
||||
}
|
||||
|
||||
$columns = intval($atts['columns']);
|
||||
ob_start();
|
||||
?>
|
||||
<div class="cannaiq-brands-grid" style="display: grid; grid-template-columns: repeat(<?php echo $columns; ?>, 1fr); gap: 20px;">
|
||||
<?php foreach ($brands as $brand): ?>
|
||||
<div class="cannaiq-brand-card" style="text-align: center; padding: 20px; background: #f9fafb; border-radius: 8px;">
|
||||
<?php if (!empty($brand['logo'])): ?>
|
||||
<img src="<?php echo esc_url($brand['logo']); ?>" alt="<?php echo esc_attr($brand['brand'] ?? $brand['name']); ?>" style="max-height: 60px; margin-bottom: 10px;" />
|
||||
<?php endif; ?>
|
||||
<h4 style="margin: 0;"><?php echo esc_html($brand['brand'] ?? $brand['name']); ?></h4>
|
||||
<?php if (!empty($brand['product_count'])): ?>
|
||||
<p style="margin: 5px 0 0; color: #666; font-size: 14px;"><?php echo intval($brand['product_count']); ?> products</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories Shortcode
|
||||
*/
|
||||
public function categories_shortcode($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'style' => 'list'
|
||||
], $atts);
|
||||
|
||||
$categories = $this->fetch_categories();
|
||||
|
||||
if (!$categories) {
|
||||
return '<p>No categories found.</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
if ($atts['style'] === 'grid') {
|
||||
?>
|
||||
<div class="cannaiq-categories-grid" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px;">
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<div class="cannaiq-category-card" style="padding: 15px; background: #f9fafb; border-radius: 8px; text-align: center;">
|
||||
<h4 style="margin: 0;"><?php echo esc_html(ucwords(str_replace('_', ' ', $cat['type'] ?? $cat['name']))); ?></h4>
|
||||
<?php if (!empty($cat['count'])): ?>
|
||||
<p style="margin: 5px 0 0; color: #666; font-size: 14px;"><?php echo intval($cat['count']); ?> products</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<ul class="cannaiq-categories-list" style="list-style: none; padding: 0; margin: 0;">
|
||||
<?php foreach ($categories as $cat): ?>
|
||||
<li style="padding: 10px 0; border-bottom: 1px solid #eee;">
|
||||
<?php echo esc_html(ucwords(str_replace('_', ' ', $cat['type'] ?? $cat['name']))); ?>
|
||||
<?php if (!empty($cat['count'])): ?>
|
||||
<span style="color: #666; font-size: 14px;">(<?php echo intval($cat['count']); ?>)</span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Discount Badge Shortcode
|
||||
*/
|
||||
public function discount_badge_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['style' => 'ribbon'], $atts);
|
||||
$product = $cannaiq_current_product;
|
||||
|
||||
$original = $product['Prices'][0] ?? $product['regular_price'] ?? null;
|
||||
$sale = $product['specialPrice'] ?? $product['sale_price'] ?? null;
|
||||
|
||||
if (!$original || !$sale || $original <= $sale) return '';
|
||||
|
||||
$percent = round((($original - $sale) / $original) * 100);
|
||||
$class = 'cannaiq-discount-ribbon cannaiq-discount-ribbon--' . esc_attr($atts['style']);
|
||||
|
||||
return sprintf('<span class="%s">%s%% OFF</span>', $class, $percent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strain Badge Shortcode
|
||||
*/
|
||||
public function strain_badge_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['style' => 'pill'], $atts);
|
||||
$strain = strtolower($cannaiq_current_product['strainType'] ?? $cannaiq_current_product['strain_type'] ?? '');
|
||||
|
||||
if (empty($strain) || !in_array($strain, ['sativa', 'indica', 'hybrid'])) return '';
|
||||
|
||||
$colors = ['sativa' => '#22c55e', 'indica' => '#8b5cf6', 'hybrid' => '#f97316'];
|
||||
$color = $colors[$strain];
|
||||
$style = $atts['style'] === 'pill' ? "background-color: {$color}; color: white;" : "color: {$color};";
|
||||
|
||||
return sprintf('<span class="cannaiq-strain-badge cannaiq-strain-badge--%s" style="%s">%s</span>',
|
||||
esc_attr($atts['style']), esc_attr($style), esc_html(strtoupper($strain)));
|
||||
}
|
||||
|
||||
/**
|
||||
* THC Shortcode
|
||||
*/
|
||||
public function thc_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['style' => 'badge'], $atts);
|
||||
$thc = $cannaiq_current_product['THCContent']['range'][0] ?? $cannaiq_current_product['THC'] ?? $cannaiq_current_product['thc_percentage'] ?? null;
|
||||
|
||||
if (!$thc || $thc <= 0) return '';
|
||||
|
||||
$formatted = number_format((float)$thc, 1) . '% THC';
|
||||
return sprintf('<span class="cannaiq-potency-badge cannaiq-potency-badge--%s">%s</span>',
|
||||
esc_attr($atts['style']), esc_html($formatted));
|
||||
}
|
||||
|
||||
/**
|
||||
* CBD Shortcode
|
||||
*/
|
||||
public function cbd_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['style' => 'badge'], $atts);
|
||||
$cbd = $cannaiq_current_product['CBDContent']['range'][0] ?? $cannaiq_current_product['CBD'] ?? $cannaiq_current_product['cbd_percentage'] ?? null;
|
||||
|
||||
if (!$cbd || $cbd <= 0) return '';
|
||||
|
||||
$formatted = number_format((float)$cbd, 1) . '% CBD';
|
||||
return sprintf('<span class="cannaiq-potency-badge cannaiq-potency-badge--%s">%s</span>',
|
||||
esc_attr($atts['style']), esc_html($formatted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects Shortcode
|
||||
*/
|
||||
public function effects_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['limit' => 3, 'icons' => 'yes'], $atts);
|
||||
$effects = $cannaiq_current_product['effects'] ?? [];
|
||||
|
||||
if (empty($effects) || !is_array($effects)) return '';
|
||||
|
||||
if (!isset($effects[0])) {
|
||||
arsort($effects);
|
||||
$effects = array_keys($effects);
|
||||
}
|
||||
|
||||
$effects = array_slice($effects, 0, intval($atts['limit']));
|
||||
return cannaiq_render_effects($effects, [
|
||||
'limit' => intval($atts['limit']),
|
||||
'show_icon' => $atts['icons'] === 'yes',
|
||||
'size' => 'medium'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Price Shortcode
|
||||
*/
|
||||
public function price_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['show_original' => 'yes', 'show_weight' => 'yes'], $atts);
|
||||
$product = $cannaiq_current_product;
|
||||
|
||||
$original = $product['Prices'][0] ?? $product['regular_price'] ?? null;
|
||||
$sale = $product['specialPrice'] ?? $product['sale_price'] ?? null;
|
||||
$weight = $product['Options'][0] ?? $product['weight'] ?? '';
|
||||
|
||||
if (!$original || $original <= 0) return '';
|
||||
|
||||
$is_sale = $sale && $sale > 0 && $sale < $original;
|
||||
ob_start();
|
||||
?>
|
||||
<span class="cannaiq-price-block">
|
||||
<?php if ($atts['show_weight'] === 'yes' && !empty($weight)): ?>
|
||||
<span class="cannaiq-price-block__weight"><?php echo esc_html($weight); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($is_sale): ?>
|
||||
<?php if ($atts['show_original'] === 'yes'): ?>
|
||||
<span class="cannaiq-price-block__original">$<?php echo number_format((float)$original, 2); ?></span>
|
||||
<?php endif; ?>
|
||||
<span class="cannaiq-price-block__sale">$<?php echo number_format((float)$sale, 2); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="cannaiq-price-block__regular">$<?php echo number_format((float)$original, 2); ?></span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cart Button Shortcode
|
||||
*/
|
||||
public function cart_button_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['text' => 'ADD TO CART', 'style' => 'solid'], $atts);
|
||||
$url = $cannaiq_current_product['menuUrl'] ?? $cannaiq_current_product['menu_url'] ?? '#';
|
||||
|
||||
return sprintf('<a href="%s" class="cannaiq-cart-button cannaiq-cart-button--%s" target="_blank" rel="noopener">%s</a>',
|
||||
esc_url($url), esc_attr($atts['style']), esc_html($atts['text']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stock Shortcode
|
||||
*/
|
||||
public function stock_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['style' => 'badge'], $atts);
|
||||
$status = $cannaiq_current_product['Status'] ?? '';
|
||||
$in_stock = ($status === 'Active' || $status === 'In Stock' || !empty($cannaiq_current_product['in_stock']));
|
||||
|
||||
$text = $in_stock ? 'In Stock' : 'Out of Stock';
|
||||
$class = 'cannaiq-stock-indicator cannaiq-stock-indicator--' . ($in_stock ? 'in-stock' : 'out-of-stock');
|
||||
if ($atts['style'] === 'badge') $class .= ' cannaiq-stock-indicator--badge';
|
||||
|
||||
$dot = $atts['style'] === 'dot' ? '<span class="cannaiq-stock-indicator__dot"></span>' : '';
|
||||
return sprintf('<span class="%s">%s%s</span>', esc_attr($class), $dot, esc_html($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Terpenes Shortcode
|
||||
*/
|
||||
public function terpenes_shortcode($atts) {
|
||||
global $cannaiq_current_product;
|
||||
if (!$cannaiq_current_product) return '';
|
||||
|
||||
$atts = shortcode_atts(['limit' => 3, 'style' => 'chips'], $atts);
|
||||
$terpenes = $cannaiq_current_product['terpenes'] ?? [];
|
||||
|
||||
if (empty($terpenes) || !is_array($terpenes)) return '';
|
||||
|
||||
$terpenes = array_slice($terpenes, 0, intval($atts['limit']));
|
||||
ob_start();
|
||||
|
||||
if ($atts['style'] === 'chips') {
|
||||
echo '<div class="cannaiq-terpenes cannaiq-terpenes--chips">';
|
||||
foreach ($terpenes as $terp) {
|
||||
$name = $terp['name'] ?? '';
|
||||
$percent = isset($terp['percent']) ? number_format((float)$terp['percent'], 2) . '%' : '';
|
||||
printf('<span class="cannaiq-terpene-chip"><span class="cannaiq-terpene-chip__name">%s</span><span class="cannaiq-terpene-chip__percent">%s</span></span>',
|
||||
esc_html($name), esc_html($percent));
|
||||
}
|
||||
echo '</div>';
|
||||
} elseif ($atts['style'] === 'text') {
|
||||
$parts = [];
|
||||
foreach ($terpenes as $terp) {
|
||||
$name = $terp['name'] ?? '';
|
||||
$percent = isset($terp['percent']) ? number_format((float)$terp['percent'], 2) . '%' : '';
|
||||
$parts[] = $name . ($percent ? ' ' . $percent : '');
|
||||
}
|
||||
echo esc_html(implode(', ', $parts));
|
||||
} else {
|
||||
echo '<div class="cannaiq-terpenes cannaiq-terpenes--list">';
|
||||
foreach ($terpenes as $terp) {
|
||||
$name = $terp['name'] ?? '';
|
||||
$percent = isset($terp['percent']) ? number_format((float)$terp['percent'], 2) . '%' : '';
|
||||
printf('<div class="cannaiq-terpene-item"><span>%s</span><span>%s</span></div>',
|
||||
esc_html($name), esc_html($percent));
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Products from API
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user