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 {
title: string;
description: string;
@ -41,11 +43,7 @@ const { title, description, date, tags } = Astro.props;
{
tags?.map((tag) => (
<li>
<a href={`/tags/${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>
<Tag name={tag} />
</li>
))
}

View file

@ -4,10 +4,9 @@ interface Props {
date: Date;
title: string;
description: string;
tags?: string[];
}
const { id, date, title, description, tags } = Astro.props;
const { id, date, title, description } = Astro.props;
---
<a href={`/posts/${id}`}>
@ -22,13 +21,6 @@ const { id, date, title, description, tags } = Astro.props;
}
</div>
<h2 class="text-2xl font-semibold mb-1">{title}</h2>
<p class="text-sm mb-3">{description}</p>
<div class="flex flex-wrap gap-2">
{
tags?.map((tag) => (
<span class="text-xs text-zinc-400 font-semibold">#{tag}</span>
))
}
</div>
<p class="text-base text-zinc-400">{description}</p>
</div>
</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 socialLinkedIn from "@/assets/images/social-linkedin.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">
@ -18,44 +19,36 @@ import socialX from "@/assets/images/social-x.svg";
<h1 class="text-5xl font-bold mt-4">Dhemas Nurjaya</h1>
<h2 class="text-xl mt-2">Passionate Software Engineer</h2>
<div class="flex flex-row gap-8 mt-8" aria-label="social links">
<a
<SocialLink
href="https://www.linkedin.com/in/dhemas-nurjaya-030890bb"
aria-label="Linkedin"
>
<Image
src={socialLinkedIn}
label="Linkedin"
icon={socialLinkedIn}
alt="LinkedIn"
loading="eager"
class="w-6 h-6 invert"
/>
</a>
<a href="https://github.com/dhemasnurjaya" aria-label="Github">
<Image
src={socialGithub}
<SocialLink
href="https://github.com/dhemasnurjaya"
label="Github"
icon={socialGithub}
alt="Github"
loading="eager"
class="w-6 h-6 invert"
/>
</a>
<a href="https://x.com/dhemaseka" aria-label="X">
<Image src={socialX} alt="X" loading="eager" class="w-6 h-6 invert" />
</a>
<a href="https://www.facebook.com/Dhemas" aria-label="Facebook">
<Image
src={socialFacebook}
<SocialLink
href="https://x.com/dhemaseka"
label="X"
icon={socialX}
alt="X"
/>
<SocialLink
href="https://www.facebook.com/Dhemas"
label="Facebook"
icon={socialFacebook}
alt="Facebook"
loading="eager"
class="w-6 h-6 invert"
/>
</a>
<a href="mailto:dhemasnurjaya@gmail.com" aria-label="Email">
<Image
src={socialEmail}
<SocialLink
href="mailto:dhemasnurjaya@gmail.com"
label="Email"
icon={socialEmail}
alt="Email"
loading="eager"
class="w-6 h-6 invert"
/>
</a>
</div>
<div class="mt-24"></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 Layout from "@/layouts/Layout.astro";
import BlogPostCard from "@/components/BlogPostCard.astro";
import PageTitle from "@/components/PageTitle.astro";
const posts = await getCollection("blog");
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"
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">
{
posts.map((post) => (
@ -21,7 +22,6 @@ posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
title={post.data.title}
date={post.data.date}
description={post.data.description}
tags={post.data.tags}
/>
</li>
))

View file

@ -2,6 +2,8 @@
import { getCollection, type CollectionEntry } from "astro:content";
import BlogPostCard from "@/components/BlogPostCard.astro";
import Layout from "@/layouts/Layout.astro";
import PageTitle from "@/components/PageTitle.astro";
import EmptyState from "@/components/EmptyState.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog");
@ -36,7 +38,7 @@ const { filteredPosts } = Astro.props;
title={`Posts tagged with "${name}" - 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 ? (
<ul class="space-y-4">
@ -53,7 +55,7 @@ const { filteredPosts } = Astro.props;
))}
</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>

View file

@ -1,6 +1,8 @@
---
import { getCollection } from "astro:content";
import Layout from "@/layouts/Layout.astro";
import PageTitle from "@/components/PageTitle.astro";
import Tag from "@/components/Tag.astro";
const posts = await getCollection("blog");
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">
<h1 class="text-4xl font-bold mb-8">Tags</h1>
<PageTitle title="Tags" />
<ul class="flex flex-wrap gap-4">
{
tags?.map(([tag, count]) => (
<li class="mb-4">
<a href={`/tags/${tag}`}>
<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>
<Tag name={tag} count={count} />
</li>
))
}