From 866076cbf870f1c32b4e0b3846b30b22f50c7ff0 Mon Sep 17 00:00:00 2001 From: Mohammed Mahmood Date: Fri, 10 Dec 2021 22:36:59 +0300 Subject: [PATCH 1/3] protect products page and fix a bug that keeps jwt even when log out --- package-lock.json | 1 + src/App.js | 36 +++++++++--------- src/pages/Dashboard.jsx | 46 ++++++++++------------- src/pages/Login.jsx | 81 +++++++++++++++++++++++------------------ src/pages/Products.jsx | 23 +++++++----- 5 files changed, 96 insertions(+), 91 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a4ab34..2b37d5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "axios-app", "version": "0.1.0", "dependencies": { "@emotion/react": "^11.7.0", diff --git a/src/App.js b/src/App.js index 7a13708..4cbe2cb 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,22 @@ -import logo from './logo.svg'; -import './App.css'; -import Dashboard from './pages/Dashboard' -import Categories from './pages/Categories' -import Products from './pages/Products' -import Login from './pages/Login' -import {Navigate, Route, Routes} from 'react-router-dom' -import {useEffect,useState} from 'react' -import Drawer from './components/Drawer' - +import logo from "./logo.svg"; +import "./App.css"; +import Dashboard from "./pages/Dashboard"; +import Categories from "./pages/Categories"; +import Products from "./pages/Products"; +import Login from "./pages/Login"; +import { Navigate, Route, Routes } from "react-router-dom"; +import { useEffect, useState } from "react"; +import Drawer from "./components/Drawer"; function App(props) { - - - return - }/> - }/> - }/> - }/> - - + return ( + + } /> + } /> + } /> + } /> + + ); } export default App; diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index 3c13707..fef3554 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -1,32 +1,24 @@ -import React, { useEffect, useState } from 'react'; -import {Navigate} from 'react-router-dom' +import React, { useEffect, useState } from "react"; +import { Navigate } from "react-router-dom"; function Dashboard(props) { - const [isLogged, setIsLogged] = React.useState(true) - useEffect(()=>{ - console.log('1') - let token; - try { - token = JSON.parse(localStorage.getItem('token')) - console.log('2') - if(!token) - setIsLogged(false) + const [isLogged, setIsLogged] = React.useState(true); + useEffect(() => { + console.log("1"); + let token; + try { + token = JSON.parse(localStorage.getItem("token")); + console.log("2"); + if (!token) setIsLogged(false); + } catch (error) { + console.log(error); + setIsLogged(false); + } + }, []); + console.log("3"); - } catch (error) { - console.log(error) - setIsLogged(false) - } - - },[]) - console.log('3') + if (!isLogged) return ; - if(!isLogged) - return - - return ( -
- Dashboard -
- ); + return
Dashboard
; } -export default Dashboard; \ No newline at end of file +export default Dashboard; diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index cf8b851..3e543b7 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,40 +1,49 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; -import axios from '../utils/axios' -import {TOKEN_KEY} from '../utils/Constants' +import React from "react"; +import { useNavigate } from "react-router-dom"; +import axios from "../utils/axios"; +import { TOKEN_KEY } from "../utils/Constants"; function Login(props) { - const navigate = useNavigate() - const [email, setEmail] = React.useState('') - const [password, setPassword] = React.useState('') + const navigate = useNavigate(); + const [email, setEmail] = React.useState(""); + const [password, setPassword] = React.useState(""); + //delete any jwt that exists + localStorage.removeItem("token"); - const login = (e)=>{ - e.preventDefault() - axios.post('/api/academy/auth/login', - { - email:email, - password:password - } - ) - .then((response)=>{ - console.log(response) - let token = response.data.token.access_token; - let data = response.data; - localStorage.setItem(TOKEN_KEY, JSON.stringify(data)) - navigate('/dashboard') - }) - .catch((err)=>{ - console.log(err) - }) - } - return ( -
-
- setEmail(e.target.value)} type="email" /> - setPassword(e.target.value)} type="password" /> - -
-
- ); + const login = (e) => { + e.preventDefault(); + axios + .post("/api/academy/auth/login", { + email: email, + password: password, + }) + .then((response) => { + console.log(response); + let token = response.data.token.access_token; + let data = response.data; + localStorage.setItem(TOKEN_KEY, JSON.stringify(data)); + navigate("/dashboard"); + }) + .catch((err) => { + console.log(err); + }); + }; + return ( +
+
+ setEmail(e.target.value)} + type="email" + /> + setPassword(e.target.value)} + type="password" + /> + +
+
+ ); } -export default Login; \ No newline at end of file +export default Login; diff --git a/src/pages/Products.jsx b/src/pages/Products.jsx index e3fcba4..7d4398e 100644 --- a/src/pages/Products.jsx +++ b/src/pages/Products.jsx @@ -1,12 +1,17 @@ -import React, { useEffect } from 'react'; - +import React, { useEffect, useState } from "react"; +import { Navigate } from "react-router"; function Products(props) { - - return ( -
- Products -
- ); + let [isLogged, setIsLogged] = useState(true); + useEffect(() => { + let token = JSON.parse(localStorage.getItem("token")); + if (!token) { + setIsLogged(false); + } + }, []); + if (!isLogged) { + return ; + } + return
Products
; } -export default Products; \ No newline at end of file +export default Products; From 1bd423300cb81870a9177c82ecaa23d7b4e88975 Mon Sep 17 00:00:00 2001 From: Mohammed Mahmood Date: Tue, 14 Dec 2021 16:06:45 +0300 Subject: [PATCH 2/3] Solve the bare minimum --- package-lock.json | 15 ++ package.json | 1 + src/App.js | 1 + src/components/Button.jsx | 10 + src/components/Drawer.jsx | 227 +++++++++++---------- src/components/Input.jsx | 27 +++ src/components/Product.jsx | 66 ++++++ src/components/Snackbar.jsx | 40 ++++ src/components/componentsStyle/Product.css | 7 + src/index.js | 34 ++- src/pages/Cart.jsx | 34 +++ src/pages/PagesStyles/ProductsPage.css | 10 + src/pages/Products.jsx | 75 ++++++- src/utils/Constants.js | 6 +- yarn.lock | 5 + 15 files changed, 428 insertions(+), 130 deletions(-) create mode 100644 src/components/Button.jsx create mode 100644 src/components/Input.jsx create mode 100644 src/components/Product.jsx create mode 100644 src/components/Snackbar.jsx create mode 100644 src/components/componentsStyle/Product.css create mode 100644 src/pages/Cart.jsx create mode 100644 src/pages/PagesStyles/ProductsPage.css diff --git a/package-lock.json b/package-lock.json index 2b37d5d..aaca79b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "react": "^17.0.2", "react-cookie": "^4.1.1", "react-dom": "^17.0.2", + "react-icons": "^4.3.1", "react-router-dom": "^6.0.2", "react-scripts": "4.0.3", "web-vitals": "^1.0.1" @@ -14639,6 +14640,14 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" }, + "node_modules/react-icons": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", @@ -30681,6 +30690,12 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" }, + "react-icons": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", + "requires": {} + }, "react-is": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", diff --git a/package.json b/package.json index 2c613e9..2de7fca 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "react": "^17.0.2", "react-cookie": "^4.1.1", "react-dom": "^17.0.2", + "react-icons": "^4.3.1", "react-router-dom": "^6.0.2", "react-scripts": "4.0.3", "web-vitals": "^1.0.1" diff --git a/src/App.js b/src/App.js index 4cbe2cb..48803b9 100644 --- a/src/App.js +++ b/src/App.js @@ -14,6 +14,7 @@ function App(props) { } /> } /> } /> + } /> } /> ); diff --git a/src/components/Button.jsx b/src/components/Button.jsx new file mode 100644 index 0000000..ab43cb1 --- /dev/null +++ b/src/components/Button.jsx @@ -0,0 +1,10 @@ +import React from "react"; +import Snackbar from "./Snackbar"; + +function Button(props) { + return ( + + ); +} + +export default Button; diff --git a/src/components/Drawer.jsx b/src/components/Drawer.jsx index 9e615f1..d6f945a 100644 --- a/src/components/Drawer.jsx +++ b/src/components/Drawer.jsx @@ -1,91 +1,97 @@ -import * as React from 'react'; -import { useEffect } from 'react' -import { styled, useTheme } from '@mui/material/styles'; -import Box from '@mui/material/Box'; -import Drawer from '@mui/material/Drawer'; -import CssBaseline from '@mui/material/CssBaseline'; -import MuiAppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import List from '@mui/material/List'; -import Typography from '@mui/material/Typography'; -import Divider from '@mui/material/Divider'; -import IconButton from '@mui/material/IconButton'; -import MenuIcon from '@mui/icons-material/Menu'; -import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; -import ChevronRightIcon from '@mui/icons-material/ChevronRight'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import InboxIcon from '@mui/icons-material/MoveToInbox'; -import MailIcon from '@mui/icons-material/Mail'; -import { Routes, Link, Route, useLocation, useNavigate } from 'react-router-dom' -import Dashboard from '../pages/Dashboard' -import Categories from '../pages/Categories' -import Products from '../pages/Products' -import LogoutIcon from '@mui/icons-material/Logout'; -import GridViewIcon from '@mui/icons-material/GridView'; -import CategoryIcon from '@mui/icons-material/Category'; -import ProductionQuantityLimitsIcon from '@mui/icons-material/ProductionQuantityLimits'; -import Login from '../pages/Login' -import Avatar from './Avatar' -import MenuItem from '@mui/material/MenuItem'; -import Menu from '@mui/material/Menu'; -import Tooltip from '@mui/material/Tooltip'; -import {navigate} from 'react-router-dom' -import {TOKEN_KEY} from '../utils/Constants' - +import * as React from "react"; +import { useEffect } from "react"; +import { styled, useTheme } from "@mui/material/styles"; +import Box from "@mui/material/Box"; +import Drawer from "@mui/material/Drawer"; +import CssBaseline from "@mui/material/CssBaseline"; +import MuiAppBar from "@mui/material/AppBar"; +import Toolbar from "@mui/material/Toolbar"; +import List from "@mui/material/List"; +import Typography from "@mui/material/Typography"; +import Divider from "@mui/material/Divider"; +import IconButton from "@mui/material/IconButton"; +import MenuIcon from "@mui/icons-material/Menu"; +import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import ListItem from "@mui/material/ListItem"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import ListItemText from "@mui/material/ListItemText"; +import InboxIcon from "@mui/icons-material/MoveToInbox"; +import MailIcon from "@mui/icons-material/Mail"; +import { + Routes, + Link, + Route, + useLocation, + useNavigate, +} from "react-router-dom"; +import Dashboard from "../pages/Dashboard"; +import Categories from "../pages/Categories"; +import Products from "../pages/Products"; +import LogoutIcon from "@mui/icons-material/Logout"; +import GridViewIcon from "@mui/icons-material/GridView"; +import CategoryIcon from "@mui/icons-material/Category"; +import ProductionQuantityLimitsIcon from "@mui/icons-material/ProductionQuantityLimits"; +import Login from "../pages/Login"; +import Avatar from "./Avatar"; +import MenuItem from "@mui/material/MenuItem"; +import Menu from "@mui/material/Menu"; +import Tooltip from "@mui/material/Tooltip"; +import { navigate } from "react-router-dom"; +import { TOKEN_KEY } from "../utils/Constants"; +import Cart from "../pages/Cart"; +import { Button } from "@mui/material"; +import { FaShoppingCart } from "react-icons/fa"; const drawerWidth = 240; - - -const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })( +const Main = styled("main", { shouldForwardProp: (prop) => prop !== "open" })( ({ theme, open }) => ({ flexGrow: 1, padding: theme.spacing(3), - transition: theme.transitions.create('margin', { + transition: theme.transitions.create("margin", { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), marginLeft: `-${drawerWidth}px`, ...(open && { - transition: theme.transitions.create('margin', { + transition: theme.transitions.create("margin", { easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen, }), marginLeft: 0, }), - }), + }) ); const AppBar = styled(MuiAppBar, { - shouldForwardProp: (prop) => prop !== 'open', + shouldForwardProp: (prop) => prop !== "open", })(({ theme, open }) => ({ - transition: theme.transitions.create(['margin', 'width'], { + transition: theme.transitions.create(["margin", "width"], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), ...(open && { width: `calc(100% - ${drawerWidth}px)`, marginLeft: `${drawerWidth}px`, - transition: theme.transitions.create(['margin', 'width'], { + transition: theme.transitions.create(["margin", "width"], { easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen, }), }), })); -const DrawerHeader = styled('div')(({ theme }) => ({ - display: 'flex', - alignItems: 'center', +const DrawerHeader = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", padding: theme.spacing(0, 1), // necessary for content to be below app bar ...theme.mixins.toolbar, - justifyContent: 'flex-end', + justifyContent: "flex-end", })); function PersistentDrawerLeft(props) { - const location = useLocation() - const navigate = useNavigate() + const location = useLocation(); + const navigate = useNavigate(); const theme = useTheme(); const [open, setOpen] = React.useState(false); @@ -100,26 +106,26 @@ function PersistentDrawerLeft(props) { }; const logout = () => { - localStorage.removeItem(TOKEN_KEY) - navigate('/login') - handleCloseUserMenu() - } - useEffect(() => { - - }, []) + localStorage.removeItem(TOKEN_KEY); + navigate("/login"); + handleCloseUserMenu(); + }; + useEffect(() => {}, []); const renderContent = (routeName) => { - console.log(routeName) + console.log(routeName); switch (routeName) { - case '/login': - return - case '/products': - return - case '/dashboard': - return - case '/categories': - return + case "/login": + return ; + case "/products": + return ; + case "/dashboard": + return ; + case "/categories": + return ; + case "/cart": + return ; } - } + }; const handleOpenUserMenu = (event) => { setAnchorElUser(event.currentTarget); }; @@ -127,66 +133,75 @@ function PersistentDrawerLeft(props) { setAnchorElUser(null); }; return ( - + - - - + + - + + + - - - - - - + + + + + + - + Logout - - {theme.direction === 'ltr' ? : } + {theme.direction === "ltr" ? ( + + ) : ( + + )} - + - + - + - + - + - + - + - + @@ -237,4 +256,4 @@ function PersistentDrawerLeft(props) { ); } -export default PersistentDrawerLeft \ No newline at end of file +export default PersistentDrawerLeft; diff --git a/src/components/Input.jsx b/src/components/Input.jsx new file mode 100644 index 0000000..03800af --- /dev/null +++ b/src/components/Input.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import TextField from "@mui/material/TextField"; + +function Input(props) { + return ( + + ); + // return ( + // + // ); +} + +export default Input; diff --git a/src/components/Product.jsx b/src/components/Product.jsx new file mode 100644 index 0000000..d6efce1 --- /dev/null +++ b/src/components/Product.jsx @@ -0,0 +1,66 @@ +import React from "react"; +import Button from "./Button"; +import Chip from "@mui/material/Chip"; + +import "./componentsStyle/Product.css"; +import { width } from "@mui/system"; +import { useState } from "react"; +const getId = () => { + return "id" + new Date().getTime(); +}; +function Product(props) { + const addItem = (object) => { + let cart = JSON.parse(localStorage.getItem("cart")); + if (!cart) { + localStorage.setItem("cart", JSON.stringify([])); + let cart = JSON.parse(localStorage.getItem("cart")); + object.id = getId(); + cart.unshift(JSON.stringify(object)); + localStorage.setItem("cart", JSON.stringify(cart)); + return; + } + object.id = getId(); + cart.unshift(JSON.stringify(object)); + localStorage.setItem("cart", JSON.stringify(cart)); + }; + const deleteItem = (itemId, productsRerenderFun) => { + let cart = JSON.parse(localStorage.getItem("cart")); + cart.splice( + cart.findIndex((obj) => { + obj = JSON.parse(obj); + return obj.id == itemId; + }), + 1 + ); + localStorage.setItem("cart", JSON.stringify(cart)); + productsRerenderFun([]); + }; + return ( +
+ Shoe +

{props.productName}

+

${props.productPrice}

+ {props.cartItem ? ( + { + deleteItem(props.object.id, props.productsRerender); + }} + /> + ) : ( +
+ ); +} + +export default Product; diff --git a/src/components/Snackbar.jsx b/src/components/Snackbar.jsx new file mode 100644 index 0000000..5abf483 --- /dev/null +++ b/src/components/Snackbar.jsx @@ -0,0 +1,40 @@ +import * as React from "react"; +import Stack from "@mui/material/Stack"; +import Button from "@mui/material/Button"; +import Snackbar from "@mui/material/Snackbar"; +import MuiAlert from "@mui/material/Alert"; + +const Alert = React.forwardRef(function Alert(props, ref) { + return ; +}); + +export default function CustomizedSnackbars(props) { + const [open, setOpen] = React.useState(false); + + const handleClick = () => { + // adds then show message + props.onClickFunction(); + setOpen(true); + }; + + const handleClose = (event, reason) => { + if (reason === "clickaway") { + return; + } + + setOpen(false); + }; + + return ( + + + + + Added to cart! + + + + ); +} diff --git a/src/components/componentsStyle/Product.css b/src/components/componentsStyle/Product.css new file mode 100644 index 0000000..07b35b5 --- /dev/null +++ b/src/components/componentsStyle/Product.css @@ -0,0 +1,7 @@ +.product { + width: 200px; + padding: 1rem; + background-color: #fff; + margin: 8px; + border-radius: 20px; +} diff --git a/src/index.js b/src/index.js index bb29a24..6e12701 100644 --- a/src/index.js +++ b/src/index.js @@ -1,34 +1,32 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import {BrowserRouter} from 'react-router-dom' -import Drawer from './components/Drawer' -import { createTheme, ThemeProvider, styled } from '@mui/material/styles'; +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; +import { BrowserRouter } from "react-router-dom"; +import { createTheme, ThemeProvider, styled } from "@mui/material/styles"; const theme = createTheme({ - palette: { - type: 'light', + type: "light", primary: { - main: '#000000', + main: "#000000", }, secondary: { - main: '#f50057', + main: "#f50057", }, }, -}) +}); ReactDOM.render( - - + + - - + + , - document.getElementById('root') + document.getElementById("root") ); // If you want to start measuring performance in your app, pass a function diff --git a/src/pages/Cart.jsx b/src/pages/Cart.jsx new file mode 100644 index 0000000..ae78f2b --- /dev/null +++ b/src/pages/Cart.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import Product from "../components/Product"; +import "./PagesStyles/ProductsPage.css"; +import { useState } from "react"; +function Cart() { + const [neglected, productsRerender] = useState( + JSON.parse(localStorage.getItem("cart")) + ); + let cart = JSON.parse(localStorage.getItem("cart")); + if (!cart || cart.length == 0) { + return
Empty cart, go shop like crazy!
; + } + + return ( +
+ {cart.map((item, index) => { + item = JSON.parse(item); + item.index = index; + return ( + + ); + })} +
+ ); +} + +export default Cart; diff --git a/src/pages/PagesStyles/ProductsPage.css b/src/pages/PagesStyles/ProductsPage.css new file mode 100644 index 0000000..e1f0d95 --- /dev/null +++ b/src/pages/PagesStyles/ProductsPage.css @@ -0,0 +1,10 @@ +.products { + display: flex; + flex-wrap: wrap; + margin: 1rem; +} +.upper-bar { + padding: 1.7rem; + display: flex; + justify-content: space-between; +} diff --git a/src/pages/Products.jsx b/src/pages/Products.jsx index 7d4398e..aa2d178 100644 --- a/src/pages/Products.jsx +++ b/src/pages/Products.jsx @@ -1,17 +1,80 @@ import React, { useEffect, useState } from "react"; import { Navigate } from "react-router"; +import { TOKEN_KEY, PRODUCTS_API } from "../utils/Constants"; +import Input from "../components/Input"; +import Product from "../components/Product"; +import axios from "axios"; +import "./PagesStyles/ProductsPage.css"; function Products(props) { - let [isLogged, setIsLogged] = useState(true); + const [isLogged, setIsLogged] = useState(true); + const [searchValue, setSearchValue] = useState(""); + const [products, setProducts] = useState([]); + const [filteredProducts, setFilteredProducts] = useState([]); + + axios.get(PRODUCTS_API).then((response) => { + setProducts(response.data); + }); useEffect(() => { - let token = JSON.parse(localStorage.getItem("token")); - if (!token) { + let userData; + try { + userData = JSON.parse(localStorage.getItem(TOKEN_KEY)); + if (userData) setIsLogged(true); + else setIsLogged(false); + } catch (error) { setIsLogged(false); } }, []); - if (!isLogged) { - return ; + + if (!isLogged) return ; + + function onSearchChange(event) { + let newSearchValue = event.target.value; + let productsCopy = [...products]; + const newFilteredProducts = productsCopy.filter((productObj) => { + return productObj.title + .toLowerCase() + .startsWith(newSearchValue.toLowerCase()); + }); + setSearchValue(newSearchValue); + setFilteredProducts(newFilteredProducts); } - return
Products
; + return ( +
+ {/* Upper section */} +
+ {/* Search div */} + +
+
+ {/* Products section */} + {filteredProducts.length === 0 && searchValue === "" + ? products.map((item) => { + return ( + + ); + }) + : filteredProducts.map((item) => { + return ( + + ); + })} +
+
+ ); } export default Products; diff --git a/src/utils/Constants.js b/src/utils/Constants.js index 1574da1..9f316a0 100644 --- a/src/utils/Constants.js +++ b/src/utils/Constants.js @@ -1,2 +1,4 @@ -export const TOKEN_KEY = 'token' -export const BASE_URL = 'https://website-backend.computiq.tech' \ No newline at end of file +export const TOKEN_KEY = "token"; +export const BASE_URL = "https://website-backend.computiq.tech"; +//export const PRODUCTS_API = "https://fakestoreapi.com/products"; +export const PRODUCTS_API = "https://fakestoreapi.com/products?limit=5"; diff --git a/yarn.lock b/yarn.lock index 113be34..bbd503e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9556,6 +9556,11 @@ "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz" "version" "6.0.9" +"react-icons@^4.3.1": + "integrity" "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==" + "resolved" "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz" + "version" "4.3.1" + "react-is@^16.7.0": "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" From be1190974ba126a792948004ebb6b6885ff80c87 Mon Sep 17 00:00:00 2001 From: Mohammed Mahmood Date: Tue, 14 Dec 2021 16:28:02 +0300 Subject: [PATCH 3/3] Add a new product component --- src/components/Product.jsx | 2 + src/components/ProductCard.jsx | 77 ++++++++++++++++++++++ src/components/componentsStyle/Product.css | 71 ++++++++++++++++++++ src/pages/Cart.jsx | 5 +- src/pages/Products.jsx | 5 +- 5 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/components/ProductCard.jsx diff --git a/src/components/Product.jsx b/src/components/Product.jsx index d6efce1..945a261 100644 --- a/src/components/Product.jsx +++ b/src/components/Product.jsx @@ -8,6 +8,8 @@ import { useState } from "react"; const getId = () => { return "id" + new Date().getTime(); }; +// this is the old product component + function Product(props) { const addItem = (object) => { let cart = JSON.parse(localStorage.getItem("cart")); diff --git a/src/components/ProductCard.jsx b/src/components/ProductCard.jsx new file mode 100644 index 0000000..c7672b4 --- /dev/null +++ b/src/components/ProductCard.jsx @@ -0,0 +1,77 @@ +import React from "react"; +import Button from "./Button"; +import Chip from "@mui/material/Chip"; + +import "./componentsStyle/Product.css"; +import { width } from "@mui/system"; +import { useState } from "react"; +const getId = () => { + return "id" + new Date().getTime(); +}; +function Product(props) { + const addItem = (object) => { + let cart = JSON.parse(localStorage.getItem("cart")); + if (!cart) { + localStorage.setItem("cart", JSON.stringify([])); + let cart = JSON.parse(localStorage.getItem("cart")); + object.id = getId(); + cart.unshift(JSON.stringify(object)); + localStorage.setItem("cart", JSON.stringify(cart)); + return; + } + object.id = getId(); + cart.unshift(JSON.stringify(object)); + localStorage.setItem("cart", JSON.stringify(cart)); + }; + const deleteItem = (itemId, productsRerenderFun) => { + let cart = JSON.parse(localStorage.getItem("cart")); + cart.splice( + cart.findIndex((obj) => { + obj = JSON.parse(obj); + return obj.id == itemId; + }), + 1 + ); + localStorage.setItem("cart", JSON.stringify(cart)); + productsRerenderFun([]); + }; + return ( +
+ +
+
+
+

{props.productName}

+

{props.productPrice}

+
+
+
+
+ {props.cartItem ? ( + { + deleteItem(props.object.id, props.productsRerender); + }} + /> + ) : ( +
+
+
+ ); +} + +export default Product; diff --git a/src/components/componentsStyle/Product.css b/src/components/componentsStyle/Product.css index 07b35b5..e8ed8fe 100644 --- a/src/components/componentsStyle/Product.css +++ b/src/components/componentsStyle/Product.css @@ -5,3 +5,74 @@ margin: 8px; border-radius: 20px; } +@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap"); +body { + padding: 0; + margin: 0; + box-sizing: border-box; + font-family: "Open Sans", sans-serif; +} + +.container { + width: 100%; + height: 80vh; + display: flex; + flex-wrap: wrap; +} + +.card { + width: 250px; + height: auto; + border-radius: 5px; + box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.2); + margin: 0 15px 20px; +} +.card > *:not(img) { + padding: 5px 10px; +} + +.card img { + width: 100%; + height: 180px; +} + +.card-body { + padding: 5px; +} +.row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px; +} + +.card-body p { + color: #3d3d3d; + margin-bottom: 20px; + font-size: 14px; +} + +.view-btn a { + padding: 5px 15px; + border: 1.5px solid #007bff; + border-radius: 3px; + text-decoration: none; + color: #007bff; +} + +.btn-group { + display: flex; +} + +.btn-group .btn a { + padding: 5px 15px; + background-color: #28a745; + color: #fff; + border-radius: 3px; + margin-left: -2px; +} +.btn-group a { + margin: 0 2px; + text-decoration: none; + color: #000; +} diff --git a/src/pages/Cart.jsx b/src/pages/Cart.jsx index ae78f2b..c1907c2 100644 --- a/src/pages/Cart.jsx +++ b/src/pages/Cart.jsx @@ -2,6 +2,7 @@ import React from "react"; import Product from "../components/Product"; import "./PagesStyles/ProductsPage.css"; import { useState } from "react"; +import ProductCard from "../components/ProductCard"; function Cart() { const [neglected, productsRerender] = useState( JSON.parse(localStorage.getItem("cart")) @@ -12,12 +13,12 @@ function Cart() { } return ( -
+
{cart.map((item, index) => { item = JSON.parse(item); item.index = index; return ( - { return ( - { return ( -