diff --git a/common/next/next.config.js b/common/next/next.config.js index a041ca8419..20989f9d01 100644 --- a/common/next/next.config.js +++ b/common/next/next.config.js @@ -49,6 +49,25 @@ const createConfig = } return [...rewriteEntries]; }, + async redirects() { + return [ + { + source: '/articles/:slug', + destination: '/stories/:slug', + statusCode: 301, + }, + { + source: '/articles', + destination: '/search/stories', + statusCode: 301, + }, + { + source: '/stories/comic', + destination: '/search/stories?format=W7d_ghAAALWY3Ujc', + statusCode: 301, + }, + ]; + }, webpack: (config, { isServer, webpack }) => { config.plugins.push( new webpack.NormalModuleReplacementPlugin( diff --git a/common/services/prismic/link-resolver.test.ts b/common/services/prismic/link-resolver.test.ts index 280966e1ef..c91d288f61 100644 --- a/common/services/prismic/link-resolver.test.ts +++ b/common/services/prismic/link-resolver.test.ts @@ -7,7 +7,7 @@ import linkResolver from './link-resolver'; */ describe('webcomic edge case', () => { test.each([ - { doc: { type: 'webcomics', uid: '1' }, path: '/articles/1' }, + { doc: { type: 'webcomics', uid: '1' }, path: '/stories/1' }, { doc: { type: 'webcomic-series', uid: '1' }, path: '/series/1' }, ])('$doc resolves to $path', ({ doc, path }) => { expect(linkResolver(doc)).toBe(path); @@ -20,7 +20,7 @@ it('resolves exhibition guides to /guides/exhibitions/{id}', () => { }); test.each([ - { doc: { type: 'articles', uid: '1' }, path: '/articles/1' }, + { doc: { type: 'articles', uid: '1' }, path: '/stories/1' }, { doc: { type: 'pages', uid: '1' }, path: '/1' }, { doc: { type: 'not a thing', uid: '1' }, path: '/' }, ])('$doc resolves to $path', ({ doc, path }) => { diff --git a/common/services/prismic/link-resolver.ts b/common/services/prismic/link-resolver.ts index 39c4fc96c5..0e4e947234 100644 --- a/common/services/prismic/link-resolver.ts +++ b/common/services/prismic/link-resolver.ts @@ -22,7 +22,8 @@ function linkResolver(doc: Props | DataProps): string { const { uid, type } = doc; if (!uid) return '/'; - if (type === 'webcomics') return `/articles/${uid}`; + if (type === 'articles') return `/stories/${uid}`; + if (type === 'webcomics') return `/stories/${uid}`; if (type === 'webcomic-series') return `/series/${uid}`; if ( type === 'exhibition-guides' || diff --git a/content/webapp/pages/articles/index.tsx b/content/webapp/pages/articles/index.tsx deleted file mode 100644 index 0c4d909487..0000000000 --- a/content/webapp/pages/articles/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { GetServerSideProps } from 'next'; -import { FunctionComponent } from 'react'; - -import { pageDescriptions } from '@weco/common/data/microcopy'; -import { getServerData } from '@weco/common/server-data'; -import { appError, AppErrorProps } from '@weco/common/services/app'; -import type { PaginatedResults } from '@weco/common/services/prismic/types'; -import { serialiseProps } from '@weco/common/utils/json'; -import { JsonLdObj } from '@weco/common/views/components/JsonLd/JsonLd'; -import PageLayout from '@weco/common/views/components/PageLayout/PageLayout'; -import SpacingSection from '@weco/common/views/components/styled/SpacingSection'; -import LayoutPaginatedResults from '@weco/content/components/LayoutPaginatedResults/LayoutPaginatedResults'; -import { createClient } from '@weco/content/services/prismic/fetch'; -import { fetchArticles } from '@weco/content/services/prismic/fetch/articles'; -import { - transformArticle, - transformArticleToArticleBasic, -} from '@weco/content/services/prismic/transformers/articles'; -import { articleLd } from '@weco/content/services/prismic/transformers/json-ld'; -import { transformQuery } from '@weco/content/services/prismic/transformers/paginated-results'; -import { ArticleBasic } from '@weco/content/types/articles'; -import { getPage } from '@weco/content/utils/query-params'; -import { setCacheControl } from '@weco/content/utils/setCacheControl'; - -type Props = { - articles: PaginatedResults; - jsonLd: JsonLdObj[]; -}; - -export const getServerSideProps: GetServerSideProps< - Props | AppErrorProps -> = async context => { - setCacheControl(context.res); - const page = getPage(context.query); - - if (typeof page !== 'number') { - return appError(context, 400, page.message); - } - - const client = createClient(context); - const articlesQuery = await fetchArticles(client, { page }); - - const articles = transformQuery(articlesQuery, transformArticle); - const jsonLd = articles.results.map(articleLd); - const basicArticles = { - ...articles, - results: articles.results.map(transformArticleToArticleBasic), - }; - - const serverData = await getServerData(context); - - return { - props: serialiseProps({ - articles: basicArticles, - jsonLd, - serverData, - }), - }; -}; - -const ArticlesPage: FunctionComponent = ({ - articles, - jsonLd, -}: Props) => { - // `articles` could be empty if somebody paginates off the end of the list, - // e.g. /articles?page=500 - const image = articles.results[0]?.image; - - return ( - - - - - - ); -}; - -export default ArticlesPage; diff --git a/content/webapp/pages/articles/[articleId].tsx b/content/webapp/pages/stories/[articleId].tsx similarity index 98% rename from content/webapp/pages/articles/[articleId].tsx rename to content/webapp/pages/stories/[articleId].tsx index 23611f2b12..b72d4947fb 100644 --- a/content/webapp/pages/articles/[articleId].tsx +++ b/content/webapp/pages/stories/[articleId].tsx @@ -25,6 +25,7 @@ import ContentPage from '@weco/content/components/ContentPage/ContentPage'; import PartNumberIndicator from '@weco/content/components/PartNumberIndicator/PartNumberIndicator'; import SeriesNavigation from '@weco/content/components/SeriesNavigation/SeriesNavigation'; import { ArticleFormatIds } from '@weco/content/data/content-format-ids'; +import useHotjar from '@weco/content/hooks/useHotjar'; import { createClient } from '@weco/content/services/prismic/fetch'; import { fetchArticle, @@ -43,6 +44,7 @@ import { getHeroPicture, } from '@weco/content/utils/page-header'; import { setCacheControl } from '@weco/content/utils/setCacheControl'; + const ContentTypeWrapper = styled.div` display: flex; align-items: baseline; @@ -163,6 +165,7 @@ const HTMLDateWrapper = styled.span.attrs({ className: font('intr', 6) })` `; const ArticlePage: FunctionComponent = ({ article, jsonLd }) => { + useHotjar(true); const [listOfSeries, setListOfSeries] = useState(); useEffect(() => { @@ -332,7 +335,7 @@ const ArticlePage: FunctionComponent = ({ article, jsonLd }) => { ; - jsonLd: JsonLdObj[]; -}; - -type ContentTypeIdAndTitle = { - id: string; - title: string; -}; - -function getContentTypeId(type: ContentType): ContentTypeIdAndTitle { - switch (type) { - case 'comic': - return { - id: ArticleFormatIds.Comic, - title: 'Comics', - }; - } -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isContentType(x: any): x is ContentType { - return contentTypes.includes(x); -} - -export const getServerSideProps: GetServerSideProps< - Props | AppErrorProps -> = async context => { - setCacheControl(context.res); - const page = getPage(context.query); - - if (typeof page !== 'number') { - return appError(context, 400, page.message); - } - - const { contentType } = context.query; - - if (isContentType(contentType)) { - const serverData = await getServerData(context); - const contentTypeInfo = getContentTypeId(contentType); - - const client = createClient(context); - const seriesQuery = await fetchSeries(client, { - filters: prismic.filter.at('my.series.format', contentTypeInfo.id), - page, - orderings: [ - { - field: 'document.first_publication_date', - direction: 'desc', - }, - ], - }); - const series = transformQuery(seriesQuery, transformSeries); - const basicSeries = { - ...series, - results: series.results.map(transformSeriesToSeriesBasic), - }; - - const jsonLd = series.results.map(articleSeriesLd); - - return { - props: serialiseProps({ - title: contentTypeInfo.title, - contentType, - series: basicSeries, - jsonLd, - serverData, - }), - }; - } - - return { notFound: true }; -}; - -const ArticleSeriesManyPage: FunctionComponent = ({ - title, - contentType, - series, - jsonLd, -}: Props) => { - const image = series.results[0]?.image; - - return ( - <> - {/* TODO: remove this when we're sure we've got the right route */} - - - - - - - - - - ); -}; - -export default ArticleSeriesManyPage; diff --git a/content/webapp/pages/stories/index.tsx b/content/webapp/pages/stories/index.tsx index 7819146422..2b4eec136e 100644 --- a/content/webapp/pages/stories/index.tsx +++ b/content/webapp/pages/stories/index.tsx @@ -248,7 +248,7 @@ const StoriesPage: FunctionComponent = ({ @@ -269,7 +269,9 @@ const StoriesPage: FunctionComponent = ({ items={comicSeries} itemsPerRow={3} itemsHaveTransparentBackground={true} - links={[{ text: 'More comics', url: '/stories/comic' }]} + links={[ + { text: 'More comics', url: '/stories?format=W7d_ghAAALWY3Ujc' }, + ]} /> @@ -307,7 +309,7 @@ const StoriesPage: FunctionComponent = ({