163 lines
4.8 KiB
TypeScript
163 lines
4.8 KiB
TypeScript
import React from 'react';
|
|
|
|
import { dbConnection } from '../../db';
|
|
import { formatDate } from '../utils';
|
|
|
|
// Extract the post type from the database return type
|
|
type Post = {
|
|
id: number;
|
|
path: string;
|
|
title: string;
|
|
date: string;
|
|
readingTime: string;
|
|
summary: string;
|
|
content: string;
|
|
tags: string[];
|
|
};
|
|
|
|
export function Home({ searchParams }: { searchParams: URLSearchParams }) {
|
|
const postsPerPage = 10;
|
|
const tags = searchParams.getAll('tag');
|
|
|
|
const currentPage = parseInt(searchParams.get('page') || "1", 10);
|
|
const totalPages = Math.ceil(dbConnection.getNumOfPosts(tags) / postsPerPage);
|
|
const offset = (currentPage - 1) * postsPerPage;
|
|
|
|
const posts = dbConnection.getPosts(postsPerPage, offset, tags); // Get posts for the current page
|
|
|
|
return (
|
|
<main>
|
|
<div className="posts-list">
|
|
{posts.length > 0 ? (
|
|
posts.map((post) => (
|
|
<PostCard key={post.id} post={post} />
|
|
))
|
|
) : (
|
|
<div className="empty-state">
|
|
<h2>No posts yet</h2>
|
|
<p>Check back soon for new blog posts!</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<Pagination searchParams={searchParams} currentPage={currentPage} totalPages={totalPages} />
|
|
</main>
|
|
);
|
|
}
|
|
|
|
function PostCard({ post }: { post: Post }) {
|
|
const tags = post.tags;
|
|
const formattedDate = formatDate(post.date);
|
|
|
|
return (
|
|
<article className="post-card">
|
|
<header className="post-card-header">
|
|
<h2 className="post-card-title">
|
|
<a href={`${post.path}`} className="post-card-link">{post.title}</a>
|
|
</h2>
|
|
<div className="post-card-meta">
|
|
<time dateTime={post.date}>{formattedDate}</time>
|
|
<span className="meta-separator">•</span>
|
|
<span className="read-time">5 min read</span>
|
|
</div>
|
|
{tags.length > 0 && (
|
|
<div className="post-card-tags">
|
|
{tags.map((tag, index) => (
|
|
<a
|
|
key={index}
|
|
href={`/?tag=${tag.toLowerCase().replace(/\s+/g, '-')}`}
|
|
className="post-tag"
|
|
>
|
|
{tag}
|
|
</a>
|
|
))}
|
|
</div>
|
|
)}
|
|
</header>
|
|
{post.summary && (
|
|
<div className="post-card-summary">
|
|
<p>{post.summary}</p>
|
|
</div>
|
|
)}
|
|
<footer className="post-card-footer">
|
|
<a href={`${post.path}`} className="read-more-link">Read more →</a>
|
|
</footer>
|
|
</article>
|
|
);
|
|
}
|
|
|
|
function Pagination({ currentPage, totalPages, searchParams }:{ currentPage: number; totalPages: number, searchParams: URLSearchParams }) {
|
|
// Calculate the range of page numbers to show
|
|
let startPage: number;
|
|
let endPage: number;
|
|
|
|
if (totalPages <= 9) {
|
|
// If there are fewer than 9 pages, show all of them
|
|
startPage = 1;
|
|
endPage = totalPages;
|
|
} else {
|
|
// If we have more than 9 pages, calculate the window
|
|
if (currentPage <= 5) {
|
|
// When we're at the start, show pages 1-9
|
|
startPage = 1;
|
|
endPage = 9;
|
|
} else if (currentPage >= totalPages - 4) {
|
|
// When we're near the end, show the last 9 pages
|
|
startPage = totalPages - 8;
|
|
endPage = totalPages;
|
|
} else {
|
|
// Otherwise, center the window around the current page
|
|
startPage = currentPage - 4;
|
|
endPage = currentPage + 4;
|
|
}
|
|
}
|
|
|
|
const pages = Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
|
|
|
|
let passThroughParams = ''
|
|
searchParams.forEach((val, key) => {
|
|
if(key != 'page')
|
|
passThroughParams += `&${key}=${val}`
|
|
})
|
|
|
|
return (
|
|
<nav className="pagination">
|
|
{/* Previous button - always visible, disabled on first page */}
|
|
<a
|
|
href={`/?page=${currentPage - 1}${passThroughParams}`}
|
|
className={`pagination-link ${currentPage === 1 ? 'disabled' : ''}`}
|
|
style={{
|
|
opacity: currentPage === 1 ? 0.5 : 1,
|
|
cursor: currentPage === 1 ? 'not-allowed' : 'pointer',
|
|
pointerEvents: currentPage === 1 ? 'none' : 'auto'
|
|
}}
|
|
>
|
|
Previous
|
|
</a>
|
|
|
|
{/* Page numbers */}
|
|
{pages.map((page) => (
|
|
<a
|
|
key={page}
|
|
href={`/?page=${page}${passThroughParams}`}
|
|
className={`pagination-link ${page === currentPage ? 'active' : ''}`}
|
|
>
|
|
{page}
|
|
</a>
|
|
))}
|
|
|
|
{/* Next button - always visible, disabled on last page */}
|
|
<a
|
|
href={`/?page=${currentPage + 1}${passThroughParams}`}
|
|
className={`pagination-link ${currentPage === totalPages ? 'disabled' : ''}`}
|
|
style={{
|
|
opacity: currentPage === totalPages ? 0.5 : 1,
|
|
cursor: currentPage === totalPages ? 'not-allowed' : 'pointer',
|
|
pointerEvents: currentPage === totalPages ? 'none' : 'auto'
|
|
}}
|
|
>
|
|
Next
|
|
</a>
|
|
</nav>
|
|
);
|
|
}
|