Expose live per-agent activity throughout the dashboard org chart and active-agent cards. - Add an activity store with polling, visibility refresh, and reduced-motion support. - Render activity labels and active indicators in org-chart nodes and agent panels. - Cover activity normalization, polling behavior, and dashboard presentation with tests. Files changed: .changeset/fn-8865-live-agent-activity.md | 7 + docs/dashboard-guide.md | 1 + packages/dashboard/app/api.ts | 11 +- .../dashboard/app/components/ActiveAgentsPanel.css | 20 ++ .../dashboard/app/components/ActiveAgentsPanel.tsx | 28 ++- packages/dashboard/app/components/AgentsView.css | 39 ++++ packages/dashboard/app/components/AgentsView.tsx | 62 +++++- .../__tests__/ActiveAgentsPanel.test.tsx | 122 ++++++++++- .../AgentsView.orgchart.activity.test.tsx | 235 +++++++++++++++++++++ .../__tests__/AgentsView.orgchart.test.tsx | 7 + .../__tests__/agentsOrgChartActivity.test.ts | 137 ++++++++++++ .../app/components/agentsOrgChartActivity.ts | 156 ++++++++++++++ .../app/hooks/__tests__/agentActivityStore.test.ts | 179 ++++++++++++++++ .../app/hooks/__tests__/useAgentActivity.test.tsx | 60 ++++++ packages/dashboard/app/hooks/agentActivityStore.ts | 221 +++++++++++++++++++ packages/dashboard/app/hooks/useAgentActivity.ts | 18 ++ packages/dashboard/app/hooks/useReducedMotion.ts | 23 ++ 17 files changed, 1307 insertions(+), 19 deletions(-) Fusion-Task-Id: FN-8865 Fusion-Task-Lineage: 04413da2-4114-4a19-9b6b-152f12c95ac6 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
24 lines
878 B
TypeScript
24 lines
878 B
TypeScript
import { useEffect, useState } from "react";
|
|
|
|
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
|
|
/** Tracks the browser motion preference and safely defaults to motion when media queries are unavailable. */
|
|
export function useReducedMotion(): boolean {
|
|
const [reducedMotion, setReducedMotion] = useState(() => (
|
|
typeof window !== "undefined"
|
|
&& typeof window.matchMedia === "function"
|
|
&& window.matchMedia(REDUCED_MOTION_QUERY).matches
|
|
));
|
|
|
|
useEffect(() => {
|
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
const mediaQuery = window.matchMedia(REDUCED_MOTION_QUERY);
|
|
const update = () => setReducedMotion(mediaQuery.matches);
|
|
update();
|
|
mediaQuery.addEventListener("change", update);
|
|
return () => mediaQuery.removeEventListener("change", update);
|
|
}, []);
|
|
|
|
return reducedMotion;
|
|
}
|