diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bc97f16..9b888b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,6 +26,7 @@ "bullmq": "^5.66.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "date-fns": "^4.1.0", "ioredis": "^5.8.2", "leaflet": "^1.9.4", @@ -3856,6 +3857,21 @@ "node": ">=0.10.0" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index a0efaec..b09ac2b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "bullmq": "^5.66.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "date-fns": "^4.1.0", "ioredis": "^5.8.2", "leaflet": "^1.9.4", diff --git a/frontend/src/components/layout/CommandBar.tsx b/frontend/src/components/layout/CommandBar.tsx new file mode 100644 index 0000000..ae79218 --- /dev/null +++ b/frontend/src/components/layout/CommandBar.tsx @@ -0,0 +1,88 @@ +/** + * Command Palette (Cmd+K) + * Global search and quick actions + */ + +'use client'; + +import { useEffect, useState } from 'react'; +import { Command } from 'cmdk'; +import { useRouter } from 'next/navigation'; + +export default function CommandBar() { + const [open, setOpen] = useState(false); + const router = useRouter(); + + // Toggle on Cmd+K + useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen((open) => !open); + } + }; + + document.addEventListener('keydown', down); + return () => document.removeEventListener('keydown', down); + }, []); + + return ( + +
setOpen(false)} /> +
+ + + + + No results found. + + + + router.push('/admin')}> + 🏠 Dashboard + + router.push('/admin/factory')}> + 🏭 Factory Floor + + router.push('/admin/intelligence/avatars')}> + πŸ‘₯ Avatar Bay + + router.push('/admin/intelligence/geo')}> + πŸ—ΊοΈ Geo Chamber + + router.push('/admin/intelligence/patterns')}> + πŸ”§ Pattern Forge + + router.push('/admin/sites')}> + 🌐 Sites + + + + + router.push('/admin/factory?action=new')}> + ✨ New Campaign + + router.push('/admin/sites/new')}> + βž• Add Site + + + + +
+ + ); +} + +function CommandItem({ children, onSelect }: { children: React.ReactNode; onSelect: () => void }) { + return ( + + {children} + + ); +} diff --git a/frontend/src/pages/dashboard.astro b/frontend/src/pages/dashboard.astro new file mode 100644 index 0000000..6f1ebe2 --- /dev/null +++ b/frontend/src/pages/dashboard.astro @@ -0,0 +1,175 @@ +--- +/** + * Dashboard - Command Deck + * Main status and overview page + */ + +import AdminLayout from '@/layouts/AdminLayout.astro'; +import { getDirectusClient, readItems } from '@/lib/directus/client'; + +// Fetch dashboard stats +const client = getDirectusClient(); + +let stats = { + sites: 0, + articles: 0, + jobs_queued: 0, + jobs_running: 0, + jobs_failed: 0, +}; + +try { + const [sites, articles, jobs] = await Promise.all([ + client.request(readItems('sites', { aggregate: { count: '*' } })), + client.request(readItems('generated_articles', { aggregate: { count: '*' } })), + client.request(readItems('generation_jobs')), + ]); + + stats.sites = sites[0]?.count || 0; + stats.articles = articles[0]?.count || 0; + + if (Array.isArray(jobs)) { + stats.jobs_queued = jobs.filter(j => j.status === 'Pending').length; + stats.jobs_running = jobs.filter(j => j.status === 'Processing').length; + stats.jobs_failed = jobs.filter(j => j.status === 'Failed').length; + } +} catch (error) { + console.error('Dashboard stats error:', error); +} +--- + + +
+ +
+
+

Command Deck

+

Operational status and system health

+
+ +
+ + +
+ +
+
+
+

Active Sites

+

{stats.sites}

+
+
+ + + +
+
+
+ + +
+
+
+

Generated Articles

+

{stats.articles}

+
+
+ + + +
+
+
+ + +
+
+
+

Jobs Queued

+

{stats.jobs_queued}

+
+
+ + + +
+
+
+ + +
+
+
+

Jobs Running

+

{stats.jobs_running}

+
+
+ + + +
+
+
+
+ + + + + +
+