@@ -35,27 +35,51 @@ export default function BlogsPage() {
3535 const [ togglingId , setTogglingId ] = useState < string | null > ( null )
3636 const [ triggeringCron , setTriggeringCron ] = useState ( false )
3737 const [ page , setPage ] = useState ( 1 )
38+ const [ total , setTotal ] = useState ( 0 )
39+ const [ stats , setStats ] = useState ( { total : 0 , published : 0 , drafts : 0 } )
3840
3941 const loadPosts = useCallback ( async ( ) => {
4042 setIsLoading ( true )
4143 setError ( null )
4244 try {
43- const data = await listBlogPosts ( { limit : 100 } )
45+ const data = await listBlogPosts ( {
46+ page,
47+ limit : PAGE_SIZE ,
48+ locale : localeFilter !== 'all' ? localeFilter : undefined ,
49+ status : statusFilter !== 'all' ? statusFilter : undefined ,
50+ } )
4451 setPosts ( data . posts )
52+ setTotal ( data . total )
4553 } catch ( err ) {
4654 console . error ( err )
4755 setError ( 'Failed to load blog posts.' )
4856 } finally {
4957 setIsLoading ( false )
5058 }
59+ } , [ page , localeFilter , statusFilter ] )
60+
61+ const loadStats = useCallback ( async ( ) => {
62+ try {
63+ const [ all , published , drafts ] = await Promise . all ( [
64+ listBlogPosts ( { limit : 1 } ) ,
65+ listBlogPosts ( { status : 'published' , limit : 1 } ) ,
66+ listBlogPosts ( { status : 'draft' , limit : 1 } ) ,
67+ ] )
68+ setStats ( { total : all . total , published : published . total , drafts : drafts . total } )
69+ } catch ( err ) {
70+ console . error ( 'Failed to load blog stats:' , err )
71+ }
5172 } , [ ] )
5273
5374 useEffect ( ( ) => { loadPosts ( ) } , [ loadPosts ] )
75+ useEffect ( ( ) => { loadStats ( ) } , [ loadStats ] )
76+
77+ // Reset to first page when filters change
78+ useEffect ( ( ) => { setPage ( 1 ) } , [ localeFilter , statusFilter ] )
5479
80+ // Search filters the currently loaded page client-side
5581 useEffect ( ( ) => {
5682 let result = posts
57- if ( localeFilter !== 'all' ) result = result . filter ( p => p . locale === localeFilter )
58- if ( statusFilter !== 'all' ) result = result . filter ( p => p . status === statusFilter )
5983 if ( search . trim ( ) ) {
6084 const q = search . toLowerCase ( )
6185 result = result . filter ( p =>
@@ -65,8 +89,7 @@ export default function BlogsPage() {
6589 )
6690 }
6791 setFiltered ( result )
68- setPage ( 1 )
69- } , [ posts , localeFilter , statusFilter , search ] )
92+ } , [ posts , search ] )
7093
7194 const handleToggleStatus = async ( post : BlogPostListItem ) => {
7295 setTogglingId ( post . id )
@@ -79,6 +102,7 @@ export default function BlogsPage() {
79102 toast . success ( `"${ post . title } " published` )
80103 }
81104 await loadPosts ( )
105+ loadStats ( )
82106 } catch ( err ) {
83107 toast . error ( err instanceof Error ? err . message : 'Failed to update status' )
84108 } finally {
@@ -93,6 +117,7 @@ export default function BlogsPage() {
93117 await deleteBlogPost ( post . id )
94118 toast . success ( 'Post deleted' )
95119 await loadPosts ( )
120+ loadStats ( )
96121 } catch ( err ) {
97122 toast . error ( err instanceof Error ? err . message : 'Failed to delete post' )
98123 } finally {
@@ -112,10 +137,7 @@ export default function BlogsPage() {
112137 }
113138 }
114139
115- const published = posts . filter ( p => p . status === 'published' ) . length
116- const drafts = posts . filter ( p => p . status === 'draft' ) . length
117- const totalPages = Math . max ( 1 , Math . ceil ( filtered . length / PAGE_SIZE ) )
118- const paginatedFiltered = filtered . slice ( ( page - 1 ) * PAGE_SIZE , page * PAGE_SIZE )
140+ const totalPages = Math . max ( 1 , Math . ceil ( total / PAGE_SIZE ) )
119141
120142 return (
121143 < div className = "space-y-6" >
@@ -144,9 +166,9 @@ export default function BlogsPage() {
144166 { /* Stats */ }
145167 < div className = "grid grid-cols-3 gap-4" >
146168 { [
147- { label : 'Total Posts' , value : posts . length , color : 'text-indigo-300' } ,
148- { label : 'Published' , value : published , color : 'text-emerald-300' } ,
149- { label : 'Drafts' , value : drafts , color : 'text-amber-300' } ,
169+ { label : 'Total Posts' , value : stats . total , color : 'text-indigo-300' } ,
170+ { label : 'Published' , value : stats . published , color : 'text-emerald-300' } ,
171+ { label : 'Drafts' , value : stats . drafts , color : 'text-amber-300' } ,
150172 ] . map ( stat => (
151173 < div key = { stat . label } className = "rounded-xl border border-white/10 bg-white/5 p-4" >
152174 < p className = "text-xs text-indigo-400/70" > { stat . label } </ p >
@@ -184,7 +206,7 @@ export default function BlogsPage() {
184206 < option value = "scheduled" > Scheduled</ option >
185207 < option value = "draft" > Draft</ option >
186208 </ select >
187- < span className = "text-xs text-indigo-400/60" > { filtered . length } posts</ span >
209+ < span className = "text-xs text-indigo-400/60" > { total } posts</ span >
188210 </ div >
189211
190212 { /* Table */ }
@@ -213,7 +235,7 @@ export default function BlogsPage() {
213235 </ tr >
214236 </ thead >
215237 < tbody className = "divide-y divide-white/5" >
216- { paginatedFiltered . map ( post => (
238+ { filtered . map ( post => (
217239 < tr key = { post . id } className = "group transition-colors hover:bg-white/5" >
218240 < td className = "px-4 py-3" >
219241 < div className = "flex items-start gap-2" >
@@ -309,7 +331,7 @@ export default function BlogsPage() {
309331 { totalPages > 1 && (
310332 < div className = "flex items-center justify-between text-sm" >
311333 < span className = "text-indigo-400/60" >
312- { ( page - 1 ) * PAGE_SIZE + 1 } –{ Math . min ( page * PAGE_SIZE , filtered . length ) } of { filtered . length }
334+ { ( page - 1 ) * PAGE_SIZE + 1 } –{ Math . min ( page * PAGE_SIZE , total ) } of { total }
313335 </ span >
314336 < div className = "flex gap-2" >
315337 < button
0 commit comments