import { useMemo } from "react"; 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"; import { AttentionRow } from "../ui"; 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 (