import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; import { useSearchParams } from "@remix-run/react"; import { ApiSearchV1ListRequest, } from "../../src/api/types"; import { ProductSearchResult, SearchResponse, SellerSearchResult, } from "../../src/api/types/models"; import { createSearchSystemApi } from "~/utils/api-client-factory"; /** * Hook to fetch autocomplete suggestions from the API * Cancels previous requests when a new search is initiated */ export const useSearchAutocomplete = (query: string) => { return useQuery({ queryKey: ["searchAutocomplete", query], queryFn: async ({ signal }) => { if (!query || query.trim() === "") { return { suggestions: [] }; } const searchApi = createSearchSystemApi(); // Create an init override function that includes the signal const initOverrides = { signal }; try { return await searchApi.apiSearchV1AutocompleteList( { q: query }, initOverrides ); } catch (error) { // If the request was aborted, return empty results if (error instanceof DOMException && error.name === "AbortError") { return { suggestions: [] }; } throw error; } }, enabled: query.trim().length > 0, staleTime: 1000 * 10, // 10 seconds refetchOnWindowFocus: false, }); }; /** * Hook to fetch search results with infinite scrolling * Reads search params from URL and fetches data accordingly */ export const useSearchInfinite = (routeQueryParam?: string) => { const [searchParams] = useSearchParams(); // Get search query from route param and decode it const query = routeQueryParam ? decodeURIComponent(routeQueryParam) : ""; // Get other search parameters from URL const orderParam = searchParams.get("order"); const priceMinParam = searchParams.get("priceMin"); const priceMaxParam = searchParams.get("priceMax"); // Build search parameters const searchQueryParams: ApiSearchV1ListRequest = { q: query, }; // Add sortBy parameter based on order if (orderParam) { // Map our order values to what the API expects in sortBy switch (orderParam) { case "cheap": searchQueryParams.sortBy = "price"; break; case "expensive": searchQueryParams.sortBy = "-price"; // Negative for descending break; case "newest": searchQueryParams.sortBy = "newest"; break; default: searchQueryParams.sortBy = orderParam; } } // Add price filters if they exist if (priceMinParam) searchQueryParams.priceMin = parseInt(priceMinParam, 10); if (priceMaxParam) searchQueryParams.priceMax = parseInt(priceMaxParam, 10); return useInfiniteQuery({ queryKey: ["search", searchQueryParams], queryFn: async ({ pageParam = 1 }) => { const searchApi = createSearchSystemApi(); return await searchApi.apiSearchV1List({ ...searchQueryParams, page: pageParam as number, }); }, getNextPageParam: (lastPage: SearchResponse, allPages) => { if (!lastPage || !lastPage.products || !lastPage.products.length) { return undefined; } // Use the length of allPages to determine the next page number const currentPage = allPages.length; // If there are products and there should be more pages, return next page number if (lastPage.products.length > 0) { return currentPage + 1; } return undefined; }, initialPageParam: 1, enabled: !!query, // Only run query if search query exists }); }; // Re-export types from the API for convenience export type { ProductSearchResult, SellerSearchResult, SearchResponse };