adds orama cloud

This commit is contained in:
Michele Riva 2023-08-16 22:39:51 +02:00
parent 6a153b627c
commit 89d1571525
6 changed files with 109 additions and 2 deletions

View File

@ -5,3 +5,5 @@ SITE_NAME="Next.js Commerce"
SHOPIFY_REVALIDATION_SECRET=""
SHOPIFY_STOREFRONT_ACCESS_TOKEN=""
SHOPIFY_STORE_DOMAIN="[your-shopify-store-subdomain].myshopify.com"
NEXT_PUBLIC_ORAMA_API_KEY=""
NEXT_PUBLIC_ORAMA_ENDPOINT=""

View File

@ -1,20 +1,38 @@
'use client';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { Results } from '@orama/orama';
import { orama, trimDescription } from 'lib/orama';
import { createUrl } from 'lib/utils';
import { useOutsideClick } from './useOutsideClick';
export default function Search() {
const router = useRouter();
const searchParams = useSearchParams();
const [searchValue, setSearchValue] = useState('');
const [searchResults, setSearchResults] = useState<Results>();
useEffect(() => {
setSearchValue(searchParams?.get('q') || '');
}, [searchParams, setSearchValue]);
useEffect(() => {
orama.search({
term: searchValue,
limit: 5,
boost: {
title: 2,
}
})
.then(setSearchResults)
.catch(console.log);
}, [searchValue]);
function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
@ -31,6 +49,14 @@ export default function Search() {
router.push(createUrl('/search', newParams));
}
const searchResultsRef = useRef(null);
useOutsideClick(searchResultsRef.current, () => {
setSearchValue('');
});
const showSearchResults = searchValue.length > 0 && !!searchResults
return (
<form onSubmit={onSubmit} className="w-max-[550px] relative w-full lg:w-80 xl:w-full">
<input
@ -45,6 +71,24 @@ export default function Search() {
<div className="absolute right-0 top-0 mr-3 flex h-full items-center">
<MagnifyingGlassIcon className="h-4" />
</div>
{
showSearchResults && (
<ul ref={searchResultsRef} className='nextra-scrollbar border border-gray-200 bg-white text-gray-100 dark:border-neutral-800 dark:bg-neutral-900 absolute top-full z-20 mt-2 overflow-auto overscroll-contain rounded-xl py-2.5 shadow-xl max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)] md:max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)] inset-x-0 ltr:md:left-auto rtl:md:right-auto contrast-more:border contrast-more:border-gray-900 contrast-more:dark:border-gray-50 w-full min-h-[100px]'>
{searchResults?.hits?.map((product) => (
<li key={product.id} className='mx-2.5 break-words rounded-md contrast-more:border text-gray-800 contrast-more:border-transparent dark:text-gray-300'>
<Link href={`/product/${product.document.handle}`} className='block scroll-m-12 px-2.5 py-2 rounded-md hover:bg-blue-600 hover:bg-opacity-10 hover:text-blue-500'>
<div className='text-base font-semibold leading-5'>
{product.document.title as string}
</div>
<div className='excerpt mt-1 text-sm leading-[1.35rem] text-gray-600 dark:text-gray-400 contrast-more:dark:text-gray-50'>
{trimDescription((product.document.description || product.document.title) as string)}
</div>
</Link>
</li>
))}
</ul>
)
}
</form>
);
}

View File

@ -0,0 +1,9 @@
import { useEffect } from 'react';
export function useOutsideClick(ref: any, onClickOut: () => void, deps = []){
useEffect(() => {
const onClick = ({target}: any) => !ref?.contains(target) && onClickOut?.()
document.addEventListener("click", onClick);
return () => document.removeEventListener("click", onClick);
}, deps);
}

16
lib/orama/index.ts Normal file
View File

@ -0,0 +1,16 @@
import { OramaClient } from '@oramacloud/client'
const ORAMA_API_KEY = process.env.NEXT_PUBLIC_ORAMA_API_KEY!
const ORAMA_ENDPOINT = process.env.NEXT_PUBLIC_ORAMA_ENDPOINT!
export const orama = new OramaClient({
endpoint: ORAMA_ENDPOINT,
api_key: ORAMA_API_KEY
})
export function trimDescription(description: string, maxSize = 80) {
if (description.length > maxSize) {
return `${description.substring(0, maxSize)}...`
}
return description
}

View File

@ -24,6 +24,8 @@
"dependencies": {
"@headlessui/react": "^1.7.15",
"@heroicons/react": "^2.0.18",
"@orama/orama": "^1.2.1",
"@oramacloud/client": "1.0.0-beta.19",
"clsx": "^2.0.0",
"next": "13.4.13-canary.15",
"react": "18.2.0",

36
pnpm-lock.yaml generated
View File

@ -1,4 +1,8 @@
lockfileVersion: '6.0'
lockfileVersion: '6.1'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
dependencies:
'@headlessui/react':
@ -7,6 +11,12 @@ dependencies:
'@heroicons/react':
specifier: ^2.0.18
version: 2.0.18(react@18.2.0)
'@orama/orama':
specifier: ^1.2.1
version: 1.2.1
'@oramacloud/client':
specifier: 1.0.0-beta.19
version: 1.0.0-beta.19
clsx:
specifier: ^2.0.0
version: 2.0.0
@ -315,6 +325,11 @@ packages:
dev: false
optional: true
/@noble/hashes@1.3.1:
resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==}
engines: {node: '>= 16'}
dev: false
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -336,6 +351,25 @@ packages:
fastq: 1.15.0
dev: true
/@orama/orama@1.2.1:
resolution: {integrity: sha512-vq3ar9REofq7Q87riamQB5y1aJc068Qwbnd7uAboeSfRTDj2+KdNiwjTEk01SVjr8eAvCx8iRZl4rkEYo0ETYw==}
engines: {node: '>= 16.0.0'}
dev: false
/@oramacloud/client@1.0.0-beta.19:
resolution: {integrity: sha512-iig7JV7xzI+PYczf4XbQTv8ai6xaA5BlJNDlQJSEcFjOZQ3q2Wf8w7soTu1tQvWcXto60mbivttV5pYAoo/h9A==}
dependencies:
'@orama/orama': 1.2.1
'@paralleldrive/cuid2': 2.2.2
react: 18.2.0
dev: false
/@paralleldrive/cuid2@2.2.2:
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
dependencies:
'@noble/hashes': 1.3.1
dev: false
/@pkgr/utils@2.4.2:
resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}