Skip to content

Commit 9d1e2c4

Browse files
authored
Added search bar to filter authors on authors page (#539)
1 parent fc79a86 commit 9d1e2c4

File tree

1 file changed

+121
-78
lines changed

1 file changed

+121
-78
lines changed

src/pages/News/AuthorsPage.tsx

Lines changed: 121 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect, useMemo } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import { motion } from 'framer-motion';
4-
import { User, Building, BookOpen } from 'lucide-react';
4+
import { User, Building, BookOpen, Search } from 'lucide-react';
55

66
import Header from '@/sections/Header';
77
import Footer from '@/sections/Footer';
@@ -17,6 +17,16 @@ const AuthorsPage: React.FC = () => {
1717
const [authors, setAuthors] = useState<AuthorWithPostCount[]>([]);
1818
const [isLoading, setIsLoading] = useState(true);
1919
const [error, setError] = useState<string | null>(null);
20+
const [searchTerm, setSearchTerm] = useState('');
21+
const [debouncedSearch, setDebouncedSearch] = useState('');
22+
23+
// Debounce search input
24+
useEffect(() => {
25+
const handler = setTimeout(() => {
26+
setDebouncedSearch(searchTerm.trim().toLowerCase());
27+
}, 300);
28+
return () => clearTimeout(handler);
29+
}, [searchTerm]);
2030

2131
useEffect(() => {
2232
const loadAllAuthors = async () => {
@@ -61,6 +71,14 @@ const AuthorsPage: React.FC = () => {
6171
navigate(`/authors/${slug}`);
6272
};
6373

74+
// Filter authors based on search
75+
const filteredAuthors = useMemo(() => {
76+
if (!debouncedSearch) return authors;
77+
return authors.filter((author) =>
78+
author.name.toLowerCase().includes(debouncedSearch),
79+
);
80+
}, [authors, debouncedSearch]);
81+
6482
const renderContent = () => {
6583
if (isLoading) {
6684
return (
@@ -95,88 +113,113 @@ const AuthorsPage: React.FC = () => {
95113
);
96114
}
97115

116+
// Search bar
98117
return (
99-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
100-
{authors.map((author, index) => (
101-
<motion.div
102-
key={author.slug}
103-
onClick={() => handleAuthorClick(author.slug)}
104-
className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/20
105-
transition-shadow duration-300 group-hover:shadow-blue-200 dark:group-hover:shadow-blue-500/30
106-
cursor-pointer group flex flex-col"
107-
initial={{ opacity: 0, y: 20 }}
108-
animate={{ opacity: 1, y: 0 }}
109-
transition={{
110-
duration: 0.5,
111-
delay: index * 0.1,
112-
scale: {
113-
type: 'spring',
114-
stiffness: 300,
115-
damping: 15,
116-
},
117-
}}
118-
whileHover={{ scale: 1.05 }}
119-
>
120-
<div className="flex flex-col items-center p-6 text-center flex-1">
121-
{/* Avatar */}
122-
<div className="flex-shrink-0 mb-4">
123-
{author.avatar ? (
124-
<img
125-
src={author.avatar}
126-
alt={author.name}
127-
className="w-24 h-24 rounded-full object-cover border-4 border-blue-100 dark:border-blue-900"
128-
/>
129-
) : (
130-
<div className="w-24 h-24 bg-blue-100 dark:bg-gray-700/50 rounded-full flex items-center justify-center">
131-
<User className="w-12 h-12 text-blue-600 dark:text-blue-400" />
118+
<div>
119+
<div className="flex justify-center mb-12">
120+
<div className="relative w-full max-w-md">
121+
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-gray-400 w-5 h-5" />
122+
<input
123+
type="text"
124+
placeholder="Search authors by name..."
125+
className="w-full pl-12 pr-4 py-3 rounded-full border border-gray-300 dark:border-gray-700
126+
bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-200
127+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all"
128+
value={searchTerm}
129+
onChange={(e) => setSearchTerm(e.target.value)}
130+
/>
131+
</div>
132+
</div>
133+
134+
{filteredAuthors.length === 0 ? (
135+
<div className="text-center py-16 text-gray-600 dark:text-gray-300">
136+
No authors found matching "
137+
<span className="font-semibold text-blue-600 dark:text-blue-400">
138+
{searchTerm}
139+
</span>
140+
"
141+
</div>
142+
) : (
143+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
144+
{filteredAuthors.map((author, index) => (
145+
<motion.div
146+
key={author.slug}
147+
onClick={() => handleAuthorClick(author.slug)}
148+
className="bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/20
149+
transition-shadow duration-300 group-hover:shadow-blue-200 dark:group-hover:shadow-blue-500/30
150+
cursor-pointer group flex flex-col"
151+
initial={{ opacity: 0, y: 20 }}
152+
animate={{ opacity: 1, y: 0 }}
153+
transition={{
154+
duration: 0.5,
155+
delay: index * 0.1,
156+
}}
157+
whileHover={{ scale: 1.05 }}
158+
>
159+
<div className="flex flex-col items-center p-6 text-center flex-1">
160+
{/* Avatar */}
161+
<div className="flex-shrink-0 mb-4">
162+
{author.avatar ? (
163+
<img
164+
src={author.avatar}
165+
alt={author.name}
166+
className="w-24 h-24 rounded-full object-cover border-4 border-blue-100 dark:border-blue-900"
167+
/>
168+
) : (
169+
<div className="w-24 h-24 bg-blue-100 dark:bg-gray-700/50 rounded-full flex items-center justify-center">
170+
<User className="w-12 h-12 text-blue-600 dark:text-blue-400" />
171+
</div>
172+
)}
132173
</div>
133-
)}
134-
</div>
135-
136-
{/* Author Info*/}
137-
<div className="flex flex-col flex-1">
138-
<h1 className="text-xl font-bold text-gray-900 dark:text-white mb-1 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
139-
{author.name}
140-
</h1>
141-
142-
{/* Title and Org */}
143-
<div className="flex flex-col items-center gap-1 mb-3">
144-
<span className="text-md text-blue-600 dark:text-blue-400 font-medium">
145-
{author.title}
146-
</span>
147-
{author.organization && (
148-
<div className="flex items-center justify-center gap-1 text-sm text-gray-700 dark:text-gray-300">
149-
<Building className="w-4 h-4" />
150-
<span className="font-medium">{author.organization}</span>
174+
175+
{/* Author Info*/}
176+
<div className="flex flex-col flex-1">
177+
<h1 className="text-xl font-bold text-gray-900 dark:text-white mb-1 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
178+
{author.name}
179+
</h1>
180+
181+
{/* Title and Org */}
182+
<div className="flex flex-col items-center gap-1 mb-3">
183+
<span className="text-md text-blue-600 dark:text-blue-400 font-medium">
184+
{author.title}
185+
</span>
186+
{author.organization && (
187+
<div className="flex items-center justify-center gap-1 text-sm text-gray-700 dark:text-gray-300">
188+
<Building className="w-4 h-4" />
189+
<span className="font-medium">
190+
{author.organization}
191+
</span>
192+
</div>
193+
)}
151194
</div>
152-
)}
153-
</div>
154195

155-
{/* Description */}
156-
<p className="text-gray-600 dark:text-gray-300 text-sm mb-4 max-w-xl line-clamp-3">
157-
{author.description}
158-
</p>
159-
160-
{/* Quick Stats */}
161-
<div className="flex flex-wrap justify-center gap-2 text-xs text-gray-600 dark:text-gray-300 mt-auto">
162-
<div className="flex items-center gap-1 bg-blue-50 dark:bg-blue-900/30 px-3 py-1 rounded-full">
163-
<BookOpen className="w-4 h-4" />
164-
<span>
165-
{author.postCount}{' '}
166-
{author.postCount === 1 ? 'Article' : 'Articles'}
167-
</span>
168-
</div>
169-
{author.organization && (
170-
<div className="flex items-center gap-1 bg-gray-50 dark:bg-gray-700/50 px-3 py-1 rounded-full">
171-
<Building className="w-4 h-4" />
172-
<span>{author.organization}</span>
196+
{/* Description */}
197+
<p className="text-gray-600 dark:text-gray-300 text-sm mb-4 max-w-xl line-clamp-3">
198+
{author.description}
199+
</p>
200+
201+
{/* Quick Stats */}
202+
<div className="flex flex-wrap justify-center gap-2 text-xs text-gray-600 dark:text-gray-300 mt-auto">
203+
<div className="flex items-center gap-1 bg-blue-50 dark:bg-blue-900/30 px-3 py-1 rounded-full">
204+
<BookOpen className="w-4 h-4" />
205+
<span>
206+
{author.postCount}{' '}
207+
{author.postCount === 1 ? 'Article' : 'Articles'}
208+
</span>
209+
</div>
210+
{author.organization && (
211+
<div className="flex items-center gap-1 bg-gray-50 dark:bg-gray-700/50 px-3 py-1 rounded-full">
212+
<Building className="w-4 h-4" />
213+
<span>{author.organization}</span>
214+
</div>
215+
)}
173216
</div>
174-
)}
217+
</div>
175218
</div>
176-
</div>
177-
</div>
178-
</motion.div>
179-
))}
219+
</motion.div>
220+
))}
221+
</div>
222+
)}
180223
</div>
181224
);
182225
};

0 commit comments

Comments
 (0)