fix(monitor): remove non-existent worker columns from job_run_logs query
The job_run_logs table tracks scheduled job orchestration, not individual worker jobs. Worker info (worker_id, worker_hostname) belongs on dispensary_crawl_jobs, not job_run_logs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Plugin Name: Crawlsy Menus
|
||||
* Plugin URI: https://creationshop.io
|
||||
* Description: Display cannabis product menus from Crawlsy with Elementor integration
|
||||
* Version: 1.4.0
|
||||
* Version: 1.5.0
|
||||
* Author: Creationshop
|
||||
* Author URI: https://creationshop.io
|
||||
* License: GPL v2 or later
|
||||
@@ -15,7 +15,7 @@ if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
define('CRAWLSY_MENUS_VERSION', '1.4.0');
|
||||
define('CRAWLSY_MENUS_VERSION', '1.5.0');
|
||||
define('CRAWLSY_MENUS_API_URL', 'https://dispos.crawlsy.com/api/v1');
|
||||
define('CRAWLSY_MENUS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('CRAWLSY_MENUS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
Binary file not shown.
@@ -1,196 +0,0 @@
|
||||
# Dispensary Analytics WordPress Plugin
|
||||
|
||||
Beautiful WordPress plugin for displaying Arizona dispensary product data with Elementor widgets and shortcodes.
|
||||
|
||||
**By CreationShop** - [https://creationshop.io](https://creationshop.io)
|
||||
|
||||
## Features
|
||||
|
||||
- **Elementor Widgets**: Drag-and-drop widgets for products, stores, brands, and specials
|
||||
- **Beautiful Carousels**: Auto-playing product sliders with Swiper.js
|
||||
- **Flexible Shortcodes**: Easy-to-use shortcodes for any page builder
|
||||
- **Field Selection**: Choose which data fields to display
|
||||
- **Multiple Styles**: Modern, Minimal, and Bold card designs
|
||||
- **Responsive Design**: Perfect on all devices
|
||||
- **High-Performance**: Lazy loading and optimized API calls
|
||||
- **SEO Friendly**: Semantic HTML and proper image alt tags
|
||||
|
||||
## Installation
|
||||
|
||||
1. Upload the `dutchie-analytics` folder to `/wp-content/plugins/`
|
||||
2. Activate the plugin through the 'Plugins' menu in WordPress
|
||||
3. Go to Settings → Dutchie Analytics
|
||||
4. Enter your API URL and authentication token
|
||||
5. Test the connection
|
||||
|
||||
## Configuration
|
||||
|
||||
### API Settings
|
||||
|
||||
Navigate to **Settings → Dutchie Analytics**:
|
||||
|
||||
- **API URL**: Your Dutchie Analytics API endpoint (e.g., `https://api.dutchieanalytics.com/api`)
|
||||
- **API Token**: Your JWT authentication token
|
||||
|
||||
## Shortcodes
|
||||
|
||||
### Products Grid
|
||||
|
||||
Display a grid of products:
|
||||
|
||||
```
|
||||
[dutchie_products store_id="1" limit="12" columns="4"]
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `store_id` (required): Store ID
|
||||
- `limit` (optional): Number of products (default: 12)
|
||||
- `columns` (optional): Grid columns (default: 4)
|
||||
- `category_id` (optional): Filter by category
|
||||
- `in_stock` (optional): Show only in-stock products (default: true)
|
||||
- `search` (optional): Search term
|
||||
- `fields` (optional): Comma-separated fields to display
|
||||
- `style` (optional): Card style - modern, minimal, bold (default: modern)
|
||||
|
||||
### Products Carousel
|
||||
|
||||
Display a sliding carousel:
|
||||
|
||||
```
|
||||
[dutchie_carousel store_id="1" limit="20" slides_per_view="4"]
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `store_id` (required): Store ID
|
||||
- `limit` (optional): Number of products (default: 20)
|
||||
- `slides_per_view` (optional): Slides visible at once (default: 4)
|
||||
- `category_id` (optional): Filter by category
|
||||
- `autoplay` (optional): Enable autoplay (default: true)
|
||||
- `style` (optional): Card style (default: modern)
|
||||
|
||||
### Stores List
|
||||
|
||||
Display all stores:
|
||||
|
||||
```
|
||||
[dutchie_stores]
|
||||
```
|
||||
|
||||
### Brands List
|
||||
|
||||
Display brands for a store:
|
||||
|
||||
```
|
||||
[dutchie_brands store_id="1"]
|
||||
```
|
||||
|
||||
### Daily Specials
|
||||
|
||||
Display store specials:
|
||||
|
||||
```
|
||||
[dutchie_specials store_id="1" date="2025-01-15"]
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `store_id` (required): Store ID
|
||||
- `date` (optional): Date for specials (default: today)
|
||||
|
||||
## Field Selection
|
||||
|
||||
Control which fields to display using the `fields` parameter:
|
||||
|
||||
```
|
||||
[dutchie_products store_id="1" fields="id,name,price,brand,thc_percentage,in_stock"]
|
||||
```
|
||||
|
||||
**Available Fields:**
|
||||
- `id` - Product ID
|
||||
- `name` - Product name
|
||||
- `brand` - Brand name
|
||||
- `price` - Price
|
||||
- `description` - Description
|
||||
- `thc_percentage` - THC %
|
||||
- `cbd_percentage` - CBD %
|
||||
- `weight` - Weight
|
||||
- `strain_type` - Strain type
|
||||
- `in_stock` - Stock status
|
||||
- `image_url_full` - High-resolution image (2000x2000)
|
||||
|
||||
## Elementor Widgets
|
||||
|
||||
Find all widgets in the **Dutchie Analytics** category in Elementor:
|
||||
|
||||
1. **Products Grid** - Customizable product grid
|
||||
2. **Products Carousel** - Auto-playing slider
|
||||
3. **Stores List** - Store directory
|
||||
4. **Brands List** - Brand badges
|
||||
5. **Specials** - Daily deals
|
||||
|
||||
### Widget Controls
|
||||
|
||||
Each widget provides:
|
||||
- **Content Tab**: Data source, filters, display options
|
||||
- **Style Tab**: Colors, spacing, card styles
|
||||
- **Advanced Tab**: CSS classes, animations
|
||||
|
||||
## Card Styles
|
||||
|
||||
### Modern (Default)
|
||||
- Clean white cards
|
||||
- Subtle shadows
|
||||
- Smooth hover effects
|
||||
- Blue accent colors
|
||||
|
||||
### Minimal
|
||||
- Simple borders
|
||||
- No shadows
|
||||
- Hover border highlights
|
||||
- Ultra-clean design
|
||||
|
||||
### Bold
|
||||
- Gradient backgrounds
|
||||
- Vibrant colors
|
||||
- Large shadows
|
||||
- Eye-catching design
|
||||
|
||||
## Customization
|
||||
|
||||
### Custom CSS
|
||||
|
||||
Add custom styles in your theme's CSS:
|
||||
|
||||
```css
|
||||
.dutchie-product-card-modern {
|
||||
border-radius: 20px;
|
||||
/* Your custom styles */
|
||||
}
|
||||
```
|
||||
|
||||
### Hooks and Filters
|
||||
|
||||
Coming soon: WordPress hooks for advanced customization.
|
||||
|
||||
## Requirements
|
||||
|
||||
- WordPress 5.0 or higher
|
||||
- PHP 7.4 or higher
|
||||
- Elementor (optional, for widgets)
|
||||
|
||||
## Support
|
||||
|
||||
For support, visit: [https://dutchieanalytics.com/support](https://dutchieanalytics.com/support)
|
||||
|
||||
## Changelog
|
||||
|
||||
### 1.0.0
|
||||
- Initial release
|
||||
- Elementor widgets
|
||||
- Shortcodes
|
||||
- Field selection
|
||||
- Multiple card styles
|
||||
- Carousel support
|
||||
|
||||
## License
|
||||
|
||||
GPL v2 or later
|
||||
@@ -1,326 +0,0 @@
|
||||
# Dispensary Analytics - User Guide
|
||||
|
||||
## Table of Contents
|
||||
1. [Installation & Setup](#installation--setup)
|
||||
2. [Plugin Configuration](#plugin-configuration)
|
||||
3. [Using Elementor Widgets](#using-elementor-widgets)
|
||||
4. [Using Shortcodes](#using-shortcodes)
|
||||
5. [Widget Reference](#widget-reference)
|
||||
6. [Shortcode Reference](#shortcode-reference)
|
||||
7. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
### Requirements
|
||||
- WordPress 5.0 or higher
|
||||
- PHP 7.4 or higher
|
||||
- Elementor plugin (for widget functionality)
|
||||
|
||||
### Installation Steps
|
||||
1. Download the `dutchie-analytics.zip` file
|
||||
2. Go to WordPress Admin → Plugins → Add New
|
||||
3. Click "Upload Plugin" and select the zip file
|
||||
4. Click "Install Now" and then "Activate"
|
||||
|
||||
---
|
||||
|
||||
## Plugin Configuration
|
||||
|
||||
### Setting Up API Connection
|
||||
|
||||
1. Go to **WordPress Admin → Settings → Dispensary Analytics**
|
||||
2. Configure the following settings:
|
||||
- **API URL**: Your backend API URL (e.g., `http://localhost:3010/api` or `https://yourdomain.com/api`)
|
||||
- **API Token**: Your authentication token (if required)
|
||||
3. Click "Save Changes"
|
||||
|
||||
---
|
||||
|
||||
## Using Elementor Widgets
|
||||
|
||||
### Finding the Widgets
|
||||
|
||||
1. Edit any page with **Elementor**
|
||||
2. Look for the **"Dutchie Analytics"** category in the widgets panel
|
||||
3. You'll find 5 widgets available:
|
||||
- Products Grid
|
||||
- Products Carousel
|
||||
- Stores List
|
||||
- Brands List
|
||||
- Specials
|
||||
|
||||
### Adding a Widget to Your Page
|
||||
|
||||
1. Open a page in **Elementor Editor**
|
||||
2. Drag the desired widget from the sidebar to your page
|
||||
3. Configure the widget settings in the left panel
|
||||
4. Click "Update" to save your changes
|
||||
|
||||
---
|
||||
|
||||
## Using Shortcodes
|
||||
|
||||
Shortcodes allow you to display dispensary data in any post, page, or widget area without using Elementor.
|
||||
|
||||
### How to Use Shortcodes
|
||||
|
||||
1. Edit any post or page
|
||||
2. Add a shortcode block (or paste directly in the classic editor)
|
||||
3. Copy and paste one of the shortcodes below
|
||||
4. Customize the parameters as needed
|
||||
5. Publish or update your page
|
||||
|
||||
---
|
||||
|
||||
## Widget Reference
|
||||
|
||||
### 1. Products Grid Widget
|
||||
|
||||
**Purpose**: Display products in a responsive grid layout
|
||||
|
||||
**Widget Settings**:
|
||||
- **Store ID** (required): The dispensary store ID
|
||||
- **Category ID** (optional): Filter by specific category
|
||||
- **Limit**: Number of products to show (default: 12)
|
||||
- **Columns**: Number of columns (default: 4)
|
||||
- **In Stock Only**: Show only available products (default: Yes)
|
||||
- **Search**: Filter by keyword
|
||||
- **Style**: Choose visual style (modern, classic, minimal)
|
||||
- **Fields**: Customize which product fields to display
|
||||
|
||||
**Elementor Location**: Dutchie Analytics → Products Grid
|
||||
|
||||
---
|
||||
|
||||
### 2. Products Carousel Widget
|
||||
|
||||
**Purpose**: Display products in a sliding carousel/slider
|
||||
|
||||
**Widget Settings**:
|
||||
- **Store ID** (required): The dispensary store ID
|
||||
- **Category ID** (optional): Filter by specific category
|
||||
- **Limit**: Number of products to show (default: 12)
|
||||
- **Slides Per View**: How many products visible at once (default: 4)
|
||||
- **Autoplay**: Enable automatic sliding (default: Yes)
|
||||
- **Loop**: Enable infinite loop (default: Yes)
|
||||
- **Navigation**: Show prev/next arrows (default: Yes)
|
||||
- **Pagination**: Show dot indicators (default: Yes)
|
||||
|
||||
**Elementor Location**: Dutchie Analytics → Products Carousel
|
||||
|
||||
---
|
||||
|
||||
### 3. Stores List Widget
|
||||
|
||||
**Purpose**: Display a list of dispensary locations
|
||||
|
||||
**Widget Settings**:
|
||||
- **Limit**: Number of stores to show (default: 10)
|
||||
- **State**: Filter by state (e.g., "Arizona")
|
||||
- **City**: Filter by city
|
||||
- **Style**: List style (cards, list, table)
|
||||
- **Show Address**: Display store address (default: Yes)
|
||||
- **Show Phone**: Display phone number (default: Yes)
|
||||
- **Show Hours**: Display business hours (default: Yes)
|
||||
|
||||
**Elementor Location**: Dutchie Analytics → Stores List
|
||||
|
||||
---
|
||||
|
||||
### 4. Brands List Widget
|
||||
|
||||
**Purpose**: Display available brands
|
||||
|
||||
**Widget Settings**:
|
||||
- **Store ID** (optional): Filter brands by store
|
||||
- **Limit**: Number of brands to show (default: 20)
|
||||
- **Style**: Display style (grid, list, carousel)
|
||||
- **Show Logo**: Display brand logos (default: Yes)
|
||||
- **Show Product Count**: Show number of products per brand (default: Yes)
|
||||
|
||||
**Elementor Location**: Dutchie Analytics → Brands List
|
||||
|
||||
---
|
||||
|
||||
### 5. Specials Widget
|
||||
|
||||
**Purpose**: Display current deals and promotions
|
||||
|
||||
**Widget Settings**:
|
||||
- **Store ID** (required): The dispensary store ID
|
||||
- **Limit**: Number of specials to show (default: 6)
|
||||
- **Active Only**: Show only active promotions (default: Yes)
|
||||
- **Style**: Display style (banner, card, minimal)
|
||||
- **Show Expiration**: Display when deal expires (default: Yes)
|
||||
|
||||
**Elementor Location**: Dutchie Analytics → Specials
|
||||
|
||||
---
|
||||
|
||||
## Shortcode Reference
|
||||
|
||||
### 1. Products Grid Shortcode
|
||||
|
||||
Display products in a grid layout.
|
||||
|
||||
**Basic Usage**:
|
||||
```
|
||||
[dutchie_products store_id="1"]
|
||||
```
|
||||
|
||||
**Full Example**:
|
||||
```
|
||||
[dutchie_products store_id="1" limit="12" category_id="5" columns="4" in_stock="true" style="modern" search="indica"]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `store_id` (required) - Store ID number
|
||||
- `limit` - Number of products (default: 12)
|
||||
- `category_id` - Filter by category ID
|
||||
- `columns` - Grid columns: 2, 3, 4, or 6 (default: 4)
|
||||
- `in_stock` - Show only in-stock: "true" or "false" (default: true)
|
||||
- `search` - Filter by keyword
|
||||
- `style` - Visual style: "modern", "classic", "minimal"
|
||||
- `fields` - Comma-separated fields: "name,price,brand,thc,cbd"
|
||||
|
||||
---
|
||||
|
||||
### 2. Products Carousel Shortcode
|
||||
|
||||
Display products in a carousel/slider.
|
||||
|
||||
**Basic Usage**:
|
||||
```
|
||||
[dutchie_carousel store_id="1"]
|
||||
```
|
||||
|
||||
**Full Example**:
|
||||
```
|
||||
[dutchie_carousel store_id="1" limit="16" slides_per_view="4" autoplay="true" loop="true"]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `store_id` (required) - Store ID number
|
||||
- `limit` - Number of products (default: 12)
|
||||
- `category_id` - Filter by category ID
|
||||
- `slides_per_view` - Products visible at once (default: 4)
|
||||
- `autoplay` - Auto-slide: "true" or "false" (default: true)
|
||||
- `loop` - Infinite loop: "true" or "false" (default: true)
|
||||
- `navigation` - Show arrows: "true" or "false" (default: true)
|
||||
- `pagination` - Show dots: "true" or "false" (default: true)
|
||||
|
||||
---
|
||||
|
||||
### 3. Stores List Shortcode
|
||||
|
||||
Display dispensary locations.
|
||||
|
||||
**Basic Usage**:
|
||||
```
|
||||
[dutchie_stores]
|
||||
```
|
||||
|
||||
**Full Example**:
|
||||
```
|
||||
[dutchie_stores limit="10" state="Arizona" city="Phoenix" style="cards"]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `limit` - Number of stores (default: 10)
|
||||
- `state` - Filter by state
|
||||
- `city` - Filter by city
|
||||
- `style` - Display style: "cards", "list", "table"
|
||||
|
||||
---
|
||||
|
||||
### 4. Brands List Shortcode
|
||||
|
||||
Display available brands.
|
||||
|
||||
**Basic Usage**:
|
||||
```
|
||||
[dutchie_brands]
|
||||
```
|
||||
|
||||
**Full Example**:
|
||||
```
|
||||
[dutchie_brands store_id="1" limit="20" style="grid"]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `store_id` - Filter by store ID
|
||||
- `limit` - Number of brands (default: 20)
|
||||
- `style` - Display style: "grid", "list", "carousel"
|
||||
|
||||
---
|
||||
|
||||
### 5. Specials Shortcode
|
||||
|
||||
Display current deals and promotions.
|
||||
|
||||
**Basic Usage**:
|
||||
```
|
||||
[dutchie_specials store_id="1"]
|
||||
```
|
||||
|
||||
**Full Example**:
|
||||
```
|
||||
[dutchie_specials store_id="1" limit="6" active_only="true" style="banner"]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `store_id` (required) - Store ID number
|
||||
- `limit` - Number of specials (default: 6)
|
||||
- `active_only` - Show only active: "true" or "false" (default: true)
|
||||
- `style` - Display style: "banner", "card", "minimal"
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Widget Not Appearing
|
||||
- Make sure Elementor is installed and activated
|
||||
- Clear Elementor cache: Elementor → Tools → Regenerate CSS & Data
|
||||
- Check if plugin is activated
|
||||
|
||||
### No Data Displaying
|
||||
- Verify API URL in Settings → Dispensary Analytics
|
||||
- Check that store_id is correct
|
||||
- Test API connection in browser
|
||||
- Check browser console for errors (F12)
|
||||
|
||||
### Shortcode Shows as Text
|
||||
- Make sure you're using the correct shortcode format: `[shortcode_name]`
|
||||
- Verify the plugin is activated
|
||||
- Check that the post/page content is not in HTML mode
|
||||
|
||||
### Styling Issues
|
||||
- Clear WordPress cache
|
||||
- Clear browser cache (Ctrl+F5)
|
||||
- Check for theme CSS conflicts
|
||||
- Try switching to a default WordPress theme temporarily
|
||||
|
||||
### API Connection Issues
|
||||
- Verify API URL is correct and accessible
|
||||
- Check if API token is required and configured
|
||||
- Ensure backend server is running
|
||||
- Check server firewall settings
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For additional support:
|
||||
- Check plugin settings: WordPress Admin → Settings → Dispensary Analytics
|
||||
- Review backend API logs
|
||||
- Contact: creationshop.io
|
||||
|
||||
---
|
||||
|
||||
## Version Information
|
||||
|
||||
**Current Version**: 1.0.2
|
||||
**Author**: Creationshop LLC
|
||||
**License**: GPL v2 or later
|
||||
@@ -1,456 +0,0 @@
|
||||
/**
|
||||
* Dutchie Analytics - Beautiful Styles
|
||||
* Modern, clean, and professional design
|
||||
*/
|
||||
|
||||
/* Products Grid */
|
||||
.dutchie-products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 24px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.dutchie-products-grid[data-columns="3"] {
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
}
|
||||
|
||||
.dutchie-products-grid[data-columns="4"] {
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
}
|
||||
|
||||
.dutchie-products-grid[data-columns="5"] {
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
}
|
||||
|
||||
/* Product Cards - Modern Style */
|
||||
.dutchie-product-card-modern {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.dutchie-product-card-modern:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
/* Product Cards - Minimal Style */
|
||||
.dutchie-product-card-minimal {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: none;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.dutchie-product-card-minimal:hover {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
/* Product Cards - Bold Style */
|
||||
.dutchie-product-card-bold {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dutchie-product-card-bold:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 12px 28px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.dutchie-product-card-bold .dutchie-product-name,
|
||||
.dutchie-product-card-bold .dutchie-product-brand,
|
||||
.dutchie-product-card-bold .dutchie-product-description {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Product Image */
|
||||
.dutchie-product-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
background: #f9fafb;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dutchie-product-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.dutchie-product-card:hover .dutchie-product-image img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Stock Badge */
|
||||
.dutchie-stock-badge {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.dutchie-stock-badge.in-stock {
|
||||
background: rgba(16, 185, 129, 0.9);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dutchie-stock-badge.out-of-stock {
|
||||
background: rgba(239, 68, 68, 0.9);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Product Content */
|
||||
.dutchie-product-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dutchie-product-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dutchie-product-brand {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
margin: 0 0 12px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dutchie-product-description {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
line-height: 1.5;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
/* Product Meta */
|
||||
.dutchie-product-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dutchie-product-price {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.dutchie-thc-badge,
|
||||
.dutchie-cbd-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.dutchie-thc-badge {
|
||||
background: #ecfdf5;
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.dutchie-cbd-badge {
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.dutchie-product-weight {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
margin: 8px 0 0 0;
|
||||
}
|
||||
|
||||
/* Carousel Wrapper */
|
||||
.dutchie-products-carousel-wrapper {
|
||||
margin: 24px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper {
|
||||
padding: 0 50px 50px 50px;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper-button-prev,
|
||||
.dutchie-products-carousel-wrapper .swiper-button-next {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background: #ffffff;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper-button-prev:hover,
|
||||
.dutchie-products-carousel-wrapper .swiper-button-next:hover {
|
||||
background: #3b82f6;
|
||||
color: #ffffff;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper-button-prev::after,
|
||||
.dutchie-products-carousel-wrapper .swiper-button-next::after {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper-pagination-bullet {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: #d1d5db;
|
||||
opacity: 1;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper-pagination-bullet-active {
|
||||
background: #3b82f6;
|
||||
width: 28px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
/* Stores Grid */
|
||||
.dutchie-stores-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.dutchie-store-card {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.dutchie-store-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.dutchie-store-logo {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
margin: 0 auto 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dutchie-store-card h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.dutchie-store-stats {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.dutchie-store-link {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: #3b82f6;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dutchie-store-link:hover {
|
||||
background: #2563eb;
|
||||
transform: scale(1.05);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Brands Grid */
|
||||
.dutchie-brands-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.dutchie-brand-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 10px 18px;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dutchie-brand-badge:hover {
|
||||
background: #3b82f6;
|
||||
color: #ffffff;
|
||||
border-color: #3b82f6;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
/* Specials Grid */
|
||||
.dutchie-specials-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 24px;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.dutchie-special-card {
|
||||
background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 8px 16px rgba(251, 191, 36, 0.3);
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dutchie-special-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dutchie-special-card img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dutchie-special-card h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.dutchie-special-card p {
|
||||
font-size: 14px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.dutchie-discount-badge {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 2px solid rgba(255, 255, 255, 0.4);
|
||||
border-radius: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.dutchie-products-grid,
|
||||
.dutchie-stores-grid,
|
||||
.dutchie-specials-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.dutchie-products-carousel-wrapper .swiper {
|
||||
padding: 0 0 40px 0;
|
||||
}
|
||||
|
||||
.dutchie-product-name {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.dutchie-product-price {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.dutchie-products-grid,
|
||||
.dutchie-stores-grid,
|
||||
.dutchie-specials-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.dutchie-loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.dutchie-loading::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 3px solid #e5e7eb;
|
||||
border-top-color: #3b82f6;
|
||||
border-radius: 50%;
|
||||
animation: dutchie-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dutchie-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Error State */
|
||||
.dutchie-error {
|
||||
background: #fee2e2;
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
color: #991b1b;
|
||||
margin: 16px 0;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Dutchie Analytics - Frontend JavaScript
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.dutchie-product-image img[data-src]').each(function() {
|
||||
imageObserver.observe(this);
|
||||
});
|
||||
}
|
||||
|
||||
// Add hover effects
|
||||
$('.dutchie-product-card').on('mouseenter', function() {
|
||||
$(this).addClass('dutchie-hover');
|
||||
}).on('mouseleave', function() {
|
||||
$(this).removeClass('dutchie-hover');
|
||||
});
|
||||
|
||||
// Optional: Track product views for analytics
|
||||
$('.dutchie-product-card').on('click', function() {
|
||||
const productId = $(this).data('product-id');
|
||||
if (productId && typeof gtag !== 'undefined') {
|
||||
gtag('event', 'view_item', {
|
||||
'item_id': productId
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# WordPress Plugin Version Bump Script
|
||||
# Usage: ./bump-version.sh [major|minor|patch]
|
||||
|
||||
PLUGIN_FILE="dutchie-analytics.php"
|
||||
BUMP_TYPE=${1:-patch} # Default to patch if not specified
|
||||
|
||||
# Get current version
|
||||
CURRENT_VERSION=$(grep "^ \* Version:" "$PLUGIN_FILE" | sed 's/.*Version: \(.*\)/\1/')
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find current version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Current version: $CURRENT_VERSION"
|
||||
|
||||
# Split version into parts
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$CURRENT_VERSION"
|
||||
MAJOR="${VERSION_PARTS[0]}"
|
||||
MINOR="${VERSION_PARTS[1]}"
|
||||
PATCH="${VERSION_PARTS[2]}"
|
||||
|
||||
# Bump version based on type
|
||||
case $BUMP_TYPE in
|
||||
major)
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
;;
|
||||
minor)
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
;;
|
||||
patch)
|
||||
PATCH=$((PATCH + 1))
|
||||
;;
|
||||
*)
|
||||
echo "Error: Invalid bump type. Use: major, minor, or patch"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
||||
echo "New version: $NEW_VERSION"
|
||||
|
||||
# Update version in plugin file
|
||||
sed -i "s/^ \* Version: .*/ * Version: $NEW_VERSION/" "$PLUGIN_FILE"
|
||||
sed -i "s/define('DUTCHIE_ANALYTICS_VERSION', '.*');/define('DUTCHIE_ANALYTICS_VERSION', '$NEW_VERSION');/" "$PLUGIN_FILE"
|
||||
|
||||
echo "✓ Version bumped from $CURRENT_VERSION to $NEW_VERSION"
|
||||
echo ""
|
||||
echo "Changes made to: $PLUGIN_FILE"
|
||||
echo " - Plugin header: Version: $NEW_VERSION"
|
||||
echo " - Constant: DUTCHIE_ANALYTICS_VERSION = '$NEW_VERSION'"
|
||||
@@ -1,130 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Dispensary Analytics
|
||||
* Plugin URI: https://creationshop.io
|
||||
* Description: Display Arizona dispensary product data, brands, and specials with beautiful Elementor widgets and shortcodes
|
||||
* Version: 1.0.2
|
||||
* Author: Creationshop LLC
|
||||
* Author URI: creationshop.io
|
||||
* License: GPL v2 or later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Text Domain: dutchie-analytics
|
||||
* Requires at least: 5.0
|
||||
* Requires PHP: 7.4
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
define('DUTCHIE_ANALYTICS_VERSION', '1.0.2');
|
||||
define('DUTCHIE_ANALYTICS_PATH', plugin_dir_path(__FILE__));
|
||||
define('DUTCHIE_ANALYTICS_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
// Autoloader
|
||||
spl_autoload_register(function ($class) {
|
||||
$prefix = 'DutchieAnalytics\\';
|
||||
$base_dir = DUTCHIE_ANALYTICS_PATH . 'includes/';
|
||||
|
||||
$len = strlen($prefix);
|
||||
if (strncmp($prefix, $class, $len) !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relative_class = substr($class, $len);
|
||||
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
|
||||
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize plugin
|
||||
function dutchie_analytics_init() {
|
||||
// Load API Client
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/API_Client.php';
|
||||
|
||||
// Load Shortcodes
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/Shortcodes.php';
|
||||
new DutchieAnalytics\Shortcodes();
|
||||
|
||||
// Load Admin Settings
|
||||
if (is_admin()) {
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/Admin_Settings.php';
|
||||
new DutchieAnalytics\Admin_Settings();
|
||||
}
|
||||
|
||||
// Register Elementor Widgets
|
||||
add_action('elementor/widgets/register', function($widgets_manager) {
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/elementor/Products_Grid_Widget.php';
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/elementor/Products_Carousel_Widget.php';
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/elementor/Stores_List_Widget.php';
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/elementor/Brands_List_Widget.php';
|
||||
require_once DUTCHIE_ANALYTICS_PATH . 'includes/elementor/Specials_Widget.php';
|
||||
|
||||
$widgets_manager->register(new \DutchieAnalytics\Elementor\Products_Grid_Widget());
|
||||
$widgets_manager->register(new \DutchieAnalytics\Elementor\Products_Carousel_Widget());
|
||||
$widgets_manager->register(new \DutchieAnalytics\Elementor\Stores_List_Widget());
|
||||
$widgets_manager->register(new \DutchieAnalytics\Elementor\Brands_List_Widget());
|
||||
$widgets_manager->register(new \DutchieAnalytics\Elementor\Specials_Widget());
|
||||
});
|
||||
|
||||
// Register Elementor Widget Category
|
||||
add_action('elementor/elements/categories_registered', function($elements_manager) {
|
||||
$elements_manager->add_category(
|
||||
'dutchie-analytics',
|
||||
[
|
||||
'title' => __('Dutchie Analytics', 'dutchie-analytics'),
|
||||
'icon' => 'fa fa-plug',
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
add_action('plugins_loaded', 'dutchie_analytics_init');
|
||||
|
||||
// Enqueue styles and scripts
|
||||
function dutchie_analytics_enqueue_assets() {
|
||||
wp_enqueue_style(
|
||||
'dutchie-analytics-styles',
|
||||
DUTCHIE_ANALYTICS_URL . 'assets/css/dutchie-analytics.css',
|
||||
[],
|
||||
DUTCHIE_ANALYTICS_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'dutchie-analytics-scripts',
|
||||
DUTCHIE_ANALYTICS_URL . 'assets/js/dutchie-analytics.js',
|
||||
['jquery'],
|
||||
DUTCHIE_ANALYTICS_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Enqueue Swiper for carousels
|
||||
wp_enqueue_style(
|
||||
'swiper-css',
|
||||
'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css',
|
||||
[],
|
||||
'11.0.0'
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'swiper-js',
|
||||
'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js',
|
||||
[],
|
||||
'11.0.0',
|
||||
true
|
||||
);
|
||||
}
|
||||
add_action('wp_enqueue_scripts', 'dutchie_analytics_enqueue_assets');
|
||||
|
||||
// Activation hook
|
||||
register_activation_hook(__FILE__, function() {
|
||||
// Set default options
|
||||
add_option('dutchie_analytics_api_url', 'http://localhost:3010/api');
|
||||
add_option('dutchie_analytics_api_token', '');
|
||||
});
|
||||
|
||||
// Deactivation hook
|
||||
register_deactivation_hook(__FILE__, function() {
|
||||
// Cleanup if needed
|
||||
});
|
||||
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics;
|
||||
|
||||
class API_Client {
|
||||
private $api_url;
|
||||
private $api_token;
|
||||
|
||||
public function __construct() {
|
||||
$this->api_url = get_option('dutchie_analytics_api_url', 'http://localhost:3010/api');
|
||||
|
||||
// Try to get API key from permissions first, fallback to manual token
|
||||
$this->api_token = get_option('dutchie_analytics_api_token', '');
|
||||
|
||||
if (empty($this->api_token) && class_exists('DutchieAnalytics\API_Permissions')) {
|
||||
$this->api_token = API_Permissions::get_site_api_key();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API request
|
||||
*/
|
||||
private function request($endpoint, $params = []) {
|
||||
$url = $this->api_url . $endpoint;
|
||||
|
||||
if (!empty($params)) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
$args = [
|
||||
'headers' => [
|
||||
'X-API-Key' => $this->api_token,
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'timeout' => 30,
|
||||
];
|
||||
|
||||
$response = wp_remote_get($url, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
error_log('Dutchie Analytics API Error: ' . $response->get_error_message());
|
||||
return null;
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
return json_decode($body, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all stores
|
||||
*/
|
||||
public function get_stores() {
|
||||
return $this->request('/stores');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single store
|
||||
*/
|
||||
public function get_store($id) {
|
||||
return $this->request('/stores/' . $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store brands
|
||||
*/
|
||||
public function get_store_brands($store_id) {
|
||||
return $this->request('/stores/' . $store_id . '/brands');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store specials
|
||||
*/
|
||||
public function get_store_specials($store_id, $date = null) {
|
||||
$params = $date ? ['date' => $date] : [];
|
||||
return $this->request('/stores/' . $store_id . '/specials', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products
|
||||
*
|
||||
* @param array $args {
|
||||
* @type int $store_id Filter by store ID
|
||||
* @type int $category_id Filter by category ID
|
||||
* @type bool $in_stock Filter by stock availability
|
||||
* @type string $search Search query
|
||||
* @type int $limit Results per page (default: 50, max: 1000)
|
||||
* @type int $offset Pagination offset
|
||||
* @type string $fields Comma-separated list of fields to return
|
||||
* }
|
||||
*/
|
||||
public function get_products($args = []) {
|
||||
$defaults = [
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
];
|
||||
|
||||
$params = wp_parse_args($args, $defaults);
|
||||
return $this->request('/products', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single product
|
||||
*/
|
||||
public function get_product($id) {
|
||||
return $this->request('/products/' . $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get categories
|
||||
*/
|
||||
public function get_categories($store_id = null) {
|
||||
$params = $store_id ? ['store_id' => $store_id] : [];
|
||||
return $this->request('/categories', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category tree
|
||||
*/
|
||||
public function get_category_tree($store_id) {
|
||||
return $this->request('/categories/tree', ['store_id' => $store_id]);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics;
|
||||
|
||||
class Admin_Settings {
|
||||
public function __construct() {
|
||||
add_action('admin_menu', [$this, 'add_settings_page']);
|
||||
add_action('admin_init', [$this, 'register_settings']);
|
||||
}
|
||||
|
||||
public function add_settings_page() {
|
||||
add_options_page(
|
||||
__('Dutchie Analytics Settings', 'dutchie-analytics'),
|
||||
__('Dutchie Analytics', 'dutchie-analytics'),
|
||||
'manage_options',
|
||||
'dutchie-analytics',
|
||||
[$this, 'render_settings_page']
|
||||
);
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
register_setting('dutchie_analytics_settings', 'dutchie_analytics_api_url');
|
||||
register_setting('dutchie_analytics_settings', 'dutchie_analytics_api_token');
|
||||
|
||||
add_settings_section(
|
||||
'dutchie_analytics_main',
|
||||
__('API Configuration', 'dutchie-analytics'),
|
||||
[$this, 'render_section_description'],
|
||||
'dutchie-analytics'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'dutchie_analytics_api_url',
|
||||
__('API URL', 'dutchie-analytics'),
|
||||
[$this, 'render_api_url_field'],
|
||||
'dutchie-analytics',
|
||||
'dutchie_analytics_main'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'dutchie_analytics_api_token',
|
||||
__('API Token', 'dutchie-analytics'),
|
||||
[$this, 'render_api_token_field'],
|
||||
'dutchie-analytics',
|
||||
'dutchie_analytics_main'
|
||||
);
|
||||
}
|
||||
|
||||
public function render_settings_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
<form action="options.php" method="post">
|
||||
<?php
|
||||
settings_fields('dutchie_analytics_settings');
|
||||
do_settings_sections('dutchie-analytics');
|
||||
submit_button(__('Save Settings', 'dutchie-analytics'));
|
||||
?>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><?php _e('Test Connection', 'dutchie-analytics'); ?></h2>
|
||||
<?php
|
||||
$api = new API_Client();
|
||||
$stores = $api->get_stores();
|
||||
|
||||
if ($stores && isset($stores['stores'])) {
|
||||
echo '<p style="color: green;"><strong>✓ Connection successful!</strong></p>';
|
||||
echo '<p>' . sprintf(__('Found %d stores', 'dutchie-analytics'), count($stores['stores'])) . '</p>';
|
||||
} else {
|
||||
echo '<p style="color: red;"><strong>✗ Connection failed</strong></p>';
|
||||
echo '<p>' . __('Please check your API URL and Token', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
?>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2><?php _e('Documentation', 'dutchie-analytics'); ?></h2>
|
||||
<h3><?php _e('Shortcodes', 'dutchie-analytics'); ?></h3>
|
||||
<ul>
|
||||
<li><code>[dutchie_products store_id="1" limit="12"]</code> - Display products grid</li>
|
||||
<li><code>[dutchie_carousel store_id="1" limit="20"]</code> - Display products carousel</li>
|
||||
<li><code>[dutchie_stores]</code> - Display all stores</li>
|
||||
<li><code>[dutchie_brands store_id="1"]</code> - Display store brands</li>
|
||||
<li><code>[dutchie_specials store_id="1"]</code> - Display today's specials</li>
|
||||
</ul>
|
||||
|
||||
<h3><?php _e('Field Selection', 'dutchie-analytics'); ?></h3>
|
||||
<p>You can select which fields to display using the <code>fields</code> parameter:</p>
|
||||
<code>[dutchie_products store_id="1" fields="id,name,price,brand,in_stock"]</code>
|
||||
|
||||
<h3><?php _e('Elementor Widgets', 'dutchie-analytics'); ?></h3>
|
||||
<p>Find all widgets in the <strong>Dutchie Analytics</strong> category in Elementor.</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_section_description() {
|
||||
echo '<p>' . __('Configure your Dutchie Analytics API connection', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
public function render_api_url_field() {
|
||||
$value = get_option('dutchie_analytics_api_url', 'http://localhost:3010/api');
|
||||
echo '<input type="text" name="dutchie_analytics_api_url" value="' . esc_attr($value) . '" class="regular-text">';
|
||||
echo '<p class="description">' . __('Example: https://api.dutchieanalytics.com/api', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
public function render_api_token_field() {
|
||||
$value = get_option('dutchie_analytics_api_token', '');
|
||||
echo '<input type="password" name="dutchie_analytics_api_token" value="' . esc_attr($value) . '" class="regular-text">';
|
||||
echo '<p class="description">' . __('Your JWT authentication token', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics;
|
||||
|
||||
class Shortcodes {
|
||||
public function __construct() {
|
||||
add_shortcode('dutchie_products', [$this, 'products_grid']);
|
||||
add_shortcode('dutchie_carousel', [$this, 'products_carousel']);
|
||||
add_shortcode('dutchie_stores', [$this, 'stores_list']);
|
||||
add_shortcode('dutchie_brands', [$this, 'brands_list']);
|
||||
add_shortcode('dutchie_specials', [$this, 'specials']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Products Grid Shortcode
|
||||
* [dutchie_products store_id="1" limit="12" category_id="5" fields="name,price,brand"]
|
||||
*/
|
||||
public function products_grid($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'store_id' => '',
|
||||
'category_id' => '',
|
||||
'limit' => 12,
|
||||
'in_stock' => 'true',
|
||||
'search' => '',
|
||||
'columns' => 4,
|
||||
'fields' => '',
|
||||
'style' => 'modern',
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['store_id'])) {
|
||||
return '<p>' . __('Please specify a store_id', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
|
||||
$args = [
|
||||
'store_id' => $atts['store_id'],
|
||||
'limit' => (int) $atts['limit'],
|
||||
];
|
||||
|
||||
if (!empty($atts['category_id'])) {
|
||||
$args['category_id'] = $atts['category_id'];
|
||||
}
|
||||
|
||||
if (!empty($atts['search'])) {
|
||||
$args['search'] = $atts['search'];
|
||||
}
|
||||
|
||||
if ($atts['in_stock'] === 'true') {
|
||||
$args['in_stock'] = true;
|
||||
}
|
||||
|
||||
if (!empty($atts['fields'])) {
|
||||
$args['fields'] = $atts['fields'];
|
||||
}
|
||||
|
||||
$data = $api->get_products($args);
|
||||
$products = $data['products'] ?? [];
|
||||
|
||||
if (empty($products)) {
|
||||
return '<p>' . __('No products found', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="dutchie-products-grid" data-columns="<?php echo esc_attr($atts['columns']); ?>">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="dutchie-product-card dutchie-product-card-<?php echo esc_attr($atts['style']); ?>">
|
||||
<?php if (!empty($product['image_url_full'])): ?>
|
||||
<div class="dutchie-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>" alt="<?php echo esc_attr($product['name']); ?>" loading="lazy">
|
||||
<?php if ($product['in_stock']): ?>
|
||||
<span class="dutchie-stock-badge in-stock"><?php _e('In Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="dutchie-stock-badge out-of-stock"><?php _e('Out of Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-content">
|
||||
<h3 class="dutchie-product-name"><?php echo esc_html($product['name']); ?></h3>
|
||||
|
||||
<?php if (!empty($product['brand'])): ?>
|
||||
<p class="dutchie-product-brand"><?php echo esc_html($product['brand']); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['description'])): ?>
|
||||
<p class="dutchie-product-description"><?php echo esc_html(wp_trim_words($product['description'], 20)); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-meta">
|
||||
<?php if (!empty($product['price'])): ?>
|
||||
<span class="dutchie-product-price">$<?php echo number_format($product['price'], 2); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['thc_percentage'])): ?>
|
||||
<span class="dutchie-thc-badge"><?php echo esc_html($product['thc_percentage']); ?>% THC</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['cbd_percentage'])): ?>
|
||||
<span class="dutchie-cbd-badge"><?php echo esc_html($product['cbd_percentage']); ?>% CBD</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($product['weight'])): ?>
|
||||
<p class="dutchie-product-weight"><?php echo esc_html($product['weight']); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Products Carousel Shortcode
|
||||
* [dutchie_carousel store_id="1" limit="20"]
|
||||
*/
|
||||
public function products_carousel($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'store_id' => '',
|
||||
'category_id' => '',
|
||||
'limit' => 20,
|
||||
'in_stock' => 'true',
|
||||
'slides_per_view' => 4,
|
||||
'autoplay' => 'true',
|
||||
'style' => 'modern',
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['store_id'])) {
|
||||
return '<p>' . __('Please specify a store_id', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
|
||||
$args = [
|
||||
'store_id' => $atts['store_id'],
|
||||
'limit' => (int) $atts['limit'],
|
||||
];
|
||||
|
||||
if (!empty($atts['category_id'])) {
|
||||
$args['category_id'] = $atts['category_id'];
|
||||
}
|
||||
|
||||
if ($atts['in_stock'] === 'true') {
|
||||
$args['in_stock'] = true;
|
||||
}
|
||||
|
||||
$data = $api->get_products($args);
|
||||
$products = $data['products'] ?? [];
|
||||
|
||||
if (empty($products)) {
|
||||
return '<p>' . __('No products found', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
$carousel_id = 'dutchie-carousel-' . uniqid();
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="dutchie-products-carousel-wrapper">
|
||||
<div class="swiper <?php echo esc_attr($carousel_id); ?>">
|
||||
<div class="swiper-wrapper">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="swiper-slide">
|
||||
<div class="dutchie-product-card dutchie-product-card-<?php echo esc_attr($atts['style']); ?>">
|
||||
<?php if (!empty($product['image_url_full'])): ?>
|
||||
<div class="dutchie-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>" alt="<?php echo esc_attr($product['name']); ?>" loading="lazy">
|
||||
<?php if ($product['in_stock']): ?>
|
||||
<span class="dutchie-stock-badge in-stock"><?php _e('In Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="dutchie-stock-badge out-of-stock"><?php _e('Out of Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-content">
|
||||
<h3 class="dutchie-product-name"><?php echo esc_html($product['name']); ?></h3>
|
||||
|
||||
<?php if (!empty($product['brand'])): ?>
|
||||
<p class="dutchie-product-brand"><?php echo esc_html($product['brand']); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-meta">
|
||||
<?php if (!empty($product['price'])): ?>
|
||||
<span class="dutchie-product-price">$<?php echo number_format($product['price'], 2); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['thc_percentage'])): ?>
|
||||
<span class="dutchie-thc-badge"><?php echo esc_html($product['thc_percentage']); ?>% THC</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="swiper-button-prev"></div>
|
||||
<div class="swiper-button-next"></div>
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
new Swiper('.<?php echo esc_js($carousel_id); ?>', {
|
||||
slidesPerView: 1,
|
||||
spaceBetween: 24,
|
||||
loop: true,
|
||||
<?php if ($atts['autoplay'] === 'true'): ?>
|
||||
autoplay: {
|
||||
delay: 3000,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
<?php endif; ?>
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev',
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
clickable: true,
|
||||
},
|
||||
breakpoints: {
|
||||
640: { slidesPerView: 2 },
|
||||
768: { slidesPerView: 3 },
|
||||
1024: { slidesPerView: <?php echo (int) $atts['slides_per_view']; ?> },
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores List Shortcode
|
||||
* [dutchie_stores]
|
||||
*/
|
||||
public function stores_list($atts) {
|
||||
$api = new API_Client();
|
||||
$data = $api->get_stores();
|
||||
$stores = $data['stores'] ?? [];
|
||||
|
||||
if (empty($stores)) {
|
||||
return '<p>' . __('No stores found', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="dutchie-stores-grid">
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<div class="dutchie-store-card">
|
||||
<?php if (!empty($store['logo_url'])): ?>
|
||||
<img src="<?php echo esc_url($store['logo_url']); ?>" alt="<?php echo esc_attr($store['name']); ?>" class="dutchie-store-logo">
|
||||
<?php endif; ?>
|
||||
<h3><?php echo esc_html($store['name']); ?></h3>
|
||||
<p class="dutchie-store-stats">
|
||||
<?php printf(__('%d Products', 'dutchie-analytics'), $store['product_count']); ?>
|
||||
•
|
||||
<?php printf(__('%d Categories', 'dutchie-analytics'), $store['category_count']); ?>
|
||||
</p>
|
||||
<?php if ($store['dutchie_url']): ?>
|
||||
<a href="<?php echo esc_url($store['dutchie_url']); ?>" target="_blank" class="dutchie-store-link">
|
||||
<?php _e('View Store', 'dutchie-analytics'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Brands List Shortcode
|
||||
* [dutchie_brands store_id="1"]
|
||||
*/
|
||||
public function brands_list($atts) {
|
||||
$atts = shortcode_atts(['store_id' => ''], $atts);
|
||||
|
||||
if (empty($atts['store_id'])) {
|
||||
return '<p>' . __('Please specify a store_id', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
$data = $api->get_store_brands($atts['store_id']);
|
||||
$brands = $data['brands'] ?? [];
|
||||
|
||||
if (empty($brands)) {
|
||||
return '<p>' . __('No brands found', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="dutchie-brands-grid">
|
||||
<?php foreach ($brands as $brand): ?>
|
||||
<div class="dutchie-brand-badge"><?php echo esc_html($brand); ?></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specials Shortcode
|
||||
* [dutchie_specials store_id="1" date="2025-01-15"]
|
||||
*/
|
||||
public function specials($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'store_id' => '',
|
||||
'date' => '',
|
||||
], $atts);
|
||||
|
||||
if (empty($atts['store_id'])) {
|
||||
return '<p>' . __('Please specify a store_id', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
$data = $api->get_store_specials($atts['store_id'], $atts['date'] ?: null);
|
||||
$specials = $data['specials'] ?? [];
|
||||
|
||||
if (empty($specials)) {
|
||||
return '<p>' . __('No specials for this date', 'dutchie-analytics') . '</p>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="dutchie-specials-grid">
|
||||
<?php foreach ($specials as $special): ?>
|
||||
<div class="dutchie-special-card">
|
||||
<?php if (!empty($special['product_image'])): ?>
|
||||
<img src="<?php echo esc_url($special['product_image']); ?>" alt="<?php echo esc_attr($special['name']); ?>">
|
||||
<?php endif; ?>
|
||||
<h3><?php echo esc_html($special['name']); ?></h3>
|
||||
<?php if (!empty($special['description'])): ?>
|
||||
<p><?php echo esc_html($special['description']); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($special['discount_percentage'])): ?>
|
||||
<span class="dutchie-discount-badge"><?php echo esc_html($special['discount_percentage']); ?>% OFF</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics\Elementor;
|
||||
|
||||
use Elementor\Widget_Base;
|
||||
use Elementor\Controls_Manager;
|
||||
use DutchieAnalytics\API_Client;
|
||||
|
||||
class Brands_List_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'dutchie_brands_list';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return __('Brands List', 'dutchie-analytics');
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-tags';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return ['dutchie-analytics'];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
[
|
||||
'label' => __('Content', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_CONTENT,
|
||||
]
|
||||
);
|
||||
|
||||
$api = new API_Client();
|
||||
$stores_data = $api->get_stores();
|
||||
$stores = [];
|
||||
if ($stores_data && isset($stores_data['stores'])) {
|
||||
foreach ($stores_data['stores'] as $store) {
|
||||
$stores[$store['id']] = $store['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_control(
|
||||
'store_id',
|
||||
[
|
||||
'label' => __('Store', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => $stores,
|
||||
'default' => array_key_first($stores) ?: '',
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
|
||||
if (empty($settings['store_id'])) {
|
||||
echo '<p>' . __('Please select a store', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
$data = $api->get_store_brands($settings['store_id']);
|
||||
$brands = $data['brands'] ?? [];
|
||||
|
||||
if (empty($brands)) {
|
||||
echo '<p>' . __('No brands found', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="dutchie-brands-grid">
|
||||
<?php foreach ($brands as $brand): ?>
|
||||
<div class="dutchie-brand-badge"><?php echo esc_html($brand); ?></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics\Elementor;
|
||||
|
||||
use Elementor\Widget_Base;
|
||||
use Elementor\Controls_Manager;
|
||||
use DutchieAnalytics\API_Client;
|
||||
|
||||
class Products_Carousel_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'dutchie_products_carousel';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return __('Products Carousel', 'dutchie-analytics');
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-slider-push';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return ['dutchie-analytics'];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
// Content Section
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
[
|
||||
'label' => __('Content', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_CONTENT,
|
||||
]
|
||||
);
|
||||
|
||||
// Store selection
|
||||
$api = new API_Client();
|
||||
$stores_data = $api->get_stores();
|
||||
$stores = [];
|
||||
if ($stores_data && isset($stores_data['stores'])) {
|
||||
foreach ($stores_data['stores'] as $store) {
|
||||
$stores[$store['id']] = $store['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_control(
|
||||
'store_id',
|
||||
[
|
||||
'label' => __('Store', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => $stores,
|
||||
'default' => array_key_first($stores) ?: '',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'category_id',
|
||||
[
|
||||
'label' => __('Category', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::TEXT,
|
||||
'placeholder' => __('Leave empty for all categories', 'dutchie-analytics'),
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'limit',
|
||||
[
|
||||
'label' => __('Number of Products', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 20,
|
||||
'min' => 1,
|
||||
'max' => 100,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'in_stock_only',
|
||||
[
|
||||
'label' => __('In Stock Only', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'slides_per_view',
|
||||
[
|
||||
'label' => __('Slides Per View', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 4,
|
||||
'min' => 1,
|
||||
'max' => 6,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'space_between',
|
||||
[
|
||||
'label' => __('Space Between Slides', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 24,
|
||||
'min' => 0,
|
||||
'max' => 100,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'autoplay',
|
||||
[
|
||||
'label' => __('Autoplay', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'loop',
|
||||
[
|
||||
'label' => __('Loop', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
// Field selection
|
||||
$this->add_control(
|
||||
'show_brand',
|
||||
[
|
||||
'label' => __('Show Brand', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'show_description',
|
||||
[
|
||||
'label' => __('Show Description', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'no',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'show_thc',
|
||||
[
|
||||
'label' => __('Show THC %', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'show_price',
|
||||
[
|
||||
'label' => __('Show Price', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
|
||||
// Style Section
|
||||
$this->start_controls_section(
|
||||
'style_section',
|
||||
[
|
||||
'label' => __('Style', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_STYLE,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'card_style',
|
||||
[
|
||||
'label' => __('Card Style', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => [
|
||||
'modern' => __('Modern', 'dutchie-analytics'),
|
||||
'minimal' => __('Minimal', 'dutchie-analytics'),
|
||||
'bold' => __('Bold', 'dutchie-analytics'),
|
||||
],
|
||||
'default' => 'modern',
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
|
||||
if (empty($settings['store_id'])) {
|
||||
echo '<p>' . __('Please select a store', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
|
||||
$args = [
|
||||
'store_id' => $settings['store_id'],
|
||||
'limit' => $settings['limit'],
|
||||
];
|
||||
|
||||
if (!empty($settings['category_id'])) {
|
||||
$args['category_id'] = $settings['category_id'];
|
||||
}
|
||||
|
||||
if ($settings['in_stock_only'] === 'yes') {
|
||||
$args['in_stock'] = true;
|
||||
}
|
||||
|
||||
$data = $api->get_products($args);
|
||||
$products = $data['products'] ?? [];
|
||||
|
||||
if (empty($products)) {
|
||||
echo '<p>' . __('No products found', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$carousel_id = 'dutchie-carousel-' . uniqid();
|
||||
$card_class = 'dutchie-product-card-' . $settings['card_style'];
|
||||
|
||||
?>
|
||||
<div class="dutchie-products-carousel-wrapper">
|
||||
<div class="swiper <?php echo esc_attr($carousel_id); ?>">
|
||||
<div class="swiper-wrapper">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="swiper-slide">
|
||||
<div class="dutchie-product-card <?php echo esc_attr($card_class); ?>">
|
||||
<?php if (!empty($product['image_url_full'])): ?>
|
||||
<div class="dutchie-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>" alt="<?php echo esc_attr($product['name']); ?>">
|
||||
<?php if ($product['in_stock']): ?>
|
||||
<span class="dutchie-stock-badge in-stock"><?php _e('In Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="dutchie-stock-badge out-of-stock"><?php _e('Out of Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-content">
|
||||
<h3 class="dutchie-product-name"><?php echo esc_html($product['name']); ?></h3>
|
||||
|
||||
<?php if ($settings['show_brand'] === 'yes' && !empty($product['brand'])): ?>
|
||||
<p class="dutchie-product-brand"><?php echo esc_html($product['brand']); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($settings['show_description'] === 'yes' && !empty($product['description'])): ?>
|
||||
<p class="dutchie-product-description"><?php echo esc_html(wp_trim_words($product['description'], 15)); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-meta">
|
||||
<?php if ($settings['show_price'] === 'yes' && !empty($product['price'])): ?>
|
||||
<span class="dutchie-product-price">$<?php echo number_format($product['price'], 2); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($settings['show_thc'] === 'yes' && !empty($product['thc_percentage'])): ?>
|
||||
<span class="dutchie-thc-badge"><?php echo esc_html($product['thc_percentage']); ?>% THC</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Navigation -->
|
||||
<div class="swiper-button-prev"></div>
|
||||
<div class="swiper-button-next"></div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
new Swiper('.<?php echo esc_js($carousel_id); ?>', {
|
||||
slidesPerView: 1,
|
||||
spaceBetween: <?php echo (int) $settings['space_between']; ?>,
|
||||
<?php if ($settings['loop'] === 'yes'): ?>
|
||||
loop: true,
|
||||
<?php endif; ?>
|
||||
<?php if ($settings['autoplay'] === 'yes'): ?>
|
||||
autoplay: {
|
||||
delay: 3000,
|
||||
disableOnInteraction: false,
|
||||
},
|
||||
<?php endif; ?>
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev',
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
clickable: true,
|
||||
},
|
||||
breakpoints: {
|
||||
640: {
|
||||
slidesPerView: 2,
|
||||
},
|
||||
768: {
|
||||
slidesPerView: 3,
|
||||
},
|
||||
1024: {
|
||||
slidesPerView: <?php echo (int) $settings['slides_per_view']; ?>,
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics\Elementor;
|
||||
|
||||
use Elementor\Widget_Base;
|
||||
use Elementor\Controls_Manager;
|
||||
use DutchieAnalytics\API_Client;
|
||||
|
||||
class Products_Grid_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'dutchie_products_grid';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return __('Products Grid', 'dutchie-analytics');
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-gallery-grid';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return ['dutchie-analytics'];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
[
|
||||
'label' => __('Content', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_CONTENT,
|
||||
]
|
||||
);
|
||||
|
||||
$api = new API_Client();
|
||||
$stores_data = $api->get_stores();
|
||||
$stores = [];
|
||||
if ($stores_data && isset($stores_data['stores'])) {
|
||||
foreach ($stores_data['stores'] as $store) {
|
||||
$stores[$store['id']] = $store['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_control(
|
||||
'store_id',
|
||||
[
|
||||
'label' => __('Store', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => $stores,
|
||||
'default' => array_key_first($stores) ?: '',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'category_id',
|
||||
[
|
||||
'label' => __('Category', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::TEXT,
|
||||
'placeholder' => __('Leave empty for all categories', 'dutchie-analytics'),
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'limit',
|
||||
[
|
||||
'label' => __('Number of Products', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 12,
|
||||
'min' => 1,
|
||||
'max' => 100,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'columns',
|
||||
[
|
||||
'label' => __('Columns', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 4,
|
||||
'min' => 1,
|
||||
'max' => 6,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'in_stock_only',
|
||||
[
|
||||
'label' => __('In Stock Only', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SWITCHER,
|
||||
'label_on' => __('Yes', 'dutchie-analytics'),
|
||||
'label_off' => __('No', 'dutchie-analytics'),
|
||||
'return_value' => 'yes',
|
||||
'default' => 'yes',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'card_style',
|
||||
[
|
||||
'label' => __('Card Style', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => [
|
||||
'modern' => __('Modern', 'dutchie-analytics'),
|
||||
'minimal' => __('Minimal', 'dutchie-analytics'),
|
||||
'bold' => __('Bold', 'dutchie-analytics'),
|
||||
],
|
||||
'default' => 'modern',
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
|
||||
if (empty($settings['store_id'])) {
|
||||
echo '<p>' . __('Please select a store', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
|
||||
$args = [
|
||||
'store_id' => $settings['store_id'],
|
||||
'limit' => $settings['limit'],
|
||||
];
|
||||
|
||||
if (!empty($settings['category_id'])) {
|
||||
$args['category_id'] = $settings['category_id'];
|
||||
}
|
||||
|
||||
if ($settings['in_stock_only'] === 'yes') {
|
||||
$args['in_stock'] = true;
|
||||
}
|
||||
|
||||
$data = $api->get_products($args);
|
||||
$products = $data['products'] ?? [];
|
||||
|
||||
if (empty($products)) {
|
||||
echo '<p>' . __('No products found', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="dutchie-products-grid" data-columns="<?php echo esc_attr($settings['columns']); ?>">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="dutchie-product-card dutchie-product-card-<?php echo esc_attr($settings['card_style']); ?>">
|
||||
<?php if (!empty($product['image_url_full'])): ?>
|
||||
<div class="dutchie-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>" alt="<?php echo esc_attr($product['name']); ?>" loading="lazy">
|
||||
<?php if ($product['in_stock']): ?>
|
||||
<span class="dutchie-stock-badge in-stock"><?php _e('In Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php else: ?>
|
||||
<span class="dutchie-stock-badge out-of-stock"><?php _e('Out of Stock', 'dutchie-analytics'); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-content">
|
||||
<h3 class="dutchie-product-name"><?php echo esc_html($product['name']); ?></h3>
|
||||
|
||||
<?php if (!empty($product['brand'])): ?>
|
||||
<p class="dutchie-product-brand"><?php echo esc_html($product['brand']); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['description'])): ?>
|
||||
<p class="dutchie-product-description"><?php echo esc_html(wp_trim_words($product['description'], 20)); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="dutchie-product-meta">
|
||||
<?php if (!empty($product['price'])): ?>
|
||||
<span class="dutchie-product-price">$<?php echo number_format($product['price'], 2); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['thc_percentage'])): ?>
|
||||
<span class="dutchie-thc-badge"><?php echo esc_html($product['thc_percentage']); ?>% THC</span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($product['cbd_percentage'])): ?>
|
||||
<span class="dutchie-cbd-badge"><?php echo esc_html($product['cbd_percentage']); ?>% CBD</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($product['weight'])): ?>
|
||||
<p class="dutchie-product-weight"><?php echo esc_html($product['weight']); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics\Elementor;
|
||||
|
||||
use Elementor\Widget_Base;
|
||||
use Elementor\Controls_Manager;
|
||||
use DutchieAnalytics\API_Client;
|
||||
|
||||
class Specials_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'dutchie_specials';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return __('Daily Specials', 'dutchie-analytics');
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-flash';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return ['dutchie-analytics'];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
[
|
||||
'label' => __('Content', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_CONTENT,
|
||||
]
|
||||
);
|
||||
|
||||
$api = new API_Client();
|
||||
$stores_data = $api->get_stores();
|
||||
$stores = [];
|
||||
if ($stores_data && isset($stores_data['stores'])) {
|
||||
foreach ($stores_data['stores'] as $store) {
|
||||
$stores[$store['id']] = $store['name'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->add_control(
|
||||
'store_id',
|
||||
[
|
||||
'label' => __('Store', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::SELECT,
|
||||
'options' => $stores,
|
||||
'default' => array_key_first($stores) ?: '',
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'date',
|
||||
[
|
||||
'label' => __('Date', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::DATE_TIME,
|
||||
'picker_options' => [
|
||||
'enableTime' => false,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
|
||||
if (empty($settings['store_id'])) {
|
||||
echo '<p>' . __('Please select a store', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$api = new API_Client();
|
||||
$date = !empty($settings['date']) ? date('Y-m-d', strtotime($settings['date'])) : null;
|
||||
$data = $api->get_store_specials($settings['store_id'], $date);
|
||||
$specials = $data['specials'] ?? [];
|
||||
|
||||
if (empty($specials)) {
|
||||
echo '<p>' . __('No specials for this date', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="dutchie-specials-grid">
|
||||
<?php foreach ($specials as $special): ?>
|
||||
<div class="dutchie-special-card">
|
||||
<?php if (!empty($special['product_image'])): ?>
|
||||
<img src="<?php echo esc_url($special['product_image']); ?>" alt="<?php echo esc_attr($special['name']); ?>" loading="lazy">
|
||||
<?php endif; ?>
|
||||
<h3><?php echo esc_html($special['name']); ?></h3>
|
||||
<?php if (!empty($special['description'])): ?>
|
||||
<p><?php echo esc_html($special['description']); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($special['discount_percentage'])): ?>
|
||||
<span class="dutchie-discount-badge"><?php echo esc_html($special['discount_percentage']); ?>% OFF</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
namespace DutchieAnalytics\Elementor;
|
||||
|
||||
use Elementor\Widget_Base;
|
||||
use Elementor\Controls_Manager;
|
||||
use DutchieAnalytics\API_Client;
|
||||
|
||||
class Stores_List_Widget extends Widget_Base {
|
||||
|
||||
public function get_name() {
|
||||
return 'dutchie_stores_list';
|
||||
}
|
||||
|
||||
public function get_title() {
|
||||
return __('Stores List', 'dutchie-analytics');
|
||||
}
|
||||
|
||||
public function get_icon() {
|
||||
return 'eicon-products';
|
||||
}
|
||||
|
||||
public function get_categories() {
|
||||
return ['dutchie-analytics'];
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
$this->start_controls_section(
|
||||
'content_section',
|
||||
[
|
||||
'label' => __('Content', 'dutchie-analytics'),
|
||||
'tab' => Controls_Manager::TAB_CONTENT,
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'columns',
|
||||
[
|
||||
'label' => __('Columns', 'dutchie-analytics'),
|
||||
'type' => Controls_Manager::NUMBER,
|
||||
'default' => 3,
|
||||
'min' => 1,
|
||||
'max' => 4,
|
||||
]
|
||||
);
|
||||
|
||||
$this->end_controls_section();
|
||||
}
|
||||
|
||||
protected function render() {
|
||||
$api = new API_Client();
|
||||
$data = $api->get_stores();
|
||||
$stores = $data['stores'] ?? [];
|
||||
|
||||
if (empty($stores)) {
|
||||
echo '<p>' . __('No stores found', 'dutchie-analytics') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="dutchie-stores-grid">
|
||||
<?php foreach ($stores as $store): ?>
|
||||
<div class="dutchie-store-card">
|
||||
<?php if (!empty($store['logo_url'])): ?>
|
||||
<img src="<?php echo esc_url($store['logo_url']); ?>" alt="<?php echo esc_attr($store['name']); ?>" class="dutchie-store-logo">
|
||||
<?php endif; ?>
|
||||
<h3><?php echo esc_html($store['name']); ?></h3>
|
||||
<p class="dutchie-store-stats">
|
||||
<?php printf(__('%d Products', 'dutchie-analytics'), $store['product_count']); ?>
|
||||
•
|
||||
<?php printf(__('%d Categories', 'dutchie-analytics'), $store['category_count']); ?>
|
||||
</p>
|
||||
<?php if ($store['dutchie_url']): ?>
|
||||
<a href="<?php echo esc_url($store['dutchie_url']); ?>" target="_blank" class="dutchie-store-link">
|
||||
<?php _e('View Store', 'dutchie-analytics'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -11,11 +11,16 @@ $columns = isset($atts['columns']) ? $atts['columns'] : 3;
|
||||
?>
|
||||
|
||||
<div class="crawlsy-product-grid crawlsy-grid-cols-<?php echo esc_attr($columns); ?>">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="crawlsy-product-card">
|
||||
<?php if (!empty($product['image_url_full'])): ?>
|
||||
<?php foreach ($products as $product):
|
||||
$image_url = $product['image_url'] ?? $product['primary_image_url'] ?? $product['image_url_full'] ?? '';
|
||||
$product_url = !empty($product['dutchie_url']) ? $product['dutchie_url'] : '#';
|
||||
?>
|
||||
<div class="crawlsy-product-card"
|
||||
<?php if ($product_url !== '#'): ?>onclick="window.open('<?php echo esc_url($product_url); ?>', '_blank')"<?php endif; ?>
|
||||
style="cursor: <?php echo ($product_url !== '#') ? 'pointer' : 'default'; ?>;">
|
||||
<?php if (!empty($image_url)): ?>
|
||||
<div class="crawlsy-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>"
|
||||
<img src="<?php echo esc_url($image_url); ?>"
|
||||
alt="<?php echo esc_attr($product['name']); ?>"
|
||||
loading="lazy" />
|
||||
</div>
|
||||
|
||||
@@ -262,11 +262,16 @@ class Crawlsy_Menus_Product_Grid_Widget extends \Elementor\Widget_Base {
|
||||
$columns = $settings['columns'];
|
||||
?>
|
||||
<div class="crawlsy-product-grid crawlsy-grid-cols-<?php echo esc_attr($columns); ?>">
|
||||
<?php foreach ($products as $product): ?>
|
||||
<div class="crawlsy-product-card">
|
||||
<?php if ($settings['show_image'] === 'yes' && !empty($product['image_url_full'])): ?>
|
||||
<?php foreach ($products as $product):
|
||||
$image_url = $product['image_url'] ?? $product['primary_image_url'] ?? $product['image_url_full'] ?? '';
|
||||
$product_url = !empty($product['dutchie_url']) ? $product['dutchie_url'] : '#';
|
||||
?>
|
||||
<div class="crawlsy-product-card"
|
||||
<?php if ($product_url !== '#'): ?>onclick="window.open('<?php echo esc_url($product_url); ?>', '_blank')"<?php endif; ?>
|
||||
style="cursor: <?php echo ($product_url !== '#') ? 'pointer' : 'default'; ?>;">
|
||||
<?php if ($settings['show_image'] === 'yes' && !empty($image_url)): ?>
|
||||
<div class="crawlsy-product-image">
|
||||
<img src="<?php echo esc_url($product['image_url_full']); ?>"
|
||||
<img src="<?php echo esc_url($image_url); ?>"
|
||||
alt="<?php echo esc_attr($product['name']); ?>"
|
||||
loading="lazy" />
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,33 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base {
|
||||
return ['general'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get products for the SELECT2 dropdown
|
||||
*
|
||||
* @return array Associative array of product_id => product_name
|
||||
*/
|
||||
protected function get_products_for_select() {
|
||||
$options = ['' => __('-- Select a Product --', 'crawlsy-menus')];
|
||||
|
||||
$plugin = Crawlsy_Menus_Plugin::instance();
|
||||
$products = $plugin->fetch_products(['limit' => 500]);
|
||||
|
||||
if ($products && is_array($products)) {
|
||||
foreach ($products as $product) {
|
||||
$label = $product['name'];
|
||||
if (!empty($product['brand'])) {
|
||||
$label = $product['brand'] . ' - ' . $label;
|
||||
}
|
||||
if (!empty($product['category'])) {
|
||||
$label .= ' (' . $product['category'] . ')';
|
||||
}
|
||||
$options[$product['id']] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
protected function register_controls() {
|
||||
|
||||
// Content Section
|
||||
@@ -36,13 +63,31 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base {
|
||||
]
|
||||
);
|
||||
|
||||
// Get products for the select dropdown
|
||||
$products_options = $this->get_products_for_select();
|
||||
|
||||
$this->add_control(
|
||||
'product_id',
|
||||
[
|
||||
'label' => __('Product ID', 'crawlsy-menus'),
|
||||
'label' => __('Select Product', 'crawlsy-menus'),
|
||||
'type' => \Elementor\Controls_Manager::SELECT2,
|
||||
'options' => $products_options,
|
||||
'default' => '',
|
||||
'label_block' => true,
|
||||
'description' => __('Search and select a product to display', 'crawlsy-menus'),
|
||||
]
|
||||
);
|
||||
|
||||
$this->add_control(
|
||||
'product_id_manual',
|
||||
[
|
||||
'label' => __('Or Enter Product ID', 'crawlsy-menus'),
|
||||
'type' => \Elementor\Controls_Manager::NUMBER,
|
||||
'default' => '',
|
||||
'description' => __('Enter the product ID to display', 'crawlsy-menus'),
|
||||
'description' => __('Manually enter a product ID if not found in dropdown', 'crawlsy-menus'),
|
||||
'condition' => [
|
||||
'product_id' => '',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -228,13 +273,16 @@ class Crawlsy_Menus_Single_Product_Widget extends \Elementor\Widget_Base {
|
||||
protected function render() {
|
||||
$settings = $this->get_settings_for_display();
|
||||
|
||||
if (empty($settings['product_id'])) {
|
||||
echo '<p>' . __('Please enter a product ID.', 'crawlsy-menus') . '</p>';
|
||||
// Use dropdown selection, or fall back to manual ID
|
||||
$product_id = !empty($settings['product_id']) ? $settings['product_id'] : $settings['product_id_manual'];
|
||||
|
||||
if (empty($product_id)) {
|
||||
echo '<p>' . __('Please select or enter a product ID.', 'crawlsy-menus') . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$plugin = Crawlsy_Menus_Plugin::instance();
|
||||
$product = $plugin->fetch_product($settings['product_id']);
|
||||
$product = $plugin->fetch_product($product_id);
|
||||
|
||||
if (!$product) {
|
||||
echo '<p>' . __('Product not found.', 'crawlsy-menus') . '</p>';
|
||||
|
||||
Reference in New Issue
Block a user