feat(FN-2975): merge fusion/fn-2975

- Stabilize QuickChatFAB search refresh lifecycle to prevent redundant or stale queries (`useTasks.ts`, `QuickChatFAB.tsx`)
- Add unit tests for QuickChatFAB component and `useTasks` hook
- Refactor QuickChatFAB styles and component structure

Commits merged:
- feat(FN-2975): complete Step 2 — stabilize search refresh lifecycle
- feat(FN-2972): merge fusion/fn-2972

Files changed:
packages/dashboard/app/components/QuickChatFAB.css | 60 ++++++++++++++++----
 packages/dashboard/app/components/QuickChatFAB.tsx | 65 +++++++++++-----------
 .../app/components/__tests__/QuickChatFAB.test.tsx | 32 +++++++++++
 .../dashboard/app/hooks/__tests__/useTasks.test.ts | 48 ++++++++++++++++
 packages/dashboard/app/hooks/useTasks.ts           |  4 +-
 5 files changed, 165 insertions(+), 44 deletions(-)

Fusion-Task-Id: FN-2975
This commit is contained in:
Fusion
2026-04-29 19:41:34 -07:00
committed by gsxdsm
parent e5157a96f8
commit 9b53fa46e8
2 changed files with 50 additions and 2 deletions

View File

@@ -1521,6 +1521,54 @@ describe("useTasks", () => {
expect(result.current.tasks[0].id).toBe("FN-001");
});
it("does not trigger immediate refresh when searchQuery changes", async () => {
vi.useFakeTimers({ shouldAdvanceTime: true });
const initialTasks = [createMockTask({ id: "FN-001", description: "Task 1" })];
const searchedTasks = [createMockTask({ id: "FN-002", description: "bug fix" })];
mockFetchTasks
.mockResolvedValueOnce(initialTasks)
.mockResolvedValue(searchedTasks);
const { rerender } = renderHook(
({ projectId, searchQuery }: { projectId?: string; searchQuery?: string }) =>
useTasks({ projectId, searchQuery }),
{ initialProps: { projectId: "project-a", searchQuery: undefined as string | undefined } }
);
await waitFor(() => {
expect(mockFetchTasks).toHaveBeenCalledTimes(1);
});
await act(async () => {
rerender({ projectId: "project-a", searchQuery: "bug" });
});
// Search query change should not trigger the initial-load effect.
expect(mockFetchTasks).toHaveBeenCalledTimes(1);
await act(async () => {
vi.advanceTimersByTime(299);
});
expect(mockFetchTasks).toHaveBeenCalledTimes(1);
await act(async () => {
vi.advanceTimersByTime(1);
});
await waitFor(() => {
expect(mockFetchTasks).toHaveBeenCalledTimes(2);
});
expect(mockFetchTasks).toHaveBeenLastCalledWith(
undefined,
undefined,
"project-a",
"bug",
false,
);
});
it("creates new EventSource for each project switch", async () => {
mockFetchTasks.mockResolvedValue([]);