Code for my personal website

feat: create pages for projects

+100
+68
src/pages/projects/[...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 + import Link from '@/components/Link.astro'; 9 + 10 + export async function getStaticPaths() { 11 + const projects = (await getCollection('projects')) 12 + .filter((post) => !post.data.draft) 13 + .sort( 14 + (a, b) => 15 + new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf() 16 + ); 17 + return projects.map((project) => ({ 18 + params: { slug: project.slug }, 19 + props: project 20 + })); 21 + } 22 + type Props = CollectionEntry<'projects'>; 23 + 24 + const project = Astro.props; 25 + const { Content } = await project.render(); 26 + --- 27 + 28 + <Layout title={project.data.title} description={project.data.description}> 29 + <Container> 30 + <div class='animate'> 31 + <Back href='/projects'> Back to projects </Back> 32 + </div> 33 + <div class='space-y-1 my-10'> 34 + <div class='animate flex items-center gap-1.5'> 35 + <div class='font-base text-sm'> 36 + <DateComponent date={new Date(project.data.date)} /> 37 + </div> 38 + &bull; 39 + <div class='font-base text-sm'> 40 + {readingTime(project.body)} 41 + </div> 42 + </div> 43 + <div class='animate text-2xl font-semibold text-black dark:text-white'> 44 + {project.data.title} 45 + </div> 46 + { 47 + (project.data.demoURL || project.data.repoURL) && ( 48 + <nav class='animate flex gap-1'> 49 + {project.data.demoURL && ( 50 + <Link href={project.data.demoURL} external> 51 + demo 52 + </Link> 53 + )} 54 + {project.data.demoURL && project.data.repoURL && <span>/</span>} 55 + {project.data.repoURL && ( 56 + <Link href={project.data.repoURL} external> 57 + repo 58 + </Link> 59 + )} 60 + </nav> 61 + ) 62 + } 63 + </div> 64 + <article class='animate'> 65 + <Content /> 66 + </article> 67 + </Container> 68 + </Layout>
+32
src/pages/projects/index.astro
··· 1 + --- 2 + import { 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 { PROJECTS } from '@/config'; 7 + 8 + const projects = (await getCollection('projects')) 9 + .filter((project) => !project.data.draft) 10 + .sort( 11 + (a, b) => new Date(b.data.date).valueOf() - new Date(a.data.date).valueOf() 12 + ); 13 + --- 14 + 15 + <Layout title={PROJECTS.TITLE} description={PROJECTS.DESCRIPTION}> 16 + <Container> 17 + <div class='space-y-10'> 18 + <div class='animate font-semibold text-black dark:text-white'> 19 + Projects 20 + </div> 21 + <ul class='animate flex flex-col gap-4'> 22 + { 23 + projects.map((project) => ( 24 + <li> 25 + <NavCard entry={project} /> 26 + </li> 27 + )) 28 + } 29 + </ul> 30 + </div> 31 + </Container> 32 + </Layout>