diff --git a/src/app/[locale]/search/page.tsx b/src/app/[locale]/search/page.tsx
index b1e48c3..b835e01 100644
--- a/src/app/[locale]/search/page.tsx
+++ b/src/app/[locale]/search/page.tsx
@@ -1,5 +1,6 @@
import { Assistant } from '@/components/assistant';
import { Hits } from '@/components/hits';
+import { NotFound } from '@/components/not-found';
import { SearchProvider } from '@/components/search-provider';
import { SearchBox } from '@/components/searchbox';
@@ -16,6 +17,7 @@ export default async function Search() {
{/* */}
+
diff --git a/src/components/assistant.tsx b/src/components/assistant.tsx
index e8428f2..ccf7c84 100644
--- a/src/components/assistant.tsx
+++ b/src/components/assistant.tsx
@@ -19,29 +19,21 @@ export const Assistant = ({ className }: AssistantProps) => {
const hits = useHits();
const querying = useLoading();
const [query] = useQuery();
- const [seenHits, setSeenHits] = useState(false);
- useEffect(() => {
- if (hits.length > 0) {
- setSeenHits(true);
- }
- }, [hits]);
-
- if (!seenHits) {
- return null;
- }
return (
-
+
@@ -50,7 +42,7 @@ export const Assistant = ({ className }: AssistantProps) => {
type AssistantListenerProps = {
query: string;
- querying: boolean;
+ querying?: boolean;
hits: V1Hit[];
};
const AssistantListener = ({
@@ -59,8 +51,6 @@ const AssistantListener = ({
querying,
}: AssistantListenerProps) => {
const [summary, setSummary] = useState('');
- // Little hack to ensure we do not display the error message between assistant requests
- const [disableErrors, setDisableErrors] = useState(true);
const queryRef = useRef(query);
useEffect(() => {
@@ -68,7 +58,6 @@ const AssistantListener = ({
// This avoids doing a double-query in between the request-response from the query API.
queryRef.current = query;
setSummary('');
- setDisableErrors(true);
}, [query]);
const { refetch, status } = useStreamRequest(
@@ -86,14 +75,13 @@ const AssistantListener = ({
useEffect(() => {
refetchHandlerFromHits(hits, queryRef.current, refetch)?.();
- setDisableErrors(false);
}, [hits, refetch]);
- if (
- !querying &&
- !disableErrors &&
- (status === 'error' || (status === 'success' && summary.trim() === ''))
- ) {
+ if (querying) {
+ return null;
+ }
+
+ if (status === 'error' || (status === 'success' && summary.trim() === '')) {
return (
(
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/src/components/not-found.tsx b/src/components/not-found.tsx
new file mode 100644
index 0000000..f354bf6
--- /dev/null
+++ b/src/components/not-found.tsx
@@ -0,0 +1,37 @@
+'use client';
+
+import { useHits, useLoading, useQuery } from '@clinia/search-sdk-react';
+import { cn } from '@clinia-ui/react';
+import { NotFoundIcon } from './not-found-icon';
+
+type NotFoundProps = {
+ className?: string;
+};
+export const NotFound = ({ className }: NotFoundProps) => {
+ const [query] = useQuery();
+ const hits = useHits();
+ const loading = useLoading();
+
+ if (hits?.length > 0 || loading) {
+ return null;
+ }
+
+ return (
+
+
+
+ No results found for '{query}'
+
+
+ There aren't any results matching your query. This can happen when
+ you search for something overly specific, or something outside the scope
+ of your dataset.
+
+
+ );
+};
diff --git a/src/components/search-provider.tsx b/src/components/search-provider.tsx
index 4781fa9..414ddea 100644
--- a/src/components/search-provider.tsx
+++ b/src/components/search-provider.tsx
@@ -1,9 +1,15 @@
'use client';
import { SearchRequest, SearchResponse } from '@/lib/client';
-import { PropsWithChildren, useCallback, useMemo } from 'react';
+import {
+ PropsWithChildren,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+} from 'react';
import { Host } from '@clinia/client-common';
-import client from '@clinia/client-datapartition';
+import datapartitionclient from '@clinia/client-datapartition';
import {
SearchParameters,
type SearchSDKOptions,
@@ -30,22 +36,37 @@ const getHost = (): Host => {
};
};
-const datapartitionClient = client(
- 'clinia',
- {
- mode: 'BearerToken',
- bearerToken: '',
- },
- {
- hosts: [getHost()],
+type DatapartitionClient = ReturnType;
+let datapartitionClient: DatapartitionClient;
+const getClient = (): DatapartitionClient => {
+ if (!datapartitionClient) {
+ datapartitionClient = datapartitionclient(
+ 'clinia',
+ {
+ mode: 'BearerToken',
+ bearerToken: '',
+ },
+ {
+ hosts: [getHost()],
+ }
+ );
}
-);
+
+ return datapartitionClient;
+};
export const SearchProvider = ({ children, state }: SearchProviderProps) => {
+ const client = useRef(
+ // Dumb value, we're just setting this to avoid having undefined in the ref.
+ datapartitionclient('clinia', { mode: 'BearerToken', bearerToken: '' })
+ );
+ useEffect(() => {
+ // We set the client in a ref to avoid hydration errors (window not defined).
+ client.current = getClient();
+ }, []);
+
const search: SearchSDKOptions['search'] = async (_collection, params) => {
- const resp = await datapartitionClient.searchClient.query<
- Record
- >({
+ return client.current.searchClient.query>({
partitionKey: 'clinia',
collectionKey: 'articles',
v1SearchParameters: {
@@ -102,7 +123,6 @@ export const SearchProvider = ({ children, state }: SearchProviderProps) => {
],
},
});
- return resp;
};
const searchForFacets: SearchSDKOptions['searchForFacets'] =