1- import React , { useState , useEffect } from 'react' ;
2- import { Link } from 'react-router-dom' ;
1+ import React , { useState , useEffect } from 'react' ;
2+ import { Link } from 'react-router-dom' ;
33import PostItem from '../components/PostItem' ;
44import useSeo from '../hooks/useSeo' ;
5- import { ArrowLeftIcon } from '@phosphor-icons/react' ;
5+ import { ArrowLeftIcon , ArticleIcon , MagnifyingGlassIcon , FunnelIcon , XCircle , X } from '@phosphor-icons/react' ;
6+ import colors from '../config/colors' ;
7+
8+ const iconColors = [
9+ "text-red-500" ,
10+ "text-orange-500" ,
11+ "text-amber-500" ,
12+ "text-yellow-500" ,
13+ "text-lime-500" ,
14+ "text-green-500" ,
15+ "text-emerald-500" ,
16+ "text-teal-500" ,
17+ "text-cyan-500" ,
18+ "text-sky-500" ,
19+ "text-blue-500" ,
20+ "text-indigo-500" ,
21+ "text-violet-500" ,
22+ ] ;
623
724const BlogPage = ( ) => {
825 useSeo ( {
@@ -24,6 +41,7 @@ const BlogPage = () => {
2441 const [ loading , setLoading ] = useState ( true ) ;
2542 const [ activeFilter , setActiveFilter ] = useState ( 'all' ) ; // New state for active filter
2643 const [ searchQuery , setSearchQuery ] = useState ( '' ) ; // New state for search query
44+ const [ iconColor , setIconColor ] = useState ( 'text-white' ) ;
2745
2846 useEffect ( ( ) => {
2947 const fetchPostSlugs = async ( ) => {
@@ -133,6 +151,16 @@ const BlogPage = () => {
133151 return matchesFilter ( ) && matchesSearch ( ) ;
134152 } ) ;
135153
154+ useEffect ( ( ) => {
155+ const randomIconColor = iconColors [ Math . floor ( Math . random ( ) * iconColors . length ) ]
156+ setIconColor ( randomIconColor ) ;
157+ } , [ searchQuery , activeFilter ] ) ;
158+
159+ const clearFilters = ( ) => {
160+ setActiveFilter ( 'all' ) ;
161+ setSearchQuery ( '' ) ;
162+ } ;
163+
136164 if ( loading ) {
137165 // Skeleton loading screen for BlogPage
138166 return (
@@ -159,107 +187,162 @@ const BlogPage = () => {
159187 return (
160188 < div className = "py-16 sm:py-24" >
161189 < div className = "mx-auto max-w-7xl px-6 lg:px-8" >
162- < div className = "mx-auto max-w-2xl text-center" >
163- < Link
164- to = "/"
165- className = "group text-primary-400 hover:underline flex items-center justify-center gap-2 text-lg mb-4"
166- >
167- < ArrowLeftIcon className = "text-xl transition-transform group-hover:-translate-x-1" /> Back to Home
168- </ Link >
169- < h1 className = "text-4xl font-semibold tracking-tight text-white sm:text-6xl" >
170- From the < span style = { { color : 'var(--fzcdx-spanner)' } } > Blog</ span >
171- </ h1 >
172- < p className = "mt-6 text-lg leading-8 text-gray-300" >
173- Catch up on the latest news and insights.
174- </ p >
175- < div className = "mt-4 text-center" >
176- < span className = "ml-2 px-3 py-1 text-base font-medium text-gray-200 bg-gray-800 rounded-full" >
177- Total: { filteredItems . length }
178- </ span >
190+ < Link
191+ to = "/"
192+ className = "group text-primary-400 hover:text-primary-300 hover:underline flex items-center gap-2 text-lg mb-8 transition-colors"
193+ >
194+ < ArrowLeftIcon className = "text-xl transition-transform group-hover:-translate-x-1" /> Back to Home
195+ </ Link >
196+
197+ { /* Header */ }
198+ < div className = "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12" >
199+ < div >
200+ < h1 className = "text-4xl font-bold font-mono tracking-tight sm:text-6xl mb-4 flex items-center text-white" >
201+ < ArticleIcon
202+ size = { 48 }
203+ weight = "fill"
204+ className = { `mr-4 mt-2 ${ iconColor } ` }
205+ />
206+ < span className = "text-gray-100" > fc</ span >
207+ < span className = "text-gray-500" > ::</ span >
208+ < span className = "text-gray-100" > posts</ span >
209+ < span className = "text-gray-500" > ::</ span >
210+ < span className = "text-gray-500" > [</ span >
211+ < span className = "text-gray-100" > { displayItems . length } </ span >
212+ < span className = "text-gray-500" > ]</ span >
213+ </ h1 >
214+ < p className = "text-gray-400 text-lg max-w-2xl font-mono" >
215+ Catch up on the latest news and insights.
216+ </ p >
179217 </ div >
218+
180219 { /* Search Input */ }
181- < div className = "mt-8 mb-4 flex justify-center" >
220+ < div className = "mt-8 mb-8 flex justify-center relative max-w-lg mx-auto group" >
221+ < div className = "absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none" >
222+ < MagnifyingGlassIcon
223+ className = "h-5 w-5 text-gray-500 group-focus-within:text-primary-400 transition-colors duration-300" />
224+ </ div >
182225 < input
183226 type = "text"
184- placeholder = "Search posts ..."
185- className = "w-full max-w-md px-4 py-2 rounded-full bg-gray-800 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500"
227+ placeholder = "Type to filter ..."
228+ className = "block w-full pl-11 pr-24 py-3 border border-gray-700/50 rounded-2xl leading-5 bg-gray-900/50 text-gray-200 placeholder-gray-600 focus:outline-none focus:bg-gray-900/80 focus:border-primary-500/50 focus: ring-4 focus:ring-primary-500/10 sm:text-sm transition-all duration-300 backdrop-blur-md shadow-lg hover:border-gray-600 hover:shadow-primary-500/5 font-sans "
186229 value = { searchQuery }
187230 onChange = { ( e ) => setSearchQuery ( e . target . value ) }
188231 />
232+ < div className = "absolute inset-y-0 right-0 pr-3 flex items-center gap-2" >
233+ { searchQuery && (
234+ < button
235+ onClick = { ( ) => setSearchQuery ( '' ) }
236+ className = "p-1 rounded-full text-gray-500 hover:text-white hover:bg-gray-700 transition-all duration-200 focus:outline-none"
237+ title = "Clear search"
238+ >
239+ < X weight = "bold" className = "h-3.5 w-3.5" />
240+ </ button >
241+ ) }
242+ < span
243+ className = { `text-xs font-mono px-2 py-1 rounded-md transition-colors duration-300 border ${ filteredItems . length === 0 ? 'border-red-500/30 text-red-400 bg-red-500/10' : 'border-gray-700/50 text-gray-500 bg-gray-800/50' } ` } >
244+ { filteredItems . length }
245+ </ span >
246+ </ div >
247+ </ div >
248+ </ div >
249+
250+ < hr className = "border-gray-800 mb-8" />
251+
252+ { /* Filter Pills */ }
253+ < div className = "flex flex-wrap items-center gap-2 mb-10" >
254+ < div className = "flex items-center gap-2 mr-2 text-gray-500 font-mono text-sm" >
255+ < FunnelIcon size = { 16 } />
256+ < span > Filter:</ span >
189257 </ div >
190- { /* Filter Buttons */ }
191- < div className = "mt-8 flex flex-wrap justify-center gap-2" >
192- { [
193- { id : 'all' , label : 'All' } ,
194- { id : 'dev' , label : 'Dev' } ,
195- { id : 'feat' , label : 'Feat' } ,
196- { id : 'rant' , label : 'Rant' } ,
197- { id : 'series' , label : 'Series' } ,
198- { id : 'gist' , label : 'Gist' } ,
199- { id : 'd&d' , label : 'D&D' } ,
200- ] . map ( ( filter ) => (
258+ { [
259+ { id : 'all' , label : 'All' } ,
260+ { id : 'dev' , label : 'Dev' } ,
261+ { id : 'feat' , label : 'Feat' } ,
262+ { id : 'rant' , label : 'Rant' } ,
263+ { id : 'series' , label : 'Series' } ,
264+ { id : 'gist' , label : 'Gist' } ,
265+ { id : 'd&d' , label : 'D&D' } ,
266+ ] . map ( ( filter ) => {
267+ const isSelected = activeFilter === filter . id ;
268+
269+ // Determine color key for mapping
270+ let colorKey = filter . id ;
271+ if ( filter . id === 'rant' ) colorKey = 'takes' ; // 'rant' maps to 'takes' color vars
272+
273+ // Resolve specific color using colors.js logic approximation or CSS vars
274+ // Since we are using style prop with vars in the previous version, let's stick to that pattern or use colors.js if available
275+ // Note: colors.js has keys like 'feat', 'series', etc.
276+ // Let's try to use the hex codes from colors.js if possible, or fallback to a default.
277+
278+ let categoryColor = colors . primary [ 400 ] ; // Default
279+ if ( filter . id === 'all' ) categoryColor = '#9ca3af' ; // gray-400
280+ else if ( filter . id === 'rant' ) categoryColor = '#065f46' ; // hardcoded matching index.css roughly or use colors.js
281+ // Actually, let's use the CSS variables for consistency with badges
282+
283+ // Construct style object
284+ const activeColor = filter . id === 'all' ? '#4b5563' :
285+ filter . id === 'rant' ? 'var(--color-takes-badge)' :
286+ filter . id === 'd&d' ? 'var(--color-dnd-badge)' :
287+ `var(--color-${ filter . id } -badge)` ;
288+
289+ const style = isSelected ? {
290+ backgroundColor : activeColor ,
291+ borderColor : activeColor ,
292+ color : filter . id === 'gist' ? 'black' : 'white' ,
293+ boxShadow : `0 0 20px ${ activeColor } `
294+ } : { } ;
295+
296+ return (
201297 < button
202298 key = { filter . id }
203299 onClick = { ( ) => setActiveFilter ( filter . id ) }
204- className = { `px-4 py-2 rounded-full text-sm font-medium transition-colors duration-200 ${
205- activeFilter === filter . id
206- ? 'text-white shadow-md'
207- : 'bg-gray-800 text-gray-400 hover:bg-gray-700 hover:text-white'
300+ style = { style }
301+ className = { `px-4 py-1.5 rounded-lg text-sm font-medium border transition-all duration-200 ${
302+ isSelected
303+ ? 'transform scale-105'
304+ : 'bg-gray-900/50 text-gray-400 border-gray-700 hover:border-gray-500 hover:text-gray-200'
208305 } `}
209- style = { {
210- backgroundColor :
211- activeFilter === filter . id
212- ? filter . id === 'all'
213- ? '#4b5563' // gray-600 for All
214- : filter . id === 'series'
215- ? 'var(--color-series-badge)'
216- : filter . id === 'd&d'
217- ? 'var(--color-dnd-badge)'
218- : filter . id === 'gist'
219- ? 'var(--color-gist-badge)'
220- : filter . id === 'feat'
221- ? 'var(--color-feat-badge)'
222- : filter . id === 'rant'
223- ? 'var(--color-takes-badge)'
224- : `var(--color-${ filter . id } -badge)` // Dynamic for dev
225- : undefined ,
226- color : activeFilter === filter . id && filter . id === 'gist' ? 'black' : undefined
227- } }
228306 >
229307 { filter . label }
230308 </ button >
231- ) ) }
232- </ div >
309+ ) ;
310+ } ) } { ( activeFilter !== 'all' || searchQuery ) && (
311+ < button onClick = { clearFilters }
312+ className = "font-arvo ml-auto text-sm text-red-400 hover:text-red-300 flex items-center gap-1 transition-colors" >
313+ < XCircle size = { 20 } /> Clear
314+ </ button >
315+ ) }
233316 </ div >
234- < div className = "mt-16" >
235- < div className = "" >
236- { filteredItems . map ( ( item ) = >
237- item . isSeries ? (
238- < PostItem
239- key = { item . slug }
240- slug = { `series/ ${ item . slug } ` }
241- title = { item . title }
242- date = { item . date } // Date of the series
243- updatedDate = { item . updated } // Updated date of the series
244- category = " series"
245- isSeries = { true }
246- authors = { item . authors } // Pass authors array for series
247- />
248- ) : (
249- < PostItem
250- key = { item . slug }
251- slug = { item . slug }
252- title = { item . title }
253- date = { item . date }
254- updatedDate = { item . updated }
255- category = { item . category }
256- series = { item . series }
257- seriesIndex = { item . seriesIndex }
258- authors = { item . authors } // Pass authors array for individual posts
259- />
260- ) ,
261- ) }
262- </ div >
317+
318+ { /*mt-12 max-w-3xl mx-auto space-y-8*/ }
319+ < div className = "mt-12 mx-auto space-y-8" >
320+ { filteredItems . map ( ( item ) =>
321+ item . isSeries ? (
322+ < PostItem
323+ key = { item . slug }
324+ slug = { `series/ ${ item . slug } ` }
325+ title = { item . title }
326+ date = { item . date } // Date of the series
327+ updatedDate = { item . updated } // Updated date of the series
328+ category = "series"
329+ isSeries = { true }
330+ authors = { item . authors } // Pass authors array for series
331+ />
332+ ) : (
333+ < PostItem
334+ key = { item . slug }
335+ slug = { item . slug }
336+ title = { item . title }
337+ date = { item . date }
338+ updatedDate = { item . updated }
339+ category = { item . category }
340+ series = { item . series }
341+ seriesIndex = { item . seriesIndex }
342+ authors = { item . authors } // Pass authors array for individual posts
343+ />
344+ ) ,
345+ ) }
263346 </ div >
264347 </ div >
265348 </ div >
0 commit comments