Code for my personal website

feat: create pages for blog

+107
+53
src/pages/blog/[...slug].astro
··· 1 + --- 2 + import { type CollectionEntry, getCollection } from 'astro:content'; 3 + import Layout from '@/layouts/Layout.astro'; 4 + import Container from '@/components/Container.astro'; 5 + import DateComponent from '@/components/Date.astro'; 6 + import { readingTime } from '@/utils'; 7 + import Back from '@/components/Back.astro'; 8 + 9 + export async function getStaticPaths() { 10 + const posts = (await getCollection('blog')) 11 + .filter((post) => !post.data.draft) 12 + .sort( 13 + (a, b) => 14 + new Date(b.data.date as string).valueOf() - 15 + new Date(a.data.date).valueOf() 16 + ); 17 + 18 + return posts.map((post) => ({ 19 + params: { slug: post.slug }, 20 + props: post 21 + })); 22 + } 23 + 24 + type Props = CollectionEntry<'blog'>; 25 + 26 + const post = Astro.props; 27 + const { Content } = await post.render(); 28 + --- 29 + 30 + <Layout title={post.data.title} description={post.data.description}> 31 + <Container> 32 + <div class='animate'> 33 + <Back href='/blog'> Back to blog </Back> 34 + </div> 35 + <div class='space-y-1 my-10'> 36 + <div class='animate flex items-center gap-1.5'> 37 + <div class='font-base text-sm'> 38 + <DateComponent date={new Date(post.data.date)} /> 39 + </div> 40 + &bull; 41 + <div class='font-base text-sm'> 42 + {readingTime(post.body)} 43 + </div> 44 + </div> 45 + <div class='animate text-2xl font-semibold text-black dark:text-white'> 46 + {post.data.title} 47 + </div> 48 + </div> 49 + <article class='animate'> 50 + <Content /> 51 + </article> 52 + </Container> 53 + </Layout>
+54
src/pages/blog/index.astro
··· 1 + --- 2 + import { type CollectionEntry, getCollection } from 'astro:content'; 3 + import Layout from '@/layouts/Layout.astro'; 4 + import Container from '@/components/Container.astro'; 5 + import NavCard from '@/components/NavCard.astro'; 6 + import { BLOG } from '@/config'; 7 + 8 + const data = (await getCollection('blog')) 9 + .filter((post) => !post.data.draft) 10 + .sort( 11 + (a, b) => new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf() 12 + ); 13 + 14 + type Acc = { 15 + [year: string]: CollectionEntry<'blog'>[]; 16 + }; 17 + 18 + const posts = data.reduce((acc: Acc, post) => { 19 + const year = new Date(post.data.date).getFullYear().toString(); 20 + if (!acc[year]) { 21 + acc[year] = []; 22 + } 23 + acc[year].push(post); 24 + return acc; 25 + }, {}); 26 + 27 + const years = Object.keys(posts).sort((a, b) => parseInt(b) - parseInt(a)); 28 + --- 29 + 30 + <Layout title={BLOG.TITLE} description={BLOG.DESCRIPTION}> 31 + <Container> 32 + <div class='space-y-10'> 33 + <div class='animate font-semibold text-black dark:text-white'>Blog</div> 34 + <div class='space-y-4'> 35 + { 36 + years.map((year) => ( 37 + <section class='animate space-y-4'> 38 + <div class='font-semibold text-black dark:text-white'>{year}</div> 39 + <div> 40 + <ul class='flex flex-col gap-4'> 41 + {posts[year].map((post) => ( 42 + <li> 43 + <NavCard entry={post} /> 44 + </li> 45 + ))} 46 + </ul> 47 + </div> 48 + </section> 49 + )) 50 + } 51 + </div> 52 + </div> 53 + </Container> 54 + </Layout>