feat: introduce new components for improved structure and functionality

This commit is contained in:
fiatcode 2026-01-22 17:48:29 +07:00
parent 2e511b5e00
commit c5e26d83c1
10 changed files with 101 additions and 62 deletions

View file

@ -1,4 +1,6 @@
--- ---
import Tag from "@/components/Tag.astro";
interface Props { interface Props {
title: string; title: string;
description: string; description: string;
@ -41,11 +43,7 @@ const { title, description, date, tags } = Astro.props;
{ {
tags?.map((tag) => ( tags?.map((tag) => (
<li> <li>
<a href={`/tags/${tag}`}> <Tag name={tag} />
<span class="text-sm text-zinc-400 font-semibold rounded-md bg-zinc-800 px-2 py-2 hover:bg-zinc-700">
#{tag}
</span>
</a>
</li> </li>
)) ))
} }

View file

@ -4,10 +4,9 @@ interface Props {
date: Date; date: Date;
title: string; title: string;
description: string; description: string;
tags?: string[];
} }
const { id, date, title, description, tags } = Astro.props; const { id, date, title, description } = Astro.props;
--- ---
<a href={`/posts/${id}`}> <a href={`/posts/${id}`}>
@ -22,13 +21,6 @@ const { id, date, title, description, tags } = Astro.props;
} }
</div> </div>
<h2 class="text-2xl font-semibold mb-1">{title}</h2> <h2 class="text-2xl font-semibold mb-1">{title}</h2>
<p class="text-sm mb-3">{description}</p> <p class="text-base text-zinc-400">{description}</p>
<div class="flex flex-wrap gap-2">
{
tags?.map((tag) => (
<span class="text-xs text-zinc-400 font-semibold">#{tag}</span>
))
}
</div>
</div> </div>
</a> </a>

View file

@ -0,0 +1,9 @@
---
interface Props {
message: string;
}
const { message } = Astro.props;
---
<p class="text-lg text-zinc-400">{message}</p>

View file

@ -6,6 +6,7 @@ import socialFacebook from "@/assets/images/social-facebook.svg";
import socialGithub from "@/assets/images/social-github.svg"; import socialGithub from "@/assets/images/social-github.svg";
import socialLinkedIn from "@/assets/images/social-linkedin.svg"; import socialLinkedIn from "@/assets/images/social-linkedin.svg";
import socialX from "@/assets/images/social-x.svg"; import socialX from "@/assets/images/social-x.svg";
import SocialLink from "@/components/SocialLink.astro";
--- ---
<div class="flex flex-col min-h-full items-center justify-center"> <div class="flex flex-col min-h-full items-center justify-center">
@ -18,44 +19,36 @@ import socialX from "@/assets/images/social-x.svg";
<h1 class="text-5xl font-bold mt-4">Dhemas Nurjaya</h1> <h1 class="text-5xl font-bold mt-4">Dhemas Nurjaya</h1>
<h2 class="text-xl mt-2">Passionate Software Engineer</h2> <h2 class="text-xl mt-2">Passionate Software Engineer</h2>
<div class="flex flex-row gap-8 mt-8" aria-label="social links"> <div class="flex flex-row gap-8 mt-8" aria-label="social links">
<a <SocialLink
href="https://www.linkedin.com/in/dhemas-nurjaya-030890bb" href="https://www.linkedin.com/in/dhemas-nurjaya-030890bb"
aria-label="Linkedin" label="Linkedin"
> icon={socialLinkedIn}
<Image
src={socialLinkedIn}
alt="LinkedIn" alt="LinkedIn"
loading="eager"
class="w-6 h-6 invert"
/> />
</a> <SocialLink
<a href="https://github.com/dhemasnurjaya" aria-label="Github"> href="https://github.com/dhemasnurjaya"
<Image label="Github"
src={socialGithub} icon={socialGithub}
alt="Github" alt="Github"
loading="eager"
class="w-6 h-6 invert"
/> />
</a> <SocialLink
<a href="https://x.com/dhemaseka" aria-label="X"> href="https://x.com/dhemaseka"
<Image src={socialX} alt="X" loading="eager" class="w-6 h-6 invert" /> label="X"
</a> icon={socialX}
<a href="https://www.facebook.com/Dhemas" aria-label="Facebook"> alt="X"
<Image />
src={socialFacebook} <SocialLink
href="https://www.facebook.com/Dhemas"
label="Facebook"
icon={socialFacebook}
alt="Facebook" alt="Facebook"
loading="eager"
class="w-6 h-6 invert"
/> />
</a> <SocialLink
<a href="mailto:dhemasnurjaya@gmail.com" aria-label="Email"> href="mailto:dhemasnurjaya@gmail.com"
<Image label="Email"
src={socialEmail} icon={socialEmail}
alt="Email" alt="Email"
loading="eager"
class="w-6 h-6 invert"
/> />
</a>
</div> </div>
<div class="mt-24"></div> <div class="mt-24"></div>
</div> </div>

View file

@ -0,0 +1,9 @@
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<h1 class="text-4xl font-bold mb-8">{title}</h1>

View file

@ -0,0 +1,17 @@
---
import { Image } from "astro:assets";
import type { ImageMetadata } from "astro";
interface Props {
href: string;
label: string;
icon: ImageMetadata;
alt: string;
}
const { href, label, icon, alt } = Astro.props;
---
<a href={href} aria-label={label}>
<Image src={icon} alt={alt} loading="eager" class="w-6 h-6 invert" />
</a>

21
src/components/Tag.astro Normal file
View file

@ -0,0 +1,21 @@
---
interface Props {
name: string;
count?: number;
href?: string;
variant?: "default" | "small";
}
const { name, count, href, variant = "default" } = Astro.props;
const linkHref = href || `/tags/${name}`;
const isSmall = variant === "small";
const textSize = isSmall ? "text-xs" : "text-sm";
---
<a href={linkHref}>
<span
class={`${textSize} text-zinc-400 font-semibold rounded-md bg-zinc-800 px-2 py-2 hover:bg-zinc-700`}
>
#{name}{count !== undefined && <sup>{count}</sup>}
</span>
</a>

View file

@ -2,6 +2,7 @@
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import Layout from "@/layouts/Layout.astro"; import Layout from "@/layouts/Layout.astro";
import BlogPostCard from "@/components/BlogPostCard.astro"; import BlogPostCard from "@/components/BlogPostCard.astro";
import PageTitle from "@/components/PageTitle.astro";
const posts = await getCollection("blog"); const posts = await getCollection("blog");
posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime()); posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
@ -11,7 +12,7 @@ posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
title="Posts - Dhemas Nurjaya" title="Posts - Dhemas Nurjaya"
description="Read the latest blog posts by Dhemas Nurjaya" description="Read the latest blog posts by Dhemas Nurjaya"
> >
<h1 class="text-4xl font-bold mb-8">Posts</h1> <PageTitle title="Posts" />
<ul class="space-y-4"> <ul class="space-y-4">
{ {
posts.map((post) => ( posts.map((post) => (
@ -21,7 +22,6 @@ posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
title={post.data.title} title={post.data.title}
date={post.data.date} date={post.data.date}
description={post.data.description} description={post.data.description}
tags={post.data.tags}
/> />
</li> </li>
)) ))

View file

@ -2,6 +2,8 @@
import { getCollection, type CollectionEntry } from "astro:content"; import { getCollection, type CollectionEntry } from "astro:content";
import BlogPostCard from "@/components/BlogPostCard.astro"; import BlogPostCard from "@/components/BlogPostCard.astro";
import Layout from "@/layouts/Layout.astro"; import Layout from "@/layouts/Layout.astro";
import PageTitle from "@/components/PageTitle.astro";
import EmptyState from "@/components/EmptyState.astro";
export async function getStaticPaths() { export async function getStaticPaths() {
const posts = await getCollection("blog"); const posts = await getCollection("blog");
@ -36,7 +38,7 @@ const { filteredPosts } = Astro.props;
title={`Posts tagged with "${name}" - Dhemas Nurjaya`} title={`Posts tagged with "${name}" - Dhemas Nurjaya`}
description={`Browse blog posts tagged with "${name}" by Dhemas Nurjaya`} description={`Browse blog posts tagged with "${name}" by Dhemas Nurjaya`}
> >
<h1 class="text-4xl font-bold mb-8">Posts tagged with "{name}"</h1> <PageTitle title={`Posts tagged with "${name}"`} />
{ {
filteredPosts.length > 0 ? ( filteredPosts.length > 0 ? (
<ul class="space-y-4"> <ul class="space-y-4">
@ -53,7 +55,7 @@ const { filteredPosts } = Astro.props;
))} ))}
</ul> </ul>
) : ( ) : (
<p class="text-lg text-zinc-400">No posts found with the tag "{name}".</p> <EmptyState message={`No posts found with the tag "${name}".`} />
) )
} }
</Layout> </Layout>

View file

@ -1,6 +1,8 @@
--- ---
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import Layout from "@/layouts/Layout.astro"; import Layout from "@/layouts/Layout.astro";
import PageTitle from "@/components/PageTitle.astro";
import Tag from "@/components/Tag.astro";
const posts = await getCollection("blog"); const posts = await getCollection("blog");
const tagsMap = new Map<string, number>(); const tagsMap = new Map<string, number>();
@ -16,16 +18,12 @@ const tags = Array.from(tagsMap.entries()).sort((a, b) =>
--- ---
<Layout title="Tags - Dhemas Nurjaya" description="Browse blog posts by tags"> <Layout title="Tags - Dhemas Nurjaya" description="Browse blog posts by tags">
<h1 class="text-4xl font-bold mb-8">Tags</h1> <PageTitle title="Tags" />
<ul class="flex flex-wrap gap-4"> <ul class="flex flex-wrap gap-4">
{ {
tags?.map(([tag, count]) => ( tags?.map(([tag, count]) => (
<li class="mb-4"> <li class="mb-4">
<a href={`/tags/${tag}`}> <Tag name={tag} count={count} />
<span class="text-lg text-zinc-400 font-semibold rounded-md bg-zinc-800 px-2 py-2 hover:bg-zinc-700">
{tag} <sup>{count}</sup>
</span>
</a>
</li> </li>
)) ))
} }