FN-8351: move org portability controls to Team
Move organization export and import from Overview to the Team section. - Render the existing portability controls in TeamArea with the established settings refresh flow - Remove the Overview mount and update focused placement coverage - Document the Team-tab location and add a patch changeset Files changed: .changeset/fn-8351-org-portability-team-tab.md | 7 +++ docs/dashboard-guide.md | 3 +- .../command-center/CommandCenterControls.tsx | 6 +- .../__tests__/CommandCenterControls.test.tsx | 15 ++--- .../components/command-center/areas/TeamArea.tsx | 8 +++ .../areas/__tests__/TeamArea.test.tsx | 66 ++++++++++++++++++++++ 6 files changed, 91 insertions(+), 14 deletions(-) Fusion-Task-Id: FN-8351 Fusion-Task-Lineage: 3877a8ec-f307-4da5-88bb-806e1428d14a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8351-org-portability-team-tab.md
Normal file
7
.changeset/fn-8351-org-portability-team-tab.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Move the org export / import card from Command Center Overview to the Team tab.
|
||||
category: feature
|
||||
dev: OrgPortabilityControls now mounts in TeamArea instead of CommandCenterControls (FN-8351).
|
||||
@@ -1218,7 +1218,8 @@ Features:
|
||||
<!-- FNXC:CommandCenterActivity 2026-06-30-00:00: Activity active-agent counts include both durable-agent usage events and ephemeral task-worker execution runs from agentRuns, because task execution can be visible without a matching usage_events row. -->
|
||||
<!-- FNXC:CommandCenterActivity 2026-07-01-00:00: Graph-owned workflow step sessions publish active-to-terminal agentRuns lifecycle rows with task lineage and step metadata, so daily activity and Activity throughput charts include new workflow execution without dashboard-side recounting. -->
|
||||
- **Overview controls dashboard** sits at the top of the Overview landing surface on desktop and mobile. It includes AI engine stop/start backed by `globalPause`, live scheduler status from executor stats, the shared Global Max Concurrent slider backed by `/api/global-concurrency`, range sliders for `maxConcurrent`, `maxTriageConcurrent`, and `maxWorktrees` that persist through `/api/settings`, and a compact theme dropdown with the same color-chip swatches and Shadcn variant list as Settings → Appearance. The four concurrency sliders ask for confirmation after a changed value settles; confirming persists the new cap, while cancel, backdrop, or Escape dismissal reverts to the last persisted value without saving. The global and current-project max-concurrent sliders show running-agent counts plus a current-use dot on the track once utilization data loads; triage and worktree sliders remain cap-only. These controls reuse existing APIs and App-level theme setters; they do not add a new backend route or second theme owner.
|
||||
- **Org export / import** lets an operator download a portable organization JSON bundle or paste one for a dry-run preview before confirming the apply step. Exports are secret-scrubbed by default: credentials and tokens are never included, while safe secret references can remain for setup in the destination project.
|
||||
<!-- FNXC:TeamArea 2026-07-18-12:30: FN-8351 moves organization export and import to the Team tab so team-level portability controls are not presented as Overview dashboard controls. -->
|
||||
- **Team tab — Org export / import** lets an operator download a portable organization JSON bundle or paste one for a dry-run preview before confirming the apply step. Exports are secret-scrubbed by default: credentials and tokens are never included, while safe secret references can remain for setup in the destination project.
|
||||
- **Configuration versions** lives in **Settings → Project → Configuration Versions**. It lists recorded project-setting revisions newest first; select **Roll back** on any revision and confirm once to restore it. The restore is recorded as a new forward revision, so it can itself be undone without manually reconstructing settings.
|
||||
<!-- FNXC:SettingsNavigation 2026-07-18-12:30: FN-8350 keeps configuration revision history out of Command Center and makes Settings its single operator destination. -->
|
||||
- **Overview** summarizes token usage/cost, autonomy, active nodes, sessions, agent runs, tasks done, model breadth, and real open signals, and includes the SDLC throughput funnel for the selected range at the bottom of the Overview content in loading, error, empty, and populated states. **Codebase tokens** is a local-only `cl100k_base`-calibrated pre-tokenization estimate; it does not upload source content or add a runtime tokenizer. **Disk size** is local apparent size (regular files and symlink entries only, not allocated blocks or directory bookkeeping). Both project-intrinsic cards render even before agent usage settles and show `Approx. (partial)` when bounded traversal is incomplete. Git projects scan non-ignored tracked files; non-git fallback excludes only directories whose basename starts with `.` or is exactly `node_modules`, `dist`, or `build`. Source candidates are lstat-gated regular files (symlinks and out-of-root candidates are skipped); disk walking does not follow symlinks. Defaults cap source scanning at 50,000 entries, 4 seconds, 64 MiB total, and 2 MiB/file, disk walking at 500,000 entries and 4 seconds, and cache results for two minutes. System telemetry now formats small byte quantities granularly as B/KB rather than displaying sub-MB values as 0 MB. Its token total and Live activity snapshot token metric refresh on a bounded live cadence and animate number changes while preserving reduced-motion preferences. The sessions card uses the selected-range `ActivityAnalytics.sessions` value already loaded for the overview. The Live activity snapshot also shows the current board-state count for tasks in progress, independent of the selected analytics date range. Overview's active-agent and daily activity values count durable-agent usage events plus ephemeral task-worker execution runs, including graph-owned workflow step sessions that publish `agentRuns` lifecycle rows, with the same agent counted once per day/range if both sources record activity. Overview includes a graph-rich software-factory snapshot with the existing top-model-consumers bar, tool-category bar, top-model token-share pie, and the daily activity multi-series line chart placed before the daily activity sparkline/trend so the richer line graph sits higher in the chart grid. These reuse the already-loaded tokens, tools, activity, and signals analytics; the signals count comes from `/api/command-center/signals` and renders unavailable (`—`) while the incidents-backed response is loading or unavailable. The chart reveal/glow accents are decorative and disabled when reduced-motion preferences are active. The SDLC completion rate is shown as a radial gauge and is calculated as cohort conversion from in-range triage entrants, so the rate is capped at 100% even when older tasks finish during the range.
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useConfirm } from "../../hooks/useConfirm";
|
||||
// FNXC:GlobalConcurrencyControls 2026-06-25-22:45: Concurrency card adopts the shared global-concurrency hook so it and the footer EngineControlMenu read/write ONE source of truth (no more duplicated fetch/debounce/clobber logic).
|
||||
import { useGlobalConcurrency } from "../../hooks/useGlobalConcurrency";
|
||||
import { ThemeDropdown } from "../ThemeDropdown";
|
||||
import { OrgPortabilityControls } from "./OrgPortabilityControls";
|
||||
import type { TaskView } from "../../hooks/useViewState";
|
||||
import "./CommandCenterControls.css";
|
||||
|
||||
@@ -59,8 +58,8 @@ const CONCURRENCY_SETTING_LABEL_KEYS: Record<keyof ConcurrencyValues, { key: str
|
||||
FNXC:CommandCenter 2026-06-21-00:00:
|
||||
Operator concurrency sliders must allow dragging each scheduler capacity control up to 50 by default while still expanding beyond 50 for already-persisted higher values so FN-6768 truthful readouts remain intact.
|
||||
|
||||
FNXC:CommandCenter 2026-06-19-13:45:
|
||||
Overview controls keep only global AI engine, Theme, and Concurrency controls. Agent org chart and Heartbeat control belong to the Team tab so team-specific hierarchy and scheduler heartbeat affordances are not duplicated across Command Center sections.
|
||||
FNXC:CommandCenter 2026-07-18-18:18:
|
||||
FN-8351 keeps Overview controls limited to global AI engine, Theme, and Concurrency controls. Org chart, heartbeat control, and organization portability now belong to the Team tab so team operations are not duplicated across Command Center sections.
|
||||
*/
|
||||
function clamp(value: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
@@ -389,7 +388,6 @@ export function CommandCenterControls({ projectId, colorTheme, themeMode, shadcn
|
||||
/>
|
||||
</section>
|
||||
|
||||
<OrgPortabilityControls projectId={projectId} onSettingsRefresh={refresh} />
|
||||
|
||||
<section className="card cc-controls-card cc-controls-card--concurrency" data-testid="cc-controls-concurrency">
|
||||
<div className="cc-controls-card-header">
|
||||
|
||||
@@ -16,15 +16,6 @@ const legacyMocks = vi.hoisted(() => ({
|
||||
updateSettings: vi.fn(),
|
||||
fetchGlobalConcurrency: vi.fn(),
|
||||
updateGlobalConcurrency: vi.fn(),
|
||||
/*
|
||||
FNXC:OrgPortability 2026-07-18-13:05:
|
||||
FN-8284 mounts OrgPortabilityControls inside CommandCenterControls; that card imports
|
||||
withProjectId + api from legacy. Partial mocks must re-export both or every controls
|
||||
suite fails with "No withProjectId export".
|
||||
*/
|
||||
api: vi.fn().mockResolvedValue({ revisions: [] }),
|
||||
withProjectId: (path: string, projectId?: string) =>
|
||||
projectId ? `${path}?projectId=${projectId}` : path,
|
||||
}));
|
||||
|
||||
vi.mock("../../../api/legacy", () => legacyMocks);
|
||||
@@ -107,6 +98,12 @@ describe("CommandCenterControls concurrency markers", () => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
it("keeps organization portability out of Overview controls", () => {
|
||||
renderControls();
|
||||
|
||||
expect(screen.queryByTestId("cc-controls-org-portability")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps the Theme card's compact dropdown interactive", () => {
|
||||
const { onColorThemeChange } = renderControls();
|
||||
const themeCard = screen.getByTestId("cc-controls-theme");
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Bar, type BarDatum } from "../charts/Bar";
|
||||
import { Sparkline } from "../charts/Sparkline";
|
||||
import { PieChart } from "../charts/recharts";
|
||||
import { resolveOrgChartLayoutMode, type OrgChartLayoutMode } from "../../agentsOrgChartLayout";
|
||||
import { OrgPortabilityControls } from "../OrgPortabilityControls";
|
||||
import { AreaShell } from "./AreaShell";
|
||||
import { useAnalyticsArea } from "./useAnalyticsArea";
|
||||
import { formatCost, formatCount } from "./areaShared";
|
||||
@@ -178,6 +179,7 @@ export function TeamArea({
|
||||
globalPaused,
|
||||
enginePaused,
|
||||
toggleEnginePause,
|
||||
refresh,
|
||||
} = useAppSettings(projectId);
|
||||
/*
|
||||
FNXC:CommandCenter 2026-06-22-00:00:
|
||||
@@ -596,6 +598,12 @@ export function TeamArea({
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
FNXC:CommandCenter 2026-07-18-18:18:
|
||||
FN-8351 moves organization export/import and configuration rollback portability from Overview controls into the Team tab. Keep the existing project and settings-refresh contract unchanged so all card operations retain their established wiring.
|
||||
*/}
|
||||
<OrgPortabilityControls projectId={projectId} onSettingsRefresh={refresh} />
|
||||
|
||||
<AreaShell
|
||||
testId="team"
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { ConfirmDialogProvider } from "../../../../hooks/useConfirm";
|
||||
import { TeamArea } from "../TeamArea";
|
||||
import type { DateRange } from "../../DateRangePicker";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
api: vi.fn(),
|
||||
fetchOrgTree: vi.fn(),
|
||||
fetchExecutorStats: vi.fn(),
|
||||
fetchSettings: vi.fn(),
|
||||
updateSettings: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
toggleEnginePause: vi.fn(),
|
||||
useAnalyticsArea: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../../../../api/legacy", () => ({
|
||||
api: (...args: unknown[]) => mocks.api(...args),
|
||||
withProjectId: (path: string, projectId?: string) => projectId ? `${path}?projectId=${projectId}` : path,
|
||||
fetchOrgTree: mocks.fetchOrgTree,
|
||||
fetchExecutorStats: mocks.fetchExecutorStats,
|
||||
fetchSettings: mocks.fetchSettings,
|
||||
updateSettings: mocks.updateSettings,
|
||||
}));
|
||||
|
||||
vi.mock("../../../../hooks/useAppSettings", () => ({
|
||||
useAppSettings: () => ({
|
||||
globalPaused: false,
|
||||
enginePaused: false,
|
||||
toggleEnginePause: mocks.toggleEnginePause,
|
||||
refresh: mocks.refresh,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../useAnalyticsArea", () => ({
|
||||
useAnalyticsArea: mocks.useAnalyticsArea,
|
||||
}));
|
||||
|
||||
const range: DateRange = { from: "2026-06-08", to: null, preset: "7d" };
|
||||
|
||||
describe("TeamArea organization portability", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.api.mockResolvedValue({ revisions: [] });
|
||||
mocks.fetchOrgTree.mockResolvedValue([]);
|
||||
mocks.fetchExecutorStats.mockResolvedValue({ globalPause: false, enginePaused: false, maxConcurrent: 2 });
|
||||
mocks.fetchSettings.mockResolvedValue({ heartbeatMultiplier: 1 });
|
||||
mocks.updateSettings.mockResolvedValue({});
|
||||
mocks.refresh.mockResolvedValue(undefined);
|
||||
mocks.useAnalyticsArea.mockReturnValue({ data: null, isLoading: false, error: null });
|
||||
});
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
it("renders the org export control in the Team tab", async () => {
|
||||
render(
|
||||
<ConfirmDialogProvider>
|
||||
<TeamArea range={range} projectId="project-1" />
|
||||
</ConfirmDialogProvider>,
|
||||
);
|
||||
|
||||
expect(await screen.findByTestId("cc-controls-org-portability")).toBeTruthy();
|
||||
expect(screen.getByRole("button", { name: "Export org bundle" })).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user