diff --git a/src/renderer/components/dashboard/Dashboard.tsx b/src/renderer/components/dashboard/Dashboard.tsx index 6f8bb4ca..d95509bb 100644 --- a/src/renderer/components/dashboard/Dashboard.tsx +++ b/src/renderer/components/dashboard/Dashboard.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, type ElementType, type ReactNode } from "react"; import { motion } from "framer-motion"; import { ArrowRight, @@ -29,6 +29,46 @@ import { RecoveryBanner } from "./RecoveryBanner"; import { BudgetAlertBanner } from "./BudgetAlertBanner"; import { ChartSection } from "./ChartSection"; +const ATTENTION_STAGGER_MAX = 8; +const ATTENTION_STAGGER_STEP = 0.04; + +function AttentionRow({ + icon: Icon, + tone, + title, + subtitle, + onClick, + index, +}: { + icon: ElementType; + tone: "warn" | "danger"; + title: ReactNode; + subtitle: ReactNode; + onClick: () => void; + index: number; +}) { + const iconClass = tone === "danger" ? "text-destructive" : "text-warning"; + const bgClass = tone === "danger" ? "bg-destructive-muted" : "bg-warning-muted"; + const delay = Math.min(index, ATTENTION_STAGGER_MAX) * ATTENTION_STAGGER_STEP; + return ( + + +
+
{title}
+
{subtitle}
+
+ +
+ ); +} + export interface DashboardProps { companyName?: string; companyId?: string; @@ -150,71 +190,48 @@ export function Dashboard(props: DashboardProps) { {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}
-
+ icon={ShieldCheck} + tone="warn" + index={i} + title={approval.payloadSummary} + subtitle={ + {approval.type.replaceAll("_", " ")} {requester ? by {requester.name} : null} -
-
- -
+ + } + onClick={() => onNavigate("approvals", approval.id)} + /> ); })} {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" : ""} -
-
- -
+ /> ); })} @@ -233,7 +250,7 @@ export function Dashboard(props: DashboardProps) { {activeWork.length > 0 ? (
- {activeWork.map(({ run, agent, task }) => ( + {activeWork.map(({ run, agent, task }, i) => (