feat: Responsive admin UI, SEO pages, and click analytics

## Responsive Admin UI
- Layout.tsx: Mobile sidebar drawer with hamburger menu
- Dashboard.tsx: 2-col grid on mobile, responsive stats cards
- OrchestratorDashboard.tsx: Responsive table with hidden columns
- PagesTab.tsx: Responsive filters and table

## SEO Pages
- New /admin/seo section with state landing pages
- SEO page generation and management
- State page content with dispensary/product counts

## Click Analytics
- Product click tracking infrastructure
- Click analytics dashboard

## Other Changes
- Consumer features scaffolding (alerts, deals, favorites)
- Health panel component
- Workers dashboard improvements
- Legacy DutchieAZ pages removed

🤖 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-07 22:48:21 -07:00
parent 38d3ea1408
commit 3bc0effa33
74 changed files with 12295 additions and 807 deletions

View File

@@ -0,0 +1,22 @@
-- SEO Pages table for CannaiQ marketing content
-- All content stored here must be sanitized before insertion
CREATE TABLE IF NOT EXISTS seo_pages (
id SERIAL PRIMARY KEY,
slug VARCHAR(255) NOT NULL UNIQUE,
type VARCHAR(50) NOT NULL, -- state, brand, competitor, landing, blog
content JSONB NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, published, archived
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Indexes for efficient lookup
CREATE INDEX IF NOT EXISTS idx_seo_pages_slug ON seo_pages(slug);
CREATE INDEX IF NOT EXISTS idx_seo_pages_type ON seo_pages(type);
CREATE INDEX IF NOT EXISTS idx_seo_pages_status ON seo_pages(status);
CREATE INDEX IF NOT EXISTS idx_seo_pages_type_status ON seo_pages(type, status);
-- Add comment explaining content requirements
COMMENT ON TABLE seo_pages IS 'SEO content for CannaiQ marketing pages. All content must use approved enterprise-safe phrasing.';
COMMENT ON COLUMN seo_pages.content IS 'JSON content with blocks structure. Must be sanitized via ContentValidator before insert.';