feat: complete marketing command center with pipeline, UI, and asset generation
- Dashboard with campaign management, asset gallery, and publishing queue - 7-agent pipeline: trend scout, research, scripts, ad creative, video, copy, distribution - Campaign form with screenshot upload, goal picker, platform selection - Campaign detail view with Details/Pipeline/Assets/Chat tabs - Two-set image generation: Gemini AI (NanoBanana MCP) + Canvas Design posters - Remotion video rendering with phone.png frame and real screenshot alignment - honeyDue branding: blue #0079FF, orange #FF9400, Inter font, warm off-white - Asset cards with source badges (Gemini/Canvas/Remotion/Playwright) - Markdown/JSON render endpoint for viewing pipeline outputs as HTML - Settings page with Tavily, Gemini, Postiz, Nextdoor integration management - Claude Chat for campaign feedback loop with streaming SSE - Postiz publishing modal with scheduling - Auth with NextAuth credentials + JWT sessions - SQLite via Prisma with better-sqlite3 adapter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
|
||||
import { CheckCircle2, Circle, Loader2, XCircle } from "lucide-react";
|
||||
import type { AgentStatus } from "@/hooks/use-pipeline-progress";
|
||||
|
||||
const AGENT_LABELS: Record<string, string> = {
|
||||
"trend-scout": "Trend Scout",
|
||||
"marketing-research-agent": "Research Agent",
|
||||
"script-writer": "Script Writer",
|
||||
"ad-creative-designer": "Ad Creative Designer",
|
||||
"video-ad-producer": "Video Ad Producer",
|
||||
"copywriter-agent": "Copywriter",
|
||||
"distribution-agent": "Distribution Agent",
|
||||
};
|
||||
|
||||
function formatDuration(ms?: number) {
|
||||
if (!ms) return "";
|
||||
if (ms < 1000) return `${ms}ms`;
|
||||
if (ms < 60000) return `${Math.round(ms / 1000)}s`;
|
||||
return `${Math.round(ms / 60000)}m ${Math.round((ms % 60000) / 1000)}s`;
|
||||
}
|
||||
|
||||
function StatusIcon({ status }: { status: AgentStatus["status"] }) {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
return <CheckCircle2 className="h-5 w-5 text-green-500" />;
|
||||
case "running":
|
||||
return <Loader2 className="h-5 w-5 animate-spin text-blue-500" />;
|
||||
case "failed":
|
||||
return <XCircle className="h-5 w-5 text-red-500" />;
|
||||
default:
|
||||
return <Circle className="h-5 w-5 text-muted-foreground" />;
|
||||
}
|
||||
}
|
||||
|
||||
export function PipelineProgress({ agents }: { agents: AgentStatus[] }) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{agents.map((agent) => (
|
||||
<div
|
||||
key={agent.agentName}
|
||||
className="flex items-center gap-3 rounded-lg border p-3"
|
||||
>
|
||||
<StatusIcon status={agent.status} />
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">
|
||||
{AGENT_LABELS[agent.agentName] || agent.agentName}
|
||||
</div>
|
||||
{agent.outputSummary && (
|
||||
<p className="text-sm text-muted-foreground line-clamp-1">
|
||||
{agent.outputSummary}
|
||||
</p>
|
||||
)}
|
||||
{agent.error && (
|
||||
<p className="text-sm text-red-500 line-clamp-1">{agent.error}</p>
|
||||
)}
|
||||
</div>
|
||||
{agent.durationMs && (
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{formatDuration(agent.durationMs)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user