diff --git a/package.json b/package.json index 2c613e9..1b9d070 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "axios-app", + "name": "task-09", "version": "0.1.0", "private": true, "dependencies": { diff --git a/src/App.js b/src/App.js index 7a13708..8c4d4a2 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,19 @@ -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 { Route, Routes } from 'react-router-dom' import Drawer from './components/Drawer' function App(props) { - - - return - }/> - }/> - }/> - }/> - - + return + } /> + } /> + } /> + } /> + } /> + } /> + } export default App; diff --git a/src/components/Drawer.jsx b/src/components/Drawer.jsx index 9e615f1..f112b06 100644 --- a/src/components/Drawer.jsx +++ b/src/components/Drawer.jsx @@ -1,6 +1,11 @@ import * as React from 'react'; -import { useEffect } from 'react' -import { styled, useTheme } from '@mui/material/styles'; +import { Link, useLocation, useNavigate } from 'react-router-dom' +import { TOKEN_KEY } from '../utils/Constants' +import Dashboard from '../pages/Dashboard' +import Categories from '../pages/Categories' +import Products from '../pages/Products' +import Login from '../pages/Login' +import Cart from '../pages/Cart'; import Box from '@mui/material/Box'; import Drawer from '@mui/material/Drawer'; import CssBaseline from '@mui/material/CssBaseline'; @@ -16,28 +21,19 @@ 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 DashboardIcon from '@mui/icons-material/Dashboard'; import CategoryIcon from '@mui/icons-material/Category'; -import ProductionQuantityLimitsIcon from '@mui/icons-material/ProductionQuantityLimits'; -import Login from '../pages/Login' +import ShoppingBagIcon from '@mui/icons-material/ShoppingBag'; 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 LocalMallIcon from '@mui/icons-material/LocalMall'; +import { styled, useTheme } from '@mui/material/styles'; const drawerWidth = 240; - - const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })( ({ theme, open }) => ({ flexGrow: 1, @@ -104,20 +100,22 @@ function PersistentDrawerLeft(props) { navigate('/login') handleCloseUserMenu() } - useEffect(() => { - }, []) const renderContent = (routeName) => { console.log(routeName) switch (routeName) { case '/login': return + case '/cart': + return case '/products': return case '/dashboard': return case '/categories': return + default: + return } } const handleOpenUserMenu = (event) => { @@ -135,7 +133,8 @@ function PersistentDrawerLeft(props) { flexGrow: 1, display: 'flex', flexDirection: 'row', - justifyContent: 'space-between' + justifyContent: 'flex-start', + alignItems: 'center' }}> - + E-Commerce + + + { navigate('/cart') }} + edge="end" + sx={{ mr: 2 }} + > + + + - - - - - - - - - - - - - - - - - - + + {['Dashboard', 'Categories', 'Products'].map((text, index) => ( + + + {text === 'Dashboard' ? : text === 'Categories' ? : } + + + + ))} + - + @@ -237,4 +245,4 @@ function PersistentDrawerLeft(props) { ); } -export default PersistentDrawerLeft \ No newline at end of file +export default PersistentDrawerLeft diff --git a/src/components/Product.jsx b/src/components/Product.jsx new file mode 100644 index 0000000..d839ec2 --- /dev/null +++ b/src/components/Product.jsx @@ -0,0 +1,54 @@ + + +import React from 'react' +import Card from '@mui/material/Card'; +import Typography from '@mui/material/Typography'; +import ButtonBase from '@mui/material/ButtonBase'; +import Chip from '@mui/material/Chip'; +import { Box } from '@mui/system'; +import { styled } from '@mui/material/styles'; + + +const Img = styled('img')({ + width: '100%', + height: '100%', + maxWidth: '300px', + maxHeight: '300px', + borderRadius: '5px', +}); + +// TODO - Implement Quantities + +const addToCart = (product) => { + if (localStorage.getItem('cart') === null) { + localStorage.setItem('cart', JSON.stringify([product])) + } else { + const cart = JSON.parse(localStorage.getItem('cart')) + cart.push(product) + localStorage.setItem('cart', JSON.stringify(cart)) + } +} + +const Product = (props) => { + return ( + + + + + + + + {props.title} + + + {props.category} + + addToCart(props)}> + Add to Cart + + + + ) +} + +export default Product diff --git a/src/index.js b/src/index.js index bb29a24..d0ed96d 100644 --- a/src/index.js +++ b/src/index.js @@ -3,30 +3,28 @@ 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 { BrowserRouter } from 'react-router-dom' +import { createTheme, ThemeProvider } from '@mui/material/styles'; const theme = createTheme({ - palette: { type: 'light', primary: { - main: '#000000', + main: '#6E85D3', }, secondary: { - main: '#f50057', + main: '#6E85D3', }, }, }) ReactDOM.render( - - + + - - + + , document.getElementById('root') ); diff --git a/src/pages/Cart.jsx b/src/pages/Cart.jsx new file mode 100644 index 0000000..d80ff5b --- /dev/null +++ b/src/pages/Cart.jsx @@ -0,0 +1,130 @@ +import Box from '@mui/material/Box'; +import { styled } from '@mui/material/styles'; +import Auth from '../utils/Auth'; +import Grid from '@mui/material/Grid'; +import Card from '@mui/material/Card'; +import Typography from '@mui/material/Typography'; +import ButtonBase from '@mui/material/ButtonBase'; +import Chip from '@mui/material/Chip'; +import { Button } from '@mui/material'; +import ShoppingBasket from '@mui/icons-material/ShoppingBasket'; + +const Img = styled('img')({ + width: '100%', + height: '100%', + maxWidth: '300px', + maxHeight: '300px', + borderRadius: '5px', +}); + +const removeFromCart = (product) => { + const cart = JSON.parse(localStorage.getItem('cart')) + const newCart = cart.filter(item => item.title !== product.title) + localStorage.setItem('cart', JSON.stringify(newCart)) + window.location.reload() +} + +function Cart(item) { + let cartItems = JSON.parse(localStorage.getItem('cart')); + + return ( + + + + + + { + (cartItems.length > 0) ? cartItems.map((item, index) => { + return ( + + + + + + + + {item.title} + + + {item.category} + + removeFromCart(item)}> + Remove from Cart + + + + ) + }) : + No items in cart, yet. + + + } + + + + + + Cart Details + + + Payment Method: + Cash On Delivery + + + Delivery Address: + 146 Inverness Lane Shakopee, MN 55379 + + + Delivery Date: + Between 13th of December & 1 January + + + Payment Status: + Pending + + + Order Status: + Pending + + + + Order Id: + YmVKXSNwTJ + + + Items Number: + {cartItems.length} + + + Total Amount: + ${cartItems.reduce((acc, item) => acc + item.price, 0)} + + + Discounted Amount: + ${cartItems.reduce((acc, item) => acc + item.price, 0) * 0.1} + + + Delivery Fee (free): + $0.00 + + + Final Amount: + ${cartItems.reduce((acc, item) => acc + item.price, 0) * 0.9} + + + + + + + + + + + + ) +} + + +Cart = Auth(Cart); + +export default Cart; diff --git a/src/pages/Categories.jsx b/src/pages/Categories.jsx index 0e9ce54..d168c1e 100644 --- a/src/pages/Categories.jsx +++ b/src/pages/Categories.jsx @@ -1,17 +1,20 @@ import React, { useEffect } from 'react'; +import Auth from '../utils/Auth'; function Categories(props) { console.log(props) - useEffect(()=>{ + useEffect(() => { console.log('use effect') let userData = JSON.parse(localStorage.getItem('myData')) console.log(userData) - },[]) + }, []) return (
- Categories +

Categories

); } -export default Categories; \ No newline at end of file +Categories = Auth(Categories) + +export default Categories; diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index 3c13707..4531bda 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -1,32 +1,15 @@ -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) - - } catch (error) { - console.log(error) - setIsLogged(false) - } - - },[]) - console.log('3') +import React from 'react'; +import Auth from '../utils/Auth'; - if(!isLogged) - return +function Dashboard(props) { return (
- Dashboard +

Dashboard

); } -export default Dashboard; \ No newline at end of file +Dashboard = Auth(Dashboard) + +export default Dashboard; diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index cf8b851..b51c298 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,40 +1,105 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; +import * as React from 'react'; import axios from '../utils/axios' -import {TOKEN_KEY} from '../utils/Constants' +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { TOKEN_KEY } from '../utils/Constants' +import Box from '@mui/material/Box'; +import Card from '@mui/material/Card'; +import TextField from '@mui/material/TextField'; +import AccountCircle from '@mui/icons-material/AccountCircle'; +import VpnKeyIcon from '@mui/icons-material/VpnKey'; +import IconButton from '@mui/material/IconButton'; +import Input from '@mui/material/Input'; +import InputLabel from '@mui/material/InputLabel'; +import InputAdornment from '@mui/material/InputAdornment'; +import FormControl from '@mui/material/FormControl'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import Button from '@mui/material/Button'; + function Login(props) { const navigate = useNavigate() - const [email, setEmail] = React.useState('') - const [password, setPassword] = React.useState('') + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [values, setValues] = useState({ + amount: '', + password: '', + weight: '', + weightRange: '', + showPassword: false, + }); + + const handleChange = (prop) => (event) => { + setValues({ ...values, [prop]: event.target.value }); + }; + + const handleClickShowPassword = () => { + setValues({ + ...values, + showPassword: !values.showPassword, + }); + }; + + const handleMouseDownPassword = (event) => { + event.preventDefault(); + }; - const login = (e)=>{ + const login = (e) => { e.preventDefault() axios.post('/api/academy/auth/login', - { - email:email, - password:password - } + { + email: email, + password: values.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) - }) + .then((response) => { + 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" /> - -
-
+ + + + + + setEmail(e.target.value)} type="email" id="input-with-sx" label="Email" variant="standard" /> + + + + + + Password + + + + {values.showPassword ? : } + + + } + /> + + + + + + + ); } -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..426a3a9 100644 --- a/src/pages/Products.jsx +++ b/src/pages/Products.jsx @@ -1,12 +1,73 @@ -import React, { useEffect } from 'react'; +import * as React from 'react'; +import { useState, useEffect } from 'react'; +import axios from 'axios'; +import Auth from '../utils/Auth'; +import Product from '../components/Product'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import HourglassBottomIcon from '@mui/icons-material/HourglassBottom'; function Products(props) { - + + const [products, setProducts] = useState([]); + + useEffect(() => { + axios.get('https://fakestoreapi.com/products?limit=10') + + .then(res => { + setProducts(res.data); + }) + .catch(err => { + console.log(err); + }); + }, []); + + let originalProducts = [...products]; + + const onSearch = (e) => { + let search = e.target.value; + + if (search.length > 0) { + + let filteredProducts = products.filter(product => { + return product.title.toLowerCase().includes(search.toLowerCase()); + }); + + setProducts(filteredProducts); + + } else { + setProducts(originalProducts); + } + } + return ( -
- Products -
+ + :not(style)': { m: 1, maxWidth: '30rem', width: '20em' }, px: '2vw', pb: '2vh' }} noValidate autoComplete="off" > + + + + + + {(products.length > 0) ? products.map((product, index) => { + return ( + + ) + }) : + + Loading Products ... + + + } + + + + + ); } -export default Products; \ No newline at end of file +Products = Auth(Products); + +export default Products; diff --git a/src/utils/Auth.jsx b/src/utils/Auth.jsx new file mode 100644 index 0000000..8049558 --- /dev/null +++ b/src/utils/Auth.jsx @@ -0,0 +1,28 @@ +import React, { useState, useEffect } from 'react'; +import { TOKEN_KEY } from './Constants' +import { useNavigate } from 'react-router-dom'; + +const Auth = (Component) => { + + const Authenticate = (props) => { + const navigate = useNavigate(); + const [isLogged, setIsLogged] = useState(true); + + useEffect(() => { + const token = localStorage.getItem(TOKEN_KEY); + if (!token) { + navigate('/login'); + } + }, [navigate]) + + if (!isLogged) { + navigate('/login') + return null + } + return + } + + return Authenticate +} + +export default Auth; diff --git a/src/utils/Constants.js b/src/utils/Constants.js index 1574da1..ebb7d22 100644 --- a/src/utils/Constants.js +++ b/src/utils/Constants.js @@ -1,2 +1,2 @@ export const TOKEN_KEY = 'token' -export const BASE_URL = 'https://website-backend.computiq.tech' \ No newline at end of file +export const BASE_URL = 'https://website-backend.computiq.tech'