Skip to content

Commit c32de12

Browse files
committed
refactor: blogpage
1 parent ceb40ea commit c32de12

File tree

1 file changed

+171
-88
lines changed

1 file changed

+171
-88
lines changed

src/pages/BlogPage.js

Lines changed: 171 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
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';
33
import PostItem from '../components/PostItem';
44
import 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

724
const 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

Comments
 (0)