mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 18:52:06 +00:00
This commit adds the necessary components and hooks to implement a beer search feature on the website. It includes the following changes: - Add a new BeerSearch API route that returns a list of beers matching a search query. - Implement a new hook useBeerPostSearch that utilizes SWR to fetch data from the API and parse it using a schema. - Add a new page SearchPage that displays a search input field and a list of beer search results. - Use lodash's debounce function to avoid making too many requests while the user is typing in the search input field.
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
import Layout from '@/components/ui/Layout';
|
|
import { NextPage } from 'next';
|
|
|
|
import { useRouter } from 'next/router';
|
|
import BeerCard from '@/components/BeerIndex/BeerCard';
|
|
import { ChangeEvent, useEffect, useState } from 'react';
|
|
import Spinner from '@/components/ui/Spinner';
|
|
|
|
import debounce from 'lodash/debounce';
|
|
import useBeerPostSearch from '@/hooks/useBeerPostSearch';
|
|
import FormLabel from '@/components/ui/forms/FormLabel';
|
|
|
|
const DEBOUNCE_DELAY = 300;
|
|
|
|
const SearchPage: NextPage = () => {
|
|
const router = useRouter();
|
|
const querySearch = router.query.search as string | undefined;
|
|
const [searchValue, setSearchValue] = useState(querySearch || '');
|
|
const { searchResults, isLoading, searchError } = useBeerPostSearch(searchValue);
|
|
|
|
const debounceSearch = debounce((value: string) => {
|
|
router.push({
|
|
pathname: '/beers/search',
|
|
query: { search: value },
|
|
});
|
|
}, DEBOUNCE_DELAY);
|
|
|
|
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
const { value } = event.target;
|
|
setSearchValue(value);
|
|
debounceSearch(value);
|
|
};
|
|
|
|
useEffect(() => {
|
|
debounce(() => {
|
|
if (!querySearch || searchValue) {
|
|
return;
|
|
}
|
|
setSearchValue(searchValue);
|
|
}, DEBOUNCE_DELAY)();
|
|
}, [querySearch, searchValue]);
|
|
|
|
const showSearchResults = !isLoading && searchResults && !searchError;
|
|
|
|
return (
|
|
<Layout>
|
|
<div className="flex h-full w-full flex-col items-center justify-center">
|
|
<div className="h-full w-full space-y-20">
|
|
<div className="flex h-[50%] w-full items-center justify-center bg-base-200">
|
|
<div className="w-8/12">
|
|
<FormLabel htmlFor="search">What are you looking for?</FormLabel>
|
|
<input
|
|
type="text"
|
|
id="search"
|
|
className="input-bordered input w-full rounded-lg"
|
|
onChange={onChange}
|
|
value={searchValue}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col items-center justify-center">
|
|
{!showSearchResults ? (
|
|
<Spinner size="lg" />
|
|
) : (
|
|
<div className="grid w-8/12 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{searchResults.map((result) => {
|
|
return <BeerCard key={result.id} post={result} />;
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Layout>
|
|
);
|
|
};
|
|
|
|
export default SearchPage;
|