From 0ad37ad433dd0aadd0f87a761b77375e20b10a5e Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Sat, 23 Dec 2023 05:51:12 -0500 Subject: [PATCH] feat: implement real search --- app/components/SearchBar.tsx | 21 +++++++++++++-------- app/root.tsx | 5 +---- app/routes/api.search.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 app/routes/api.search.ts diff --git a/app/components/SearchBar.tsx b/app/components/SearchBar.tsx index 10aa413..05d1ff5 100644 --- a/app/components/SearchBar.tsx +++ b/app/components/SearchBar.tsx @@ -11,6 +11,7 @@ import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/outline"; import { flushSync } from "react-dom"; import { Link, + useFetcher, useLocation, useNavigate, useSearchParams, @@ -40,6 +41,8 @@ const SearchBar = ({ sites }: { sites: SiteList }) => { const [dirtyInput, setDirtyInput] = useState(false); const [results, setResults] = useState([]); + const fetcher = useFetcher(); + const handleSearch = useCallback( async (event: FormEvent) => { event.preventDefault(); @@ -50,22 +53,24 @@ const SearchBar = ({ sites }: { sites: SiteList }) => { newSearchParams.set("q", query); } else { newSearchParams.delete("q"); + return; } navigate(`${pathname}?${newSearchParams.toString()}`); - // Perform search and update results state - // const searchResults = await fetchResults(query); - // Mock the search results - const searchResults = await getResults({ query }); - - setResults(searchResults); - setIsLoading(false); - setActiveInput(false); + fetcher.load(`/api/search?${newSearchParams}`); }, [query, searchParams, navigate, pathname] ); + useEffect(() => { + if (fetcher.data) { + setResults(fetcher.data as SearchResult[]); + setIsLoading(false); + setActiveInput(false); + } + }, [fetcher.data]); + const isActive = results.length > 0 || dirtyInput; return ( diff --git a/app/root.tsx b/app/root.tsx index 8394e81..614f807 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -9,13 +9,10 @@ import { } from "@remix-run/react"; import Header from "@/components/LayoutHeader"; // Adjust the import path as needed import Footer from "@/components/LayoutFooter"; // Adjust the import path as needed -import globalStyles from "../styles/global.scss?inline"; -import { cssBundleHref } from "@remix-run/css-bundle"; // Adjust the import path as needed +import globalStyles from "../styles/global.scss?url"; export const links: LinksFunction = () => [ { rel: "stylesheet", href: globalStyles }, - - ...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), // Add your Google font links here // Example: { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400&display=swap" }, // Example: { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Jaldi:wght@400&display=swap" }, diff --git a/app/routes/api.search.ts b/app/routes/api.search.ts new file mode 100644 index 0000000..69e81f8 --- /dev/null +++ b/app/routes/api.search.ts @@ -0,0 +1,26 @@ +import { type LoaderFunctionArgs } from "@remix-run/node"; +import search from "@/lib/search.js"; + +export async function loader({ request }: LoaderFunctionArgs) { + const searchParams = new URL(request.url).searchParams; + + const query = searchParams.get("q"); + + if (!query || !query.length) { + throw new Response("Invalid query", { + status: 400, + }); + } + + const results = await search.index("articles").search(query); + + return results.hits.map((item) => { + return { + id: item.id, + timestamp: item.createdAt, + title: item.title, + description: "", + slug: item.slug, + }; + }); +}