aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2021-12-20 22:59:32 +0100
committerArmand Philippot <git@armandphilippot.com>2021-12-20 22:59:32 +0100
commit168ed3215f659d44215fd02ac05cc1fe58be4e06 (patch)
tree48d15a4743749f483f50f919ffa94e38185588a1 /src
parentb181f93f0f26570696d3a90b58a966c56ac61047 (diff)
chore: create search view
Diffstat (limited to 'src')
-rw-r--r--src/components/PostsList/PostsList.tsx2
-rw-r--r--src/components/SearchForm/SearchForm.tsx4
-rw-r--r--src/pages/recherche/index.tsx110
-rw-r--r--src/services/graphql/queries.ts15
4 files changed, 127 insertions, 4 deletions
diff --git a/src/components/PostsList/PostsList.tsx b/src/components/PostsList/PostsList.tsx
index 03e8834..da72032 100644
--- a/src/components/PostsList/PostsList.tsx
+++ b/src/components/PostsList/PostsList.tsx
@@ -39,7 +39,7 @@ const PostsList = ({
const getPostsList = () => {
return data.map((page) => {
if (page.posts.length === 0) {
- return <p>{t`No results found.`}</p>;
+ return <p key="no-result">{t`No results found.`}</p>;
} else {
return (
<Fragment key={page.pageInfo.endCursor}>
diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx
index c02c224..e37ba9e 100644
--- a/src/components/SearchForm/SearchForm.tsx
+++ b/src/components/SearchForm/SearchForm.tsx
@@ -1,11 +1,13 @@
import { ButtonSubmit } from '@components/Buttons';
import { Form, Input } from '@components/Form';
import { t } from '@lingui/macro';
+import { useRouter } from 'next/router';
import { FormEvent, useEffect, useRef, useState } from 'react';
const SearchForm = ({ isOpened }: { isOpened: boolean }) => {
const [query, setQuery] = useState('');
const inputRef = useRef<HTMLInputElement>(null);
+ const router = useRouter();
useEffect(() => {
setTimeout(() => {
@@ -17,6 +19,8 @@ const SearchForm = ({ isOpened }: { isOpened: boolean }) => {
const launchSearch = (e: FormEvent) => {
e.preventDefault();
+ router.push({ pathname: '/recherche', query: { s: query } });
+ setQuery('');
};
return (
diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx
new file mode 100644
index 0000000..6de5816
--- /dev/null
+++ b/src/pages/recherche/index.tsx
@@ -0,0 +1,110 @@
+import { Button } from '@components/Buttons';
+import Layout from '@components/Layouts/Layout';
+import PostsList from '@components/PostsList/PostsList';
+import { config } from '@config/website';
+import { t } from '@lingui/macro';
+import { getPublishedPosts } from '@services/graphql/queries';
+import { NextPageWithLayout } from '@ts/types/app';
+import { PostsList as PostsListData } from '@ts/types/blog';
+import { loadTranslation } from '@utils/helpers/i18n';
+import { GetStaticProps } from 'next';
+import Head from 'next/head';
+import { useRouter } from 'next/router';
+import { ReactElement, useEffect, useState } from 'react';
+import useSWRInfinite from 'swr/infinite';
+
+const Search: NextPageWithLayout = () => {
+ const [query, setQuery] = useState('');
+ const router = useRouter();
+
+ useEffect(() => {
+ if (!router.isReady) return;
+
+ if (router.query?.s && typeof router.query.s === 'string') {
+ setQuery(router.query.s);
+ }
+ }, [router.isReady, router.query.s]);
+
+ const getKey = (pageIndex: number, previousData: PostsListData) => {
+ if (previousData && !previousData.posts) return null;
+
+ const args =
+ pageIndex === 0
+ ? { first: config.postsPerPage, searchQuery: query }
+ : {
+ first: config.postsPerPage,
+ after: previousData.pageInfo.endCursor,
+ searchQuery: query,
+ };
+
+ return args;
+ };
+
+ const { data, error, size, setSize } = useSWRInfinite(
+ getKey,
+ getPublishedPosts
+ );
+
+ const head = {
+ title: t`Search results for ${query}] | Armand Philippot`,
+ description: t`Discover search results for ${query}].`,
+ };
+
+ const isLoadingInitialData = !data && !error;
+ const isLoadingMore: boolean =
+ isLoadingInitialData ||
+ (size > 0 && data !== undefined && typeof data[size - 1] === 'undefined');
+
+ if (error) return <div>{t`Failed to load.`}</div>;
+ if (!data) return <div>{t`Loading...`}</div>;
+
+ const hasNextPage = data && data[data.length - 1].pageInfo.hasNextPage;
+
+ return (
+ <>
+ <Head>
+ <title>{head.title}</title>
+ <meta name="description" content={head.description} />
+ </Head>
+ <article>
+ <header>
+ <h1>
+ {query
+ ? t`Search results for: ${query}`
+ : t({
+ id: 'msg.search',
+ comment: 'Search page title',
+ message: 'Search',
+ })}
+ </h1>
+ </header>
+ <div>
+ <PostsList data={data} showYears={false} />
+ {hasNextPage && (
+ <Button
+ isDisabled={isLoadingMore}
+ clickHandler={() => setSize(size + 1)}
+ >{t`Load more?`}</Button>
+ )}
+ </div>
+ </article>
+ </>
+ );
+};
+
+Search.getLayout = (page: ReactElement) => <Layout>{page}</Layout>;
+
+export const getStaticProps: GetStaticProps = async (context) => {
+ const translation = await loadTranslation(
+ context.locale!,
+ process.env.NODE_ENV === 'production'
+ );
+
+ return {
+ props: {
+ translation,
+ },
+ };
+};
+
+export default Search;
diff --git a/src/services/graphql/queries.ts b/src/services/graphql/queries.ts
index b449612..652caa1 100644
--- a/src/services/graphql/queries.ts
+++ b/src/services/graphql/queries.ts
@@ -28,13 +28,22 @@ import { fetchApi } from './api';
export const getPublishedPosts = async ({
first = 10,
after = '',
+ searchQuery = '',
+}: {
+ first: number;
+ after?: string;
+ searchQuery?: string;
}): Promise<PostsList> => {
const query = gql`
- query AllPublishedPosts($first: Int, $after: String) {
+ query AllPublishedPosts($first: Int, $after: String, $searchQuery: String) {
posts(
after: $after
first: $first
- where: { status: PUBLISH, orderby: { field: DATE, order: DESC } }
+ where: {
+ status: PUBLISH
+ orderby: { field: DATE, order: DESC }
+ search: $searchQuery
+ }
) {
edges {
cursor
@@ -91,7 +100,7 @@ export const getPublishedPosts = async ({
}
`;
- const variables = { first, after };
+ const variables = { first, after, searchQuery };
const response = await fetchApi<RawPostsList>(query, variables);
const formattedPosts = response.posts.edges.map((post) => {
const formattedPost = getFormattedPostPreview(post.node);