-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
216 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { search } from '@/lib/search' | ||
|
||
export async function GET(request: Request) { | ||
const { searchParams } = new URL(request.url) | ||
|
||
const query = searchParams.get('query') | ||
|
||
if (!query || query.length < 3) { | ||
return new Response(JSON.stringify({ data: [] })) | ||
} | ||
|
||
const recipes = await search(query) | ||
|
||
const data = recipes.map(recipe => ({ | ||
id: recipe.id, | ||
label: recipe.title, | ||
value: `/${recipe.category.slug}/${recipe.slug}` | ||
})) | ||
|
||
return new Response(JSON.stringify({ data })) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
'use client' | ||
import { Fragment, useState } from 'react' | ||
import { Combobox, Transition } from '@headlessui/react' | ||
import { useRouter } from 'next/navigation' | ||
import { | ||
useQuery, | ||
QueryClient, | ||
QueryClientProvider, | ||
} from '@tanstack/react-query' | ||
// import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid' | ||
|
||
const queryClient = new QueryClient() | ||
|
||
function useSearch({ query }: { query: string }) { | ||
return useQuery({ | ||
queryKey: ['search', query], | ||
enabled: query !== '', | ||
queryFn: async () => { | ||
const res = await fetch('/api/search?query=' + encodeURIComponent(query)); | ||
return res.json(); | ||
}, | ||
}) | ||
} | ||
|
||
interface Props { | ||
query: string | ||
onChange: (value: string) => void | ||
onSelected: (value: string) => void | ||
loading?: boolean | ||
error?: string | ||
results?: { id: string, label: string, url: string }[] | ||
} | ||
|
||
|
||
function SearchForm({ query, onChange, onSelected, loading, error, results = [] }: Props) { | ||
return ( | ||
<div className="top-16 w-72"> | ||
<Combobox onChange={(recipe: { value: string }) => onSelected(recipe.value)}> | ||
<div className="relative mt-1"> | ||
<div className="relative w-full cursor-default overflow-hidden rounded-lg bg-white text-left shadow-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-teal-300 sm:text-sm"> | ||
<Combobox.Input | ||
className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:ring-0" | ||
displayValue={(person: { name: string } | null) => person ? person.name : ''} | ||
onChange={(event) => onChange(event.target.value)} | ||
/> | ||
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2"> | ||
{/* <ChevronUpDownIcon | ||
className="h-5 w-5 text-gray-400" | ||
aria-hidden="true" | ||
/> */} | ||
V | ||
</Combobox.Button> | ||
</div> | ||
<Transition | ||
as={Fragment} | ||
leave="transition ease-in duration-100" | ||
leaveFrom="opacity-100" | ||
leaveTo="opacity-0" | ||
afterLeave={() => onChange('')} | ||
> | ||
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> | ||
{results?.length === 0 && query !== '' ? ( | ||
<div className="relative cursor-default select-none py-2 px-4 text-gray-700"> | ||
Nothing found. | ||
</div> | ||
) : ( | ||
(results ?? []).map((recipe) => ( | ||
<Combobox.Option | ||
key={recipe.id} | ||
className={({ active }) => | ||
`relative cursor-default select-none py-2 pl-10 pr-4 ${active ? 'bg-teal-600 text-white' : 'text-gray-900' | ||
}` | ||
} | ||
value={recipe} | ||
> | ||
{({ selected, active }) => ( | ||
<> | ||
<span | ||
className={`block truncate ${selected ? 'font-medium' : 'font-normal' | ||
}`} | ||
> | ||
{recipe.label} | ||
</span> | ||
{selected ? ( | ||
<span | ||
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${active ? 'text-white' : 'text-teal-600' | ||
}`} | ||
> | ||
{/* <CheckIcon className="h-5 w-5" aria-hidden="true" /> */} | ||
V | ||
</span> | ||
) : null} | ||
</> | ||
)} | ||
</Combobox.Option> | ||
)) | ||
)} | ||
</Combobox.Options> | ||
</Transition> | ||
</div> | ||
</Combobox> | ||
</div> | ||
) | ||
} | ||
|
||
function SearchWidget({ onSelected }: { onSelected: (value: string) => void }) { | ||
const [query, setQuery] = useState('') | ||
|
||
const { status, data, error, isFetching } = useSearch({ query }) | ||
|
||
return ( | ||
<SearchForm | ||
query={query} | ||
onChange={(value) => setQuery(value)} | ||
onSelected={onSelected} | ||
loading={isFetching} | ||
results={data?.data ?? []} | ||
/> | ||
) | ||
} | ||
|
||
export function Search() { | ||
const router = useRouter() | ||
|
||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
<SearchWidget onSelected={url => router.push(url)} /> | ||
</QueryClientProvider> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { PrismaClient } from '@prisma/client' | ||
const prisma = new PrismaClient() | ||
|
||
export async function search(query: string) { | ||
if (!query || query.length < 3) { | ||
return [] | ||
} | ||
|
||
const recipes = await prisma.recipe.findMany({ | ||
where: { | ||
OR: [ | ||
{ title: { contains: query } } | ||
] | ||
}, | ||
include: { | ||
category: true, | ||
} | ||
}) | ||
|
||
return recipes | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1315,6 +1315,13 @@ | |
resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" | ||
integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== | ||
|
||
"@headlessui/react@^1.7.16": | ||
version "1.7.16" | ||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.16.tgz#9c458c9c4dbb708258c9e8da3fe5363f915f7b11" | ||
integrity sha512-2MphIAZdSUacZBT6EXk8AJkj+EuvaaJbtCyHTJrPsz8inhzCl7qeNPI1uk1AUvCgWylVtdN8cVVmnhUDPxPy3g== | ||
dependencies: | ||
client-only "^0.0.1" | ||
|
||
"@humanwhocodes/config-array@^0.11.8": | ||
version "0.11.8" | ||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" | ||
|
@@ -2528,6 +2535,19 @@ | |
dependencies: | ||
tslib "^2.4.0" | ||
|
||
"@tanstack/[email protected]": | ||
version "4.32.0" | ||
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.32.0.tgz#e0f4a830283612430450c13badd353766423f523" | ||
integrity sha512-ei4IYwL2kmlKSlCw9WgvV7PpXi0MiswVwfQRxawhJA690zWO3dU49igaQ/UMTl+Jy9jj9dK5IKAYvbX7kUvviQ== | ||
|
||
"@tanstack/react-query@^4.32.0": | ||
version "4.32.0" | ||
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.32.0.tgz#701b45b149cfd4b54a68705f9100973db3ba5d5d" | ||
integrity sha512-B8WUMcByYAH9500ENejDCATOmEZhqjtS9wsfiQ3BNa+s+yAynY8SESI8WWHhSqUmjd0pmCSFRP6BOUGSda3QXA== | ||
dependencies: | ||
"@tanstack/query-core" "4.32.0" | ||
use-sync-external-store "^1.2.0" | ||
|
||
"@testing-library/dom@^9.0.0": | ||
version "9.3.1" | ||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.1.tgz#8094f560e9389fb973fe957af41bf766937a9ee9" | ||
|
@@ -3965,7 +3985,7 @@ cli-table3@^0.6.1: | |
optionalDependencies: | ||
"@colors/colors" "1.5.0" | ||
|
||
[email protected]: | ||
[email protected], client-only@^0.0.1: | ||
version "0.0.1" | ||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" | ||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== | ||
|
@@ -8502,6 +8522,7 @@ streamsearch@^1.1.0: | |
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== | ||
|
||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: | ||
name string-width-cjs | ||
version "4.2.3" | ||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" | ||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== | ||
|
@@ -9155,6 +9176,11 @@ use-resize-observer@^9.1.0: | |
dependencies: | ||
"@juggle/resize-observer" "^3.3.1" | ||
|
||
use-sync-external-store@^1.2.0: | ||
version "1.2.0" | ||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" | ||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== | ||
|
||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: | ||
version "1.0.2" | ||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" | ||
|