mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-05-29 22:37:44 +02:00
feat: add blog (#320)
This commit is contained in:
@ -12,9 +12,9 @@ const Error404: React.FC<FooterProps> = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='Divlo - 404' />
|
||||
<Head title='404 | Divlo' />
|
||||
|
||||
<Header />
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
<ErrorPage statusCode={404} message={t('errors:not-found')} />
|
||||
</main>
|
||||
|
@ -12,9 +12,9 @@ const Error500: React.FC<FooterProps> = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='Divlo - 500' />
|
||||
<Head title='500 | Divlo' />
|
||||
|
||||
<Header />
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
<ErrorPage statusCode={500} message={t('errors:server-error')} />
|
||||
</main>
|
||||
|
@ -4,7 +4,7 @@ import { ThemeProvider } from 'next-themes'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import UniversalCookie from 'universal-cookie'
|
||||
|
||||
import 'tailwindcss/tailwind.css'
|
||||
import 'styles/global.css'
|
||||
import '@fontsource/montserrat/400.css'
|
||||
import '@fontsource/montserrat/600.css'
|
||||
|
||||
|
79
pages/blog/[slug].tsx
Normal file
79
pages/blog/[slug].tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { GetStaticProps, GetStaticPaths } from 'next'
|
||||
import { MDXRemote } from 'next-mdx-remote'
|
||||
import date from 'date-and-time'
|
||||
import 'prism-themes/themes/prism-one-dark.css'
|
||||
|
||||
import { Head } from 'components/Head'
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer, FooterProps } from 'components/Footer'
|
||||
import type { Post } from 'utils/blog'
|
||||
|
||||
interface BlogPostPageProps extends FooterProps {
|
||||
post: Post
|
||||
}
|
||||
|
||||
const BlogPostPage: React.FC<BlogPostPageProps> = (props) => {
|
||||
const { version, post } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head
|
||||
title={`${post.frontmatter.title} | Divlo`}
|
||||
description={post.frontmatter.description}
|
||||
/>
|
||||
|
||||
<Header />
|
||||
<main className='flex flex-col flex-wrap flex-1 items-center'>
|
||||
<div className='flex flex-col items-center my-10'>
|
||||
<h1 className='text-3xl font-semibold'>{post.frontmatter.title}</h1>
|
||||
<p className='mt-2' data-cy='blog-post-date'>
|
||||
{date.format(new Date(post.frontmatter.publishedOn), 'DD/MM/YYYY')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='prose mb-10 px-8'>
|
||||
<MDXRemote
|
||||
{...post.source}
|
||||
components={{
|
||||
a: (props: React.ComponentPropsWithoutRef<'a'>) => (
|
||||
<a target='_blank' rel='noopener noreferrer' {...props} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<Footer version={version} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<BlogPostPageProps> = async (
|
||||
context
|
||||
) => {
|
||||
const slug = context?.params?.slug
|
||||
const { getPostBySlug } = await import('utils/blog')
|
||||
const post = await getPostBySlug(slug)
|
||||
if (post == null || (post != null && !post.frontmatter.isPublished)) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/404',
|
||||
permanent: false
|
||||
}
|
||||
}
|
||||
}
|
||||
const { readPackage } = await import('read-pkg')
|
||||
const { version } = await readPackage()
|
||||
return { props: { version, post } }
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const { getPosts } = await import('utils/blog')
|
||||
const posts = await getPosts()
|
||||
return {
|
||||
paths: posts.map((post) => {
|
||||
return { params: { slug: post.slug } }
|
||||
}),
|
||||
fallback: false
|
||||
}
|
||||
}
|
||||
|
||||
export default BlogPostPage
|
77
pages/blog/index.tsx
Normal file
77
pages/blog/index.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import Link from 'next/link'
|
||||
import date from 'date-and-time'
|
||||
|
||||
import { Head } from 'components/Head'
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer, FooterProps } from 'components/Footer'
|
||||
import { ShadowContainer } from 'components/design/ShadowContainer'
|
||||
import type { PostMetadata } from 'utils/blog'
|
||||
|
||||
const blogDescription =
|
||||
'The latest news about my journey of learning computer science.'
|
||||
|
||||
interface BlogPageProps extends FooterProps {
|
||||
posts: PostMetadata[]
|
||||
}
|
||||
|
||||
const BlogPage: React.FC<BlogPageProps> = (props) => {
|
||||
const { version, posts } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='Blog | Divlo' description={blogDescription} />
|
||||
|
||||
<Header />
|
||||
<main className='flex flex-col flex-wrap flex-1 items-center'>
|
||||
<div className='flex flex-col items-center mt-10'>
|
||||
<h1 className='text-4xl font-semibold'>Blog</h1>
|
||||
<p className='mt-6' data-cy='blog-post-date'>
|
||||
{blogDescription}
|
||||
</p>
|
||||
</div>
|
||||
<div className='w-full flex items-center justify-center p-8'>
|
||||
<div className='w-[1600px]' data-cy='blog-posts'>
|
||||
{posts.map((post, index) => {
|
||||
const postPublishedOn = date.format(
|
||||
new Date(post.frontmatter.publishedOn),
|
||||
'DD/MM/YYYY'
|
||||
)
|
||||
return (
|
||||
<Link href={`/blog/${post.slug}`} key={index}>
|
||||
<a data-cy='blog-post'>
|
||||
<ShadowContainer className='p-6 cursor-pointer transition duration-200 ease-in-out hover:-translate-y-2'>
|
||||
<h2
|
||||
data-cy='blog-post-title'
|
||||
className='text-xl font-semibold'
|
||||
>
|
||||
{post.frontmatter.title}
|
||||
</h2>
|
||||
<p data-cy='blog-post-date' className='mt-2'>
|
||||
{postPublishedOn}
|
||||
</p>
|
||||
<p data-cy='blog-post-description' className='mt-3'>
|
||||
{post.frontmatter.description}
|
||||
</p>
|
||||
</ShadowContainer>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<Footer version={version} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps<BlogPageProps> = async () => {
|
||||
const { readPackage } = await import('read-pkg')
|
||||
const { getPosts } = await import('utils/blog')
|
||||
const posts = await getPosts()
|
||||
const { version } = await readPackage()
|
||||
return { props: { version, posts } }
|
||||
}
|
||||
|
||||
export default BlogPage
|
@ -21,7 +21,7 @@ const Home: React.FC<FooterProps> = (props) => {
|
||||
<>
|
||||
<Head />
|
||||
|
||||
<Header />
|
||||
<Header showLanguage />
|
||||
<main className='flex flex-col md:mx-auto md:max-w-4xl lg:max-w-7xl'>
|
||||
<Section isMain id='about'>
|
||||
<Profile />
|
||||
|
Reference in New Issue
Block a user