├── .gitignore ├── LICENSE ├── README.md ├── db.json ├── design ├── details.png └── home.png ├── package-lock.json ├── package.json ├── previews ├── mobile.gif └── web.gif ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── profile.jpg └── robots.txt └── src ├── App.js ├── App.test.js ├── BASE_URL.js ├── components ├── About.js ├── Search.js ├── base │ ├── Footer.js │ └── Nav.js └── products │ ├── ProductCard.js │ └── TableItem.js ├── index.css ├── index.js ├── logo.svg ├── pages ├── AboutPage.js ├── Home.css ├── Home.js ├── ProductDetails.js ├── Products.js └── SearchPage.js ├── reportWebVitals.js ├── routes.js ├── setupTests.js └── technologies.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .vercel 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Merve Karabulut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dummy Amazon Clone with React - NozamA :joy: 2 | 3 | In this assignment, I tried to build a responsive dummy amazon clone. 4 | 5 | Its name is NozamA, very creative, I know :sunglasses: 6 | 7 | You can see all products, search specific product and display each product details. 8 | 9 | **[DEMO](https://react-product-app.vercel.app/)** 10 | 11 | ## Technologies :rocket: 12 | - React 13 | - React Router Dom 14 | - Bootstrap 5 15 | - axios 16 | - React Paginate 17 | - Fake Store API 18 | - JSON-Server 19 | 20 | ## Design 21 | | Homepage| Details | 22 | | ------ | ----- | 23 | | | | 24 | 25 | 26 | ## Previews 27 | 28 | - Desktop 29 | 30 | ![Desktop Preview](previews/web.gif) 31 | 32 | - Mobile 33 | 34 | ![Mobile Preview](previews/mobile.gif) 35 | 36 | ## How to install 37 | 38 | ```bash 39 | git pull https://github.com/mervekrblt/upschool-react-assignment.git 40 | 41 | npm install 42 | 43 | npm start 44 | ``` 45 | ## Run local database 46 | ```bash 47 | npm run database 48 | ``` 49 | 50 | ### Task Lists 51 | 52 | - [x] Create Routes and components 53 | - [x] Create all routes 54 | - [x] Home Page 55 | - [x] Navbar 56 | - [x] Footer 57 | - [x] Products Page 58 | - [x] Product Card components 59 | - [x] About Page 60 | - [x] Search Page 61 | - [x] Search component 62 | - [x] Product Details Page 63 | - [x] Product Details Card component 64 | - [x] Products in home page with pagination -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "title": "laptop", 6 | "price": 109.95, 7 | "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday", 8 | "category": ["men's clothing", "clothing"], 9 | "image": "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg", 10 | "rating": { 11 | "rate": 3.9, 12 | "count": 120 13 | } 14 | }, 15 | { 16 | "id": 2, 17 | "title": "tshirt", 18 | "price": 22.3, 19 | "description": "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket.", 20 | "category": [ 21 | "men's clothing", 22 | "clothing" 23 | ], 24 | "image": "https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg", 25 | "rating": { 26 | "rate": 4.1, 27 | "count": 259 28 | } 29 | }, 30 | { 31 | "id": 3, 32 | "title": "jacket", 33 | "price": 55.99, 34 | "description": "great outerwear jackets for Spring/Autumn/Winter, suitable for many occasions, such as working, hiking, camping, mountain/rock climbing, cycling, traveling or other outdoors. Good gift choice for you or your family member. A warm hearted love to Father, husband or son in this thanksgiving or Christmas Day.", 35 | "category": [ 36 | "men's clothing", 37 | "clothing" 38 | ], 39 | "image": "https://fakestoreapi.com/img/71li-ujtlUL._AC_UX679_.jpg", 40 | "rating": { 41 | "rate": 4.7, 42 | "count": 500 43 | } 44 | }, 45 | { 46 | "id": 4, 47 | "title": "jacket", 48 | "price": 15.99, 49 | "description": "The color could be slightly different between on the screen and in practice. / Please note that body builds vary by person, therefore, detailed size information should be reviewed below on the product description.", 50 | "category": [ 51 | "men's clothing", 52 | "clothing" 53 | ], 54 | "image": "https://fakestoreapi.com/img/71YXzeOuslL._AC_UY879_.jpg", 55 | "rating": { 56 | "rate": 2.1, 57 | "count": 430 58 | } 59 | }, 60 | { 61 | "id": 5, 62 | "title": "bracelet", 63 | "price": 695, 64 | "description": "From our Legends Collection, the Naga was inspired by the mythical water dragon that protects the ocean's pearl. Wear facing inward to be bestowed with love and abundance, or outward for protection.", 65 | "category": [ 66 | "jewelery", 67 | "bracelet" 68 | ], 69 | "image": "https://fakestoreapi.com/img/71pWzhdJNwL._AC_UL640_QL65_ML3_.jpg", 70 | "rating": { 71 | "rate": 4.6, 72 | "count": 400 73 | } 74 | }, 75 | { 76 | "id": 6, 77 | "title": "micropave", 78 | "price": 168, 79 | "description": "Satisfaction Guaranteed. Return or exchange any order within 30 days.Designed and sold by Hafeez Center in the United States. Satisfaction Guaranteed. Return or exchange any order within 30 days.", 80 | "category": [ 81 | "jewelery", 82 | "micropave" 83 | ], 84 | "image": "https://fakestoreapi.com/img/61sbMiUnoGL._AC_UL640_QL65_ML3_.jpg", 85 | "rating": { 86 | "rate": 3.9, 87 | "count": 70 88 | } 89 | }, 90 | { 91 | "id": 7, 92 | "title": "ring", 93 | "price": 9.99, 94 | "description": "Classic Created Wedding Engagement Solitaire Diamond Promise Ring for Her. Gifts to spoil your love more for Engagement, Wedding, Anniversary, Valentine's Day...", 95 | "category": [ 96 | "jewelery", 97 | "ring" 98 | ], 99 | "image": "https://fakestoreapi.com/img/71YAIFU48IL._AC_UL640_QL65_ML3_.jpg", 100 | "rating": { 101 | "rate": 3, 102 | "count": 400 103 | } 104 | }, 105 | { 106 | "id": 8, 107 | "title": "earring", 108 | "price": 10.99, 109 | "description": "Rose Gold Plated Double Flared Tunnel Plug Earrings. Made of 316L Stainless Steel", 110 | "category": ["jewelery", "earring"], 111 | "image": "https://fakestoreapi.com/img/51UDEzMJVpL._AC_UL640_QL65_ML3_.jpg", 112 | "rating": { 113 | "rate": 1.9, 114 | "count": 100 115 | } 116 | }, 117 | { 118 | "id": 9, 119 | "title": "usb", 120 | "price": 64, 121 | "description": "USB 3.0 and USB 2.0 Compatibility Fast data transfers Improve PC Performance High Capacity; Compatibility Formatted NTFS for Windows 10, Windows 8.1, Windows 7; Reformatting may be required for other operating systems; Compatibility may vary depending on user’s hardware configuration and operating system", 122 | "category": [ 123 | "electronics", 124 | "device" 125 | ], 126 | "image": "https://fakestoreapi.com/img/61IBBVJvSDL._AC_SY879_.jpg", 127 | "rating": { 128 | "rate": 3.3, 129 | "count": 203 130 | } 131 | }, 132 | { 133 | "id": 10, 134 | "title": "ssd", 135 | "price": 109, 136 | "description": "Easy upgrade for faster boot up, shutdown, application load and response (As compared to 5400 RPM SATA 2.5” hard drive; Based on published specifications and internal benchmarking tests using PCMark vantage scores) Boosts burst write performance, making it ideal for typical PC workloads The perfect balance of performance and reliability Read/write speeds of up to 535MB/s/450MB/s (Based on internal testing; Performance may vary depending upon drive capacity, host device, OS and application.)", 137 | "category": [ 138 | "electronics", 139 | "device" 140 | ], 141 | "image": "https://fakestoreapi.com/img/61U7T1koQqL._AC_SX679_.jpg", 142 | "rating": { 143 | "rate": 2.9, 144 | "count": 470 145 | } 146 | }, 147 | { 148 | "id": 11, 149 | "title": "ssd", 150 | "price": 109, 151 | "description": "3D NAND flash are applied to deliver high transfer speeds Remarkable transfer speeds that enable faster bootup and improved overall system performance. The advanced SLC Cache Technology allows performance boost and longer lifespan 7mm slim design suitable for Ultrabooks and Ultra-slim notebooks. Supports TRIM command, Garbage Collection technology, RAID, and ECC (Error Checking & Correction) to provide the optimized performance and enhanced reliability.", 152 | "category": [ 153 | "electronics", 154 | "device" 155 | ], 156 | "image": "https://fakestoreapi.com/img/71kWymZ+c+L._AC_SX679_.jpg", 157 | "rating": { 158 | "rate": 4.8, 159 | "count": 319 160 | } 161 | }, 162 | { 163 | "id": 12, 164 | "title": "ssd", 165 | "price": 114, 166 | "description": "Expand your PS4 gaming experience, Play anywhere Fast and easy, setup Sleek design with high capacity, 3-year manufacturer's limited warranty", 167 | "category": [ 168 | "electronics", 169 | "device" 170 | ], 171 | "image": "https://fakestoreapi.com/img/61mtL65D4cL._AC_SX679_.jpg", 172 | "rating": { 173 | "rate": 4.8, 174 | "count": 400 175 | } 176 | }, 177 | { 178 | "id": 13, 179 | "title": "laptop", 180 | "price": 599, 181 | "description": "21. 5 inches Full HD (1920 x 1080) widescreen IPS display And Radeon free Sync technology. No compatibility for VESA Mount Refresh Rate: 75Hz - Using HDMI port Zero-frame design | ultra-thin | 4ms response time | IPS panel Aspect ratio - 16: 9. Color Supported - 16. 7 million colors. Brightness - 250 nit Tilt angle -5 degree to 15 degree. Horizontal viewing angle-178 degree. Vertical viewing angle-178 degree 75 hertz", 182 | "category": [ 183 | "electronics", 184 | "device" 185 | ], 186 | "image": "https://fakestoreapi.com/img/81QpkIctqPL._AC_SX679_.jpg", 187 | "rating": { 188 | "rate": 2.9, 189 | "count": 250 190 | } 191 | }, 192 | { 193 | "id": 14, 194 | "title": "monitor ", 195 | "price": 999.99, 196 | "description": "49 INCH SUPER ULTRAWIDE 32:9 CURVED GAMING MONITOR with dual 27 inch screen side by side QUANTUM DOT (QLED) TECHNOLOGY, HDR support and factory calibration provides stunningly realistic and accurate color and contrast 144HZ HIGH REFRESH RATE and 1ms ultra fast response time work to eliminate motion blur, ghosting, and reduce input lag", 197 | "category": ["electronics", "device"], 198 | "image": "https://fakestoreapi.com/img/81Zt42ioCgL._AC_SX679_.jpg", 199 | "rating": { 200 | "rate": 2.2, 201 | "count": 140 202 | } 203 | }, 204 | { 205 | "id": 15, 206 | "title": "snowboard", 207 | "price": 56.99, 208 | "description": "Note:The Jackets is US standard size, Please choose size as your usual wear Material: 100% Polyester; Detachable Liner Fabric: Warm Fleece. Detachable Functional Liner: Skin Friendly, Lightweigt and Warm.Stand Collar Liner jacket, keep you warm in cold weather. Zippered Pockets: 2 Zippered Hand Pockets, 2 Zippered Pockets on Chest (enough to keep cards or keys)and 1 Hidden Pocket Inside.Zippered Hand Pockets and Hidden Pocket keep your things secure. Humanized Design: Adjustable and Detachable Hood and Adjustable cuff to prevent the wind and water,for a comfortable fit. 3 in 1 Detachable Design provide more convenience, you can separate the coat and inner as needed, or wear it together. It is suitable for different season and help you adapt to different climates", 209 | "category": ["women's clothing", "clothing"], 210 | "image": "https://fakestoreapi.com/img/51Y5NI-I5jL._AC_UX679_.jpg", 211 | "rating": { 212 | "rate": 2.6, 213 | "count": 235 214 | } 215 | } 216 | ] 217 | } -------------------------------------------------------------------------------- /design/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/design/details.png -------------------------------------------------------------------------------- /design/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/design/home.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-product-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.1", 7 | "@testing-library/react": "^12.1.2", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^0.24.0", 10 | "bootstrap": "^5.1.3", 11 | "json-server": "^0.17.0", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-paginate": "^8.1.0", 15 | "react-router-dom": "^6.2.1", 16 | "react-scripts": "5.0.0", 17 | "web-vitals": "^2.1.2" 18 | }, 19 | "scripts": { 20 | "database": "json-server --watch database.json --port 4000", 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /previews/mobile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/previews/mobile.gif -------------------------------------------------------------------------------- /previews/web.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/previews/web.gif -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 15 | 19 | 20 | 29 | NozamA 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mervekrblt/upschool-react-assignment/97b5e279c142100eec2aa58bd2b4f29c93f6d51e/public/profile.jpg -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | //import { useState } from 'react' 2 | 3 | import { Route, Routes } from "react-router-dom"; 4 | import Footer from "./components/base/Footer"; 5 | import Nav from "./components/base/Nav"; 6 | import routes from "./routes" 7 | 8 | function App() { 9 | //const [data, setData] = useState(null) 10 | 11 | /*fetch("http://localhost:4000/products") 12 | .then(response => response.json()) 13 | .then(data => setData(data))*/ 14 | 15 | return ( 16 | <> 17 | 18 | 19 | {routes.map(route => }> )} 20 | 21 | 22 | 23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/BASE_URL.js: -------------------------------------------------------------------------------- 1 | const BASE_URL = "https://my-json-server.typicode.com/mervekrblt/upschool-react-assignment/products" 2 | 3 | export default BASE_URL -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import { useLocation } from "react-router-dom" 2 | import { useState, useEffect } from "react" 3 | import technologies from "../technologies" 4 | 5 | const About = () => 6 | { 7 | const [show, setShow] = useState(true) 8 | const location = useLocation() 9 | useEffect(() => 10 | { 11 | if (location.pathname === "/") 12 | { 13 | setShow(false) 14 | } 15 | }, [location.pathname]) 16 | //console.log(show, location) 17 | return <> 18 |
19 |
20 |
21 |
22 |

Project Details

23 |

In this assignment, I tried to build a dummy amazon clone.
Well, I know that it looks like nonprofessional

24 |

I used Fake Store data. I got data from there and used it with JSON-Server.
Why did I do this?
I just wanted to try JSON-Server and its API that Fake Store API doesn't have.

25 |

You can see all products, search specific product and display each product details.

26 |
27 |
28 |

Technologies That I Used

29 |
30 | {technologies.map(data => 31 | // eslint-disable-next-line react/jsx-no-target-blank 32 | 33 | {data.title} 34 | ) 35 | } 36 |
37 |
38 |
39 |
40 |
41 | 42 | {show &&
43 |
44 | Profile 45 |

About Merve

46 |

Merve is a self-taught developer, and she works as a front-end developer in a company. After she graduated from food engineering, she found her passion in coding. She is one of the women of 25 who graduated from Women Developer Academy Turkey 2021. She is also a Women Techmakers Ambassador. 47 | 48 |
Fun facts about her, she can center a div without "google it" sometimes, well she said "sometimes". Also, she likes to watch anime, learn new things, challenge herself.

49 |
50 |
} 51 | 52 | } 53 | export default About -------------------------------------------------------------------------------- /src/components/Search.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | import { useLocation, useNavigate } from 'react-router-dom' 3 | 4 | const Search = (props) => 5 | { 6 | const location = useLocation() 7 | const navigation = useNavigate() 8 | //const urlParams = new URLSearchParams(location.search) 9 | //const search = urlParams.get("q") 10 | const [q, setQ] = useState("") 11 | //console.log(location) 12 | 13 | const inputHandler = (e) => 14 | { 15 | e.preventDefault() 16 | // PROBLEMMM 17 | //console.log(e.target.search.value) 18 | setQ(e.target.search.value) 19 | console.log(q) 20 | navigation(`/search?q=${(e.target.search.value).toLowerCase()}`) 21 | } 22 | 23 | useEffect(() => { 24 | //if(location.search === "") 25 | if(location.search.length === 0) { 26 | console.log("setQ sıfırla") 27 | setQ("") 28 | console.log(q) 29 | } 30 | }, [location, q]) 31 | 32 | 33 | return <> 34 |
35 |
36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 | } 44 | export default Search -------------------------------------------------------------------------------- /src/components/base/Footer.js: -------------------------------------------------------------------------------- 1 | const Footer = () => 2 | { 3 | return <> 4 | 46 | 47 | } 48 | export default Footer -------------------------------------------------------------------------------- /src/components/base/Nav.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | import routes from '../../routes' 3 | 4 | const Nav = () => 5 | { 6 | return <> 7 | 24 | 25 | } 26 | export default Nav -------------------------------------------------------------------------------- /src/components/products/ProductCard.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom" 2 | 3 | const ProductCard = (props) => { 4 | const {image, title, description, id} = props 5 | return <> 6 |
7 | Card 8 |
9 |
{title}
10 |

{description}

11 |
12 | Details 13 |
14 | 15 | } 16 | export default ProductCard -------------------------------------------------------------------------------- /src/components/products/TableItem.js: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom" 2 | 3 | const TableItem = (props) => 4 | { 5 | const { image, title, description, id } = props 6 | 7 | const navigation = useNavigate() 8 | const goDetails = () => { 9 | navigation(`/product-details/${id}`) 10 | } 11 | 12 | return <> 13 | 14 | 15 | {id} 16 | products 17 | {title} 18 | {description.split(" ").slice(0, 5).join(" ")}... 19 | 20 | 21 | } 22 | export default TableItem -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import { BrowserRouter } from "react-router-dom"; 7 | import 'bootstrap/dist/css/bootstrap.min.css' 8 | import "bootstrap/dist/js/bootstrap.bundle"; 9 | 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | 19 | // If you want to start measuring performance in your app, pass a function 20 | // to log results (for example: reportWebVitals(console.log)) 21 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 22 | reportWebVitals(); 23 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/AboutPage.js: -------------------------------------------------------------------------------- 1 | import About from "../components/About" 2 | 3 | const AboutPage = () => { 4 | return <> 5 | 6 | 7 | } 8 | export default AboutPage -------------------------------------------------------------------------------- /src/pages/Home.css: -------------------------------------------------------------------------------- 1 | .pagination { 2 | margin-top: 45px; 3 | display: flex; 4 | justify-content: center; 5 | list-style: none; 6 | outline: none; 7 | } 8 | 9 | .pagination>.active>a { 10 | background-color: #0000007a; 11 | } 12 | 13 | .pagination>li>a { 14 | border: 1px solid #bd81a586; 15 | padding: 5px 10px; 16 | outline: none; 17 | cursor: pointer; 18 | } 19 | 20 | .pagination>li>a, 21 | .pagination>li>span { 22 | color: #070707; 23 | background-color: rgb(247, 247, 247); 24 | } -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react" 2 | import ReactPaginate from 'react-paginate'; 3 | import BASE_URL from "../BASE_URL" 4 | import About from "../components/About" 5 | import Search from "../components/Search" 6 | import ProductCard from '../components/products/ProductCard' 7 | import './Home.css' 8 | 9 | const Home = () => 10 | { 11 | const [currentPage, setCurrentPage] = useState(0); 12 | const [data, setData] = useState([]); 13 | 14 | const PER_PAGE = 3; 15 | const offset = currentPage * PER_PAGE; 16 | const currentPageData = data 17 | .slice(offset, offset + PER_PAGE) 18 | const pageCount = Math.ceil(data.length / PER_PAGE); 19 | 20 | useEffect(() => 21 | { 22 | fetchData(); 23 | }, []); 24 | function fetchData() 25 | { 26 | fetch(BASE_URL) 27 | .then((res) => res.json()) 28 | .then((data) => 29 | { 30 | setData(data); 31 | }); 32 | } 33 | 34 | function handlePageClick({ selected: selectedPage }) 35 | { 36 | setCurrentPage(selectedPage); 37 | } 38 | return <> 39 |

NozamA

40 | 41 |
42 |
43 | {currentPageData.map(product => 44 |
45 | 52 | 53 |
54 | )} 55 |
56 | 57 |
58 | 59 | 70 | 71 | 72 | 73 | 74 | } 75 | export default Home -------------------------------------------------------------------------------- /src/pages/ProductDetails.js: -------------------------------------------------------------------------------- 1 | import { useLocation } from 'react-router-dom' 2 | import { useEffect, useState } from 'react' 3 | import BASE_URL from '../BASE_URL' 4 | 5 | const ProductDetails = () => 6 | { 7 | const location = useLocation() 8 | const id = Number(location.pathname.split("/")[2]) 9 | const [product, setProduct] = useState([]) 10 | const [loading, setLoading] = useState(true) 11 | 12 | const getProduct = async () => 13 | { 14 | fetch(`${BASE_URL}/${id}`) 15 | .then((res) => res.json()) 16 | .then(data => 17 | { 18 | setProduct(data) 19 | setLoading(false) 20 | }) 21 | 22 | } 23 | //console.log(loading) 24 | useEffect(() => 25 | { 26 | getProduct() 27 | // eslint-disable-next-line react-hooks/exhaustive-deps 28 | }, []) 29 | 30 | return <> 31 | {loading &&
32 |
33 | Loading... 34 |
35 |
36 | } 37 | {!loading &&
38 |
39 |
40 | product 41 |
42 |
43 |

{product.title}

44 |

45 | {product.description} 46 |

47 |

Categories

48 | {product.category.map(item => {item})} 49 |

Price

50 |

{product.price} $

51 |
52 |
53 |
} 54 | 55 | } 56 | export default ProductDetails -------------------------------------------------------------------------------- /src/pages/Products.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import axios from 'axios' 3 | import BASE_URL from '../BASE_URL' 4 | //import ProductCard from '../components/products/ProductCard' 5 | import TableItem from '../components/products/TableItem' 6 | 7 | 8 | const Products = () => 9 | { 10 | const [products, setProducts] = useState([]) 11 | const [loading, setLoading] = useState(true) 12 | 13 | const getAllProducts = async () => 14 | { 15 | const res = await axios.get(BASE_URL) 16 | const data = res.data 17 | setProducts(data) 18 | setLoading(false) 19 | } 20 | 21 | useEffect(() => 22 | { 23 | // call this function when page is created 24 | getAllProducts() 25 | console.log(products) 26 | // eslint-disable-next-line react-hooks/exhaustive-deps 27 | }, []) 28 | 29 | console.log(products) 30 | return <> 31 | {loading &&
32 |
33 | Loading... 34 |
35 |
36 | } 37 | 38 | {!loading && 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {products.map(product => 52 | 60 | 61 | )} 62 | 63 |
#Product ImageTitleDescription
64 |
} 65 | 66 | 67 | } 68 | export default Products -------------------------------------------------------------------------------- /src/pages/SearchPage.js: -------------------------------------------------------------------------------- 1 | import { useLocation } from "react-router-dom" 2 | import Search from "../components/Search" 3 | import ProductCard from "../components/products/ProductCard" 4 | import BASE_URL from "../BASE_URL" 5 | import { useEffect, useState } from "react" 6 | 7 | const SearchPage = () => 8 | { 9 | const location = useLocation() 10 | const urlParams = new URLSearchParams(location.search) 11 | const search = urlParams.get("q") 12 | const [products, setProducts] = useState([]) 13 | console.log(search) 14 | 15 | /*const filterProducts = () => { 16 | fetch(`${BASE_URL}?title=${search}`) 17 | .then(res => res.json()) 18 | .then(data => setProducts(data)) 19 | }*/ 20 | 21 | useEffect(() => 22 | { 23 | fetch(`${BASE_URL}?title=${search}`) 24 | .then(res => res.json()) 25 | .then(data => setProducts(data)) 26 | }, [search]) 27 | 28 | console.log(products) 29 | console.log(!products.length) 30 | return <> 31 |

What are you looking for...

32 | 33 | {(!products.length && search !== null) &&
34 |
35 |

There is no item about "{search}" try again

36 |

Hint: Search for ssd, laptop etc.

37 |

I am not Amazon, I dont have all products :)

38 |
39 | 40 |
} 41 | 42 |
43 |
44 | {products.map(product =>
45 | 52 | 53 |
)} 54 |
55 |
56 | 57 | } 58 | export default SearchPage -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | import Home from './pages/Home' 2 | import AboutPage from './pages/AboutPage' 3 | import Products from './pages/Products' 4 | import SearchPage from './pages/SearchPage' 5 | import ProductDetails from './pages/ProductDetails' 6 | const routes = [ 7 | { title: "Home", path: "/", element: Home, isNav: true }, 8 | { title: "About", path: "about", element: AboutPage, isNav: true }, 9 | { title: "Products", path: "products", element: Products, isNav: true }, 10 | { title: "Search", path: "search", element: SearchPage, isNav: true }, 11 | { title: "ProductDetails", path: "product-details/:id", element: ProductDetails, isNav: false }, 12 | 13 | ] 14 | 15 | export default routes -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/technologies.js: -------------------------------------------------------------------------------- 1 | const technologies = [ 2 | { id: 1, title: "React Paginate", url:"https://www.npmjs.com/package/react-paginate" }, 3 | { id: 2, title: "React", url:"https://reactjs.org/" }, 4 | { id: 3, title: "Bootstrap 5", url:"hhttps://getbootstrap.com/" }, 5 | { id: 4, title: "React Router Dom", url:"https://reactrouterdotcom.fly.dev/docs/en/v6" }, 6 | { id: 5, title: "JSON Server", url:"https://www.npmjs.com/package/json-server" }, 7 | { id: 6, title: "Fake Store API", url:"https://fakestoreapi.com/" }, 8 | ] 9 | 10 | export default technologies --------------------------------------------------------------------------------