📈 Velocity Distribution
-🎯 Active Campaigns
-⚙️ Production Queue
- -| Status | -Campaign | -Progress | -Velocity | -Created | -Actions | -
|---|---|---|---|---|---|
| Loading queue... | |||||
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
Monitoring Content Velocity & Integrity
+| Status | -Campaign | -Progress | -Velocity | -Created | -Actions | -
|---|---|---|---|---|---|
| Loading queue... | |||||