Skip to content

Commit 2ec3d0d

Browse files
hh
1 parent fc0cbca commit 2ec3d0d

36 files changed

Lines changed: 785178 additions & 769979 deletions

components/Layout.js

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useState } from 'react';
2-
import SideNav from '@/components/Navigation/SideNav';
32
import Link from 'next/link';
4-
import { MenuIcon, X } from 'lucide-react';
3+
import { Dialog, DialogBackdrop, DialogPanel, TransitionChild } from '@headlessui/react';
4+
import { Bars3Icon as MenuIcon, XMarkIcon as X } from '@heroicons/react/24/outline';
5+
import SideNav from '@/components/Navigation/SideNav';
56

67
export default function Layout( { children } ) {
78
const [ isSidebarOpen, setIsSidebarOpen ] = useState( false );
@@ -11,70 +12,83 @@ export default function Layout( { children } ) {
1112
};
1213

1314
return (
14-
<div className="w-full overflow-x-hidden bg-slate-950 text-white">
15-
<div className="relative flex min-h-screen w-full flex-wrap text-white">
16-
{/* Sidebar */ }
17-
<div className={ `fixed inset-y-0 left-0 z-50 w-auto max-w-xs transform transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] lg:relative lg:translate-x-0 ${ isSidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0' }` }>
18-
<div className="glass-strong h-full border-r border-white/20 shadow-xl">
19-
<div className="flex items-center justify-between gap-4 border-b border-white/5 px-5 py-4 backdrop-blur">
20-
<Link
21-
className="group flex items-center gap-2 text-lg font-semibold tracking-wide"
22-
href="/"
23-
passHref
24-
>
25-
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-purple-500/10 to-indigo-500/10 text-sm font-bold uppercase shadow-ring">
26-
CPA
27-
</span>
28-
<span className="text-shadow transition-colors group-hover:text-indigo-100">
29-
CARD PRICE APP
30-
</span>
31-
</Link>
32-
<button
33-
className="text-white/80 transition hover:text-white lg:hidden"
34-
onClick={ toggleSidebar }
35-
title="Close"
36-
>
37-
<X size={ 26 } />
38-
</button>
15+
<div className=" w-full glass dark:bg-gray-900">
16+
<Dialog open={ isSidebarOpen } onClose={ setIsSidebarOpen } className="relative z-50 lg:hidden">
17+
<DialogBackdrop
18+
transition
19+
className="fixed inset-0 backdrop bg-black/20 transition-opacity duration-300 ease-linear data-closed:opacity-0"
20+
/>
21+
<div className="fixed inset-0 flex">
22+
<DialogPanel
23+
transition
24+
className="relative mr-16 flex w-fit max-w-xs flex-1 transform transition duration-300 ease-in-out data-closed:-translate-x-full"
25+
>
26+
<TransitionChild>
27+
<div className="absolute top-0 left-full flex w-16 justify-center pt-5 duration-300 ease-in-out data-closed:opacity-0">
28+
<button type="button" onClick={ toggleSidebar } className="-m-2.5 p-2.5">
29+
<span className="sr-only">Close sidebar</span>
30+
<X title="Close" aria-hidden="true" className="size-7 text-red-600" />
31+
</button>
32+
</div>
33+
</TransitionChild>
34+
<div className="glass backdrop flex grow flex-col gap-y-5 overflow-y-auto pb-2 dark:bg-gray-900 dark:ring dark:ring-white/10 dark:before:pointer-events-none dark:before:absolute dark:before:inset-0 dark:before:bg-black/10">
35+
<div className="relative flex shrink-0 items-start">
36+
<img
37+
alt="Card Price App"
38+
src="images/logo-with-name-side.jpg"
39+
className="h-8 w-auto dark:hidden"
40+
/>
41+
<img
42+
alt="Card Price App"
43+
src="images/logo-with-name-side.jpg"
44+
className="hidden h-8 w-auto dark:block"
45+
/>
46+
</div>
47+
<nav className="relative flex flex-1 flex-col">
48+
<SideNav />
49+
</nav>
3950
</div>
40-
<SideNav />
51+
</DialogPanel>
52+
</div>
53+
</Dialog>
54+
<div className="glass hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-72 lg:flex-col dark:bg-gray-900">
55+
<div className="flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 px-6 dark:border-white/10 dark:bg-black/10">
56+
<div className="flex shrink-0 items-center">
57+
<img
58+
alt="Card Price App"
59+
src="images/logo-with-name-side.jpg"
60+
className="h-16 w-full object-cover rounded bg-fixed bg-clip-padding"
61+
/>
4162
</div>
63+
<nav className="flex flex-1 flex-col">
64+
<SideNav />
65+
</nav>
4266
</div>
43-
44-
{/* Main Content */ }
45-
<div className="flex w-full flex-1 flex-col overflow-x-hidden">
46-
<header className="sticky top-0 z-30 flex items-center justify-between border-b border-white/50 bg-slate-900/10 px-4 py-3 backdrop-blur lg:hidden">
47-
<Link href="/" passHref className="text-base font-semibold tracking-wide">
48-
<span className="text-shadow backdrop font-semibold">CARD PRICE APP</span>
49-
</Link>
50-
<button
51-
className="rounded-md border border-white/10 bg-white/5 p-2 text-white/90 transition hover:bg-white/10"
52-
title="Menu"
53-
onClick={ toggleSidebar }
54-
>
55-
<MenuIcon size={ 30 } />
56-
</button>
57-
</header>
58-
59-
<main className="mx-auto w-full max-w-7xl">
60-
<div className="w-full">
61-
<div className="backdrop text-shadow relative p-2">
62-
{ children }
63-
</div>
64-
</div>
65-
</main>
67+
</div>
68+
<div className="lg:pl-72 w-full">
69+
<div className="sticky top-0 z-40 flex items-center gap-x-6 glass backdrop px-4 py-4 shadow-xs sm:px-6 lg:hidden dark:bg-gray-900 dark:shadow-none dark:after:pointer-events-none dark:after:absolute dark:after:inset-0 dark:after:border-b dark:after:border-white/10 dark:after:bg-black/10">
70+
<button
71+
type="button"
72+
onClick={ toggleSidebar }
73+
className=" text-gray-700 hover:text-gray-900 lg:hidden dark:text-gray-400 dark:hover:text-white"
74+
title="Open sidebar"
75+
>
76+
<span className="sr-only">Open sidebar</span>
77+
<MenuIcon color={ "white" } aria-hidden="true" className="size-6" />
78+
</button>
79+
<Link href="/" className="flex-1 text-sm font-semibold leading-6 text-shadow text-white">
80+
CARD PRICE APP
81+
</Link>
82+
<div className=" flex items-center">
83+
<span className="sr-only">User profile placeholder</span>
84+
</div>
6685
</div>
86+
<main className="mx-auto">
87+
<div className=" mx-1 my-2 ">
88+
{ children }
89+
</div>
90+
</main>
6791
</div>
68-
69-
{ isSidebarOpen && (
70-
<button
71-
type="button"
72-
className="fixed inset-0 z-30 bg-black/85 lg:hidden"
73-
onClick={ toggleSidebar }
74-
aria-label="Close sidebar overlay"
75-
/>
76-
) }
7792
</div>
7893
);
79-
}
80-
94+
};

components/Navigation/Breadcrumb.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,19 @@ const Breadcrumb = () => {
4444
}
4545

4646
// 4. Cards In Set: [SET NAME]
47-
const onSetPage = pathname.includes( '/[setName]' ) || pathname.includes( '/card-details' );
48-
if ( query.set_name && onSetPage ) {
49-
crumbs.push( {
50-
label: `Cards In Set: ${ query.set_name }`,
51-
href: `/yugioh/sets/${ encodeURIComponent( query.letter ) }/${ encodeURIComponent( query.set_name ) }`,
52-
} );
53-
}
5447

5548
// 5. Context-specific parent
56-
if ( pathname.includes( 'card-details' ) ) {
49+
if ( pathname.includes( '/card-details' ) ) {
5750
if ( query.source === 'set' ) {
5851
crumbs.push( {
5952
label: `Cards In Set: ${ query.set_name }`,
6053
href: `/yugioh/sets/${ encodeURIComponent( query.letter ) }/${ encodeURIComponent( query.set_name ) }`
6154
} );
6255
} else if ( query.source === 'collection' ) {
56+
crumbs.push( {
57+
label: `Cards In Set: ${ query.set_name }`,
58+
href: `/yugioh/sets/${ encodeURIComponent( query.letter ) }/${ encodeURIComponent( query.set_name ) }`
59+
} );
6360
crumbs.push( {
6461
label: 'My Collection',
6562
href: '/yugioh/my-collection'
@@ -76,7 +73,7 @@ const Breadcrumb = () => {
7673

7774
return (
7875
<nav
79-
className="flex flex-wrap whitespace-break-spaces border-2 border-zinc-200 bg-transparent glass text-shadow rounded-sm w-full"
76+
className="flex flex-wrap whitespace-break-spaces bg-transparent backdrop glass text-shadow rounded-sm w-full"
8077
aria-label="Breadcrumb"
8178
>
8279
<ol className="mx-auto sm:mx-0 sm:float-start flex flex-wrap w-fit space-x-4 px-4 sm:px-6 lg:px-8">

components/Navigation/SideNav.js

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ export default function SideNav() {
6464
return match?.href ?? NAV_LINKS[ 0 ].href;
6565
} );
6666

67+
68+
useEffect( () => {
69+
const currentPath = normalizePath( router.asPath ?? router.pathname ?? "" );
70+
const match = NAV_LINKS.find( ( link ) => link.matcher( currentPath ) );
71+
if ( match ) {
72+
setActiveHref( match.href );
73+
}
74+
}, [ router.asPath, router.pathname ] );
75+
76+
77+
6778
useMemo( () => {
6879
const checkAuth = async () => {
6980
try {
@@ -80,14 +91,6 @@ export default function SideNav() {
8091
checkAuth();
8192
}, [ router ] );
8293

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 ] );
90-
9194
const handleLogout = async () => {
9295
try {
9396
await fetch( "/api/auth/logout", {
@@ -102,7 +105,7 @@ export default function SideNav() {
102105
};
103106

104107
return (
105-
<nav className="mx-auto w-[300px] max-h-screen flex-col justify-evenly sm:block">
108+
<nav className="mx-auto w-full">
106109
<ul className="block flex-1 px-2 py-2 gap-3">
107110
{ NAV_LINKS.map( ( item ) => {
108111
const isActive = activeHref === item.href;
@@ -120,8 +123,8 @@ export default function SideNav() {
120123
<Image
121124
src={ item.icon }
122125
alt={ `${ item.label } icon` }
123-
width={ 40 }
124-
height={ 40 }
126+
width={ 24 }
127+
height={ 24 }
125128
className="navLink-iconImage object-center object-cover"
126129
/>
127130
</span>
@@ -132,11 +135,11 @@ export default function SideNav() {
132135
</li>
133136
);
134137
} ) }
135-
<div className="mt-6 space-y-3">
138+
<div className="block mt-5 py-1 text-sm/6 text-gray-900 focus:bg-gray-50 focus:outline-hidden">
136139
{ isAuthenticated ? (
137140
<button
138141
onClick={ handleLogout }
139-
className="mx-auto block w-fit px-24 py-3 rounded-lg border border-white/10 bg-red-500/20 text-sm font-semibold tracking-wide text-red-100 transition hover:bg-red-500/30"
142+
className="mx-auto w-full block text-nowrap px-16 py-3 rounded-lg border border-white bg-red-500/20 text-center text-sm font-semibold tracking-wide text-red-100 transition hover:bg-red-500/30"
140143
title="Log out"
141144
>
142145
Log Out
@@ -146,16 +149,14 @@ export default function SideNav() {
146149
<Link
147150
href="/login"
148151
title="Log in"
149-
className="mx-auto block w-fit px-24 py-3 rounded-lg border border-white/10 bg-white/5 text-center text-sm font-semibold tracking-wide text-white/90 transition hover:bg-white/10"
152+
className="mx-auto w-full text-nowrap block px-16 py-3 rounded-lg border border-white bg-white/5 text-center text-sm font-semibold tracking-wide text-white/90 transition hover:bg-white/10"
150153
>
151154
Log In
152155
</Link>
153156
)
154157
) }
155158
</div>
156159
</ul>
157-
158-
159160
</nav>
160161
);
161162
}

components/Notification.js

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,52 @@
11
"use client";
2-
import React, { useState, useEffect } from 'react';
3-
import { XMarkIcon } from '@heroicons/react/20/solid';
2+
import { useEffect, useState } from "react";
3+
import { XMarkIcon } from "@heroicons/react/20/solid";
4+
5+
const HIDE_DELAY_MS = 2500;
6+
7+
const Notification = ( { show = false, setShow = () => { }, message = "" } ) => {
8+
const [ isVisible, setIsVisible ] = useState( show );
49

5-
const Notification = ( { show, setShow, message } ) => {
610
useEffect( () => {
711
if ( show ) {
8-
const timer = setTimeout( () => setShow( false ), 1500 );
12+
setIsVisible( true );
13+
const timer = setTimeout( () => {
14+
setIsVisible( false );
15+
setShow( false );
16+
}, HIDE_DELAY_MS );
917
return () => clearTimeout( timer );
1018
}
19+
20+
const hideTimer = setTimeout( () => setIsVisible( false ), 200 );
21+
return () => clearTimeout( hideTimer );
1122
}, [ show, setShow ] );
1223

24+
if ( !isVisible && !show ) {
25+
return null;
26+
}
27+
1328
return (
1429
<div
1530
aria-live="assertive"
16-
className={ `pointer-events-none relative inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6 transition duration-300 ${ show ? 'opacity-100 translate-y-0 sm:translate-x-0' : 'opacity-0 translate-y-2 sm:translate-y-0 sm:translate-x-2' }` }
31+
className="pointer-events-none fixed inset-0 z-[2000] flex items-center justify-center p-4"
1732
>
18-
<div className="flex w-full flex-col items-center space-y-4 sm:items-center">
19-
<div className="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5">
20-
<div className="p-4">
21-
<div className="flex items-center">
22-
<div className="flex w-0 flex-1 justify-between">
23-
<p className="w-0 flex-1 text-sm font-medium text-gray-900">{ message }</p>
24-
</div>
25-
<div className="ml-4 flex flex-shrink-0">
26-
<button
27-
type="button"
28-
className="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
29-
onClick={ () => setShow( false ) }
30-
>
31-
<span className="sr-only">Close</span>
32-
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
33-
</button>
34-
</div>
35-
</div>
36-
</div>
33+
<div
34+
className={ `pointer-events-auto w-full max-w-md transform rounded-lg bg-white/10 glass backdrop px-6 py-8 text-white shadow-2xl ring-1 ring-black/20 transition-all duration-200 ${ show ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-3" }` }
35+
role="status"
36+
>
37+
<div className="flex items-start gap-4">
38+
<p className="flex-1 text-base font-semibold leading-6">{ message }</p>
39+
<button
40+
type="button"
41+
className="rounded-md p-1 text-slate-300 transition hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-slate-200 focus-visible:ring-offset-slate-900"
42+
onClick={ () => {
43+
setIsVisible( false );
44+
setShow( false );
45+
} }
46+
>
47+
<span className="sr-only">Dismiss notification</span>
48+
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
49+
</button>
3750
</div>
3851
</div>
3952
</div>

components/Sports/SelectedCardsPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ const SelectedCardsPanel: React.FC<SelectedCardsPanelProps> = ( {
1919
}
2020

2121
return (
22-
<div className=" rounded-lg p-4 mb-4">
22+
<div className="rounded-lg p-4 mb-4">
2323
<h2 className="text-xl font-bold text-white mb-4">Selected Cards</h2>
2424
<div className="space-y-2">
2525
{selectedCards.map( ( card ) => (
2626
<div
2727
key={card.id}
28-
className="flex items-center justify-between bg-zinc-900 rounded p-2"
28+
className="flex items-center justify-between bg-zinc-900 rounded p-2 text-shadow font-extrabold"
2929
>
3030
<div className="flex-1 backdrop text-shadow glass-card ">
3131
<p className="text-white">{card.productName}</p>

0 commit comments

Comments
 (0)