11import Link from "next/link" ;
2- import { useMemo , useState } from "react" ;
2+ import Image from "next/image" ;
3+ import { useEffect , useState } from "react" ;
34import { useRouter } from "next/router" ;
45
6+ const normalizePath = ( path ) => {
7+ if ( ! path ) {
8+ return "/" ;
9+ }
10+
11+ const [ base ] = path . split ( "?" ) ;
12+ const cleanBase = base . split ( "#" ) [ 0 ] ;
13+ if ( ! cleanBase || cleanBase === "/" ) {
14+ return cleanBase || "/" ;
15+ }
16+
17+ return cleanBase . replace ( / \/ + $ / , "" ) ;
18+ } ;
19+
20+ const NAV_LINKS = [
21+ {
22+ href : "/yugioh" ,
23+ label : "Yu-Gi-Oh! Card Prices" ,
24+ title : "Search for current Yu-Gi-Oh! card prices" ,
25+ icon : "/images/icons/yugioh/yugiohPrices.svg" ,
26+ matcher : ( path ) => path === "/yugioh" ,
27+ } ,
28+ {
29+ href : "/yugioh/sets/set-index" ,
30+ label : "Browse Yu-Gi-Oh! Sets" ,
31+ title : "Browse Yu-Gi-Oh! sets" ,
32+ icon : "/images/icons/yugioh/browseSets.svg" ,
33+ matcher : ( path ) => path === "/yugioh/sets/set-index" || path . startsWith ( "/yugioh/sets/" ) ,
34+ } ,
35+ {
36+ href : "/yugioh/deck-builder" ,
37+ label : "Yu-Gi-Oh! Deck Builder" ,
38+ title : "Build and test decks" ,
39+ icon : "/images/icons/yugioh/deckBuilder.svg" ,
40+ matcher : ( path ) => path . startsWith ( "/yugioh/deck-builder" ) ,
41+ } ,
42+ {
43+ href : "/sports" ,
44+ label : "Sports Card Prices" ,
45+ title : "Get current sports card prices" ,
46+ icon : "/images/icons/sports/sportsPrices.svg" ,
47+ matcher : ( path ) => path === "/sports" || path . startsWith ( "/sports/" ) ,
48+ } ,
49+ {
50+ href : "/yugioh/my-collection" ,
51+ label : "My Collection" ,
52+ title : "View your saved cards" ,
53+ icon : "/images/icons/yugioh/collection.svg" ,
54+ matcher : ( path ) => path . startsWith ( "/yugioh/my-collection" ) ,
55+ } ,
56+ ] ;
57+
558export default function SideNav ( ) {
6- const [ isAuthenticated , setIsAuthenticated ] = useState ( false ) ;
759 const router = useRouter ( ) ;
60+ const [ isAuthenticated , setIsAuthenticated ] = useState ( false ) ;
61+ const [ activeHref , setActiveHref ] = useState ( ( ) => {
62+ const currentPath = normalizePath ( router . asPath ?? router . pathname ?? "" ) ;
63+ const match = NAV_LINKS . find ( ( link ) => link . matcher ( currentPath ) ) ;
64+ return match ?. href ?? NAV_LINKS [ 0 ] . href ;
65+ } ) ;
866
9- useMemo ( ( ) => {
67+ useEffect ( ( ) => {
1068 const checkAuth = async ( ) => {
1169 try {
1270 const res = await fetch ( "/api/auth/validate" , {
@@ -20,7 +78,15 @@ export default function SideNav() {
2078 } ;
2179
2280 checkAuth ( ) ;
23- } , [ router ] ) ;
81+ } , [ ] ) ;
82+
83+ useEffect ( ( ) => {
84+ const currentPath = normalizePath ( router . asPath ?? router . pathname ?? "" ) ;
85+ const match = NAV_LINKS . find ( ( link ) => link . matcher ( currentPath ) ) ;
86+ if ( match ) {
87+ setActiveHref ( match . href ) ;
88+ }
89+ } , [ router . asPath , router . pathname ] ) ;
2490
2591 const handleLogout = async ( ) => {
2692 try {
@@ -35,33 +101,44 @@ export default function SideNav() {
35101 }
36102 } ;
37103
38- const links = [
39- { href : "/yugioh" , label : "Yu-Gi-Oh! Card Prices" , title : "Search for current Yu-Gi-Oh! card prices" } ,
40- { href : "/yugioh/sets/set-index" , label : "Browse Yu-Gi-Oh! Sets" , title : "Browse Yu-Gi-Oh! sets" } ,
41- { href : "/yugioh/deck-builder" , label : "Yu-Gi-Oh! Deck Builder" , title : "Build and test decks" } ,
42- { href : "/sports" , label : "Sports Card Prices" , title : "Get current sports card prices" } ,
43- { href : "/yugioh/my-collection" , label : "My Collection" , title : "View your saved cards" } ,
44- ] ;
45-
46104 return (
47- < nav className = "flex max-h-fit flex-col justify-between px-4 py-6" >
48- < ul className = "space-y-3" >
49- { links . map ( ( item ) => (
50- < li key = { item . href } className = "navButton" >
51- < Link href = { item . href } title = { item . title } passHref >
52- < span className = "block" >
53- { item . label }
54- </ span >
55- </ Link >
56- </ li >
57- ) ) }
105+ < nav className = "mx-auto w-[300px] inline-flex max-h-screen flex-col justify-evenly" >
106+ < ul className = "block flex-1 px-2 py-2 gap-3" >
107+ { NAV_LINKS . map ( ( item ) => {
108+ const isActive = activeHref === item . href ;
109+
110+ return (
111+ < li key = { item . href } className = "navButton max-w-fit text-nowrap" >
112+ < Link
113+ href = { item . href }
114+ title = { item . title }
115+ className = { `navLink${ isActive ? " navLink-active" : "" } ` }
116+ onClick = { ( ) => setActiveHref ( item . href ) }
117+ aria-current = { isActive ? "page" : undefined }
118+ >
119+ < span className = "navLink-circle" >
120+ < Image
121+ src = { item . icon }
122+ alt = { `${ item . label } icon` }
123+ width = { 40 }
124+ height = { 40 }
125+ className = "navLink-iconImage object-center object-cover"
126+ />
127+ </ span >
128+ < span className = "navLink-label" >
129+ { item . label }
130+ </ span >
131+ </ Link >
132+ </ li >
133+ ) ;
134+ } ) }
58135 </ ul >
59136
60137 < div className = "mt-6 space-y-3" >
61138 { isAuthenticated ? (
62139 < button
63140 onClick = { handleLogout }
64- className = "w-full rounded-lg border border-white/10 bg-red-500/20 px-4 py-2 text-sm font-semibold tracking-wide text-red-100 transition hover:bg-red-500/30"
141+ className = "block w-full max-w-prose rounded-lg border border-white/10 bg-red-500/20 px-2 py-2 text-sm font-semibold tracking-wide text-red-100 transition hover:bg-red-500/30"
65142 title = "Log out"
66143 >
67144 Log Out
@@ -71,8 +148,7 @@ export default function SideNav() {
71148 < Link
72149 href = "/login"
73150 title = "Log in"
74- passHref
75- className = "block w-full rounded-lg border border-white/10 bg-white/5 px-4 py-2 text-center text-sm font-semibold tracking-wide text-white/90 transition hover:bg-white/10"
151+ className = "block w-full max-w-prose rounded-lg border border-white/10 bg-white/5 px-2 py-2 text-center text-sm font-semibold tracking-wide text-white/90 transition hover:bg-white/10"
76152 >
77153 Log In
78154 </ Link >
@@ -82,3 +158,4 @@ export default function SideNav() {
82158 </ nav >
83159 ) ;
84160}
161+
0 commit comments