From bf474d675aec354a56ba2eb082b2cece507d7f76 Mon Sep 17 00:00:00 2001 From: sherzod4033 Date: Tue, 20 Jan 2026 21:59:37 +0500 Subject: [PATCH] feat: add posts page with search functionality --- src/app/layout.tsx | 2 +- src/app/posts/page.tsx | 210 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/app/posts/page.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index f1184cf39..d69036841 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -55,7 +55,7 @@ export default function RootLayout({ children: React.ReactNode; }) { return ( - + {children} ); diff --git a/src/app/posts/page.tsx b/src/app/posts/page.tsx new file mode 100644 index 000000000..6e1d2dfaf --- /dev/null +++ b/src/app/posts/page.tsx @@ -0,0 +1,210 @@ +'use client'; + +import * as React from 'react'; + +interface Post { + userId: number; + id: number; + title: string; + body: string; +} + +export default function PostsPage() { + const [posts, setPosts] = React.useState([]); + const [searchQuery, setSearchQuery] = React.useState(''); + const [loading, setLoading] = React.useState(true); + const [error, setError] = React.useState(null); + + React.useEffect(() => { + const fetchPosts = async () => { + try { + setLoading(true); + const response = await fetch( + 'https://jsonplaceholder.typicode.com/posts', + ); + if (!response.ok) { + throw new Error('Failed to fetch posts'); + } + const data = await response.json(); + setPosts(data); + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred'); + } finally { + setLoading(false); + } + }; + + fetchPosts(); + }, []); + + const filteredPosts = React.useMemo(() => { + if (!searchQuery.trim()) return posts; + return posts.filter((post) => + post.title.toLowerCase().includes(searchQuery.toLowerCase()), + ); + }, [posts, searchQuery]); + + return ( +
+
+ {/* Header */} +
+

+ Posts Gallery +

+

+ Explore {posts.length} amazing posts +

+
+ + {/* Search Input */} +
+
+
+ + + +
+ setSearchQuery(e.target.value)} + className='w-full rounded-2xl border-2 border-gray-200 bg-white py-4 pl-12 pr-4 text-gray-900 shadow-lg transition-all duration-300 placeholder:text-gray-400 focus:border-indigo-500 focus:outline-none focus:ring-4 focus:ring-indigo-100' + /> + {searchQuery && ( + + )} +
+ {searchQuery && ( +

+ Found {filteredPosts.length} post + {filteredPosts.length !== 1 ? 's' : ''} +

+ )} +
+ + {/* Loading State */} + {loading && ( +
+
+
+ )} + + {/* Error State */} + {error && ( +
+
+ + + +
+

+ Error Loading Posts +

+

{error}

+
+ )} + + {/* Posts Grid */} + {!loading && !error && ( + <> + {filteredPosts.length === 0 ? ( +
+
+ + + +
+

+ No posts found +

+

Try adjusting your search query

+
+ ) : ( +
+ {filteredPosts.map((post) => ( +
+ {/* Gradient accent */} +
+ + {/* Post number badge */} +
+ + Post #{post.id} + +
+
+ + {/* Title */} +

+ {post.title} +

+ + {/* Body */} +

+ {post.body} +

+ + {/* Hover effect overlay */} +
+
+ ))} +
+ )} + + )} +
+
+ ); +}