Skip to content

Commit b9352d9

Browse files
committed
add App component with routing, filters and overlay system
1 parent c912c2e commit b9352d9

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

frontend/src/App.jsx

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import React, { useState, useMemo } from 'react'
2+
import Header from './components/Header'
3+
import Sidebar from './components/Sidebar'
4+
import ProductGrid from './components/ProductGrid'
5+
import ProductDetail from './components/ProductDetail'
6+
import { ALL_PRODUCTS } from './data/products'
7+
import { motion, AnimatePresence } from 'framer-motion'
8+
import { X, ShoppingBag, Heart, User, ArrowRight } from 'lucide-react'
9+
10+
function App() {
11+
const [selectedCategories, setSelectedCategories] = useState([]);
12+
const [selectedThemes, setSelectedThemes] = useState([]);
13+
const [selectedProduct, setSelectedProduct] = useState(null);
14+
const [activeOverlay, setActiveOverlay] = useState(null);
15+
16+
const toggleFilter = (item, type) => {
17+
if (type === 'category') {
18+
setSelectedCategories(prev =>
19+
prev.includes(item) ? prev.filter(i => i !== item) : [...prev, item]
20+
);
21+
} else {
22+
setSelectedThemes(prev =>
23+
prev.includes(item) ? prev.filter(i => i !== item) : [...prev, item]
24+
);
25+
}
26+
};
27+
28+
const handleNavigate = (destination) => {
29+
if (destination === 'home' || destination === 'MEN' || destination === 'SNEAKERS') {
30+
setSelectedProduct(null);
31+
setSelectedCategories([]);
32+
setSelectedThemes([]);
33+
}
34+
};
35+
36+
const filteredProducts = useMemo(() => {
37+
return ALL_PRODUCTS.filter(product => {
38+
const categoryMatch = selectedCategories.length === 0 || selectedCategories.includes(product.category);
39+
const themeMatch = selectedThemes.length === 0 || selectedThemes.includes(product.theme);
40+
return categoryMatch && themeMatch;
41+
});
42+
}, [selectedCategories, selectedThemes]);
43+
44+
return (
45+
<div className="zappify-app">
46+
<Header
47+
onOpenOverlay={setActiveOverlay}
48+
onNavigate={handleNavigate}
49+
/>
50+
51+
<main className="app-main">
52+
<div className="container-broad main-layout">
53+
<AnimatePresence mode="wait">
54+
{!selectedProduct ? (
55+
<motion.div
56+
key="grid"
57+
className="grid-view"
58+
initial={{ opacity: 0, x: -20 }}
59+
animate={{ opacity: 1, x: 0 }}
60+
exit={{ opacity: 0, x: -20 }}
61+
transition={{ duration: 0.3 }}
62+
>
63+
<div className="layout-split">
64+
<Sidebar
65+
selectedCategories={selectedCategories}
66+
selectedThemes={selectedThemes}
67+
onToggleFilter={toggleFilter}
68+
/>
69+
<ProductGrid products={filteredProducts} onProductClick={setSelectedProduct} />
70+
</div>
71+
</motion.div>
72+
) : (
73+
<motion.div
74+
key="detail"
75+
className="detail-view"
76+
initial={{ opacity: 0, y: 20 }}
77+
animate={{ opacity: 1, y: 0 }}
78+
exit={{ opacity: 0, y: -20 }}
79+
transition={{ duration: 0.4 }}
80+
>
81+
<ProductDetail product={selectedProduct} onBack={() => setSelectedProduct(null)} />
82+
</motion.div>
83+
)}
84+
</AnimatePresence>
85+
</div>
86+
</main>
87+
88+
<footer className="zappify-footer">
89+
<div className="container-broad">
90+
<div className="footer-bottom">
91+
<p>&copy; 2026 Zappify Shoe Store. All rights reserved.</p>
92+
</div>
93+
</div>
94+
</footer>
95+
96+
<AnimatePresence>
97+
{activeOverlay && (
98+
<Overlay
99+
type={activeOverlay}
100+
onClose={() => setActiveOverlay(null)}
101+
/>
102+
)}
103+
</AnimatePresence>
104+
105+
</div>
106+
)
107+
}
108+
109+
const Overlay = ({ type, onClose }) => {
110+
const isDrawer = type === 'cart' || type === 'wishlist';
111+
112+
return (
113+
<div className="overlay-system">
114+
<motion.div
115+
className="backdrop"
116+
initial={{ opacity: 0 }}
117+
animate={{ opacity: 1 }}
118+
exit={{ opacity: 0 }}
119+
onClick={onClose}
120+
/>
121+
122+
{isDrawer ? (
123+
<motion.div
124+
className="drawer"
125+
initial={{ x: '100%' }}
126+
animate={{ x: 0 }}
127+
exit={{ x: '100%' }}
128+
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
129+
>
130+
<div className="overlay-header">
131+
<h3>{type === 'cart' ? 'SHOPPING BAG' : 'MY WISHLIST'}</h3>
132+
<button className="close-btn" onClick={onClose}><X size={24} /></button>
133+
</div>
134+
<div className="overlay-content">
135+
<div className="empty-state">
136+
{type === 'cart' ? <ShoppingBag size={48} /> : <Heart size={48} />}
137+
<p>Your {type} is currently empty.</p>
138+
<button className="btn-primary" onClick={onClose}>CONTINUE SHOPPING</button>
139+
</div>
140+
</div>
141+
</motion.div>
142+
) : (
143+
<motion.div
144+
className="modal"
145+
initial={{ opacity: 0, scale: 0.9, y: 20 }}
146+
animate={{ opacity: 1, scale: 1, y: 0 }}
147+
exit={{ opacity: 0, scale: 0.9, y: 20 }}
148+
>
149+
<div className="modal-header">
150+
<button className="close-btn" onClick={onClose}><X size={24} /></button>
151+
</div>
152+
<div className="modal-content login-modal">
153+
<User size={48} className="user-icon-large" />
154+
<h2>Welcome Back</h2>
155+
<p>Login to your Zappify account</p>
156+
157+
<div className="auth-form">
158+
<input type="email" placeholder="Email Address" />
159+
<input type="password" placeholder="Password" />
160+
<button className="btn-primary auth-btn">SIGN IN</button>
161+
<div className="separator"><span>OR CONTINUE WITH</span></div>
162+
<button className="btn-outline google-btn">GOOGLE</button>
163+
</div>
164+
165+
<p className="auth-footer">Don't have an account? <span>Sign Up</span></p>
166+
</div>
167+
</motion.div>
168+
)}
169+
170+
</div>
171+
);
172+
};
173+
174+
export default App

0 commit comments

Comments
 (0)