import type { GetStaticProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useCallback } from 'react';
import { useIntl } from 'react-intl';
import {
getLayout,
Heading,
LinksWidget,
Notice,
PostsList,
Spinner,
SearchForm,
type SearchFormSubmit,
PageHeader,
Page,
PageSidebar,
PageBody,
} from '../../components';
import {
convertWPThematicPreviewToPageLink,
convertWPTopicPreviewToPageLink,
fetchThematicsCount,
fetchThematicsList,
fetchTopicsCount,
fetchTopicsList,
} from '../../services/graphql';
import styles from '../../styles/pages/blog.module.scss';
import type {
GraphQLConnection,
NextPageWithLayout,
WPThematicPreview,
WPTopicPreview,
} from '../../types';
import { CONFIG } from '../../utils/config';
import { ROUTES } from '../../utils/constants';
import {
getLinksItemData,
getPostsWithUrl,
getSchemaFrom,
getSearchResultsPageGraph,
getWebPageGraph,
} from '../../utils/helpers';
import { loadTranslation, type Messages } from '../../utils/helpers/server';
import {
useArticlesList,
useBreadcrumbs,
useThematicsList,
useTopicsList,
} from '../../utils/hooks';
const NoResults = () => {
const intl = useIntl();
const router = useRouter();
const searchSubmitHandler: SearchFormSubmit = useCallback(
async ({ query: searchQuery }) => {
if (!searchQuery)
return {
messages: {
error: intl.formatMessage({
defaultMessage: 'Query must be longer than one character.',
description: 'SearchPage: invalid query message',
id: 'e3ppRI',
}),
},
validator: (value) => value.query.length > 1,
};
await router.push({ pathname: ROUTES.SEARCH, query: { s: searchQuery } });
return undefined;
},
[intl, router]
);
return (
{router.query.s
? intl.formatMessage({
defaultMessage:
'No results found. Would you like to try a new search?',
description: 'SearchPage: no results',
id: 'E+ROR5',
})
: intl.formatMessage({
defaultMessage: 'Please use the form below to start searching:',
description: 'SearchPage: search for message',
id: 'A0TsHP',
})}
);
};
type SearchPageProps = {
data: {
thematics: GraphQLConnection;
topics: GraphQLConnection;
};
translation: Messages;
};
/**
* Search page.
*/
const SearchPage: NextPageWithLayout = ({ data }) => {
const intl = useIntl();
const { asPath, query } = useRouter();
const {
articles,
error,
firstNewResultIndex,
isLoading,
isLoadingMore,
isRefreshing,
hasNextPage,
loadMore,
} = useArticlesList({
perPage: CONFIG.postsPerPage,
searchQuery: typeof query.s === 'string' ? query.s : undefined,
});
const { isLoading: areThematicsLoading, thematics } = useThematicsList({
fallback: data.thematics,
input: { first: data.thematics.pageInfo.total },
});
const { isLoading: areTopicsLoading, topics } = useTopicsList({
fallback: data.topics,
input: { first: data.topics.pageInfo.total },
});
const messages = {
loading: {
thematicsList: intl.formatMessage({
defaultMessage: 'Thematics are loading...',
description: 'SearchPage: loading thematics message',
id: 'qFqWQH',
}),
topicsList: intl.formatMessage({
defaultMessage: 'Topics are loading...',
description: 'SearchPage: loading topics message',
id: 'tLflgC',
}),
},
pageTitle: query.s
? intl.formatMessage(
{
defaultMessage: 'Search results for "{query}"',
description: 'SearchPage: SEO - Page title',
id: 'N+3eau',
},
{ query: query.s as string }
)
: intl.formatMessage({
defaultMessage: 'Search',
description: 'SearchPage: SEO - Page title',
id: 'WDwNDl',
}),
seo: {
metaDesc: query.s
? intl.formatMessage(
{
defaultMessage:
'Discover search results for {query} on {websiteName} website.',
description: 'SearchPage: SEO - Meta description',
id: 'bW6Zda',
},
{ query: query.s as string, websiteName: CONFIG.name }
)
: intl.formatMessage(
{
defaultMessage: 'Search for a post on {websiteName} website.',
description: 'SearchPage: SEO - Meta description',
id: 'rEp1mS',
},
{ websiteName: CONFIG.name }
),
title: query.s
? intl.formatMessage(
{
defaultMessage: 'Search results for {query} - {websiteName}',
description: 'SearchPage: SEO - Page title',
id: 'QRDdye',
},
{ query: query.s as string, websiteName: CONFIG.name }
)
: intl.formatMessage(
{
defaultMessage: 'Search - {websiteName}',
description: 'SearchPage: SEO - Page title',
id: 'NqVQYo',
},
{ websiteName: CONFIG.name }
),
},
widgets: {
thematicsListTitle: intl.formatMessage({
defaultMessage: 'Thematics',
description: 'SearchPage: thematics list widget title',
id: 'Dq6+WH',
}),
topicsListTitle: intl.formatMessage({
defaultMessage: 'Topics',
description: 'SearchPage: topics list widget title',
id: 'N804XO',
}),
},
};
const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumbs();
const jsonLd = getSchemaFrom([
query.s
? getSearchResultsPageGraph({
breadcrumb: breadcrumbSchema,
description: messages.seo.metaDesc,
slug: asPath,
title: messages.pageTitle,
})
: getWebPageGraph({
breadcrumb: breadcrumbSchema,
description: messages.seo.metaDesc,
slug: asPath,
title: messages.pageTitle,
}),
]);
const pageUrl = `${CONFIG.url}${asPath}`;
return (
{messages.seo.title}
{/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */}
{/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}
{query.s &&
((articles?.length && articles[0].edges.length) || isLoading) ? (
page.edges.map((edge) => edge.node)
)
)
: []
}
sortByYear
total={articles ? articles[0].pageInfo.total : undefined}
/>
) : (
)}
{error ? (
{intl.formatMessage({
defaultMessage: 'Failed to load.',
description: 'SearchPage: failed to load text',
id: 'fOe8rH',
})}
) : null}
{areThematicsLoading ? (
{messages.loading.thematicsList}
) : (
{messages.widgets.thematicsListTitle}
}
items={getLinksItemData(
thematics.edges.map((edge) =>
convertWPThematicPreviewToPageLink(edge.node)
)
)}
/>
)}
{areTopicsLoading ? (
{messages.loading.topicsList}
) : (
{messages.widgets.topicsListTitle}
}
items={getLinksItemData(
topics.edges.map((edge) =>
convertWPTopicPreviewToPageLink(edge.node)
)
)}
/>
)}
);
};
SearchPage.getLayout = (page) => getLayout(page);
export const getStaticProps: GetStaticProps = async ({
locale,
}) => {
const totalThematics = await fetchThematicsCount();
const thematics = await fetchThematicsList({ first: totalThematics });
const totalTopics = await fetchTopicsCount();
const topics = await fetchTopicsList({ first: totalTopics });
const translation = await loadTranslation(locale);
return {
props: {
data: {
thematics,
topics,
},
translation,
},
};
};
export default SearchPage;