Skip to content

Commit 19a6cae

Browse files
committed
add search bar in feeds page
1 parent 05af647 commit 19a6cae

2 files changed

Lines changed: 67 additions & 15 deletions

File tree

‎apps/codebility/app/home/feeds/_components/Feed.tsx‎

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,57 @@
11
"use client";
22

3-
import { useEffect, useState } from "react";
3+
import { useEffect, useMemo, useState } from "react";
44
import DefaultPagination from "@/components/ui/pagination";
55
import { useFeedsStore } from "@/store/feeds-store";
66

77
import Post from "./PostCard";
88

99
interface FeedProp {
1010
isAdmin: boolean;
11-
onCreatePost?: () => void;
11+
searchQuery?: string;
1212
}
1313

14-
export default function Feed({ isAdmin }: FeedProp) {
14+
export default function Feed({ isAdmin, searchQuery }: FeedProp) {
1515
const posts = useFeedsStore((state) => state.posts);
1616
const fetchPosts = useFeedsStore((state) => state.fetchPosts);
1717

1818
const [currentPage, setCurrentPage] = useState(1);
1919

20+
useEffect(() => {
21+
fetchPosts();
22+
}, [fetchPosts]);
23+
24+
// Filter posts by title or author's first/last name (case-insensitive)
25+
const filteredPosts = useMemo(() => {
26+
if (!searchQuery || searchQuery.trim() === "") return posts;
27+
28+
const query = searchQuery.toLowerCase();
29+
30+
return posts.filter((post) => {
31+
const titleMatch = post.title?.toLowerCase().includes(query);
32+
const firstNameMatch = post.author_id?.first_name
33+
?.toLowerCase()
34+
.includes(query);
35+
const lastNameMatch = post.author_id?.last_name
36+
?.toLowerCase()
37+
.includes(query);
38+
39+
return titleMatch || firstNameMatch || lastNameMatch;
40+
});
41+
}, [posts, searchQuery]);
42+
2043
const postsPerPage = 6;
21-
const totalPages = Math.ceil(posts.length / postsPerPage);
44+
const totalPages = Math.ceil(filteredPosts.length / postsPerPage);
2245
const startIndex = (currentPage - 1) * postsPerPage;
23-
const currentPosts = posts.slice(startIndex, startIndex + postsPerPage);
46+
const currentPosts = filteredPosts.slice(
47+
startIndex,
48+
startIndex + postsPerPage,
49+
);
2450

51+
// Reset to first page when search changes
2552
useEffect(() => {
26-
fetchPosts();
27-
}, []);
53+
setCurrentPage(1);
54+
}, [searchQuery]);
2855

2956
const handleDeletePost = (deletedPostId: string | number) => {
3057
useFeedsStore.setState((state) => ({
@@ -52,13 +79,16 @@ export default function Feed({ isAdmin }: FeedProp) {
5279
/>
5380
))}
5481
</div>
55-
<DefaultPagination
56-
totalPages={totalPages}
57-
currentPage={currentPage}
58-
handleNextPage={handleNextPage}
59-
handlePreviousPage={handlePreviousPage}
60-
setCurrentPage={setCurrentPage}
61-
/>
82+
83+
{filteredPosts.length > 0 && (
84+
<DefaultPagination
85+
totalPages={totalPages}
86+
currentPage={currentPage}
87+
handleNextPage={handleNextPage}
88+
handlePreviousPage={handlePreviousPage}
89+
setCurrentPage={setCurrentPage}
90+
/>
91+
)}
6292
</>
6393
);
6494
}

‎apps/codebility/app/home/feeds/page.tsx‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { getUserRole } from "./_services/action";
1212
export default function FeedsPage() {
1313
const [isAdmin, setIsAdmin] = useState(true);
1414
const [isModalOpen, setIsModalOpen] = useState(false);
15+
const [searchQuery, setSearchQuery] = useState("");
16+
const [debouncedQuery, setDebouncedQuery] = useState("");
1517
const { user } = useUserStore();
1618

1719
useEffect(() => {
@@ -24,6 +26,14 @@ export default function FeedsPage() {
2426
if (user) fetchRole();
2527
}, [user]);
2628

29+
useEffect(() => {
30+
const handler = setTimeout(() => {
31+
setDebouncedQuery(searchQuery);
32+
}, 500); // triggers after 500ms of inactivity
33+
34+
return () => clearTimeout(handler);
35+
}, [searchQuery]);
36+
2737
const openModal = () => setIsModalOpen(true);
2838
const closeModal = () => setIsModalOpen(false);
2939

@@ -45,7 +55,19 @@ export default function FeedsPage() {
4555
Create Post
4656
</Button>
4757

48-
<Feed isAdmin={isAdmin} />
58+
{/* Search bar */}
59+
<div className="mb-6">
60+
<input
61+
type="text"
62+
placeholder="Search by title or author name..."
63+
value={searchQuery}
64+
onChange={(e) => setSearchQuery(e.target.value)}
65+
className="focus:border-customBlue-500 focus:ring-customBlue-500 dark:focus:border-customBlue-400 dark:focus:ring-customBlue-400 h-11 w-full rounded-lg border border-gray-300 bg-gray-50 px-4 text-sm text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-1 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400"
66+
/>
67+
</div>
68+
69+
{/* Feed component with live search */}
70+
<Feed isAdmin={isAdmin} searchQuery={debouncedQuery} />
4971
</div>
5072

5173
<CreatePostModal isOpen={isModalOpen} onClose={closeModal} />

0 commit comments

Comments
 (0)