68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import type { Article } from '@/lib/types'
|
|
import ArticleCard from './ArticleCard'
|
|
|
|
interface Props {
|
|
categorySlug: string
|
|
initialCount: number
|
|
hasMore?: boolean
|
|
}
|
|
|
|
export default function LoadMoreButton({ categorySlug, initialCount, hasMore: initialHasMore = true }: Props) {
|
|
const [articles, setArticles] = useState<Article[]>([])
|
|
const [offset, setOffset] = useState(initialCount)
|
|
const [loading, setLoading] = useState(false)
|
|
const [hasMore, setHasMore] = useState(initialHasMore)
|
|
|
|
async function loadMore() {
|
|
setLoading(true)
|
|
try {
|
|
const params = new URLSearchParams({
|
|
'filter[status][_eq]': 'published',
|
|
'filter[category][slug][_eq]': categorySlug,
|
|
sort: '-published_at',
|
|
limit: '12',
|
|
offset: String(offset),
|
|
fields:
|
|
'id,title,slug,excerpt,featured_image,published_at,is_featured,category.id,category.name,category.slug',
|
|
access_token: process.env.NEXT_PUBLIC_DIRECTUS_TOKEN ?? '',
|
|
})
|
|
const res = await fetch(
|
|
`${process.env.NEXT_PUBLIC_DIRECTUS_URL}/items/articles?${params}`
|
|
)
|
|
const data = await res.json()
|
|
const newArticles: Article[] = data.data ?? []
|
|
setArticles((prev) => [...prev, ...newArticles])
|
|
setOffset((prev) => prev + newArticles.length)
|
|
if (newArticles.length < 12) setHasMore(false)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{articles.length > 0 && (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 mt-4">
|
|
{articles.map((article) => (
|
|
<ArticleCard key={article.id} article={article} />
|
|
))}
|
|
</div>
|
|
)}
|
|
{hasMore && (
|
|
<div className="flex justify-center mt-8 pb-8">
|
|
<button
|
|
onClick={loadMore}
|
|
disabled={loading}
|
|
className="bg-bg-card border border-border text-text-secondary text-sm font-medium px-6 py-2.5 rounded-sm hover:border-accent hover:text-accent transition-colors disabled:opacity-50"
|
|
>
|
|
{loading ? 'Loading…' : 'Load more'}
|
|
</button>
|
|
</div>
|
|
)}
|
|
</>
|
|
)
|
|
}
|