11import { blogSource } from "@/lib/source" ;
2- import { FeaturedPost } from "@/components/blog/FeaturedPost" ;
32import BlogCard from "@/components/blog/BlogCard" ;
43import { Separator } from "@/components/ui/separator" ;
54import Link from "next/link" ;
65import { CategoryTabs } from "@/components/blog/Category" ;
76import Subscribe from "@/components/blog/Subscribe" ;
7+ import { SearchInput } from "@/components/blog/SearchInput" ;
8+ import Image from "next/image" ;
89
910export const metadata = {
1011 title : "Blog | NocoDB" ,
1112 description : "Insights, tutorials, and updates from the team building the future of no-code databases." ,
1213}
1314
1415export default async function BlogPage ( { searchParams} : {
15- searchParams ?: Promise < { category ?: string ; page ?: string } >
16+ searchParams ?: Promise < { category ?: string ; page ?: string ; search ?: string } >
1617} ) {
1718 const posts = blogSource . getPages ( ) . sort (
1819 ( a , b ) => new Date ( b . data . date ) . getTime ( ) - new Date ( a . data . date ) . getTime ( )
1920 ) ;
2021
21- const featuredPost = posts ?. length ? posts [ 0 ] : null ;
22-
23- const latestPosts = posts . filter ( post => post . data ?. title !== featuredPost ?. data ?. title ) ;
24-
2522 const categories = new Set < string > ( ) ;
26- latestPosts . forEach ( ( post ) => {
23+ posts . forEach ( ( post ) => {
2724 categories . add ( post . data . category ?? "Uncategorized" ) ;
2825 } ) ;
2926
3027 const selectedCategory = ( await searchParams ) ?. category ;
28+ const searchQuery = ( await searchParams ) ?. search ?. toLowerCase ( ) || '' ;
3129 const currentPage = parseInt ( ( await searchParams ) ?. page || "1" , 10 ) ;
3230 const postsPerPage = 15 ;
3331
3432 const categoryFilteredPosts = selectedCategory
35- ? latestPosts . filter ( ( post ) => ( post . data . category ?? "Uncategorized" ) === selectedCategory )
36- : latestPosts ;
33+ ? posts . filter ( ( post ) => ( post . data . category ?? "Uncategorized" ) === selectedCategory )
34+ : posts ;
35+
36+ const searchFilteredPosts = searchQuery
37+ ? categoryFilteredPosts . filter ( ( post ) => {
38+ const titleMatch = post . data . title ?. toLowerCase ( ) . includes ( searchQuery ) ;
39+ const descriptionMatch = post . data . description ?. toLowerCase ( ) . includes ( searchQuery ) ;
40+ const authorMatch = post . data . author ?. toLowerCase ( ) . includes ( searchQuery ) ;
41+ return titleMatch || descriptionMatch || authorMatch ;
42+ } )
43+ : categoryFilteredPosts ;
3744
3845 const startIndex = ( currentPage - 1 ) * postsPerPage ;
3946 const endIndex = startIndex + postsPerPage ;
40- const displayedPosts = categoryFilteredPosts . slice ( 0 , endIndex ) ;
47+ const displayedPosts = searchFilteredPosts . slice ( 0 , endIndex ) ;
4148
42- const hasMorePosts = endIndex < categoryFilteredPosts . length ;
49+ const hasMorePosts = endIndex < searchFilteredPosts . length ;
4350 const nextPage = currentPage + 1 ;
4451 return (
4552 < main className = "py-8 w-full md:py-12" >
46- < div className = "container py-20 lg:py -16" >
47- < h1 className = "text-center text- nc-content-grey-emphasis text-[40px] font-semibold leading-15.5" >
53+ < div className = "container py-10 lg:pt -16 lg:pb-8 " >
54+ < h1 className = "text-nc-content-grey-emphasis text-[40px] font-semibold leading-15.5" >
4855 Blog
4956 </ h1 >
50- < h5 className = "text-nc-content-grey-subtle text-center mt-10 lg:mt-6 text-base leading-6 font-medium" >
51- Insights, tutorials, and updates < br />
52- from the team building the future of no-code databases.
57+ < h5 className = "text-nc-content-grey-subtle mt-6 lg:mt-2 text-base leading-6 font-medium" >
58+ Insights, tutorials, and updates from the team building the future of no-code databases.
5359 </ h5 >
60+ < div className = "mt-6" >
61+ < SearchInput />
62+ </ div >
5463 </ div >
55- < div className = "container mx-auto" >
56- { featuredPost && (
57- < FeaturedPost post = { featuredPost } />
58- ) }
59- </ div >
60-
61- < Subscribe />
6264
63- < div className = "container mt-5 lg:mt-20" >
64- < CategoryTabs categories = { Array . from ( categories ) } selectedCategory = { selectedCategory } />
65+
66+ < div className = "container mt-5" >
67+
68+ < CategoryTabs categories = { Array . from ( categories ) } selectedCategory = { selectedCategory } searchQuery = { searchQuery } />
6569 < Separator className = "border-nc-border-grey-medium" />
6670 </ div >
6771
6872 < div className = "container py-8 lg:pt-15 lg:pb-20 gap-8 lg:gap-16 grid grid-cols-1 lg:grid-cols-2" >
69- { displayedPosts . map ( ( post ) => (
70- < BlogCard post = { post } key = { post . url } />
71- ) ) }
73+ { displayedPosts . length === 0 ? (
74+ < div className = "col-span-full flex flex-col items-center justify-center py-16" >
75+ < Image
76+ src = "/img/no-search-result-found.png"
77+ alt = "No results found"
78+ width = { 400 }
79+ height = { 300 }
80+ className = "mb-6"
81+ />
82+ < h2 className = "text-2xl font-semibold text-nc-content-grey-emphasis mb-2" >
83+ No posts found
84+ </ h2 >
85+ < p className = "text-nc-content-grey-subtle" >
86+ { searchQuery
87+ ? `No blog posts match "${ searchQuery } ". Try a different search term.`
88+ : selectedCategory
89+ ? `No blog posts found in the "${ selectedCategory } " category.`
90+ : "No blog posts available." }
91+ </ p >
92+ </ div >
93+ ) : (
94+ displayedPosts . map ( ( post ) => (
95+ < BlogCard post = { post } key = { post . url } />
96+ ) )
97+ ) }
7298 </ div >
7399
74100 { hasMorePosts && (
@@ -79,6 +105,7 @@ export default async function BlogPage({searchParams}: {
79105 pathname : "/blog" ,
80106 query : {
81107 ...( selectedCategory ? { category : selectedCategory } : { } ) ,
108+ ...( searchQuery ? { search : searchQuery } : { } ) ,
82109 page : nextPage . toString ( ) ,
83110 } ,
84111 } }
@@ -89,6 +116,7 @@ export default async function BlogPage({searchParams}: {
89116 </ div >
90117 ) }
91118
119+ < Subscribe />
92120 < Separator />
93121 </ main >
94122 ) ;
0 commit comments