import { useMemo } from "react";
import { motion } from "framer-motion";
import {
ArrowRight,
Briefcase,
CurrencyCircleDollar,
Robot,
ShieldCheck,
Warning,
XCircle,
} from "@phosphor-icons/react";
import type {
ActivityRecord,
AgentRecord,
AgentMessageRecord,
ApprovalRecord,
CostRecord,
GoalRecord,
ProjectRecord,
RunRecord,
SectionId,
TaskRecord,
} from "@shared/types";
import { formatMoney } from "../../lib/formatters";
import { useT } from "../../i18n";
import { MetricCard } from "./MetricsGrid";
import { ActivityFeed } from "./ActivityFeed";
import { RecoveryBanner } from "./RecoveryBanner";
import { BudgetAlertBanner } from "./BudgetAlertBanner";
import { ChartSection } from "./ChartSection";
export interface DashboardProps {
companyName?: string;
companyId?: string;
agents: AgentRecord[];
tasks: TaskRecord[];
runs: RunRecord[];
approvals: ApprovalRecord[];
goals: GoalRecord[];
projects: ProjectRecord[];
activity: ActivityRecord[];
costs: CostRecord[];
messages?: AgentMessageRecord[];
onNavigate: (section: SectionId, entityId?: string) => void;
}
export function Dashboard(props: DashboardProps) {
const t = useT();
const { companyName, companyId, agents, tasks, runs, approvals, activity, costs, messages = [], onNavigate } = props;
const agentMap = useMemo(() => new Map(agents.map((a) => [a.id, a])), [agents]);
const taskMap = useMemo(() => new Map(tasks.map((t) => [t.id, t])), [tasks]);
const activeAgents = useMemo(() => agents.filter((a) => a.status !== "terminated" && a.status !== "pending_approval"), [agents]);
const activeTasks = useMemo(() => tasks.filter((t) => t.status === "in_progress" || t.status === "in_review"), [tasks]);
const pendingApprovals = useMemo(() => approvals.filter((a) => a.state === "pending"), [approvals]);
const totalSpend = useMemo(() => costs.reduce((sum, c) => sum + (c.amountUsd ?? 0), 0), [costs]);
// Attention items
const failedRuns = useMemo(() => runs.filter((r) =>
(r.status === "failed" || r.status === "timed_out") ||
(r.status === "interrupted" && r.summary !== "Application shut down." && r.summary !== "Worker process crashed.")
), [runs]);
const urgentMessages = useMemo(() => messages.filter((m) => !m.readAt && (m.priority === "urgent" || m.channel === "incident")), [messages]);
const hasAttentionItems = pendingApprovals.length > 0 || failedRuns.length > 0 || urgentMessages.length > 0;
// Active work — agents currently running tasks
const activeWork = useMemo(() => {
return runs
.filter((r) => r.status === "running" || r.status === "queued")
.map((run) => {
const agent = agentMap.get(run.agentId);
const task = taskMap.get(run.taskId);
return { run, agent, task };
})
.slice(0, 8);
}, [runs, agentMap, taskMap]);
return (
{/* Company status line */}
{companyName ? (
{companyName}
{activeAgents.length} {activeAgents.length === 1 ? "agent" : "agents"} active
{activeTasks.length} {activeTasks.length === 1 ? "task" : "tasks"} in progress
{tasks.filter((tk) => tk.status === "done").length} completed
) : null}
{/* Conditional banners */}
{/* Key Metrics — 4 cards */}
0 ? "accent" : "default"}
onClick={() => onNavigate("tasks")}
subtitle={`${tasks.filter((t) => t.status === "todo").length} ${t("dash.queued")}`}
/>
0 ? "warn" : "default"}
onClick={() => onNavigate("approvals")}
subtitle={pendingApprovals.length > 0 ? t("dash.needsAttention") : t("dash.allClear")}
/>
a.status === "running").length > 0 ? "success" : "default"}
onClick={() => onNavigate("agents")}
subtitle={`${activeAgents.filter((a) => a.status === "running").length} running`}
/>
onNavigate("settings")}
/>
{/* Performance Charts */}
{companyId ?
: null}
{/* Needs Attention — moved to top, only shows when items exist */}
{hasAttentionItems ? (
{t("dash.needsAttentionSection")}
{pendingApprovals.slice(0, 5).map((approval, i) => {
const requester = approval.requestedByAgentId ? agentMap.get(approval.requestedByAgentId) : undefined;
return (
onNavigate("approvals", approval.id)}
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 flex w-full items-center gap-3 rounded-lg border border-[color:var(--destructive)] border-opacity-20 bg-warning-muted px-4 py-3 text-left transition hover:border-opacity-40"
>
{approval.payloadSummary}
{approval.type.replaceAll("_", " ")}
{requester ? by {requester.name} : null}
);
})}
{failedRuns.slice(0, 3).map((run, i) => {
const agent = agentMap.get(run.agentId);
return (
onNavigate("runs", run.id)}
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 flex w-full items-center gap-3 rounded-lg border border-[color:var(--destructive)] border-opacity-20 bg-destructive-muted px-4 py-3 text-left transition hover:border-opacity-40"
>
{run.summary || "Run failed"}
{agent?.name} — {run.errorMessage?.slice(0, 80) ?? "Unknown error"}
);
})}
{urgentMessages.slice(0, 3).map((msg, i) => {
const sender = agentMap.get(msg.fromAgentId);
return (
onNavigate("communication", msg.id)}
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 flex w-full items-center gap-3 rounded-lg border border-[color:var(--destructive)] border-opacity-20 bg-warning-muted px-4 py-3 text-left transition hover:border-opacity-40"
>
{msg.subject}
{sender?.name ?? "Unknown"} — {msg.channel}{msg.priority === "urgent" ? " · urgent" : ""}
);
})}
) : null}
{/* Active Work — what's running right now */}
Active Work
{activeWork.length > 0 ? (
{activeWork.map(({ run, agent, task }) => (
))}
) : (
{t("dash.allQuiet")}
No tasks currently running
)}
{/* Recent Activity */}
{t("dash.recentActivity")}
);
}