feat(KB-039): complete Step 5 — App integration with UsageIndicator modal

This commit is contained in:
gsxdsm
2026-03-29 21:50:28 -07:00
parent e8e8183ecb
commit fd33cb5ca0
2 changed files with 27 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ import type { SectionId } from "./components/SettingsModal";
import { ToastContainer } from "./components/ToastContainer"; import { ToastContainer } from "./components/ToastContainer";
import { GitHubImportModal } from "./components/GitHubImportModal"; import { GitHubImportModal } from "./components/GitHubImportModal";
import { GitManagerModal } from "./components/GitManagerModal"; import { GitManagerModal } from "./components/GitManagerModal";
import { UsageIndicator } from "./components/UsageIndicator";
import { useTasks } from "./hooks/useTasks"; import { useTasks } from "./hooks/useTasks";
import { ToastProvider, useToast } from "./hooks/useToast"; import { ToastProvider, useToast } from "./hooks/useToast";
import { useTheme } from "./hooks/useTheme"; import { useTheme } from "./hooks/useTheme";
@@ -22,6 +23,7 @@ function AppInner() {
const [detailTask, setDetailTask] = useState<TaskDetail | null>(null); const [detailTask, setDetailTask] = useState<TaskDetail | null>(null);
const [settingsOpen, setSettingsOpen] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false);
const [githubImportOpen, setGitHubImportOpen] = useState(false); const [githubImportOpen, setGitHubImportOpen] = useState(false);
const [usageOpen, setUsageOpen] = useState(false);
const [terminalOpen, setTerminalOpen] = useState(false); const [terminalOpen, setTerminalOpen] = useState(false);
const [settingsInitialSection, setSettingsInitialSection] = useState<SectionId | undefined>(undefined); const [settingsInitialSection, setSettingsInitialSection] = useState<SectionId | undefined>(undefined);
const [maxConcurrent, setMaxConcurrent] = useState(2); const [maxConcurrent, setMaxConcurrent] = useState(2);
@@ -104,6 +106,10 @@ function AppInner() {
setIsPlanningOpen(false); setIsPlanningOpen(false);
}, [addToast]); }, [addToast]);
// Usage indicator handlers
const handleOpenUsage = useCallback(() => setUsageOpen(true), []);
const handleCloseUsage = useCallback(() => setUsageOpen(false), []);
const handleToggleAutoMerge = useCallback(async () => { const handleToggleAutoMerge = useCallback(async () => {
const next = !autoMerge; const next = !autoMerge;
setAutoMerge(next); setAutoMerge(next);
@@ -158,6 +164,7 @@ function AppInner() {
onOpenSettings={() => setSettingsOpen(true)} onOpenSettings={() => setSettingsOpen(true)}
onOpenGitHubImport={() => setGitHubImportOpen(true)} onOpenGitHubImport={() => setGitHubImportOpen(true)}
onOpenPlanning={handlePlanningOpen} onOpenPlanning={handlePlanningOpen}
onOpenUsage={handleOpenUsage}
onToggleTerminal={handleToggleTerminal} onToggleTerminal={handleToggleTerminal}
globalPaused={globalPaused} globalPaused={globalPaused}
enginePaused={enginePaused} enginePaused={enginePaused}
@@ -242,6 +249,10 @@ function AppInner() {
isOpen={terminalOpen} isOpen={terminalOpen}
onClose={handleTerminalClose} onClose={handleTerminalClose}
/> />
<UsageIndicator
isOpen={usageOpen}
onClose={handleCloseUsage}
/>
<ToastContainer toasts={toasts} onRemove={removeToast} /> <ToastContainer toasts={toasts} onRemove={removeToast} />
</> </>
); );

View File

@@ -2871,7 +2871,11 @@ describe("TaskExecutor usage limit detection", () => {
); );
expect(store.updateSettings).toHaveBeenCalledWith({ globalPause: true }); expect(store.updateSettings).toHaveBeenCalledWith({ globalPause: true });
// Task should still be marked as failed // Task should still be marked as failed
<<<<<<< HEAD
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) }); expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) });
=======
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: "rate_limit_error: Rate limit exceeded" });
>>>>>>> kb/kb-082
expect(onError).toHaveBeenCalled(); expect(onError).toHaveBeenCalled();
}); });
@@ -2903,7 +2907,11 @@ describe("TaskExecutor usage limit detection", () => {
expect(onUsageLimitHitSpy).not.toHaveBeenCalled(); expect(onUsageLimitHitSpy).not.toHaveBeenCalled();
// Task should still be marked as failed // Task should still be marked as failed
<<<<<<< HEAD
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) }); expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) });
=======
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: "connection refused" });
>>>>>>> kb/kb-082
}); });
it("works without usageLimitPauser (backward compatible)", async () => { it("works without usageLimitPauser (backward compatible)", async () => {
@@ -2928,7 +2936,11 @@ describe("TaskExecutor usage limit detection", () => {
}); });
// Should not crash — just mark as failed // Should not crash — just mark as failed
<<<<<<< HEAD
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) }); expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) });
=======
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: "rate_limit_error: Rate limit exceeded" });
>>>>>>> kb/kb-082
expect(onError).toHaveBeenCalled(); expect(onError).toHaveBeenCalled();
}); });
@@ -2972,7 +2984,11 @@ describe("TaskExecutor usage limit detection", () => {
"rate_limit_error: Rate limit exceeded", "rate_limit_error: Rate limit exceeded",
); );
// Task should be marked as failed // Task should be marked as failed
<<<<<<< HEAD
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) }); expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: expect.any(String) });
=======
expect(store.updateTask).toHaveBeenCalledWith("KB-001", { status: "failed", error: "rate_limit_error: Rate limit exceeded" });
>>>>>>> kb/kb-082
// onError callback should fire // onError callback should fire
expect(onError).toHaveBeenCalled(); expect(onError).toHaveBeenCalled();
}); });