1- import React from 'react' ;
1+ import React , { useState } from 'react' ;
22import { Link } from 'react-router-dom' ;
33import {
44 ArrowLeftIcon ,
55 Trophy ,
66 Lock ,
77 Info ,
88 BellSlash ,
9+ FunnelIcon ,
10+ XCircle ,
911} from '@phosphor-icons/react' ;
1012import useSeo from '../hooks/useSeo' ;
1113import { useAchievements } from '../context/AchievementContext' ;
@@ -27,6 +29,13 @@ const AchievementsPage = () => {
2729 } ) ;
2830
2931 const { unlockedAchievements, showAchievementToast } = useAchievements ( ) ;
32+ const [ selectedCategories , setSelectedCategories ] = useState ( [ ] ) ;
33+
34+ // Extract unique categories for filter pills
35+ const uniqueCategories = [
36+ 'All' , // Add 'All' category to display all achievements
37+ ...new Set ( ACHIEVEMENTS . map ( ( ach ) => ach . category ) ) ,
38+ ] . sort ( ) ;
3039
3140 // Calculate progress
3241 const unlockedCount = Object . keys ( unlockedAchievements ) . filter (
@@ -35,6 +44,29 @@ const AchievementsPage = () => {
3544 const totalCount = ACHIEVEMENTS . length ;
3645 const progressPercentage = Math . round ( ( unlockedCount / totalCount ) * 100 ) ;
3746
47+ const toggleCategory = ( category ) => {
48+ if ( category === 'All' ) {
49+ setSelectedCategories ( [ ] ) ;
50+ } else {
51+ setSelectedCategories ( ( prev ) =>
52+ prev . includes ( category )
53+ ? prev . filter ( ( c ) => c !== category )
54+ : [ ...prev , category ] ,
55+ ) ;
56+ }
57+ } ;
58+
59+ const clearFilters = ( ) => {
60+ setSelectedCategories ( [ ] ) ;
61+ } ;
62+
63+ const filteredAchievements = ACHIEVEMENTS . filter ( ( achievement ) => {
64+ const matchesCategory =
65+ selectedCategories . length === 0 || // If no categories selected, show all
66+ selectedCategories . includes ( achievement . category ) ;
67+ return matchesCategory ;
68+ } ) ;
69+
3870 return (
3971 < div className = "py-16 sm:py-24" >
4072 < div className = "mx-auto max-w-7xl px-6 lg:px-8" >
@@ -54,6 +86,36 @@ const AchievementsPage = () => {
5486 Discover hidden features and explore the depths of Fezcodex.
5587 </ p >
5688
89+ { /* Filter Pills */ }
90+ < div className = "flex flex-wrap items-center justify-center gap-2 mt-8 max-w-2xl mx-auto" >
91+ < div className = "flex items-center gap-2 mr-2 text-gray-500 font-mono text-sm" >
92+ < FunnelIcon size = { 16 } />
93+ < span > Filter:</ span >
94+ </ div >
95+ { uniqueCategories . map ( ( category ) => {
96+ const isSelected = selectedCategories . includes ( category ) || ( category === 'All' && selectedCategories . length === 0 ) ;
97+ const colorClass = isSelected ? 'bg-yellow-500/20 text-yellow-300 border-yellow-500/50' : 'bg-gray-900/50 text-gray-400 border-gray-700 hover:border-gray-500 hover:text-gray-200' ;
98+ return (
99+ < button
100+ key = { category }
101+ onClick = { ( ) => toggleCategory ( category ) }
102+ className = { `px-3 py-1 rounded-full text-sm font-medium border transition-colors duration-200 ${ colorClass } ` }
103+ >
104+ { category }
105+ </ button >
106+ ) ;
107+ } ) }
108+
109+ { selectedCategories . length > 0 && (
110+ < button
111+ onClick = { clearFilters }
112+ className = "ml-2 text-sm text-red-400 hover:text-red-300 flex items-center gap-1 transition-colors"
113+ >
114+ < XCircle size = { 20 } /> Clear
115+ </ button >
116+ ) }
117+ </ div >
118+
57119 { /* Progress Bar */ }
58120 < div className = "mt-8 max-w-md mx-auto" >
59121 < div className = "flex justify-between text-sm text-gray-400 mb-2" >
@@ -118,21 +180,21 @@ const AchievementsPage = () => {
118180 </ div >
119181
120182 < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-16" >
121- { ACHIEVEMENTS . map ( ( achievement ) => {
183+ { filteredAchievements . map ( ( achievement ) => {
122184 const isUnlocked = unlockedAchievements [ achievement . id ] ?. unlocked ;
123185
124186 return (
125187 < div
126188 key = { achievement . id }
127189 className = { `relative overflow-hidden rounded-xl border p-6 transition-all duration-300 ${
128190 isUnlocked
129- ? 'bg-gray-800 /40 border-yellow -500/30 shadow-[0_0_15px_rgba(234,179,8 ,0.1 )]'
191+ ? 'bg-amber-900 /40 border-amber -500/30 shadow-[0_0_15px_rgba(245,158,11 ,0.2 )]'
130192 : 'bg-gray-900/40 border-gray-800 opacity-70 grayscale'
131193 } `}
132194 >
133195 < div className = "flex items-start justify-between" >
134196 < div
135- className = { `p-3 rounded-lg ${ isUnlocked ? 'bg-yellow -500/20 text-yellow -400' : 'bg-gray-800 text-gray-500' } ` }
197+ className = { `p-3 rounded-lg ${ isUnlocked ? 'bg-amber -500/20 text-amber -400' : 'bg-gray-800 text-gray-500' } ` }
136198 >
137199 { isUnlocked ? (
138200 achievement . icon
@@ -141,15 +203,15 @@ const AchievementsPage = () => {
141203 ) }
142204 </ div >
143205 { isUnlocked && (
144- < span className = "text-xs font-mono text-green -400 bg-green -900/30 px-2 py-1 rounded border border-green -800" >
206+ < span className = "text-xs font-mono text-amber -400 bg-amber -900/30 px-2 py-1 rounded border border-amber -800" >
145207 UNLOCKED
146208 </ span >
147209 ) }
148210 </ div >
149211
150212 < div className = "mt-4" >
151213 < h3
152- className = { `text-xl font-bold ${ isUnlocked ? 'text-white ' : 'text-gray-500' } ` }
214+ className = { `text-xl font-bold ${ isUnlocked ? 'text-amber-100 ' : 'text-gray-500' } ` }
153215 >
154216 { achievement . title }
155217 </ h3 >
0 commit comments