|
| 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>© 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