From 246ea249fb8c930433d0a634a05c253e57346723 Mon Sep 17 00:00:00 2001 From: cawcenter Date: Fri, 12 Dec 2025 22:54:34 -0500 Subject: [PATCH] feat: Enhance Factory Dashboard UI with React/Shadcn and API polling --- .../admin/content/ContentFactoryDashboard.tsx | 219 +++++ frontend/src/pages/admin/factory.astro | 797 +----------------- 2 files changed, 225 insertions(+), 791 deletions(-) create mode 100644 frontend/src/components/admin/content/ContentFactoryDashboard.tsx diff --git a/frontend/src/components/admin/content/ContentFactoryDashboard.tsx b/frontend/src/components/admin/content/ContentFactoryDashboard.tsx new file mode 100644 index 0000000..50d206d --- /dev/null +++ b/frontend/src/components/admin/content/ContentFactoryDashboard.tsx @@ -0,0 +1,219 @@ +import React, { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; + +export default function ContentFactoryDashboard() { + const [stats, setStats] = useState({ total: 0, ghost: 0, indexed: 0 }); + const [queues, setQueues] = useState([]); + const [campaigns, setCampaigns] = useState([]); + const [logs, setLogs] = useState([]); + const [loading, setLoading] = useState(true); + + const DIRECTUS_ADMIN_URL = "https://spark.jumpstartscaling.com/admin"; + + useEffect(() => { + loadData(); + const interval = setInterval(loadData, 10000); // Poll every 10s + return () => clearInterval(interval); + }, []); + + const loadData = async () => { + try { + // Fetch Stats + const statsRes = await fetch('/api/seo/stats'); + const statsData = await statsRes.json(); + if (statsData.success) { + setStats({ + total: statsData.total, + ghost: statsData.breakdown?.sitemap?.ghost || 0, + indexed: statsData.breakdown?.sitemap?.indexed || 0 + }); + } else { + // Fallback if error (e.g. 500) + setStats({ total: 0, ghost: 0, indexed: 0 }); + } + + // Fetch Campaigns + const campaignsRes = await fetch('/api/admin/campaigns').then(r => r.json()).catch(() => ({ campaigns: [] })); + setCampaigns(campaignsRes.campaigns || []); + + // Fetch Jobs / Queues + const queuesRes = await fetch('/api/admin/queues').then(r => r.json()).catch(() => ({ queues: [] })); + setQueues(queuesRes.queues || []); + + // Fetch Activity Log + const logsRes = await fetch('/api/admin/worklog').then(r => r.json()).catch(() => ({ logs: [] })); + // API might return { logs: [...] } or just array? Assuming { logs: ... } based on others + // Converting logs to match UI expected format if necessary + // logsRes structure depends on worklog.ts implementation. + // Let's assume it returns { logs: [] } + setLogs(logsRes.logs || []); + + setLoading(false); + } catch (error) { + console.error("Dashboard Load Error:", error); + setLoading(false); + } + }; + + const StatusBadge = ({ status }) => { + const colors = { + active: 'bg-green-600', + paused: 'bg-yellow-600', + completed: 'bg-blue-600', + draft: 'bg-slate-600', + Pending: 'bg-slate-600', + Processing: 'bg-blue-600', + Complete: 'bg-green-600', + Failed: 'bg-red-600' + }; + return {status}; + }; + + if (loading) return
Initializing Factory Command Center...
; + + return ( +
+ {/* Header Actions */} +
+
+

Production Overview

+

Monitoring Content Velocity & Integrity

+
+
+ + +
+
+ + {/* KPI Grid */} +
+ + + Total Articles + + +
{stats.total}
+
+
+ + + Ghost (Staged) + + +
{stats.ghost}
+
+
+ + + Indexed (Live) + + +
{stats.indexed}
+
+
+ + + Active Jobs + + +
{queues.filter(q => q.status === 'Processing').length}
+
+
+
+ +
+ {/* Active Campaigns */} + + + + Active Campaigns + + + Recent campaign activity and status + + + + + + Campaign Name + Mode + Status + Target + + + + {campaigns.length > 0 ? campaigns.map((campaign) => ( + + {campaign.name} + {campaign.location_mode || 'Standard'} + + {campaign.batch_count || 0} + + )) : ( + + No active campaigns + + )} + +
+
+
+ + {/* Production Queue */} + + + Production Jobs + Recent generation tasks + + +
+ {queues.length > 0 ? queues.map((job) => ( +
+
+
Job #{job.id.substring(0, 8)}
+
Target: {job.target_quantity} articles
+
+ +
+ )) : ( +
Queue is empty
+ )} + +
+
+
+
+ + {/* Work Log / Activity */} + + + System Activity Log + Real-time backend operations + + +
+ {logs.length > 0 ? logs.map((log, i) => ( +
+ [{new Date(log.timestamp).toLocaleTimeString()}]{' '} + {log.action.toUpperCase()}{' '} + {log.collection}{' '} + by {log.user?.email || 'System'} +
+ )) : ( +
No recent activity
+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/pages/admin/factory.astro b/frontend/src/pages/admin/factory.astro index da727f5..a393f54 100644 --- a/frontend/src/pages/admin/factory.astro +++ b/frontend/src/pages/admin/factory.astro @@ -1,794 +1,9 @@ --- -/** - * Factory Command Center Dashboard - * - * The main control panel for the SEO Content Factory. - * Uses React islands for interactive components. - */ -import BaseLayout from '../../layouts/BaseLayout.astro'; +import Layout from '@/layouts/AdminLayout.astro'; +import ContentFactoryDashboard from '@/components/admin/content/ContentFactoryDashboard'; --- - - -
- -
-
-

🏭 Factory Command Center

-
- - IDLE -
-
-
- - -
-
-
📝
-
- 0 - Total Articles -
-
-
-
👻
-
- 0 - Ghost (Hidden) -
-
-
-
🔍
-
- 0 - Indexed (Live) -
-
-
-
-
- 0 - In Queue -
-
-
- - -
- -
-

📈 Velocity Distribution

-
- -
-
- Ramp Up - Weekend Throttle -
-
- - -
-

🎯 Active Campaigns

-
-
Loading campaigns...
-
-
- - -
-
-

⚙️ Production Queue

- -
-
- - - - - - - - - - - - - - -
StatusCampaignProgressVelocityCreatedActions
Loading queue...
-
-
- - -
-
-

💻 Work Log

- -
-
-
[SYSTEM] Factory initialized. Ready for commands.
-
-
-
- - - - - - + +
+
- - - - - +