@@ -5,54 +5,99 @@ import ProductGrid from './components/ProductGrid'
55import ProductDetail from './components/ProductDetail'
66import { ALL_PRODUCTS } from './data/products'
77import { motion , AnimatePresence } from 'framer-motion'
8- import { X , ShoppingBag , Heart , User , ArrowRight } from 'lucide-react'
8+ import { X , ShoppingBag , Heart , User , Trash2 } from 'lucide-react'
99
1010function App ( ) {
1111 const [ selectedCategories , setSelectedCategories ] = useState ( [ ] ) ;
1212 const [ selectedThemes , setSelectedThemes ] = useState ( [ ] ) ;
1313 const [ selectedProduct , setSelectedProduct ] = useState ( null ) ;
1414 const [ activeOverlay , setActiveOverlay ] = useState ( null ) ;
15+ const [ cartItems , setCartItems ] = useState ( [ ] ) ;
16+ const [ wishlistItems , setWishlistItems ] = useState ( [ ] ) ;
17+ const [ sortOption , setSortOption ] = useState ( 'recommended' ) ;
1518
1619 const toggleFilter = ( item , type ) => {
1720 if ( type === 'category' ) {
18- setSelectedCategories ( prev =>
21+ setSelectedCategories ( prev =>
1922 prev . includes ( item ) ? prev . filter ( i => i !== item ) : [ ...prev , item ]
2023 ) ;
2124 } else {
22- setSelectedThemes ( prev =>
25+ setSelectedThemes ( prev =>
2326 prev . includes ( item ) ? prev . filter ( i => i !== item ) : [ ...prev , item ]
2427 ) ;
2528 }
2629 } ;
2730
31+ const [ activeNav , setActiveNav ] = useState ( 'MEN' ) ;
32+
33+ const [ sneakersView , setSneakersView ] = useState ( false ) ;
34+
2835 const handleNavigate = ( destination ) => {
29- if ( destination === 'home' || destination === 'MEN' || destination === 'SNEAKERS' ) {
30- setSelectedProduct ( null ) ;
31- setSelectedCategories ( [ ] ) ;
32- setSelectedThemes ( [ ] ) ;
36+ setSelectedProduct ( null ) ;
37+ setSelectedThemes ( [ ] ) ;
38+ setSelectedCategories ( [ ] ) ;
39+ if ( destination === 'SNEAKERS' ) {
40+ setSneakersView ( true ) ;
41+ } else {
42+ setSneakersView ( false ) ;
3343 }
44+ if ( destination !== 'home' ) setActiveNav ( destination ) ;
45+ } ;
46+
47+ const addToCart = ( product , size ) => {
48+ setCartItems ( prev => {
49+ const exists = prev . find ( i => i . id === product . id && i . size === size ) ;
50+ if ( exists ) {
51+ return prev . map ( i => i . id === product . id && i . size === size ? { ...i , qty : i . qty + 1 } : i ) ;
52+ }
53+ return [ ...prev , { ...product , size, qty : 1 } ] ;
54+ } ) ;
55+ } ;
56+
57+ const removeFromCart = ( id , size ) => {
58+ setCartItems ( prev => prev . filter ( i => ! ( i . id === id && i . size === size ) ) ) ;
59+ } ;
60+
61+ const toggleWishlist = ( product ) => {
62+ setWishlistItems ( prev =>
63+ prev . find ( i => i . id === product . id )
64+ ? prev . filter ( i => i . id !== product . id )
65+ : [ ...prev , product ]
66+ ) ;
3467 } ;
3568
69+ const isWishlisted = ( id ) => wishlistItems . some ( i => i . id === id ) ;
70+
3671 const filteredProducts = useMemo ( ( ) => {
37- return ALL_PRODUCTS . filter ( product => {
72+ let result = ALL_PRODUCTS . filter ( product => {
73+ const sneakerMatch = sneakersView ? product . id >= 30 && product . id <= 44 : true ;
3874 const categoryMatch = selectedCategories . length === 0 || selectedCategories . includes ( product . category ) ;
3975 const themeMatch = selectedThemes . length === 0 || selectedThemes . includes ( product . theme ) ;
40- return categoryMatch && themeMatch ;
76+ return sneakerMatch && categoryMatch && themeMatch ;
4177 } ) ;
42- } , [ selectedCategories , selectedThemes ] ) ;
78+
79+ if ( sortOption === 'low-high' ) result = [ ...result ] . sort ( ( a , b ) => a . price - b . price ) ;
80+ else if ( sortOption === 'high-low' ) result = [ ...result ] . sort ( ( a , b ) => b . price - a . price ) ;
81+ else if ( sortOption === 'newest' ) result = [ ...result ] . sort ( ( a , b ) => b . id - a . id ) ;
82+
83+ return result ;
84+ } , [ selectedCategories , selectedThemes , sortOption , sneakersView ] ) ;
4385
4486 return (
4587 < div className = "zappify-app" >
46- < Header
47- onOpenOverlay = { setActiveOverlay }
88+ < Header
89+ onOpenOverlay = { setActiveOverlay }
4890 onNavigate = { handleNavigate }
91+ cartCount = { cartItems . reduce ( ( sum , i ) => sum + i . qty , 0 ) }
92+ wishlistCount = { wishlistItems . length }
93+ activeNav = { activeNav }
4994 />
50-
95+
5196 < main className = "app-main" >
5297 < div className = "container-broad main-layout" >
5398 < AnimatePresence mode = "wait" >
5499 { ! selectedProduct ? (
55- < motion . div
100+ < motion . div
56101 key = "grid"
57102 className = "grid-view"
58103 initial = { { opacity : 0 , x : - 20 } }
@@ -61,24 +106,37 @@ function App() {
61106 transition = { { duration : 0.3 } }
62107 >
63108 < div className = "layout-split" >
64- < Sidebar
109+ < Sidebar
65110 selectedCategories = { selectedCategories }
66111 selectedThemes = { selectedThemes }
67112 onToggleFilter = { toggleFilter }
68113 />
69- < ProductGrid products = { filteredProducts } onProductClick = { setSelectedProduct } />
114+ < ProductGrid
115+ products = { filteredProducts }
116+ onProductClick = { setSelectedProduct }
117+ sortOption = { sortOption }
118+ onSortChange = { setSortOption }
119+ onToggleWishlist = { toggleWishlist }
120+ isWishlisted = { isWishlisted }
121+ />
70122 </ div >
71123 </ motion . div >
72124 ) : (
73- < motion . div
125+ < motion . div
74126 key = "detail"
75127 className = "detail-view"
76128 initial = { { opacity : 0 , y : 20 } }
77129 animate = { { opacity : 1 , y : 0 } }
78130 exit = { { opacity : 0 , y : - 20 } }
79131 transition = { { duration : 0.4 } }
80132 >
81- < ProductDetail product = { selectedProduct } onBack = { ( ) => setSelectedProduct ( null ) } />
133+ < ProductDetail
134+ product = { selectedProduct }
135+ onBack = { ( ) => setSelectedProduct ( null ) }
136+ onAddToCart = { addToCart }
137+ onToggleWishlist = { toggleWishlist }
138+ isWishlisted = { isWishlisted ( selectedProduct . id ) }
139+ />
82140 </ motion . div >
83141 ) }
84142 </ AnimatePresence >
@@ -95,32 +153,38 @@ function App() {
95153
96154 < AnimatePresence >
97155 { activeOverlay && (
98- < Overlay
99- type = { activeOverlay }
100- onClose = { ( ) => setActiveOverlay ( null ) }
156+ < Overlay
157+ type = { activeOverlay }
158+ onClose = { ( ) => setActiveOverlay ( null ) }
159+ cartItems = { cartItems }
160+ wishlistItems = { wishlistItems }
161+ onRemoveFromCart = { removeFromCart }
162+ onToggleWishlist = { toggleWishlist }
163+ onSwitchOverlay = { setActiveOverlay }
101164 />
102165 ) }
103166 </ AnimatePresence >
104-
105- </ div >
167+ </ div >
106168 )
107169}
108170
109- const Overlay = ( { type, onClose } ) => {
171+ const Overlay = ( { type, onClose, cartItems, wishlistItems, onRemoveFromCart, onToggleWishlist, onSwitchOverlay } ) => {
172+ const [ isSignUp , setIsSignUp ] = useState ( false ) ;
110173 const isDrawer = type === 'cart' || type === 'wishlist' ;
174+ const cartTotal = cartItems . reduce ( ( sum , i ) => sum + i . price * i . qty , 0 ) ;
111175
112176 return (
113177 < div className = "overlay-system" >
114- < motion . div
178+ < motion . div
115179 className = "backdrop"
116180 initial = { { opacity : 0 } }
117181 animate = { { opacity : 1 } }
118182 exit = { { opacity : 0 } }
119183 onClick = { onClose }
120184 />
121-
185+
122186 { isDrawer ? (
123- < motion . div
187+ < motion . div
124188 className = "drawer"
125189 initial = { { x : '100%' } }
126190 animate = { { x : 0 } }
@@ -131,16 +195,66 @@ const Overlay = ({ type, onClose }) => {
131195 < h3 > { type === 'cart' ? 'SHOPPING BAG' : 'MY WISHLIST' } </ h3 >
132196 < button className = "close-btn" onClick = { onClose } > < X size = { 24 } /> </ button >
133197 </ div >
198+
134199 < 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 >
200+ { type === 'cart' ? (
201+ cartItems . length === 0 ? (
202+ < div className = "empty-state" >
203+ < ShoppingBag size = { 48 } />
204+ < p > Your bag is currently empty.</ p >
205+ < button className = "btn-primary" onClick = { onClose } > CONTINUE SHOPPING</ button >
206+ </ div >
207+ ) : (
208+ < div className = "cart-items" >
209+ { cartItems . map ( ( item , i ) => (
210+ < div key = { i } className = "cart-item" >
211+ < img src = { item . image } alt = { item . name } />
212+ < div className = "cart-item-info" >
213+ < p className = "cart-item-name" > { item . name } </ p >
214+ < p className = "cart-item-size" > Size: UK { item . size } </ p >
215+ < p className = "cart-item-qty" > Qty: { item . qty } </ p >
216+ < p className = "cart-item-price" > ₹ { item . price * item . qty } </ p >
217+ </ div >
218+ < button className = "remove-btn" onClick = { ( ) => onRemoveFromCart ( item . id , item . size ) } >
219+ < Trash2 size = { 16 } />
220+ </ button >
221+ </ div >
222+ ) ) }
223+ < div className = "cart-total" >
224+ < span > Total</ span >
225+ < span > ₹ { cartTotal } </ span >
226+ </ div >
227+ < button className = "btn-primary checkout-btn" > PROCEED TO CHECKOUT</ button >
228+ </ div >
229+ )
230+ ) : (
231+ wishlistItems . length === 0 ? (
232+ < div className = "empty-state" >
233+ < Heart size = { 48 } />
234+ < p > Your wishlist is currently empty.</ p >
235+ < button className = "btn-primary" onClick = { onClose } > CONTINUE SHOPPING</ button >
236+ </ div >
237+ ) : (
238+ < div className = "cart-items" >
239+ { wishlistItems . map ( ( item , i ) => (
240+ < div key = { i } className = "cart-item" >
241+ < img src = { item . image } alt = { item . name } />
242+ < div className = "cart-item-info" >
243+ < p className = "cart-item-name" > { item . name } </ p >
244+ < p className = "cart-item-price" > ₹ { item . price } </ p >
245+ </ div >
246+ < button className = "remove-btn" onClick = { ( ) => onToggleWishlist ( item ) } >
247+ < Trash2 size = { 16 } />
248+ </ button >
249+ </ div >
250+ ) ) }
251+ </ div >
252+ )
253+ ) }
140254 </ div >
141255 </ motion . div >
142256 ) : (
143- < motion . div
257+ < motion . div
144258 className = "modal"
145259 initial = { { opacity : 0 , scale : 0.9 , y : 20 } }
146260 animate = { { opacity : 1 , scale : 1 , y : 0 } }
@@ -151,23 +265,27 @@ const Overlay = ({ type, onClose }) => {
151265 </ div >
152266 < div className = "modal-content login-modal" >
153267 < User size = { 48 } className = "user-icon-large" />
154- < h2 > Welcome Back</ h2 >
155- < p > Login to your Zappify account</ p >
156-
268+ < h2 > { isSignUp ? 'Create Account' : ' Welcome Back' } </ h2 >
269+ < p > { isSignUp ? 'Join Zappify today' : ' Login to your Zappify account' } </ p >
270+
157271 < div className = "auth-form" >
272+ { isSignUp && < input type = "text" placeholder = "Full Name" /> }
158273 < input type = "email" placeholder = "Email Address" />
159274 < input type = "password" placeholder = "Password" />
160- < button className = "btn-primary auth-btn" > SIGN IN</ button >
275+ { isSignUp && < input type = "password" placeholder = "Confirm Password" /> }
276+ < button className = "btn-primary auth-btn" > { isSignUp ? 'CREATE ACCOUNT' : 'SIGN IN' } </ button >
161277 < div className = "separator" > < span > OR CONTINUE WITH</ span > </ div >
162278 < button className = "btn-outline google-btn" > GOOGLE</ button >
163279 </ div >
164-
165- < p className = "auth-footer" > Don't have an account? < span > Sign Up</ span > </ p >
280+
281+ < p className = "auth-footer" >
282+ { isSignUp ? 'Already have an account? ' : "Don't have an account? " }
283+ < span onClick = { ( ) => setIsSignUp ( ! isSignUp ) } > { isSignUp ? 'Sign In' : 'Sign Up' } </ span >
284+ </ p >
166285 </ div >
167286 </ motion . div >
168287 ) }
169-
170- </ div >
288+ </ div >
171289 ) ;
172290} ;
173291
0 commit comments