Skip to content

Commit 87a2696

Browse files
committed
restore web UI with sidebar
1 parent 40c44d1 commit 87a2696

3 files changed

Lines changed: 99 additions & 58 deletions

File tree

frontend/src/App.jsx

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState, useMemo } from 'react'
22
import Header from './components/Header'
3+
import Sidebar from './components/Sidebar'
34
import ProductGrid from './components/ProductGrid'
45
import ProductDetail from './components/ProductDetail'
56
import { ALL_PRODUCTS } from './data/products'
@@ -9,13 +10,13 @@ import { useGoogleLogin } from '@react-oauth/google'
910
import Checkout from './components/Checkout'
1011
import AccountModal from './components/AccountModal'
1112

12-
const CATEGORIES = ['All', 'Men Low Top Sneakers', 'Men High Top Sneakers', 'Men Mid Top Sneakers', 'Men Clogs', 'Men Slip-ons'];
13-
1413
function App() {
1514
const [selectedCategories, setSelectedCategories] = useState([]);
15+
const [selectedThemes, setSelectedThemes] = useState([]);
1616
const [selectedProduct, setSelectedProduct] = useState(null);
1717
const [activeOverlay, setActiveOverlay] = useState(null);
1818
const [searchQuery, setSearchQuery] = useState('');
19+
const [activeNav, setActiveNav] = useState('MEN');
1920
const [sneakersView, setSneakersView] = useState(false);
2021
const [showCheckout, setShowCheckout] = useState(false);
2122
const [showAccount, setShowAccount] = useState(false);
@@ -55,10 +56,20 @@ function App() {
5556
setPlacedOrders([]);
5657
};
5758

59+
const toggleFilter = (item, type) => {
60+
if (type === 'category') {
61+
setSelectedCategories(prev => prev.includes(item) ? prev.filter(i => i !== item) : [...prev, item]);
62+
} else {
63+
setSelectedThemes(prev => prev.includes(item) ? prev.filter(i => i !== item) : [...prev, item]);
64+
}
65+
};
66+
5867
const handleNavigate = (destination) => {
5968
setSelectedProduct(null);
6069
setSelectedCategories([]);
70+
setSelectedThemes([]);
6171
setSneakersView(destination === 'SNEAKERS');
72+
if (destination !== 'home') setActiveNav(destination);
6273
};
6374

6475
const addToCart = (product, size) => {
@@ -96,13 +107,14 @@ function App() {
96107
return ALL_PRODUCTS.filter(product => {
97108
const sneakerMatch = sneakersView ? product.id >= 30 && product.id <= 44 : true;
98109
const categoryMatch = selectedCategories.length === 0 || selectedCategories.includes(product.category);
110+
const themeMatch = selectedThemes.length === 0 || selectedThemes.includes(product.theme);
99111
const searchMatch = searchQuery.trim() === '' ||
100112
product.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
101113
product.brand.toLowerCase().includes(searchQuery.toLowerCase()) ||
102114
product.category.toLowerCase().includes(searchQuery.toLowerCase());
103-
return sneakerMatch && categoryMatch && searchMatch;
115+
return sneakerMatch && categoryMatch && themeMatch && searchMatch;
104116
});
105-
}, [selectedCategories, sneakersView, searchQuery]);
117+
}, [selectedCategories, selectedThemes, sneakersView, searchQuery]);
106118

107119
return (
108120
<div className="zappify-app">
@@ -111,6 +123,7 @@ function App() {
111123
onNavigate={handleNavigate}
112124
cartCount={cartItems.reduce((sum, i) => sum + i.qty, 0)}
113125
wishlistCount={wishlistItems.length}
126+
activeNav={activeNav}
114127
loggedInUser={loggedInUser}
115128
onOpenAccount={() => setShowAccount(true)}
116129
searchQuery={searchQuery}
@@ -129,26 +142,19 @@ function App() {
129142
exit={{ opacity: 0, x: -20 }}
130143
transition={{ duration: 0.3 }}
131144
>
132-
<div className="category-chips">
133-
{CATEGORIES.map(cat => (
134-
<button
135-
key={cat}
136-
className={`cat-chip ${selectedCategories.length === 0 && cat === 'All' ? 'active' : selectedCategories.includes(cat) ? 'active' : ''}`}
137-
onClick={() => cat === 'All'
138-
? setSelectedCategories([])
139-
: setSelectedCategories(prev => prev.includes(cat) ? prev.filter(c => c !== cat) : [...prev, cat])
140-
}
141-
>
142-
{cat}
143-
</button>
144-
))}
145+
<div className="layout-split">
146+
<Sidebar
147+
selectedCategories={selectedCategories}
148+
selectedThemes={selectedThemes}
149+
onToggleFilter={toggleFilter}
150+
/>
151+
<ProductGrid
152+
products={filteredProducts}
153+
onProductClick={setSelectedProduct}
154+
onToggleWishlist={toggleWishlist}
155+
isWishlisted={isWishlisted}
156+
/>
145157
</div>
146-
<ProductGrid
147-
products={filteredProducts}
148-
onProductClick={setSelectedProduct}
149-
onToggleWishlist={toggleWishlist}
150-
isWishlisted={isWishlisted}
151-
/>
152158
</motion.div>
153159
) : (
154160
<motion.div

frontend/src/components/Header.jsx

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,49 @@
1+
import { useState } from 'react';
12
import { Search, ShoppingBag, Heart, User, X } from 'lucide-react';
23

3-
const Header = ({ onOpenOverlay, onNavigate, cartCount, wishlistCount, loggedInUser, onOpenAccount, searchQuery, onSearch }) => {
4+
const Header = ({ onOpenOverlay, onNavigate, cartCount, wishlistCount, activeNav, loggedInUser, onOpenAccount, searchQuery, onSearch }) => {
5+
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
6+
47
return (
5-
<header className="sticky-header glass">
8+
<header className="sticky-header">
69
<nav className="container-broad">
710
<div className="header-wrapper">
811
<div className="brand-logo" onClick={() => onNavigate('home')} style={{ cursor: 'pointer' }}>
912
<span className="logo-accent">Z</span>appify
1013
</div>
1114

15+
<ul className={`nav-links ${isMobileMenuOpen ? 'mobile-open' : ''}`}>
16+
<li>
17+
<button
18+
className={`nav-item ${activeNav === 'MEN' ? 'active' : ''}`}
19+
onClick={() => { onNavigate('MEN'); setIsMobileMenuOpen(false); }}
20+
>
21+
MEN
22+
</button>
23+
</li>
24+
<li>
25+
<button
26+
className={`nav-item ${activeNav === 'SNEAKERS' ? 'active' : ''}`}
27+
onClick={() => { onNavigate('SNEAKERS'); setIsMobileMenuOpen(false); }}
28+
>
29+
SNEAKERS
30+
</button>
31+
</li>
32+
</ul>
33+
34+
<div className="search-container">
35+
<Search className="search-icon" size={18} />
36+
<input
37+
type="text"
38+
placeholder="What are you looking for?"
39+
value={searchQuery}
40+
onChange={(e) => onSearch(e.target.value)}
41+
/>
42+
{searchQuery && (
43+
<button className="search-clear" onClick={() => onSearch('')}><X size={16} /></button>
44+
)}
45+
</div>
46+
1247
<div className="user-actions">
1348
<button className="action-btn" title="Wishlist" onClick={() => onOpenOverlay('wishlist')}>
1449
<Heart size={22} />
@@ -35,19 +70,6 @@ const Header = ({ onOpenOverlay, onNavigate, cartCount, wishlistCount, loggedInU
3570
)}
3671
</div>
3772
</div>
38-
39-
<div className="header-search">
40-
<Search className="search-icon" size={18} />
41-
<input
42-
type="text"
43-
placeholder="What are you looking for?"
44-
value={searchQuery}
45-
onChange={(e) => onSearch(e.target.value)}
46-
/>
47-
{searchQuery && (
48-
<button className="search-clear" onClick={() => onSearch('')}><X size={16} /></button>
49-
)}
50-
</div>
5173
</nav>
5274
</header>
5375
);

frontend/src/index.css

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,10 @@ ul {
291291
position: sticky;
292292
top: 0;
293293
z-index: 1000;
294-
background: var(--white);
294+
background: #ffffff;
295+
height: 80px;
296+
display: flex;
297+
align-items: center;
295298
border-bottom: 1px solid var(--slate-100);
296299
transition: var(--transition-smooth);
297300
}
@@ -300,42 +303,52 @@ ul {
300303
display: flex;
301304
align-items: center;
302305
justify-content: space-between;
303-
padding: 16px 0 8px;
306+
gap: 2rem;
304307
}
305308

306309
.brand-logo {
307310
font-family: 'Outfit', sans-serif;
308-
font-size: 1.8rem;
309-
font-weight: 900;
311+
font-size: 2.2rem;
312+
font-weight: 800;
310313
color: var(--deep-slate);
311-
letter-spacing: -1px;
314+
letter-spacing: -1.5px;
312315
}
313316

314317
.logo-accent {
315318
color: var(--brand-red);
316319
}
317320

318-
.header-search {
321+
.nav-links {
319322
display: flex;
320-
align-items: center;
321-
gap: 10px;
322-
background: var(--slate-100);
323-
border-radius: 14px;
324-
padding: 10px 16px;
325-
margin-bottom: 12px;
323+
gap: 2.5rem;
324+
list-style: none;
326325
}
327326

328-
.header-search input {
329-
flex: 1;
327+
.nav-item {
328+
font-size: 1rem;
329+
font-weight: 700;
330+
color: var(--slate-600);
331+
position: relative;
332+
padding: 0.5rem 0;
333+
letter-spacing: 0.5px;
334+
background: none;
330335
border: none;
331-
background: transparent;
332-
font-size: 14px;
333-
color: var(--deep-slate);
334-
outline: none;
336+
cursor: pointer;
337+
transition: 0.3s;
335338
}
336339

337-
.header-search input::placeholder {
338-
color: var(--slate-400);
340+
.nav-item.active {
341+
color: var(--brand-red);
342+
}
343+
344+
.nav-item.active::after {
345+
content: '';
346+
position: absolute;
347+
bottom: 0;
348+
left: 0;
349+
width: 100%;
350+
height: 3px;
351+
background: var(--brand-red);
339352
}
340353

341354
.search-container {

0 commit comments

Comments
 (0)