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

@@ -447,5 +447,55 @@ export function createMultiStateRoutes(pool: Pool): Router {
}
});
// =========================================================================
// Health Check Endpoint
// =========================================================================
/**
* GET /api/health/analytics
* Health check for analytics subsystem
*/
router.get('/health/analytics', async (req: Request, res: Response) => {
try {
const startTime = Date.now();
// Check materialized view is accessible
const result = await pool.query(`
SELECT COUNT(*) as state_count,
MAX(refreshed_at) as last_refresh
FROM mv_state_metrics
`);
const dbLatency = Date.now() - startTime;
const stateCount = parseInt(result.rows[0]?.state_count || '0', 10);
const lastRefresh = result.rows[0]?.last_refresh;
// Check if data is stale (more than 24 hours old)
const isStale = lastRefresh
? Date.now() - new Date(lastRefresh).getTime() > 24 * 60 * 60 * 1000
: true;
res.json({
success: true,
status: isStale ? 'degraded' : 'healthy',
data: {
statesInCache: stateCount,
lastRefresh: lastRefresh || null,
isStale,
dbLatencyMs: dbLatency,
},
timestamp: new Date().toISOString(),
});
} catch (error: any) {
console.error('[MultiState] Health check failed:', error);
res.status(503).json({
success: false,
status: 'unhealthy',
error: error.message,
timestamp: new Date().toISOString(),
});
}
});
return router;
}