Files
fusion/packages/dashboard/app/hooks/useReducedMotion.ts
gsxdsm 234430a978 FN-8865: show live agent activity in org charts
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>
2026-08-09 17:10:49 -07:00

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;
}