feat(ui): sync issues search with URL query parameter

Debounces search input (300ms) and syncs it to a ?q= URL parameter so
searches persist across navigation and can be shared via URL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-03-06 07:46:44 -06:00
parent eb607f7df8
commit 2d21045424
2 changed files with 34 additions and 4 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo } from "react";
import { useEffect, useMemo, useCallback, useRef } from "react";
import { useSearchParams } from "@/lib/router";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { issuesApi } from "../api/issues";
@@ -14,9 +14,30 @@ import { CircleDot } from "lucide-react";
export function Issues() {
const { selectedCompanyId } = useCompany();
const { setBreadcrumbs } = useBreadcrumbs();
const [searchParams] = useSearchParams();
const [searchParams, setSearchParams] = useSearchParams();
const queryClient = useQueryClient();
const initialSearch = searchParams.get("q") ?? "";
const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
const handleSearchChange = useCallback((search: string) => {
clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
setSearchParams((prev) => {
const next = new URLSearchParams(prev);
if (search.trim()) {
next.set("q", search.trim());
} else {
next.delete("q");
}
return next;
}, { replace: true });
}, 300);
}, [setSearchParams]);
useEffect(() => {
return () => clearTimeout(debounceRef.current);
}, []);
const { data: agents } = useQuery({
queryKey: queryKeys.agents.list(selectedCompanyId!),
queryFn: () => agentsApi.list(selectedCompanyId!),
@@ -69,6 +90,8 @@ export function Issues() {
liveIssueIds={liveIssueIds}
viewStateKey="paperclip:issues-view"
initialAssignees={searchParams.get("assignee") ? [searchParams.get("assignee")!] : undefined}
initialSearch={initialSearch}
onSearchChange={handleSearchChange}
onUpdateIssue={(id, data) => updateIssue.mutate({ id, data })}
/>
);