feat(KB-659): add agents view toggle to view selector
- Add agents view option to view selector in Header component - Update App.tsx to handle agents view routing - Add tests for view selector toggle behavior
This commit is contained in:
@@ -21,6 +21,7 @@ import { ScheduledTasksModal } from "./components/ScheduledTasksModal";
|
||||
import { ActivityLogModal } from "./components/ActivityLogModal";
|
||||
import { WorkflowStepManager } from "./components/WorkflowStepManager";
|
||||
import { AgentListModal } from "./components/AgentListModal";
|
||||
import { AgentsView } from "./components/AgentsView";
|
||||
import { useTasks } from "./hooks/useTasks";
|
||||
import { ToastProvider, useToast } from "./hooks/useToast";
|
||||
import { useTheme } from "./hooks/useTheme";
|
||||
@@ -49,11 +50,11 @@ function AppInner() {
|
||||
const [autoMerge, setAutoMerge] = useState(true);
|
||||
const [globalPaused, setGlobalPaused] = useState(false);
|
||||
const [enginePaused, setEnginePaused] = useState(false);
|
||||
const [view, setView] = useState<"board" | "list">(() => {
|
||||
const [view, setView] = useState<"board" | "list" | "agents">(() => {
|
||||
// Initialize from localStorage if available
|
||||
if (typeof window !== "undefined") {
|
||||
const saved = localStorage.getItem("kb-dashboard-view");
|
||||
if (saved === "list" || saved === "board") {
|
||||
if (saved === "list" || saved === "board" || saved === "agents") {
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +114,7 @@ function AppInner() {
|
||||
localStorage.setItem("kb-dashboard-view", view);
|
||||
}, [view]);
|
||||
|
||||
const handleChangeView = useCallback((newView: "board" | "list") => {
|
||||
const handleChangeView = useCallback((newView: "board" | "list" | "agents") => {
|
||||
setView(newView);
|
||||
}, []);
|
||||
|
||||
@@ -276,7 +277,9 @@ function AppInner() {
|
||||
searchQuery={searchQuery}
|
||||
onSearchChange={setSearchQuery}
|
||||
/>
|
||||
{view === "board" ? (
|
||||
{view === "agents" ? (
|
||||
<AgentsView addToast={addToast} />
|
||||
) : view === "board" ? (
|
||||
<Board
|
||||
tasks={tasks}
|
||||
maxConcurrent={maxConcurrent}
|
||||
|
||||
@@ -34,8 +34,8 @@ interface HeaderProps {
|
||||
enginePaused?: boolean;
|
||||
onToggleGlobalPause?: () => void;
|
||||
onToggleEnginePause?: () => void;
|
||||
view?: "board" | "list";
|
||||
onChangeView?: (view: "board" | "list") => void;
|
||||
view?: "board" | "list" | "agents";
|
||||
onChangeView?: (view: "board" | "list" | "agents") => void;
|
||||
searchQuery?: string;
|
||||
onSearchChange?: (query: string) => void;
|
||||
}
|
||||
@@ -238,6 +238,15 @@ export function Header({
|
||||
>
|
||||
<List size={16} />
|
||||
</button>
|
||||
<button
|
||||
className={`view-toggle-btn${view === "agents" ? " active" : ""}`}
|
||||
onClick={() => onChangeView("agents")}
|
||||
title="Agents view"
|
||||
aria-label="Agents view"
|
||||
aria-pressed={view === "agents"}
|
||||
>
|
||||
<Bot size={16} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -210,6 +210,47 @@ describe("Header", () => {
|
||||
expect(boardBtn.getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
// ── Agents View Toggle ──────────────────────────────────────────
|
||||
|
||||
it("renders agents view button in view toggle when onChangeView is provided", () => {
|
||||
const onChangeView = vi.fn();
|
||||
render(<Header view="board" onChangeView={onChangeView} />);
|
||||
const agentsBtn = screen.getByTitle("Agents view");
|
||||
expect(agentsBtn).toBeDefined();
|
||||
});
|
||||
|
||||
it("calls onChangeView with 'agents' when agents view button is clicked", () => {
|
||||
const onChangeView = vi.fn();
|
||||
render(<Header view="board" onChangeView={onChangeView} />);
|
||||
const agentsBtn = screen.getByTitle("Agents view");
|
||||
fireEvent.click(agentsBtn);
|
||||
expect(onChangeView).toHaveBeenCalledWith("agents");
|
||||
});
|
||||
|
||||
it("marks agents view button as active when view is 'agents'", () => {
|
||||
const onChangeView = vi.fn();
|
||||
render(<Header view="agents" onChangeView={onChangeView} />);
|
||||
const agentsBtn = screen.getByTitle("Agents view");
|
||||
expect(agentsBtn.className).toContain("active");
|
||||
expect(agentsBtn.getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
|
||||
it("does not mark agents view button as active when view is 'board'", () => {
|
||||
const onChangeView = vi.fn();
|
||||
render(<Header view="board" onChangeView={onChangeView} />);
|
||||
const agentsBtn = screen.getByTitle("Agents view");
|
||||
expect(agentsBtn.className).not.toContain("active");
|
||||
expect(agentsBtn.getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
it("does not mark board view button as active when view is 'agents'", () => {
|
||||
const onChangeView = vi.fn();
|
||||
render(<Header view="agents" onChangeView={onChangeView} />);
|
||||
const boardBtn = screen.getByTitle("Board view");
|
||||
expect(boardBtn.className).not.toContain("active");
|
||||
expect(boardBtn.getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
// ── Terminal Button ─────────────────────────────────────────────
|
||||
|
||||
it("renders terminal button with correct title", () => {
|
||||
|
||||
Reference in New Issue
Block a user