feat: WordPress plugin versioning + heatmap fix + dynamic latest download

- Add VERSION file (1.5.4) for tracking WP plugin version
- Update plugin headers to 1.5.4 (cannaiq-menus.php, crawlsy-menus.php)
- Add dynamic /downloads/cannaiq-menus-latest.zip route that auto-redirects
  to highest version (no manual symlinks needed)
- Update frontend download links to use -latest.zip
- Fix StateHeatmap.tsx to parse API values as numbers (fixes string concat bug)
- Document versioning rules in CLAUDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kelly
2025-12-09 09:32:26 -07:00
parent e2166bc25f
commit 37dfea25e1
9 changed files with 77 additions and 9 deletions

View File

@@ -1195,3 +1195,32 @@ Every analytics v2 endpoint must:
--- ---
# END Analytics V2 spec extension # END Analytics V2 spec extension
---
## WordPress Plugin Versioning
The WordPress plugin version is tracked in `wordpress-plugin/VERSION`.
**Current version:** Check `wordpress-plugin/VERSION` for the latest version.
**Versioning rules:**
- **Minor bumps (x.x.N)**: Bug fixes, small improvements - default for most changes
- **Middle bumps (x.N.0)**: New features, significant improvements
- **Major bumps (N.0.0)**: Breaking changes, major rewrites - only when user explicitly requests
**When making WP plugin changes:**
1. Read `wordpress-plugin/VERSION` to get current version
2. Bump the version number (minor by default)
3. Update both files:
- `wordpress-plugin/VERSION`
- Plugin header `Version:` in `cannaiq-menus.php` and/or `crawlsy-menus.php`
- The `define('..._VERSION', '...')` constant in each plugin file
**Plugin files:**
| File | Brand | API URL |
|------|-------|---------|
| `cannaiq-menus.php` | CannaIQ | `https://cannaiq.co/api/v1` |
| `crawlsy-menus.php` | Crawlsy (legacy) | `https://cannaiq.co/api/v1` |
Both plugins use the same API endpoint. The Crawlsy version exists for backward compatibility with existing installations.

Binary file not shown.

View File

@@ -32,6 +32,37 @@ app.use('/images', express.static(LOCAL_IMAGES_PATH));
// Serve static downloads (plugin files, etc.) // Serve static downloads (plugin files, etc.)
// Uses ./public/downloads relative to working directory (works for both Docker and local dev) // Uses ./public/downloads relative to working directory (works for both Docker and local dev)
const LOCAL_DOWNLOADS_PATH = process.env.LOCAL_DOWNLOADS_PATH || './public/downloads'; const LOCAL_DOWNLOADS_PATH = process.env.LOCAL_DOWNLOADS_PATH || './public/downloads';
// Dynamic "latest" redirect for WordPress plugin - finds highest version automatically
app.get('/downloads/cannaiq-menus-latest.zip', (req, res) => {
const fs = require('fs');
const path = require('path');
try {
const files = fs.readdirSync(LOCAL_DOWNLOADS_PATH);
const pluginFiles = files
.filter((f: string) => f.match(/^cannaiq-menus-\d+\.\d+\.\d+\.zip$/))
.sort((a: string, b: string) => {
const vA = a.match(/(\d+)\.(\d+)\.(\d+)/);
const vB = b.match(/(\d+)\.(\d+)\.(\d+)/);
if (!vA || !vB) return 0;
for (let i = 1; i <= 3; i++) {
const diff = parseInt(vB[i]) - parseInt(vA[i]);
if (diff !== 0) return diff;
}
return 0;
});
if (pluginFiles.length > 0) {
const latestFile = pluginFiles[0];
res.redirect(302, `/downloads/${latestFile}`);
} else {
res.status(404).json({ error: 'No plugin versions found' });
}
} catch (err) {
res.status(500).json({ error: 'Failed to find latest plugin' });
}
});
app.use('/downloads', express.static(LOCAL_DOWNLOADS_PATH)); app.use('/downloads', express.static(LOCAL_DOWNLOADS_PATH));
// Simple health check for load balancers/K8s probes // Simple health check for load balancers/K8s probes

View File

@@ -20,7 +20,7 @@ import {
} from 'lucide-react'; } from 'lucide-react';
const API_URL = import.meta.env.VITE_API_URL || ''; const API_URL = import.meta.env.VITE_API_URL || '';
const PLUGIN_DOWNLOAD_URL = `${API_URL}/downloads/cannaiq-menus-1.5.3.zip`; const PLUGIN_DOWNLOAD_URL = `${API_URL}/downloads/cannaiq-menus-latest.zip`;
import { api } from '../lib/api'; import { api } from '../lib/api';
interface VersionInfo { interface VersionInfo {

View File

@@ -23,7 +23,7 @@ export default function LandingPage() {
<Link to="/login" className="px-8 py-3 bg-white text-emerald-700 font-semibold rounded-lg hover:bg-gray-100 transition-colors shadow-lg"> <Link to="/login" className="px-8 py-3 bg-white text-emerald-700 font-semibold rounded-lg hover:bg-gray-100 transition-colors shadow-lg">
Sign In Sign In
</Link> </Link>
<a href="/downloads/cannaiq-menus-1.5.3.zip" className="px-8 py-3 border-2 border-white text-white font-semibold rounded-lg hover:bg-white hover:text-emerald-700 transition-colors"> <a href="/downloads/cannaiq-menus-latest.zip" className="px-8 py-3 border-2 border-white text-white font-semibold rounded-lg hover:bg-white hover:text-emerald-700 transition-colors">
Download WordPress Plugin Download WordPress Plugin
</a> </a>
</div> </div>
@@ -84,10 +84,10 @@ export default function LandingPage() {
<div className="text-emerald-400">[cannaiq_product id="123"]</div> <div className="text-emerald-400">[cannaiq_product id="123"]</div>
</div> </div>
<a <a
href="/downloads/cannaiq-menus-1.5.3.zip" href="/downloads/cannaiq-menus-latest.zip"
className="inline-block px-8 py-3 bg-emerald-600 text-white font-semibold rounded-lg hover:bg-emerald-700 transition-colors shadow-lg" className="inline-block px-8 py-3 bg-emerald-600 text-white font-semibold rounded-lg hover:bg-emerald-700 transition-colors shadow-lg"
> >
Download CannaIQ Menus v1.5.3 Download CannaIQ Menus Plugin
</a> </a>
</div> </div>
</div> </div>

1
wordpress-plugin/VERSION Normal file
View File

@@ -0,0 +1 @@
1.5.4

View File

@@ -36,9 +36,16 @@ zip -r "${OUTPUT_DIR}/${OUTPUT_FILE}" . \
-x "assets/css/crawlsy-menus.css" \ -x "assets/css/crawlsy-menus.css" \
-x "assets/js/crawlsy-menus.js" -x "assets/js/crawlsy-menus.js"
# Create/update the "latest" symlink
cd "${OUTPUT_DIR}"
rm -f cannaiq-menus-latest.zip
ln -s "${OUTPUT_FILE}" cannaiq-menus-latest.zip
echo "" echo ""
echo "Build complete!" echo "Build complete!"
echo " File: ${OUTPUT_DIR}/${OUTPUT_FILE}" echo " File: ${OUTPUT_DIR}/${OUTPUT_FILE}"
echo " Size: $(ls -lh "${OUTPUT_DIR}/${OUTPUT_FILE}" | awk '{print $5}')" echo " Size: $(ls -lh "${OUTPUT_DIR}/${OUTPUT_FILE}" | awk '{print $5}')"
echo "" echo ""
echo "Download URL: https://cannaiq.co/downloads/cannaiq-menus-${VERSION}.zip" echo "Download URLs:"
echo " Versioned: https://cannaiq.co/downloads/cannaiq-menus-${VERSION}.zip"
echo " Latest: https://cannaiq.co/downloads/cannaiq-menus-latest.zip"

View File

@@ -3,7 +3,7 @@
* Plugin Name: CannaIQ Menus * Plugin Name: CannaIQ Menus
* Plugin URI: https://cannaiq.co * Plugin URI: https://cannaiq.co
* Description: Display cannabis product menus from CannaIQ with Elementor integration. Real-time menu data updated daily. * Description: Display cannabis product menus from CannaIQ with Elementor integration. Real-time menu data updated daily.
* Version: 1.5.3 * Version: 1.5.4
* Author: CannaIQ * Author: CannaIQ
* Author URI: https://cannaiq.co * Author URI: https://cannaiq.co
* License: GPL v2 or later * License: GPL v2 or later
@@ -15,7 +15,7 @@ if (!defined('ABSPATH')) {
exit; // Exit if accessed directly exit; // Exit if accessed directly
} }
define('CANNAIQ_MENUS_VERSION', '1.5.3'); define('CANNAIQ_MENUS_VERSION', '1.5.4');
define('CANNAIQ_MENUS_API_URL', 'https://cannaiq.co/api/v1'); define('CANNAIQ_MENUS_API_URL', 'https://cannaiq.co/api/v1');
define('CANNAIQ_MENUS_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CANNAIQ_MENUS_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CANNAIQ_MENUS_PLUGIN_URL', plugin_dir_url(__FILE__)); define('CANNAIQ_MENUS_PLUGIN_URL', plugin_dir_url(__FILE__));

View File

@@ -3,7 +3,7 @@
* Plugin Name: Crawlsy Menus * Plugin Name: Crawlsy Menus
* Plugin URI: https://creationshop.io * Plugin URI: https://creationshop.io
* Description: Display cannabis product menus from Crawlsy with Elementor integration * Description: Display cannabis product menus from Crawlsy with Elementor integration
* Version: 1.5.2 * Version: 1.5.4
* Author: Creationshop * Author: Creationshop
* Author URI: https://creationshop.io * Author URI: https://creationshop.io
* License: GPL v2 or later * License: GPL v2 or later
@@ -15,7 +15,7 @@ if (!defined('ABSPATH')) {
exit; // Exit if accessed directly exit; // Exit if accessed directly
} }
define('CRAWLSY_MENUS_VERSION', '1.5.2'); define('CRAWLSY_MENUS_VERSION', '1.5.4');
define('CRAWLSY_MENUS_API_URL', 'https://cannaiq.co/api/v1'); define('CRAWLSY_MENUS_API_URL', 'https://cannaiq.co/api/v1');
define('CRAWLSY_MENUS_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CRAWLSY_MENUS_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CRAWLSY_MENUS_PLUGIN_URL', plugin_dir_url(__FILE__)); define('CRAWLSY_MENUS_PLUGIN_URL', plugin_dir_url(__FILE__));