feat: ArticleGrid and Homepage

This commit is contained in:
achmad
2026-05-28 22:30:17 +07:00
parent 1de59aba84
commit cffc2808b5
2 changed files with 56 additions and 98 deletions
+27 -98
View File
@@ -1,101 +1,30 @@
import Image from "next/image"; import { getSiteSettings, getArticles } from '@/lib/directus'
import HeroSection from '@/components/home/HeroSection'
import ArticleGrid from '@/components/home/ArticleGrid'
export const revalidate = false
export default async function HomePage() {
let heroArticle = null
let latestArticles: import('@/lib/types').Article[] = []
try {
const [settings, articles] = await Promise.all([
getSiteSettings(),
getArticles({ limit: 12 }),
])
heroArticle = settings.hero_article
latestArticles = settings.hero_article
? articles.filter((a) => a.id !== settings.hero_article!.id)
: articles
} catch {
// Directus not available yet
}
export default function Home() {
return ( return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]"> <>
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start"> {heroArticle && <HeroSection article={heroArticle} />}
<Image <ArticleGrid articles={latestArticles} title="Latest" />
className="dark:invert" </>
src="https://nextjs.org/icons/next.svg" )
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="https://nextjs.org/icons/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
} }
+29
View File
@@ -0,0 +1,29 @@
import ArticleCard from '@/components/article/ArticleCard'
import type { Article } from '@/lib/types'
interface Props {
articles: Article[]
title?: string
}
export default function ArticleGrid({ articles, title }: Props) {
if (articles.length === 0) return null
return (
<section className="max-w-[1200px] mx-auto px-6 py-6">
{title && (
<div className="flex items-center gap-4 mb-5">
<h2 className="text-xs font-bold text-text-primary uppercase tracking-widest shrink-0">
{title}
</h2>
<div className="flex-1 h-px bg-border" />
</div>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{articles.map((article) => (
<ArticleCard key={article.id} article={article} />
))}
</div>
</section>
)
}