refactor(frontend/builder): address review comments in BuilderChatPanel

- Remove useCallback from handleApplyAction (violates AGENTS.md)
- Import TEXTAREA_MAX_LENGTH from PanelInput instead of duplicating constant
- Remove dead @tanstack/react-query mock and associated invalidateQueries test
This commit is contained in:
majdyz
2026-04-11 00:03:10 +07:00
parent 7804e03e7a
commit bb071a9c88
2 changed files with 29 additions and 64 deletions

View File

@@ -52,11 +52,6 @@ vi.mock("@/services/environment", () => ({
environment: { getAGPTServerBaseUrl: () => "http://localhost:8000" },
}));
const mockInvalidateQueries = vi.fn();
vi.mock("@tanstack/react-query", () => ({
useQueryClient: () => ({ invalidateQueries: mockInvalidateQueries }),
}));
const mockToast = vi.fn();
vi.mock("@/components/molecules/Toast/use-toast", () => ({
useToast: () => ({ toast: mockToast }),
@@ -108,7 +103,6 @@ beforeEach(() => {
mockSetNodes.mockClear();
mockSetEdges.mockClear();
mockPostV2CreateSession.mockClear();
mockInvalidateQueries.mockClear();
mockSendMessage.mockClear();
mockSetMessages.mockClear();
mockToast.mockClear();
@@ -428,29 +422,6 @@ describe("useBuilderChatPanel flowID reset", () => {
});
});
describe("useBuilderChatPanel apply does not trigger cache refetch", () => {
it("does NOT call invalidateQueries after applying an update_node_input action (prevents refetch overwriting local state)", () => {
mockNodes.push({
id: "n1",
data: { hardcodedValues: { existing: "val" } },
});
mockFlowID = "flow-cache";
const { result } = renderHook(() => useBuilderChatPanel());
act(() => {
result.current.handleApplyAction({
type: "update_node_input",
nodeId: "n1",
key: "query",
value: "new val",
});
});
expect(mockInvalidateQueries).not.toHaveBeenCalled();
});
});
describe("useBuilderChatPanel handleApplyAction", () => {
it("update_node_input: calls setNodes with merged hardcodedValues (bypasses history)", () => {
mockNodes.push({

View File

@@ -7,7 +7,6 @@ import { DefaultChatTransport } from "ai";
import {
type KeyboardEvent,
type RefObject,
useCallback,
useEffect,
useMemo,
useRef,
@@ -31,12 +30,10 @@ import {
parseGraphActions,
serializeGraphForChat,
} from "./helpers";
import { TEXTAREA_MAX_LENGTH } from "./components/PanelInput";
type SendMessageFn = ReturnType<typeof useChat>["sendMessage"];
/** Maximum characters accepted by `sendRawMessage`, mirroring the textarea `maxLength`. */
const MAX_RAW_MESSAGE_LENGTH = 4000;
/**
* Per-graph session cache with a simple LRU cap.
* Maps flowID → sessionId so the same chat session is reused each time the
@@ -452,35 +449,32 @@ export function useBuilderChatPanel({
}
}
const handleApplyAction = useCallback(
(action: GraphAction) => {
const deps: ApplyActionDeps = {
toast,
setNodes,
setEdges,
setUndoStack,
setAppliedActionKeys,
};
let applied = false;
if (action.type === "update_node_input") {
applied = applyUpdateNodeInput(action, deps);
} else if (action.type === "connect_nodes") {
applied = applyConnectNodes(action, deps);
} else {
// Exhaustiveness guard — TypeScript ensures all GraphAction types are handled above.
const _: never = action;
void _;
}
if (applied) {
setAppliedActionKeys((prev) => {
const next = new Set(prev);
next.add(getActionKey(action));
return next;
});
}
},
[toast, setNodes, setEdges],
);
function handleApplyAction(action: GraphAction) {
const deps: ApplyActionDeps = {
toast,
setNodes,
setEdges,
setUndoStack,
setAppliedActionKeys,
};
let applied = false;
if (action.type === "update_node_input") {
applied = applyUpdateNodeInput(action, deps);
} else if (action.type === "connect_nodes") {
applied = applyConnectNodes(action, deps);
} else {
// Exhaustiveness guard — TypeScript ensures all GraphAction types are handled above.
const _: never = action;
void _;
}
if (applied) {
setAppliedActionKeys((prev) => {
const next = new Set(prev);
next.add(getActionKey(action));
return next;
});
}
}
function handleUndoLastAction() {
// Read the current stack directly rather than inside the setUndoStack updater.
@@ -502,8 +496,8 @@ export function useBuilderChatPanel({
function sendRawMessage(text: string) {
if (!text || !canSend) return;
const trimmed =
text.length > MAX_RAW_MESSAGE_LENGTH
? text.slice(0, MAX_RAW_MESSAGE_LENGTH)
text.length > TEXTAREA_MAX_LENGTH
? text.slice(0, TEXTAREA_MAX_LENGTH)
: text;
sendMessage({ text: trimmed });
}