reroute-js 0.46.0 → 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -4
- package/_/store/package.json +1 -3
- package/_/store/src/client/components/Header.tsx +8 -20
- package/_/store/src/client/components/ProductCard.tsx +9 -16
- package/_/store/src/client/routes/[404].tsx +20 -31
- package/_/store/src/client/routes/categories/[category].tsx +23 -28
- package/_/store/src/client/routes/categories/index.tsx +19 -27
- package/_/store/src/client/routes/index.tsx +27 -37
- package/_/store/src/client/routes/products/[id].tsx +35 -48
- package/_/store/src/client/routes/products/index.tsx +20 -50
- package/_/store/src/client/theme.css +907 -95
- package/cli/bin.d.ts +1 -1
- package/cli/bin.js +78 -176
- package/cli/bin.js.map +11 -11
- package/cli/index.d.ts +1 -1
- package/cli/index.js +17 -112
- package/cli/index.js.map +5 -5
- package/cli/src/cli.d.ts +1 -1
- package/cli/src/commands/analyze.d.ts +1 -1
- package/cli/src/commands/build.d.ts +1 -1
- package/cli/src/commands/dev.d.ts +1 -1
- package/cli/src/commands/gen.d.ts +1 -1
- package/cli/src/commands/gen.d.ts.map +1 -1
- package/cli/src/commands/index.d.ts +1 -1
- package/cli/src/commands/init.d.ts +1 -1
- package/cli/src/commands/lib/assets.d.ts +1 -1
- package/cli/src/commands/lib/bundler.d.ts +1 -1
- package/cli/src/commands/lib/command.d.ts +1 -1
- package/cli/src/commands/lib/env.d.ts +1 -1
- package/cli/src/commands/lib/index.d.ts +2 -2
- package/cli/src/commands/lib/index.d.ts.map +1 -1
- package/cli/src/commands/lib/log.d.ts +1 -1
- package/cli/src/commands/lib/markdown/availability.d.ts +1 -1
- package/cli/src/commands/lib/markdown/index.d.ts +1 -1
- package/cli/src/commands/lib/markdown/processor.d.ts +1 -1
- package/cli/src/commands/lib/production.d.ts +1 -1
- package/cli/src/commands/lib/server.d.ts +1 -1
- package/cli/src/commands/lib/streaming/analyzer.d.ts +1 -1
- package/cli/src/commands/lib/streaming/suspense.d.ts +1 -1
- package/cli/src/commands/lib/terminal-ui.d.ts +1 -1
- package/cli/src/commands/lib/theme.d.ts +20 -0
- package/cli/src/commands/lib/theme.d.ts.map +1 -0
- package/cli/src/commands/lib/version.d.ts +1 -1
- package/cli/src/commands/og.d.ts +1 -1
- package/cli/src/commands/start.d.ts +1 -1
- package/cli/src/index.d.ts +1 -1
- package/core/index.d.ts +1 -1
- package/core/index.js +12 -12
- package/core/index.js.map +6 -6
- package/core/src/bundler/hash.d.ts +1 -1
- package/core/src/bundler/index.d.ts +1 -1
- package/core/src/config.d.ts +1 -1
- package/core/src/content/discovery.d.ts +1 -1
- package/core/src/content/index.d.ts +1 -1
- package/core/src/content/metadata.d.ts +1 -1
- package/core/src/i18n/catalog.d.ts +1 -1
- package/core/src/i18n/discover.d.ts +1 -1
- package/core/src/i18n/index.d.ts +1 -1
- package/core/src/i18n/messages.d.ts +1 -1
- package/core/src/i18n/negotiate.d.ts +1 -1
- package/core/src/i18n/pathname.d.ts +1 -1
- package/core/src/i18n/resolve.d.ts +1 -1
- package/core/src/i18n/types.d.ts +1 -1
- package/core/src/index.d.ts +1 -1
- package/core/src/llms/extractor.d.ts +1 -1
- package/core/src/llms/formatter.d.ts +1 -1
- package/core/src/llms/full-generator.d.ts +1 -1
- package/core/src/llms/index-generator.d.ts +1 -1
- package/core/src/llms/index.d.ts +1 -1
- package/core/src/og/discovery.d.ts +1 -1
- package/core/src/og/index.d.ts +1 -1
- package/core/src/og/meta.d.ts +1 -1
- package/core/src/og/render.d.ts +1 -1
- package/core/src/og/types.d.ts +1 -1
- package/core/src/robots/discovery.d.ts +1 -1
- package/core/src/robots/generator.d.ts +1 -1
- package/core/src/robots/index.d.ts +1 -1
- package/core/src/robots/policies.d.ts +1 -1
- package/core/src/rss/discovery.d.ts +1 -1
- package/core/src/rss/generator.d.ts +1 -1
- package/core/src/rss/index.d.ts +1 -1
- package/core/src/sitemap/discovery.d.ts +1 -1
- package/core/src/sitemap/generator.d.ts +1 -1
- package/core/src/sitemap/index.d.ts +1 -1
- package/core/src/ssr/index.d.ts +1 -1
- package/core/src/ssr/lib/cache.d.ts +1 -1
- package/core/src/ssr/lib/cleanup.d.ts +1 -1
- package/core/src/ssr/lib/collections.d.ts +1 -1
- package/core/src/ssr/lib/compression.d.ts +1 -1
- package/core/src/ssr/lib/compute/content.d.ts +1 -1
- package/core/src/ssr/lib/compute/index.d.ts +1 -1
- package/core/src/ssr/lib/compute/layouts.d.ts +1 -1
- package/core/src/ssr/lib/compute/routes.d.ts +1 -1
- package/core/src/ssr/lib/content-path.d.ts +1 -1
- package/core/src/ssr/lib/data.d.ts +1 -1
- package/core/src/ssr/lib/html.d.ts +1 -1
- package/core/src/ssr/lib/imports.d.ts +1 -1
- package/core/src/ssr/lib/index.d.ts +1 -1
- package/core/src/ssr/lib/layouts.d.ts +1 -1
- package/core/src/ssr/lib/metadata.d.ts +1 -1
- package/core/src/ssr/lib/mime.d.ts +1 -1
- package/core/src/ssr/lib/modules.d.ts +1 -1
- package/core/src/ssr/lib/path.d.ts +1 -1
- package/core/src/ssr/lib/preload.d.ts +1 -1
- package/core/src/ssr/lib/route-match.d.ts +1 -1
- package/core/src/ssr/lib/scripts/collections.d.ts +1 -1
- package/core/src/ssr/lib/scripts/data.d.ts +1 -1
- package/core/src/ssr/lib/scripts/escape.d.ts +1 -1
- package/core/src/ssr/lib/scripts/feeds.d.ts +1 -1
- package/core/src/ssr/lib/scripts/index.d.ts +1 -1
- package/core/src/ssr/lib/seed.d.ts +1 -1
- package/core/src/ssr/lib/serialize.d.ts +1 -1
- package/core/src/ssr/lib/setup.d.ts +2 -2
- package/core/src/ssr/lib/setup.d.ts.map +1 -1
- package/core/src/ssr/lib/styles.d.ts +3 -3
- package/core/src/ssr/lib/styles.d.ts.map +1 -1
- package/core/src/ssr/lib/template.d.ts +1 -1
- package/core/src/ssr/lib/types.d.ts +1 -1
- package/core/src/ssr/render.d.ts +2 -2
- package/core/src/ssr/render.d.ts.map +1 -1
- package/core/src/ssr/stream.d.ts +2 -2
- package/core/src/ssr/stream.d.ts.map +1 -1
- package/elysia/index.d.ts +1 -1
- package/elysia/index.js +21 -21
- package/elysia/index.js.map +9 -9
- package/elysia/src/index.d.ts +1 -1
- package/elysia/src/libs/assets.d.ts +1 -1
- package/elysia/src/libs/cache.d.ts +1 -1
- package/elysia/src/libs/caching.d.ts +1 -1
- package/elysia/src/libs/http.d.ts +1 -1
- package/elysia/src/libs/i18n.d.ts +1 -1
- package/elysia/src/libs/image.d.ts +1 -1
- package/elysia/src/libs/index.d.ts +1 -1
- package/elysia/src/libs/llms.d.ts +1 -1
- package/elysia/src/libs/response.d.ts +1 -1
- package/elysia/src/libs/serving.d.ts +1 -1
- package/elysia/src/plugin.d.ts +1 -1
- package/elysia/src/routes/artifacts.d.ts +1 -1
- package/elysia/src/routes/content.d.ts +1 -1
- package/elysia/src/routes/image.d.ts +1 -1
- package/elysia/src/routes/index.d.ts +1 -1
- package/elysia/src/routes/internal.d.ts +1 -1
- package/elysia/src/routes/llms.d.ts +1 -1
- package/elysia/src/routes/og.d.ts +1 -1
- package/elysia/src/routes/redirects.d.ts +1 -1
- package/elysia/src/routes/robots.d.ts +1 -1
- package/elysia/src/routes/rss.d.ts +1 -1
- package/elysia/src/routes/search.d.ts +1 -1
- package/elysia/src/routes/sitemap.d.ts +1 -1
- package/elysia/src/routes/ssr.d.ts +2 -2
- package/elysia/src/routes/ssr.d.ts.map +1 -1
- package/elysia/src/routes/static.d.ts +1 -1
- package/elysia/src/types.d.ts +1 -1
- package/package.json +2 -5
- package/react/index.d.ts +1 -1
- package/react/index.js +2 -2
- package/react/index.js.map +1 -1
- package/react/src/components/ClientOnly.d.ts +1 -1
- package/react/src/components/ContentRoute.d.ts +1 -1
- package/react/src/components/Image.d.ts +1 -1
- package/react/src/components/LazyRoute.d.ts +1 -1
- package/react/src/components/Link.d.ts +1 -1
- package/react/src/components/Markdown.d.ts +1 -1
- package/react/src/components/Outlet.d.ts +1 -1
- package/react/src/components/index.d.ts +1 -1
- package/react/src/hooks/index.d.ts +1 -1
- package/react/src/hooks/useContent.d.ts +1 -1
- package/react/src/hooks/useData.d.ts +1 -1
- package/react/src/hooks/useFeed.d.ts +1 -1
- package/react/src/hooks/useLayoutData.d.ts +1 -1
- package/react/src/hooks/useLlms.d.ts +1 -1
- package/react/src/hooks/useNavigate.d.ts +1 -1
- package/react/src/hooks/useParams.d.ts +1 -1
- package/react/src/hooks/useRouter.d.ts +1 -1
- package/react/src/hooks/useSearch.d.ts +1 -1
- package/react/src/hooks/useSearchParams.d.ts +1 -1
- package/react/src/hooks/useToc.d.ts +1 -1
- package/react/src/i18n/LocaleProvider.d.ts +1 -1
- package/react/src/i18n/helpers.d.ts +1 -1
- package/react/src/i18n/index.d.ts +1 -1
- package/react/src/index.d.ts +1 -1
- package/react/src/lib/collection.d.ts +1 -1
- package/react/src/lib/content.d.ts +1 -1
- package/react/src/lib/head.d.ts +1 -1
- package/react/src/lib/index.d.ts +1 -1
- package/react/src/lib/lazy-route.d.ts +1 -1
- package/react/src/lib/route-loader.d.ts +1 -1
- package/react/src/providers/ContentProvider.d.ts +1 -1
- package/react/src/providers/RerouteProvider.d.ts +1 -1
- package/react/src/providers/RouterProvider.d.ts +1 -1
- package/react/src/providers/index.d.ts +1 -1
- package/react/src/types/any.d.ts +1 -1
- package/react/src/types/index.d.ts +1 -1
- package/react/src/types/router.d.ts +1 -1
- package/telemetry/react.d.ts +1 -1
- package/telemetry/react.js +2 -2
- package/telemetry/react.js.map +1 -1
- package/telemetry/server.d.ts +1 -1
- package/telemetry/server.js +2 -2
- package/telemetry/server.js.map +4 -4
- package/telemetry/src/react/api.d.ts +1 -1
- package/telemetry/src/react/context.d.ts +1 -1
- package/telemetry/src/react/index.d.ts +1 -1
- package/telemetry/src/react/telemetry.d.ts +1 -1
- package/telemetry/src/server/context.d.ts +1 -1
- package/telemetry/src/server/headers/extractor.d.ts +1 -1
- package/telemetry/src/server/headers/index.d.ts +1 -1
- package/telemetry/src/server/headers/presets.d.ts +1 -1
- package/telemetry/src/server/index.d.ts +1 -1
- package/telemetry/src/server/instrumentation.d.ts +1 -1
- package/telemetry/src/server/plugin.d.ts +1 -1
- package/telemetry/src/server/sourcemap.d.ts +1 -1
- package/cli/src/commands/lib/tailwind.d.ts +0 -20
- package/cli/src/commands/lib/tailwind.d.ts.map +0 -1
|
@@ -4,7 +4,6 @@ import Header from '../../components/Header';
|
|
|
4
4
|
import ProductCard from '../../components/ProductCard';
|
|
5
5
|
import { getProduct, getProductsByCategory, type Product } from '../../lib/api';
|
|
6
6
|
|
|
7
|
-
// Enable SSR data fetching for this route
|
|
8
7
|
const ssr = {
|
|
9
8
|
async data({ params }: { params: { id?: string } }) {
|
|
10
9
|
try {
|
|
@@ -45,7 +44,6 @@ function ProductDetailPage() {
|
|
|
45
44
|
const params = useParams<{ id: string }>();
|
|
46
45
|
const productId = params.id ? Number.parseInt(params.id, 10) : null;
|
|
47
46
|
|
|
48
|
-
// Read SSR-seeded or prefetched data for this route
|
|
49
47
|
const routeData = useData<{
|
|
50
48
|
product: Product | null;
|
|
51
49
|
relatedProducts: Product[];
|
|
@@ -58,7 +56,6 @@ function ProductDetailPage() {
|
|
|
58
56
|
const relatedProducts = data?.relatedProducts ?? [];
|
|
59
57
|
const error = data?.error ?? (productId ? null : 'Invalid product ID');
|
|
60
58
|
|
|
61
|
-
// Fallback on client nav if hover prefetch didn't run
|
|
62
59
|
useEffect(() => {
|
|
63
60
|
if (typeof window === 'undefined') return;
|
|
64
61
|
if (routeData !== undefined) return;
|
|
@@ -80,7 +77,6 @@ function ProductDetailPage() {
|
|
|
80
77
|
|
|
81
78
|
const handleAddToCart = () => {
|
|
82
79
|
setAddingToCart(true);
|
|
83
|
-
// Simulate adding to cart
|
|
84
80
|
setTimeout(() => {
|
|
85
81
|
setAddingToCart(false);
|
|
86
82
|
alert(`Added ${product?.title} to cart!`);
|
|
@@ -95,10 +91,12 @@ function ProductDetailPage() {
|
|
|
95
91
|
return (
|
|
96
92
|
<>
|
|
97
93
|
<Header />
|
|
98
|
-
<main className='
|
|
99
|
-
<h1 className='
|
|
100
|
-
<div className='
|
|
101
|
-
<div className='
|
|
94
|
+
<main className='page-main'>
|
|
95
|
+
<h1 className='page-title'>Loading...</h1>
|
|
96
|
+
<div className='state-loading'>
|
|
97
|
+
<div className='state-loading-pulse'>
|
|
98
|
+
Loading product details...
|
|
99
|
+
</div>
|
|
102
100
|
</div>
|
|
103
101
|
</main>
|
|
104
102
|
</>
|
|
@@ -109,11 +107,11 @@ function ProductDetailPage() {
|
|
|
109
107
|
return (
|
|
110
108
|
<>
|
|
111
109
|
<Header />
|
|
112
|
-
<main className='
|
|
113
|
-
<div className='
|
|
110
|
+
<main className='page-main'>
|
|
111
|
+
<div className='state-error'>
|
|
114
112
|
{error || 'Product not found'}
|
|
115
|
-
<div className='
|
|
116
|
-
<Link to='/products' className='link
|
|
113
|
+
<div className='state-error-action'>
|
|
114
|
+
<Link to='/products' className='link link-semibold'>
|
|
117
115
|
← Back to Products
|
|
118
116
|
</Link>
|
|
119
117
|
</div>
|
|
@@ -127,9 +125,8 @@ function ProductDetailPage() {
|
|
|
127
125
|
<>
|
|
128
126
|
<Header />
|
|
129
127
|
|
|
130
|
-
<main className='
|
|
131
|
-
|
|
132
|
-
<nav className='flex items-center gap-2 text-sm text-gray-600 mb-8'>
|
|
128
|
+
<main className='page-main'>
|
|
129
|
+
<nav className='breadcrumb'>
|
|
133
130
|
<Link to='/' className='link'>
|
|
134
131
|
Home
|
|
135
132
|
</Link>
|
|
@@ -142,13 +139,11 @@ function ProductDetailPage() {
|
|
|
142
139
|
{product.category}
|
|
143
140
|
</Link>
|
|
144
141
|
<span>/</span>
|
|
145
|
-
<span className='
|
|
142
|
+
<span className='breadcrumb-current'>{product.title}</span>
|
|
146
143
|
</nav>
|
|
147
144
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{/* Product Image */}
|
|
151
|
-
<div className='card flex items-center justify-center p-8 min-h-[500px]'>
|
|
145
|
+
<div className='product-detail-grid'>
|
|
146
|
+
<div className='card product-detail-image'>
|
|
152
147
|
<Image
|
|
153
148
|
src={product.image}
|
|
154
149
|
alt={product.title}
|
|
@@ -157,43 +152,36 @@ function ProductDetailPage() {
|
|
|
157
152
|
quality={85}
|
|
158
153
|
sizes='(max-width: 1024px) 90vw, 350px'
|
|
159
154
|
priority
|
|
160
|
-
className='
|
|
155
|
+
className='product-detail-image-img'
|
|
161
156
|
style={{ viewTransitionName: `product-image-${product.id}` }}
|
|
162
157
|
/>
|
|
163
158
|
</div>
|
|
164
159
|
|
|
165
|
-
|
|
166
|
-
<div className='flex flex-col gap-6'>
|
|
160
|
+
<div className='product-detail-info'>
|
|
167
161
|
<div className='badge-primary'>{product.category}</div>
|
|
168
162
|
|
|
169
|
-
<h1 className='
|
|
170
|
-
{product.title}
|
|
171
|
-
</h1>
|
|
163
|
+
<h1 className='product-detail-title'>{product.title}</h1>
|
|
172
164
|
|
|
173
165
|
{product.rating && (
|
|
174
|
-
<div className='
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
<span>({product.rating.count} reviews)</span>
|
|
181
|
-
</div>
|
|
166
|
+
<div className='product-rating-lg'>
|
|
167
|
+
<span className='product-rating-star'>⭐</span>
|
|
168
|
+
<span className='product-rating-value'>
|
|
169
|
+
{product.rating.rate}
|
|
170
|
+
</span>
|
|
171
|
+
<span>({product.rating.count} reviews)</span>
|
|
182
172
|
</div>
|
|
183
173
|
)}
|
|
184
174
|
|
|
185
|
-
<div className='
|
|
186
|
-
${product.price.toFixed(2)}
|
|
187
|
-
</div>
|
|
175
|
+
<div className='price-lg'>${product.price.toFixed(2)}</div>
|
|
188
176
|
|
|
189
|
-
<p className='
|
|
177
|
+
<p className='product-detail-description'>
|
|
190
178
|
{product.description}
|
|
191
179
|
</p>
|
|
192
180
|
|
|
193
|
-
<div className='
|
|
181
|
+
<div className='product-actions'>
|
|
194
182
|
<button
|
|
195
183
|
type='button'
|
|
196
|
-
className='btn-primary
|
|
184
|
+
className='btn-primary btn-grow'
|
|
197
185
|
onClick={handleAddToCart}
|
|
198
186
|
disabled={addingToCart}
|
|
199
187
|
>
|
|
@@ -209,24 +197,23 @@ function ProductDetailPage() {
|
|
|
209
197
|
</button>
|
|
210
198
|
</div>
|
|
211
199
|
|
|
212
|
-
<div className='
|
|
213
|
-
<div className='
|
|
214
|
-
<span className='
|
|
200
|
+
<div className='product-perks'>
|
|
201
|
+
<div className='product-perk'>
|
|
202
|
+
<span className='product-perk-check'>✓</span>
|
|
215
203
|
<span>Free shipping on orders over $50</span>
|
|
216
204
|
</div>
|
|
217
|
-
<div className='
|
|
218
|
-
<span className='
|
|
205
|
+
<div className='product-perk'>
|
|
206
|
+
<span className='product-perk-check'>✓</span>
|
|
219
207
|
<span>30-day return policy</span>
|
|
220
208
|
</div>
|
|
221
|
-
<div className='
|
|
222
|
-
<span className='
|
|
209
|
+
<div className='product-perk'>
|
|
210
|
+
<span className='product-perk-check'>✓</span>
|
|
223
211
|
<span>Secure checkout</span>
|
|
224
212
|
</div>
|
|
225
213
|
</div>
|
|
226
214
|
</div>
|
|
227
215
|
</div>
|
|
228
216
|
|
|
229
|
-
{/* Related Products */}
|
|
230
217
|
{relatedProducts.length > 0 && (
|
|
231
218
|
<section className='animate-in'>
|
|
232
219
|
<h2 className='section-heading'>Related Products</h2>
|
|
@@ -54,7 +54,6 @@ function ProductsPage() {
|
|
|
54
54
|
const [loading, setLoading] = useState(!routeData);
|
|
55
55
|
const [error, setError] = useState<string | null>(routeData?.error || null);
|
|
56
56
|
|
|
57
|
-
// Load categories on mount (if not provided by SSR)
|
|
58
57
|
useEffect(() => {
|
|
59
58
|
if (categories.length > 0) return;
|
|
60
59
|
getCategories()
|
|
@@ -65,9 +64,7 @@ function ProductsPage() {
|
|
|
65
64
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
65
|
}, [categories.length]);
|
|
67
66
|
|
|
68
|
-
// Load products based on category (skip initial when SSR provided for 'all')
|
|
69
67
|
useEffect(() => {
|
|
70
|
-
// When SSR provided initial products just stop loading
|
|
71
68
|
if (products.length > 0) {
|
|
72
69
|
setFilteredProducts(products);
|
|
73
70
|
setLoading(false);
|
|
@@ -96,11 +93,9 @@ function ProductsPage() {
|
|
|
96
93
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
97
94
|
}, [selectedCategory, products]);
|
|
98
95
|
|
|
99
|
-
// Filter and sort products
|
|
100
96
|
useEffect(() => {
|
|
101
97
|
let result = [...products];
|
|
102
98
|
|
|
103
|
-
// Apply search filter
|
|
104
99
|
if (searchQuery.trim()) {
|
|
105
100
|
const query = searchQuery.toLowerCase();
|
|
106
101
|
result = result.filter(
|
|
@@ -111,7 +106,6 @@ function ProductsPage() {
|
|
|
111
106
|
);
|
|
112
107
|
}
|
|
113
108
|
|
|
114
|
-
// Apply sorting
|
|
115
109
|
switch (sortBy) {
|
|
116
110
|
case 'price-asc':
|
|
117
111
|
result.sort((a, b) => a.price - b.price);
|
|
@@ -123,7 +117,6 @@ function ProductsPage() {
|
|
|
123
117
|
result.sort((a, b) => a.title.localeCompare(b.title));
|
|
124
118
|
break;
|
|
125
119
|
default:
|
|
126
|
-
// Keep default order
|
|
127
120
|
break;
|
|
128
121
|
}
|
|
129
122
|
|
|
@@ -134,26 +127,22 @@ function ProductsPage() {
|
|
|
134
127
|
<>
|
|
135
128
|
<Header />
|
|
136
129
|
|
|
137
|
-
<main className='
|
|
138
|
-
<div className='
|
|
130
|
+
<main className='page-main'>
|
|
131
|
+
<div className='page-intro'>
|
|
139
132
|
<h1 className='section-heading'>All Products</h1>
|
|
140
133
|
<p className='section-subheading'>
|
|
141
134
|
Browse our complete collection of quality products
|
|
142
135
|
</p>
|
|
143
136
|
</div>
|
|
144
137
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<label
|
|
149
|
-
className='font-semibold text-gray-700'
|
|
150
|
-
htmlFor='category-filter'
|
|
151
|
-
>
|
|
138
|
+
<div className='filter-bar'>
|
|
139
|
+
<div className='filter-group'>
|
|
140
|
+
<label className='filter-label' htmlFor='category-filter'>
|
|
152
141
|
Category:
|
|
153
142
|
</label>
|
|
154
143
|
<select
|
|
155
144
|
id='category-filter'
|
|
156
|
-
className='select min
|
|
145
|
+
className='select select-min'
|
|
157
146
|
value={selectedCategory}
|
|
158
147
|
onChange={(e) => setSelectedCategory(e.target.value)}
|
|
159
148
|
>
|
|
@@ -169,54 +158,38 @@ function ProductsPage() {
|
|
|
169
158
|
<input
|
|
170
159
|
type='search'
|
|
171
160
|
placeholder='Search products...'
|
|
172
|
-
className='input
|
|
161
|
+
className='input input-grow'
|
|
173
162
|
value={searchQuery}
|
|
174
163
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
175
164
|
/>
|
|
176
165
|
|
|
177
|
-
<div className='
|
|
178
|
-
<span className='
|
|
179
|
-
<div className='
|
|
166
|
+
<div className='filter-group'>
|
|
167
|
+
<span className='filter-label'>Sort:</span>
|
|
168
|
+
<div className='filter-sort-group'>
|
|
180
169
|
<button
|
|
181
170
|
type='button'
|
|
182
|
-
className={`btn
|
|
183
|
-
sortBy === 'default'
|
|
184
|
-
? 'bg-primary-600 text-white border-primary-600'
|
|
185
|
-
: 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50'
|
|
186
|
-
}`}
|
|
171
|
+
className={`sort-btn${sortBy === 'default' ? ' is-active' : ''}`}
|
|
187
172
|
onClick={() => setSortBy('default')}
|
|
188
173
|
>
|
|
189
174
|
Default
|
|
190
175
|
</button>
|
|
191
176
|
<button
|
|
192
177
|
type='button'
|
|
193
|
-
className={`btn-
|
|
194
|
-
sortBy === 'price-asc'
|
|
195
|
-
? 'bg-primary-600 text-white border-primary-600'
|
|
196
|
-
: 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50'
|
|
197
|
-
}`}
|
|
178
|
+
className={`sort-btn${sortBy === 'price-asc' ? ' is-active' : ''}`}
|
|
198
179
|
onClick={() => setSortBy('price-asc')}
|
|
199
180
|
>
|
|
200
181
|
Price: Low to High
|
|
201
182
|
</button>
|
|
202
183
|
<button
|
|
203
184
|
type='button'
|
|
204
|
-
className={`btn-
|
|
205
|
-
sortBy === 'price-desc'
|
|
206
|
-
? 'bg-primary-600 text-white border-primary-600'
|
|
207
|
-
: 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50'
|
|
208
|
-
}`}
|
|
185
|
+
className={`sort-btn${sortBy === 'price-desc' ? ' is-active' : ''}`}
|
|
209
186
|
onClick={() => setSortBy('price-desc')}
|
|
210
187
|
>
|
|
211
188
|
Price: High to Low
|
|
212
189
|
</button>
|
|
213
190
|
<button
|
|
214
191
|
type='button'
|
|
215
|
-
className={`btn-
|
|
216
|
-
sortBy === 'name-asc'
|
|
217
|
-
? 'bg-primary-600 text-white border-primary-600'
|
|
218
|
-
: 'bg-white text-gray-700 border border-gray-300 hover:bg-gray-50'
|
|
219
|
-
}`}
|
|
192
|
+
className={`sort-btn${sortBy === 'name-asc' ? ' is-active' : ''}`}
|
|
220
193
|
onClick={() => setSortBy('name-asc')}
|
|
221
194
|
>
|
|
222
195
|
Name: A-Z
|
|
@@ -225,24 +198,21 @@ function ProductsPage() {
|
|
|
225
198
|
</div>
|
|
226
199
|
|
|
227
200
|
{!loading && (
|
|
228
|
-
<div className='
|
|
201
|
+
<div className='filter-count'>
|
|
229
202
|
Showing {filteredProducts.length} of {products.length} products
|
|
230
203
|
</div>
|
|
231
204
|
)}
|
|
232
205
|
</div>
|
|
233
206
|
|
|
234
|
-
{/* Products Grid */}
|
|
235
207
|
{loading ? (
|
|
236
|
-
<div className='
|
|
237
|
-
<div className='
|
|
208
|
+
<div className='state-loading'>
|
|
209
|
+
<div className='state-loading-pulse'>Loading products...</div>
|
|
238
210
|
</div>
|
|
239
211
|
) : error ? (
|
|
240
|
-
<div className='
|
|
241
|
-
{error}
|
|
242
|
-
</div>
|
|
212
|
+
<div className='state-error'>{error}</div>
|
|
243
213
|
) : filteredProducts.length === 0 ? (
|
|
244
|
-
<div className='
|
|
245
|
-
<div className='
|
|
214
|
+
<div className='state-empty'>
|
|
215
|
+
<div className='state-empty-icon'>🔍</div>
|
|
246
216
|
<div>No products found matching your criteria</div>
|
|
247
217
|
</div>
|
|
248
218
|
) : (
|