mirror of
https://github.com/AnimeThemes/animethemes-web.git
synced 2026-07-11 01:24:31 +02:00
feat: Reworked document pages (#194)
* Moved document pages from /wiki sub-path to root path. * Added a blog index page. * Added a page header to document pages. * Added redirects for the moved pages. * Removed wiki index page, because it will be a document page in the future. * Removed terms of service and privacy policy pages, because these will be document pages in the future.
This commit is contained in:
@@ -65,6 +65,20 @@ const nextConfig = {
|
||||
]
|
||||
}
|
||||
];
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/wiki/blog/:slug*",
|
||||
destination: "/blog/:slug*",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/wiki/status/:slug*",
|
||||
destination: "/blog/status_:slug*",
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ const StyledMarkdown = styled.div`
|
||||
}
|
||||
|
||||
& p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@@ -100,6 +101,10 @@ const StyledMarkdown = styled.div`
|
||||
& ${Card} {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
& img {
|
||||
border-radius: ${theme.scalars.borderRadiusCard};
|
||||
}
|
||||
`;
|
||||
|
||||
interface MarkdownProps {
|
||||
|
||||
+18
-17
@@ -156,6 +156,7 @@ export type Image = {
|
||||
|
||||
export type Page = {
|
||||
body: Scalars['String'];
|
||||
created_at: Scalars['String'];
|
||||
id: Maybe<Scalars['Int']>;
|
||||
name: Scalars['String'];
|
||||
slug: Scalars['String'];
|
||||
@@ -705,6 +706,18 @@ export type RandomThemeQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type RandomThemeQuery = { searchTheme: { data: Array<{ slug: string, type: string, sequence: number | null, group: string | null, id: number, entries: Array<{ version: number | null, episodes: string | null, spoiler: boolean, nsfw: boolean, videos: Array<{ basename: string, id: number, tags: string, resolution: number | null, nc: boolean, subbed: boolean, lyrics: boolean, uncen: boolean, source: VideoSource | null, overlap: VideoOverlap, audio: { basename: string } }> }>, anime: { slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null }> } };
|
||||
|
||||
export type DocumentPageQueryVariables = Exact<{
|
||||
pageSlug: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type DocumentPageQuery = { page: { name: string, body: string, created_at: string } | null };
|
||||
|
||||
export type DocumentPageAllQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type DocumentPageAllQuery = { pageAll: Array<{ slug: string }> };
|
||||
|
||||
export type DumpIndexPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -769,6 +782,11 @@ export type ArtistIndexPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type ArtistIndexPageQuery = { artistAll: Array<{ slug: string, name: string }> };
|
||||
|
||||
export type DocumentIndexPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type DocumentIndexPageQuery = { pageAll: Array<{ slug: string, name: string, created_at: string }> };
|
||||
|
||||
export type CharacterFragmentFragment = { id: number, seed: number, name: string, source: string, image: string, theme: { slug: string, type: string, sequence: number | null, group: string | null, id: number, anime: { slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null, entries: Array<{ version: number | null, videos: Array<{ id: number, basename: string, tags: string, audio: { basename: string } }> }> } | null };
|
||||
|
||||
export type RoundFragmentFragment = { tier: number, name: string | null, pairings: Array<{ order: number, group: number, votesA: number | null, votesB: number | null, characterA: { id: number, seed: number, name: string, source: string, image: string, theme: { slug: string, type: string, sequence: number | null, group: string | null, id: number, anime: { slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null, entries: Array<{ version: number | null, videos: Array<{ id: number, basename: string, tags: string, audio: { basename: string } }> }> } | null }, characterB: { id: number, seed: number, name: string, source: string, image: string, theme: { slug: string, type: string, sequence: number | null, group: string | null, id: number, anime: { slug: string, name: string, images: Array<{ link: string, facet: string | null }> }, song: { title: string | null, performances: Array<{ as: string | null, artist: { slug: string, name: string } }> } | null, entries: Array<{ version: number | null, videos: Array<{ id: number, basename: string, tags: string, audio: { basename: string } }> }> } | null } }> };
|
||||
@@ -901,23 +919,6 @@ export type StudioIndexPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type StudioIndexPageQuery = { studioAll: Array<{ slug: string, name: string }> };
|
||||
|
||||
export type DocumentPageQueryVariables = Exact<{
|
||||
pageSlug: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type DocumentPageQuery = { page: { name: string, body: string } | null };
|
||||
|
||||
export type DocumentPageAllQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type DocumentPageAllQuery = { pageAll: Array<{ slug: string }> };
|
||||
|
||||
export type DocumentIndexPageQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type DocumentIndexPageQuery = { pageAll: Array<{ slug: string, name: string }> };
|
||||
|
||||
export type SeasonDetailPageQueryVariables = Exact<{
|
||||
year?: InputMaybe<Scalars['Int']>;
|
||||
season: Scalars['String'];
|
||||
|
||||
@@ -65,11 +65,11 @@ const resolvers: IResolvers = {
|
||||
transformer: (seasons, _, { year }) => seasons.map((season: string) => ({ value: season, year: { value: year } }))
|
||||
}),
|
||||
page: apiResolver({
|
||||
endpoint: (_, { slug }) => `/page/${slug}?fields[page]=id,slug,name,body`,
|
||||
endpoint: (_, { slug }) => `/page/${slug}?fields[page]=id,slug,name,body,created_at`,
|
||||
extractor: (result) => result.page
|
||||
}),
|
||||
pageAll: apiResolver({
|
||||
endpoint: () => `/page`,
|
||||
endpoint: () => `/page?fields[page]=id,slug,name,body,created_at`,
|
||||
extractor: (result) => result.pages,
|
||||
pagination: true
|
||||
}),
|
||||
|
||||
@@ -216,6 +216,7 @@ const typeDefs = `
|
||||
slug: String!
|
||||
name: String!
|
||||
body: String!
|
||||
created_at: String!
|
||||
}
|
||||
|
||||
type FeaturedTheme {
|
||||
|
||||
@@ -59,6 +59,7 @@ interface DocumentPageParams extends ParsedUrlQuery {
|
||||
export default function DocumentPage({ page }: DocumentPageProps) {
|
||||
return (
|
||||
<>
|
||||
<PageHeader title={page.name} author={page.source.frontmatter?.author} createdAt={page.created_at} />
|
||||
<StyledGrid>
|
||||
<SEO title={page.name}/>
|
||||
<Markdown source={page.source} />
|
||||
@@ -72,12 +73,29 @@ export default function DocumentPage({ page }: DocumentPageProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface PageHeaderProps {
|
||||
title: string;
|
||||
author?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
function PageHeader({ title, author, createdAt }: PageHeaderProps) {
|
||||
return (
|
||||
<div>
|
||||
<Text variant="h1">{title}</Text>
|
||||
{ !!author && <Text variant="small" color="text-muted">By: {author} • </Text> }
|
||||
<Text variant="small" color="text-muted">{new Date(createdAt).toLocaleDateString("en", { dateStyle: "long" })}</Text>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<DocumentPageProps, DocumentPageParams> = async ({ params }) => {
|
||||
const { data, apiRequests } = await fetchData<DocumentPageQuery, DocumentPageQueryVariables>(gql`
|
||||
query DocumentPage($pageSlug: String!) {
|
||||
page(slug: $pageSlug) {
|
||||
name
|
||||
body
|
||||
created_at
|
||||
}
|
||||
}
|
||||
`, params && {
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { GetStaticProps } from "next";
|
||||
import getSharedPageProps from "utils/getSharedPageProps";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { DocumentPageProps } from "pages/wiki/[...pageSlug]";
|
||||
import serializeMarkdown from "utils/serializeMarkdown";
|
||||
|
||||
export { default } from "pages/wiki/[...pageSlug]/index";
|
||||
|
||||
export const getStaticProps: GetStaticProps<DocumentPageProps> = async () => {
|
||||
const markdown = readFileSync(join(process.cwd(), "src/res/markdown/policy.md"), "utf-8");
|
||||
|
||||
return {
|
||||
props: {
|
||||
...getSharedPageProps(),
|
||||
page: {
|
||||
name: "Privacy Policy",
|
||||
...await serializeMarkdown(markdown),
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { GetStaticProps } from "next";
|
||||
import getSharedPageProps from "utils/getSharedPageProps";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import type { DocumentPageProps } from "pages/wiki/[...pageSlug]";
|
||||
import serializeMarkdown from "utils/serializeMarkdown";
|
||||
|
||||
export { default } from "pages/wiki/[...pageSlug]/index";
|
||||
|
||||
export const getStaticProps: GetStaticProps<DocumentPageProps> = async () => {
|
||||
const markdown = readFileSync(join(process.cwd(), "src/res/markdown/terms.md"), "utf-8");
|
||||
|
||||
return {
|
||||
props: {
|
||||
...getSharedPageProps(),
|
||||
page: {
|
||||
name: "Terms of Service",
|
||||
...await serializeMarkdown(markdown),
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import { fetchData } from "lib/server";
|
||||
import { SEO } from "components/seo";
|
||||
import { Text } from "components/text";
|
||||
import type { SharedPageProps } from "utils/getSharedPageProps";
|
||||
import getSharedPageProps from "utils/getSharedPageProps";
|
||||
import type { GetStaticProps } from "next";
|
||||
import gql from "graphql-tag";
|
||||
import type { DocumentIndexPageQuery } from "generated/graphql";
|
||||
import { BackToTopButton, Button } from "components/button";
|
||||
import { Card } from "components/card";
|
||||
import Link from "next/link";
|
||||
import { Column, Row } from "components/box";
|
||||
import { groupBy } from "lodash-es";
|
||||
import { Fragment } from "react";
|
||||
|
||||
interface DocumentIndexPageProps extends SharedPageProps, DocumentIndexPageQuery {}
|
||||
|
||||
export default function DocumentIndexPage({ pageAll }: DocumentIndexPageProps) {
|
||||
const pageGroups = Object.entries(
|
||||
groupBy(pageAll, (page) => new Date(page.created_at).getFullYear())
|
||||
).sort((a, b) => parseInt(b[0]) - parseInt(a[0]));
|
||||
|
||||
return <>
|
||||
<SEO title="Blog"/>
|
||||
<BackToTopButton/>
|
||||
<Text variant="h1">Blog</Text>
|
||||
<Text>On our blog we share the latest updates to the AnimeThemes site as well as other interesting news surrounding the project.</Text>
|
||||
{pageGroups.map(([year, pages], index) => (
|
||||
<Fragment key={year}>
|
||||
{index > 0 && <Text variant="h2">{year}</Text>}
|
||||
{pages.map((page) => (
|
||||
<Link key={page.slug} href={`/${page.slug}`}>
|
||||
<Card>
|
||||
<Row style={{ "--justify-content": "space-between", "--align-items": "center", "--gap": "16px" }}>
|
||||
<Column style={{ "--gap": "8px" }}>
|
||||
<Text color="text-primary" link>{page.name}</Text>
|
||||
<Text variant="small" color="text-muted">Posted on: {new Date(page.created_at).toLocaleDateString("en", { dateStyle: "long" })}</Text>
|
||||
</Column>
|
||||
<Button variant="silent">Read more</Button>
|
||||
</Row>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</Fragment>
|
||||
))}
|
||||
</>;
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<DocumentIndexPageProps> = async () => {
|
||||
const { data, apiRequests } = await fetchData<DocumentIndexPageQuery>(gql`
|
||||
query DocumentIndexPage {
|
||||
pageAll {
|
||||
slug
|
||||
name
|
||||
created_at
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
return {
|
||||
props: {
|
||||
...getSharedPageProps(apiRequests),
|
||||
pageAll: data.pageAll
|
||||
.filter((page) => page.slug.startsWith("blog/") || page.slug.startsWith("status/"))
|
||||
.sort((a, b) => Date.parse(b.created_at) - Date.parse(a.created_at))
|
||||
},
|
||||
// Revalidate after 3 hours (= 10800 seconds).
|
||||
revalidate: 10800
|
||||
};
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
import { fetchData } from "lib/server";
|
||||
import { SEO } from "components/seo";
|
||||
import { Text } from "components/text";
|
||||
import type { SharedPageProps } from "utils/getSharedPageProps";
|
||||
import getSharedPageProps from "utils/getSharedPageProps";
|
||||
import type { GetStaticProps } from "next";
|
||||
import gql from "graphql-tag";
|
||||
import type { DocumentIndexPageQuery } from "generated/graphql";
|
||||
import { TextLink } from "components/text/TextLink";
|
||||
import { AlphabeticalIndex } from "components/index";
|
||||
import { BackToTopButton } from "components/button";
|
||||
|
||||
interface DocumentIndexPageProps extends SharedPageProps, DocumentIndexPageQuery {}
|
||||
|
||||
export default function DocumentIndexPage({ pageAll }: DocumentIndexPageProps) {
|
||||
return <>
|
||||
<SEO title="Wiki"/>
|
||||
<BackToTopButton/>
|
||||
<Text variant="h1">Wiki</Text>
|
||||
<AlphabeticalIndex items={pageAll}>
|
||||
{(page) => (
|
||||
<TextLink key={page.slug} href={`/wiki/${page.slug}`}>
|
||||
{page.name}
|
||||
</TextLink>
|
||||
)}
|
||||
</AlphabeticalIndex>
|
||||
</>;
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<DocumentIndexPageProps> = async () => {
|
||||
const { data, apiRequests } = await fetchData<DocumentIndexPageQuery>(gql`
|
||||
query DocumentIndexPage {
|
||||
pageAll {
|
||||
slug
|
||||
name
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
return {
|
||||
props: {
|
||||
...getSharedPageProps(apiRequests),
|
||||
pageAll: data.pageAll
|
||||
},
|
||||
// Revalidate after 3 hours (= 10800 seconds).
|
||||
revalidate: 10800
|
||||
};
|
||||
};
|
||||
@@ -20,6 +20,7 @@ export default async function serializeMarkdown(markdown: Compatible) {
|
||||
[rehypePrettyCode, { theme: "github-dark-dimmed" }],
|
||||
],
|
||||
},
|
||||
parseFrontmatter: true,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user