feat(FN-4727): add github tracking toggle to quick entry box
Adds a GitHub tracking toggle to the QuickEntryBox component with co-located tests, shipped as a patch to `@runfusion/fusion`. Fusion-Task-Id: FN-4727
This commit is contained in:
committed by
gsxdsm
parent
b61db63661
commit
e9403df6ba
5
.changeset/fn-4727-quick-entry-github-toggle.md
Normal file
5
.changeset/fn-4727-quick-entry-github-toggle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Add GitHub tracking indicator/toggle to the quick task entry dropdown — shows project default and overrides for the next task.
|
||||
@@ -12,6 +12,7 @@ import { getScopedItem, removeScopedItem, setScopedItem } from "../utils/project
|
||||
import { useNodes } from "../hooks/useNodes";
|
||||
import type { NodeInfo } from "../api";
|
||||
import { NodeHealthDot } from "./NodeHealthDot";
|
||||
import { ProviderIcon } from "./ProviderIcon";
|
||||
|
||||
const STORAGE_KEY = "kb-quick-entry-text";
|
||||
const ALLOWED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
||||
@@ -157,6 +158,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
const [settings, setSettings] = useState<Settings | null>(null);
|
||||
const [selectedPresetId, setSelectedPresetId] = useState<string | undefined>(undefined);
|
||||
const [isFastMode, setIsFastMode] = useState(false);
|
||||
const [githubTrackingOverride, setGithubTrackingOverride] = useState<boolean | null>(null);
|
||||
const [priority, setPriority] = useState<TaskPriority>(DEFAULT_TASK_PRIORITY);
|
||||
const [nodeId, setNodeId] = useState<string | undefined>(undefined);
|
||||
const { nodes } = useNodes();
|
||||
@@ -435,6 +437,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
setPlanningModelId(undefined);
|
||||
setSelectedPresetId(undefined);
|
||||
setIsFastMode(false);
|
||||
setGithubTrackingOverride(null);
|
||||
setPriority(DEFAULT_TASK_PRIORITY);
|
||||
setNodeId(undefined);
|
||||
setShowDeps(false);
|
||||
@@ -508,6 +511,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
planningModelProvider: hasPlanningOverride ? planningProvider : undefined,
|
||||
planningModelId: hasPlanningOverride ? planningModelId : undefined,
|
||||
...(isFastMode ? { executionMode: "fast" } : {}),
|
||||
githubTracking: githubTrackingOverride !== null ? { enabled: githubTrackingOverride } : undefined,
|
||||
priority,
|
||||
nodeId,
|
||||
});
|
||||
@@ -555,6 +559,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
addToast,
|
||||
resetForm,
|
||||
isFastMode,
|
||||
githubTrackingOverride,
|
||||
priority,
|
||||
nodeId,
|
||||
]);
|
||||
@@ -1325,6 +1330,11 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
const selectedAgent = selectedAgentId ? agents.find((agent) => agent.id === selectedAgentId) : undefined;
|
||||
const selectedAgentLabel = selectedAgent?.name ?? selectedAgentId;
|
||||
const selectedNode = nodeId ? nodes.find((node) => node.id === nodeId) : undefined;
|
||||
const projectGithubTrackingDefault = settings?.githubTrackingEnabledByDefault === true;
|
||||
const effectiveGithubTracking = githubTrackingOverride ?? projectGithubTrackingDefault;
|
||||
const githubToggleLabel = effectiveGithubTracking
|
||||
? `GitHub tracking ON for next task (project default: ${projectGithubTrackingDefault ? "on" : "off"})`
|
||||
: "GitHub tracking OFF for next task";
|
||||
|
||||
// Show expanded controls based on disclosure state (user preference), not textarea focus
|
||||
const showExpandedControls = isDisclosureExpanded;
|
||||
@@ -1835,6 +1845,19 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
Fast
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm ${effectiveGithubTracking ? "btn-primary" : ""}`}
|
||||
onClick={() => setGithubTrackingOverride(!effectiveGithubTracking)}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
aria-pressed={effectiveGithubTracking}
|
||||
data-testid="quick-entry-github-toggle"
|
||||
title={githubToggleLabel}
|
||||
aria-label={githubToggleLabel}
|
||||
>
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-task-create btn-sm"
|
||||
|
||||
@@ -134,6 +134,7 @@ vi.mock("lucide-react", () => ({
|
||||
Bot: () => null,
|
||||
Server: () => null,
|
||||
Flag: () => null,
|
||||
Github: () => null,
|
||||
Maximize2: () => null,
|
||||
Minimize2: () => null,
|
||||
}));
|
||||
@@ -756,6 +757,57 @@ describe("QuickEntryBox", () => {
|
||||
expect(fastToggle.getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
it("renders GitHub toggle off by default when project setting is disabled", async () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: false,
|
||||
} as any);
|
||||
renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
|
||||
const githubToggle = await screen.findByTestId("quick-entry-github-toggle");
|
||||
expect(githubToggle.getAttribute("aria-pressed")).toBe("false");
|
||||
expect(githubToggle.className.includes("btn-primary")).toBe(false);
|
||||
});
|
||||
|
||||
it("renders GitHub toggle on by default when project setting is enabled", async () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: true,
|
||||
} as any);
|
||||
renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").className.includes("btn-primary")).toBe(true);
|
||||
});
|
||||
|
||||
it("flips GitHub toggle from both project-default directions", async () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: false,
|
||||
} as any);
|
||||
const { unmount } = renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
|
||||
const githubToggle = await screen.findByTestId("quick-entry-github-toggle");
|
||||
expect(githubToggle.getAttribute("aria-pressed")).toBe("false");
|
||||
fireEvent.click(githubToggle);
|
||||
expect(githubToggle.getAttribute("aria-pressed")).toBe("true");
|
||||
unmount();
|
||||
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: true,
|
||||
} as any);
|
||||
renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
fireEvent.click(screen.getByTestId("quick-entry-github-toggle"));
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("false");
|
||||
});
|
||||
|
||||
it.each(["Enter", "Save"] as const)("submits executionMode=fast when Fast is active via %s", async (submitPath) => {
|
||||
const { props } = renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
@@ -796,6 +848,71 @@ describe("QuickEntryBox", () => {
|
||||
expect(firstPayload.executionMode).toBeUndefined();
|
||||
});
|
||||
|
||||
it("omits githubTracking when toggle is untouched", async () => {
|
||||
const { props } = renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "No github override" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
const payload = props.onCreate.mock.calls[0]?.[0];
|
||||
expect(payload.githubTracking).toBeUndefined();
|
||||
});
|
||||
|
||||
it("submits githubTracking override when toggle is clicked", async () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: false,
|
||||
} as any);
|
||||
const { props } = renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
const githubToggle = await screen.findByTestId("quick-entry-github-toggle");
|
||||
fireEvent.click(githubToggle);
|
||||
fireEvent.change(textarea, { target: { value: "Override github tracking" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
description: "Override github tracking",
|
||||
githubTracking: { enabled: true },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("resets GitHub toggle to project default after successful task creation", async () => {
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
githubTrackingEnabledByDefault: true,
|
||||
} as any);
|
||||
const { props } = renderQuickEntryBox({ availableModels: undefined });
|
||||
expandQuickEntry();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId("quick-entry-github-toggle"));
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("false");
|
||||
|
||||
fireEvent.change(textarea, { target: { value: "Reset github toggle" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expandQuickEntry();
|
||||
expect(screen.getByTestId("quick-entry-github-toggle").getAttribute("aria-pressed")).toBe("true");
|
||||
});
|
||||
|
||||
it("resets Fast toggle to standard after successful task creation", async () => {
|
||||
const { props } = renderQuickEntryBox({});
|
||||
expandQuickEntry();
|
||||
|
||||
Reference in New Issue
Block a user