11
22import React , { useState , useMemo } from 'react' ;
33import { useQuery } from '@tanstack/react-query' ;
4- import { Calendar , User , ArrowRight } from 'lucide-react' ;
4+ import { Calendar , User , ArrowRight , ChevronLeft , ChevronRight } from 'lucide-react' ;
55import { Link } from 'react-router-dom' ;
66import Navbar from '../components/Navbar' ;
77import Footer from '../components/Footer' ;
88import BlogFilter from '../components/BlogFilter' ;
99import { motion } from 'framer-motion' ;
10+ import { Button } from '../components/ui/button' ;
1011
1112interface BlogPost {
1213 id : string ;
@@ -28,29 +29,26 @@ interface BlogPost {
2829}
2930
3031const fetchBlogPosts = async ( ) : Promise < BlogPost [ ] > => {
31- const response = await fetch ( 'https://blog-api.checkcle.io/api/collections/blog_detail/records' ) ;
32+ const response = await fetch ( 'https://blog-api.checkcle.io/api/collections/blog_detail/records?sort=-published_at ' ) ;
3233 if ( ! response . ok ) {
3334 throw new Error ( 'Failed to fetch blog posts' ) ;
3435 }
3536 const data = await response . json ( ) ;
36- const items : BlogPost [ ] = data . items || [ ] ;
37-
38- // Sort posts by published_at (or fallback to created) in descending order
39- return items . sort ( ( a , b ) =>
40- new Date ( b . published_at || b . created ) . getTime ( ) - new Date ( a . published_at || a . created ) . getTime ( )
41- ) ;
37+ return data . items || [ ] ;
4238} ;
4339
44-
4540const Blog = ( ) => {
4641 const [ selectedCategory , setSelectedCategory ] = useState ( 'all' ) ;
42+ const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
43+ const postsPerPage = 6 ;
44+
4745 const { data : posts , isLoading, error } = useQuery ( {
4846 queryKey : [ 'blog-posts' ] ,
4947 queryFn : fetchBlogPosts ,
5048 } ) ;
5149
52- const { filteredPosts, postCounts } = useMemo ( ( ) => {
53- if ( ! posts ) return { filteredPosts : [ ] , postCounts : { } } ;
50+ const { filteredPosts, postCounts, paginatedPosts , totalPages } = useMemo ( ( ) => {
51+ if ( ! posts ) return { filteredPosts : [ ] , postCounts : { } , paginatedPosts : [ ] , totalPages : 0 } ;
5452
5553 const publishedPosts = posts . filter ( post => post . status === 'published' ) ;
5654
@@ -82,9 +80,19 @@ const Blog = () => {
8280 post . categories . includes ( selectedCategory )
8381 ) ;
8482
85- return { filteredPosts : filtered , postCounts : counts } ;
86- } , [ posts , selectedCategory ] ) ;
83+ // Calculate pagination
84+ const totalPages = Math . ceil ( filtered . length / postsPerPage ) ;
85+ const startIndex = ( currentPage - 1 ) * postsPerPage ;
86+ const endIndex = startIndex + postsPerPage ;
87+ const paginatedPosts = filtered . slice ( startIndex , endIndex ) ;
88+
89+ return { filteredPosts : filtered , postCounts : counts , paginatedPosts, totalPages } ;
90+ } , [ posts , selectedCategory , currentPage , postsPerPage ] ) ;
8791
92+ // Reset to first page when category changes
93+ React . useEffect ( ( ) => {
94+ setCurrentPage ( 1 ) ;
95+ } , [ selectedCategory ] ) ;
8896
8997 const formatDate = ( dateString : string ) => {
9098 return new Date ( dateString ) . toLocaleDateString ( 'en-US' , {
@@ -121,8 +129,13 @@ const Blog = () => {
121129 ) ;
122130 }
123131
124- const featuredPosts = filteredPosts . filter ( post => post . is_featured ) ;
125- const regularPosts = filteredPosts . filter ( post => ! post . is_featured ) ;
132+ const featuredPosts = paginatedPosts . filter ( post => post . is_featured ) ;
133+ const regularPosts = paginatedPosts . filter ( post => ! post . is_featured ) ;
134+
135+ const handlePageChange = ( page : number ) => {
136+ setCurrentPage ( page ) ;
137+ window . scrollTo ( { top : 0 , behavior : 'smooth' } ) ;
138+ } ;
126139
127140 return (
128141 < div className = "flex flex-col min-h-screen bg-black text-white" >
@@ -138,7 +151,7 @@ const Blog = () => {
138151 transition = { { duration : 0.8 } }
139152 className = "text-5xl md:text-6xl font-bold mb-6 gradient-text"
140153 >
141- CheckCle Blog
154+ CheckCle Community
142155 </ motion . h1 >
143156 < motion . p
144157 initial = { { opacity : 0 , y : 20 } }
@@ -151,7 +164,6 @@ const Blog = () => {
151164 </ div >
152165 </ section >
153166
154- { /* Blog Filter */ }
155167 < section className = "py-8 bg-[#020617]" >
156168 < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
157169 < BlogFilter
@@ -166,7 +178,7 @@ const Blog = () => {
166178 { featuredPosts . length > 0 && (
167179 < section className = "py-16 bg-[#020617]" >
168180 < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
169- { selectedCategory === 'all' && < h3 className = "text-2xl font-bold mb-12 text-center" > Featured Posts</ h3 > }
181+ { selectedCategory === 'all' && < h3 className = "text-2xl font-bold mb-12 text-center" > Featured Posts</ h3 > }
170182 < div className = "grid md:grid-cols-2 lg:grid-cols-3 gap-8" >
171183 { featuredPosts . map ( ( post , index ) => (
172184 < motion . article
@@ -217,8 +229,7 @@ const Blog = () => {
217229 </ section >
218230 ) }
219231
220-
221- { regularPosts . length > 0 && (
232+ { regularPosts . length > 0 && (
222233 < section className = "py-16" >
223234 < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
224235 { selectedCategory === 'all' && featuredPosts . length > 0 && < h3 className = "text-2xl font-bold mb-12 text-center" > Latest Posts</ h3 > }
@@ -241,8 +252,8 @@ const Blog = () => {
241252 ) : (
242253 < div className = "text-white text-6xl font-bold opacity-50" > R</ div >
243254 ) }
244- </ div >
245- < div className = "p-6" >
255+ </ div >
256+ < div className = "p-6" >
246257 < div className = "flex items-center text-sm text-gray-400 mb-3" >
247258 < User className = "w-4 h-4 mr-2" />
248259 < span > { post . author_name } </ span >
@@ -268,9 +279,57 @@ const Blog = () => {
268279 </ motion . article >
269280 ) ) }
270281 </ div >
282+ </ div >
283+ </ section >
284+ ) }
271285
286+ { /* Pagination */ }
287+ { totalPages > 1 && (
288+ < section className = "py-8" >
289+ < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
290+ < div className = "flex justify-center items-center gap-4" >
291+ < Button
292+ variant = "outline"
293+ size = "default"
294+ onClick = { ( ) => handlePageChange ( currentPage - 1 ) }
295+ disabled = { currentPage === 1 }
296+ className = "flex items-center gap-2 px-4 py-2 border-emerald-400/30 text-emerald-400 hover:bg-emerald-400/10 disabled:opacity-50 disabled:cursor-not-allowed"
297+ >
298+ < ChevronLeft className = "w-4 h-4" />
299+ Previous
300+ </ Button >
301+
302+ < div className = "flex items-center gap-2" >
303+ { Array . from ( { length : totalPages } , ( _ , i ) => i + 1 ) . map ( ( page ) => (
304+ < Button
305+ key = { page }
306+ variant = { currentPage === page ? "default" : "ghost" }
307+ size = "default"
308+ onClick = { ( ) => handlePageChange ( page ) }
309+ className = { `min-w-[40px] h-10 ${
310+ currentPage === page
311+ ? "bg-emerald-400 text-black hover:bg-emerald-500"
312+ : "text-emerald-400 hover:bg-emerald-400/10"
313+ } `}
314+ >
315+ { page }
316+ </ Button >
317+ ) ) }
318+ </ div >
319+
320+ < Button
321+ variant = "outline"
322+ size = "default"
323+ onClick = { ( ) => handlePageChange ( currentPage + 1 ) }
324+ disabled = { currentPage === totalPages }
325+ className = "flex items-center gap-2 px-4 py-2 border-emerald-400/30 text-emerald-400 hover:bg-emerald-400/10 disabled:opacity-50 disabled:cursor-not-allowed"
326+ >
327+ Next
328+ < ChevronRight className = "w-4 h-4" />
329+ </ Button >
330+ </ div >
272331 </ div >
273- </ section >
332+ </ section >
274333 ) }
275334 </ main >
276335
0 commit comments