diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 6e2156bf..a106d75b 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -808,12 +808,7 @@ export function App() { projects={companyProjects} activity={filteredActivity} costs={companyCosts} - connectors={snapshot.connectors} - meetings={companyMeetings} - sprints={companySprints} messages={snapshot.agentMessages?.filter((message) => !currentCompany || message.companyId === currentCompany.id) ?? []} - browserActions={browserActions} - workflows={snapshot.workflows?.filter((workflow) => !currentCompany || workflow.companyId === currentCompany.id) ?? []} onNavigate={navigateTo} /> ) : null} diff --git a/src/renderer/components/dashboard.tsx b/src/renderer/components/dashboard.tsx index 52813b01..7528f833 100644 --- a/src/renderer/components/dashboard.tsx +++ b/src/renderer/components/dashboard.tsx @@ -1,12 +1,5 @@ export { Dashboard } from "./dashboard/Dashboard"; export type { DashboardProps } from "./dashboard/Dashboard"; export { KanbanBoard } from "./dashboard/KanbanBoard"; -export { HealthRing, computeHealthScore } from "./dashboard/HealthCard"; export { MetricCard } from "./dashboard/MetricsGrid"; export { ActivityFeed } from "./dashboard/ActivityFeed"; -export { WorkforceGrid } from "./dashboard/QuickActions"; -export { DepartmentOverview } from "./dashboard/DepartmentOverview"; -export { TaskPipeline } from "./dashboard/TaskPipeline"; -export { SprintProgress } from "./dashboard/SprintProgress"; -export { GoalProgress } from "./dashboard/GoalProgress"; -export { NeedsAttention } from "./dashboard/NeedsAttention"; diff --git a/src/renderer/components/dashboard/Dashboard.tsx b/src/renderer/components/dashboard/Dashboard.tsx index 29f95e5a..4be9db87 100644 --- a/src/renderer/components/dashboard/Dashboard.tsx +++ b/src/renderer/components/dashboard/Dashboard.tsx @@ -2,46 +2,28 @@ import { useMemo } from "react"; import { ArrowRight, Briefcase, - Cpu, CurrencyCircleDollar, - Heartbeat, - Lightning, + Robot, ShieldCheck, - Target, - TreeStructure, - Users, Warning, + XCircle, } from "@phosphor-icons/react"; import type { ActivityRecord, AgentRecord, AgentMessageRecord, ApprovalRecord, - BrowserActionRecord, - ConnectorRecord, CostRecord, GoalRecord, - MeetingRecord, ProjectRecord, RunRecord, SectionId, - SprintRecord, TaskRecord, - WorkflowPipelineRecord, } from "@shared/types"; -import { isConnectorExecutionReady } from "@shared/connector-policy"; import { formatMoney } from "../../lib/formatters"; -import { StatusPill } from "../ui"; import { useT } from "../../i18n"; -import { computeHealthScore, HealthRing } from "./HealthCard"; import { MetricCard } from "./MetricsGrid"; import { ActivityFeed } from "./ActivityFeed"; -import { WorkforceGrid } from "./QuickActions"; -import { DepartmentOverview } from "./DepartmentOverview"; -import { TaskPipeline } from "./TaskPipeline"; -import { SprintProgress } from "./SprintProgress"; -import { GoalProgress } from "./GoalProgress"; -import { NeedsAttention } from "./NeedsAttention"; import { RecoveryBanner } from "./RecoveryBanner"; import { BudgetAlertBanner } from "./BudgetAlertBanner"; @@ -54,194 +36,206 @@ export interface DashboardProps { projects: ProjectRecord[]; activity: ActivityRecord[]; costs: CostRecord[]; - connectors: ConnectorRecord[]; messages?: AgentMessageRecord[]; - browserActions?: BrowserActionRecord[]; - workflows?: WorkflowPipelineRecord[]; - meetings?: MeetingRecord[]; - sprints?: SprintRecord[]; onNavigate: (section: SectionId, entityId?: string) => void; } export function Dashboard(props: DashboardProps) { const t = useT(); - const { agents, tasks, runs, approvals, goals, projects, activity, costs, connectors, messages = [], browserActions = [], workflows = [], sprints = [], onNavigate } = props; + const { agents, tasks, runs, approvals, activity, costs, messages = [], onNavigate } = props; - const healthScore = useMemo( - () => computeHealthScore({ agents, tasks, runs, approvals, connectors, messages, browserActions, workflows }), - [agents, tasks, runs, approvals, connectors, messages, browserActions, workflows], - ); - - const liveRuns = useMemo(() => runs.filter((r) => r.status === "running" || r.status === "queued"), [runs]); - const failedRuns = useMemo(() => runs.filter((r) => r.status === "failed" || r.status === "timed_out" || r.status === "interrupted"), [runs]); - const pendingApprovals = useMemo(() => approvals.filter((a) => a.state === "pending"), [approvals]); - const activeTasks = useMemo(() => tasks.filter((t) => t.status === "in_progress" || t.status === "in_review"), [tasks]); - const activeGoals = useMemo(() => goals.filter((g) => g.status === "active"), [goals]); - const totalSpend = useMemo(() => costs.reduce((sum, c) => sum + (c.amountUsd ?? 0), 0), [costs]); const activeAgents = useMemo(() => agents.filter((a) => a.status !== "terminated" && a.status !== "pending_approval"), [agents]); - const readyConnectors = useMemo(() => connectors.filter((connector) => isConnectorExecutionReady(connector)), [connectors]); - const activeSprint = useMemo(() => sprints.find((s) => s.status === "active"), [sprints]); - const unreadMessages = useMemo(() => messages.filter((message) => !message.readAt), [messages]); - const urgentMessages = useMemo(() => messages.filter((message) => !message.readAt && (message.priority === "urgent" || message.channel === "incident")), [messages]); - const blockedBrowserActions = useMemo(() => browserActions.filter((action) => action.status === "approval_required" || action.status === "failed"), [browserActions]); - const stalledWorkflows = useMemo(() => workflows.filter((workflow) => workflow.status === "paused" || workflow.status === "failed"), [workflows]); + 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"), [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 = agents.find((a) => a.id === run.agentId); + const task = tasks.find((t) => t.id === run.taskId); + return { run, agent, task }; + }) + .slice(0, 8); + }, [runs, agents, tasks]); return (