├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.css
├── App.js
├── Assets
│ ├── burguer.png
│ ├── burguers-icon.svg
│ ├── combo.png
│ ├── combos-icon.svg
│ ├── desserts-icon.svg
│ ├── desserts.png
│ ├── drink.png
│ ├── drinks-icon.svg
│ ├── pizza-icon.svg
│ └── pizza.png
├── Components
│ ├── Checkout
│ │ ├── Checkout.js
│ │ ├── Checkout.module.css
│ │ └── Form
│ │ │ ├── Form.js
│ │ │ ├── Form.module.css
│ │ │ └── Fragments
│ │ │ ├── Address.js
│ │ │ ├── Address.module.css
│ │ │ ├── AddressDefault.js
│ │ │ ├── AddressDefault.module.css
│ │ │ ├── Input.js
│ │ │ ├── Input.module.css
│ │ │ ├── RadioDelivery.js
│ │ │ ├── RadioDelivery.module.css
│ │ │ ├── RadioPayment.js
│ │ │ └── RadioPayment.module.css
│ ├── Finish
│ │ ├── Finish.js
│ │ └── Finish.module.css
│ └── Home
│ │ ├── Cart
│ │ ├── Cart.js
│ │ ├── Cart.module.css
│ │ ├── CartItem.js
│ │ ├── CartItem.module.css
│ │ ├── ObsItem.js
│ │ └── ObsItem.module.css
│ │ ├── Home.js
│ │ ├── Home.module.css
│ │ ├── Nav
│ │ ├── HomeNav.js
│ │ └── HomeNav.module.css
│ │ └── Product
│ │ ├── ProductItem.js
│ │ ├── ProductItem.module.css
│ │ ├── Products.js
│ │ └── Products.module.css
├── GlobalContext.js
├── Hooks
│ ├── useForm.js
│ └── useMedia.js
└── index.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_*
2 | *.log
3 | logs
4 | /node_modules
5 | /build
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RDelivery
2 |
3 | A delivery website created with ReactJS for online ordering.
4 |
5 | ## Demo
6 |
7 | View project in production: https://danielmafra.github.io/rdelivery/
8 |
9 | #### Desktop demo:
10 |
11 | 
12 |
13 | #### Smartphone demo:
14 |
15 | 
16 |
17 | ## Run Locally
18 |
19 | Clone the project
20 |
21 | ```bash
22 | git clone https://github.com/DanielMafra/RDelivery-frontend-ReactJS.git
23 | ```
24 |
25 | Go to the project directory
26 |
27 | ```bash
28 | cd RDelivery-frontend-ReactJS
29 | ```
30 |
31 | Install dependencies
32 |
33 | ```bash
34 | yarn install
35 | ```
36 |
37 | Start the server
38 |
39 | ```bash
40 | yarn start
41 | ```
42 |
43 | ## Deployment
44 |
45 | To deploy this project run
46 |
47 | ```bash
48 | yarn build
49 | ```
50 |
51 | ## API
52 |
53 | - This project uses a fake My JSON Server API for list products.
54 |
55 | If you want to register your own products, just clone this repository, modify and upload it to GitHub: https://github.com/DanielMafra/api.git
56 |
57 | After that, just follow the instructions that are in https://my-json-server.typicode.com/
58 |
59 | - The project also uses the ViaCEP API to get the address more dynamically.
60 |
61 | You can find out more about at https://viacep.com.br/
62 |
63 | ## Features
64 |
65 | - Consume data through API
66 | - Breakpoints for Desktop and Smartphone
67 | - Increment and decrement products in the cart
68 | - Add note to product
69 | - Form data validation
70 | - ContextAPI and custom Hooks
71 | - Phone field mask
72 | - Get address via API through zip code
73 | - Save the address used in localStorage
74 | - Use the address in localStorage as default
75 |
76 | ## Authors
77 |
78 | This project was coded by [@danielmafra](https://www.github.com/danielmafra) using a UI Design created by [@isadorastan](https://github.com/isadorastan).
79 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rdelivery",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.11.4",
7 | "@testing-library/react": "^11.1.0",
8 | "@testing-library/user-event": "^12.1.10",
9 | "history": "^5.0.0",
10 | "react": "^17.0.2",
11 | "react-dom": "^17.0.2",
12 | "react-router-dom": "^6.0.0-beta.0",
13 | "react-scripts": "4.0.3",
14 | "web-vitals": "^1.0.1"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
25 | RDelivery
26 |
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/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/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap');
2 |
3 | * {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | body {
10 | font-family: 'Poppins', sans-serif;
11 | }
12 |
13 | img {
14 | display: block;
15 | max-width: 100%;
16 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './App.css';
3 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
4 | import Home from './Components/Home/Home';
5 | import Checkout from './Components/Checkout/Checkout';
6 | import Finish from './Components/Finish/Finish';
7 | import { GlobalStorage } from './GlobalContext';
8 |
9 | function App() {
10 | return (
11 |
12 |
13 |
14 | } />
15 | } /* this route is only for use on GitHub Pages */ />
16 | } />
17 | } />
18 |
19 |
20 |
21 | );
22 | }
23 |
24 | export default App;
25 |
--------------------------------------------------------------------------------
/src/Assets/burguer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/src/Assets/burguer.png
--------------------------------------------------------------------------------
/src/Assets/burguers-icon.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/Assets/combo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/src/Assets/combo.png
--------------------------------------------------------------------------------
/src/Assets/combos-icon.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/src/Assets/desserts-icon.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/Assets/desserts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/src/Assets/desserts.png
--------------------------------------------------------------------------------
/src/Assets/drink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/src/Assets/drink.png
--------------------------------------------------------------------------------
/src/Assets/drinks-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/Assets/pizza-icon.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/Assets/pizza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DanielMafra/RDelivery-frontend-ReactJS/9f2129d4fe0fae658ba49c0f21becf77452b1724/src/Assets/pizza.png
--------------------------------------------------------------------------------
/src/Components/Checkout/Checkout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import useMedia from '../../Hooks/useMedia';
4 | import Cart from '../Home/Cart/Cart';
5 | import Form from './Form/Form';
6 | import styles from './Checkout.module.css';
7 |
8 | const Checkout = () => {
9 | const mobile = useMedia('(max-width: 480px)');
10 |
11 | return (
12 |
13 |
14 |
← Voltar
15 |
16 |
17 |
18 |
19 | {!mobile ? (
20 |
21 | ) : (
22 | ''
23 | )}
24 |
25 | );
26 | };
27 |
28 | export default Checkout;
--------------------------------------------------------------------------------
/src/Components/Checkout/Checkout.module.css:
--------------------------------------------------------------------------------
1 | .checkout {
2 | background-color: #fafafa;
3 | display: flex;
4 | height: 100vh;
5 | }
6 |
7 | .mainCheckout {
8 | margin-top: 16px;
9 | margin-left: 16px;
10 | }
11 |
12 | .mainCheckout a {
13 | text-decoration: none;
14 | color: #000;
15 | font-weight: 600;
16 | font-size: 24px;
17 | }
18 |
19 | .formArea {
20 | margin-top: 32px;
21 | margin-left: 64px;
22 | justify-self: center;
23 | }
24 |
25 | @keyframes enterLeft {
26 | to {
27 | opacity: 1;
28 | transform: initial;
29 | }
30 | }
31 |
32 | @media (max-width: 480px){
33 |
34 | .checkout {
35 | height: auto;
36 | }
37 |
38 | .openCheckout {
39 | display: block;
40 | opacity: 0;
41 | transform: translateX(500px);
42 | animation: enterLeft .3s forwards;
43 | }
44 |
45 | .mainCheckout {
46 | height: 100%;
47 | margin: 16px 0px 0px 0px;
48 | }
49 |
50 | .formArea{
51 | margin-left: 0px;
52 | max-width: 100vw;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Form.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import useForm from '../../../Hooks/useForm';
3 | import { GlobalContext } from '../../../GlobalContext';
4 | import { useNavigate } from 'react-router-dom';
5 | import Input from './Fragments/Input';
6 | import RadioDelivery from './Fragments/RadioDelivery';
7 | import styles from './Form.module.css';
8 |
9 | const Form = () => {
10 | const { typeBuy, number, complement, address, typePayment, cart, total, order, setOrder, user } = React.useContext(GlobalContext);
11 | const phone = useForm('phone');
12 | const name = useForm();
13 | const navigate = useNavigate();
14 |
15 | function handleSubmit(event) {
16 | event.preventDefault();
17 | if (name.validate() && phone.validate()) {
18 | if (typeBuy !== '' && typeBuy === 'store') {
19 | setOrder({
20 | name: name.value,
21 | phone: phone.value,
22 | cart: {
23 | ...cart,
24 | totalPrice: total
25 | }
26 | });
27 | navigate('/completed');
28 | console.log(order);
29 | } else if (typeBuy !== '' && typeBuy === 'delivery' && address !== null) {
30 | if (number !== '' && complement !== '' && typePayment !== '') {
31 | setOrder({
32 | name: name.value,
33 | phone: phone.value,
34 | cart: {
35 | ...cart,
36 | totalPrice: total,
37 | },
38 | address: {
39 | ...address,
40 | number: number,
41 | complement: complement
42 | },
43 | payment: typePayment
44 | });
45 | navigate('/completed');
46 | console.log(order);
47 | }
48 | } else if (typeBuy !== '' && typeBuy === 'delivery' && user !== null) {
49 | setOrder({
50 | name: name.value,
51 | phone: phone.value,
52 | cart: {
53 | ...cart,
54 | totalPrice: total,
55 | },
56 | address: {
57 | ...user
58 | },
59 | payment: typePayment
60 | });
61 | navigate('/completed');
62 | console.log(order);
63 | }
64 | }
65 | }
66 |
67 | return (
68 |
76 | );
77 | };
78 |
79 | export default Form;
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Form.module.css:
--------------------------------------------------------------------------------
1 | .form {
2 | background-color: #fff;
3 | padding: 32px 48px;
4 | border-radius: 8px;
5 | width: 721px;
6 | display: flex;
7 | flex-direction: column;
8 | }
9 |
10 | .title {
11 | font-size: 22px;
12 | font-weight: 600;
13 | line-height: 1;
14 | margin-bottom: 32px;
15 | }
16 |
17 | .subTitle {
18 | line-height: 1;
19 | margin-top: 44px;
20 | }
21 |
22 | .form button.confirm {
23 | background-color: #ff2351;
24 | color: #fff;
25 | font-size: 16px;
26 | font-weight: 600;
27 | border: none;
28 | border-radius: 8px;
29 | width: 348px;
30 | height: 46px;
31 | cursor: pointer;
32 | transition: .3s;
33 | align-self: center;
34 | margin-top: 56px;
35 | }
36 |
37 | .form button.confirm:hover {
38 | background-color: rgb(255, 35, 51);
39 | transform: scale(1.01);
40 | }
41 |
42 | @media (max-width: 480px){
43 |
44 | .form {
45 | padding: 16px;
46 | max-width: 90%;
47 | margin: 0 auto;
48 | }
49 |
50 | .form button.confirm {
51 | max-width: 100%;
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/Address.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../../GlobalContext';
3 | import RadioPayment from './RadioPayment';
4 | import styles from './Address.module.css';
5 |
6 | const Address = () => {
7 | const { cep, setCep, number, setNumber, complement, setComplement, address } = React.useContext(GlobalContext);
8 | const [error, setError] = React.useState(false);
9 |
10 | function validateCep() {
11 | if (cep.length < 8) {
12 | setError(true);
13 | } else {
14 | setError(false);
15 | }
16 | }
17 |
18 | return (
19 |
20 |
21 |
{ setCep(event.target.value); setError(false) }} onBlur={validateCep} value={cep} />
22 | {error &&
Digite um CEP válido
}
23 | {address && (
24 | <>
25 |
26 |
{address.rua}
27 |
{address.cidade} / {address.uf} - {address.bairro}
28 |
29 |
30 |
setNumber(event.target.value)} value={number} />
31 |
32 |
setComplement(event.target.value)} value={complement} />
33 |
Pagamento
34 |
Método de pagamento:
35 |
36 | >
37 | )}
38 |
39 | )
40 | }
41 |
42 | export default Address;
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/Address.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-top: 38px;
3 | }
4 |
5 | .input {
6 | border: 1px solid #dadada;
7 | display: block;
8 | width: 100%;
9 | font-size: 16px;
10 | padding: 12px 16px;
11 | border-radius: 8px;
12 | background: transparent;
13 | transition: 0.3s;
14 | }
15 |
16 | .input::placeholder {
17 | font-family: 'Poppins';
18 | font-size: 16px;
19 | color: #848484;
20 | }
21 |
22 | .input:focus,
23 | .input:hover {
24 | outline: none;
25 | border-color: #fdc844;
26 | background: transparent;
27 |
28 | }
29 |
30 | .label {
31 | display: block;
32 | font-weight: 500;
33 | line-height: 1;
34 | padding-bottom: 8px;
35 | }
36 |
37 | .complement {
38 | display: block;
39 | font-weight: 500;
40 | line-height: 1;
41 | padding-bottom: 8px;
42 | margin-top: 20px;
43 | }
44 |
45 | .error {
46 | color: #ff2351;
47 | font-size: 14px;
48 | margin-top: 4px;
49 | }
50 |
51 | .address {
52 | margin-top: 38px;
53 | margin-bottom: 38px;
54 | color: #848484;
55 | }
56 |
57 | .subTitle {
58 | line-height: 1;
59 | margin-top: 52px;
60 | }
61 |
62 | .typePay {
63 | font-weight: 600;
64 | margin-top: 24px;
65 | margin-bottom: 14px;
66 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/AddressDefault.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../../GlobalContext';
3 | import RadioPayment from './RadioPayment';
4 | import styles from './AddressDefault.module.css';
5 |
6 | const AddressDefault = () => {
7 | const { user, setUser } = React.useContext(GlobalContext);
8 |
9 | function handleAddress() {
10 | setUser(null);
11 | }
12 |
13 | return (
14 |
15 |
Endereço padrão:
16 |
17 |
{user.rua}, {user.number}
18 |
{user.cidade} - {user.uf}
19 |
{user.complement}
20 |
21 |
22 |
Pagamento
23 |
Método de pagamento:
24 |
25 |
26 | )
27 | }
28 |
29 | export default AddressDefault;
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/AddressDefault.module.css:
--------------------------------------------------------------------------------
1 | .defaultAddress {
2 | font-weight: 600;
3 | margin-top: 24px;
4 | margin-bottom: 14px;
5 | }
6 |
7 | .cardAddress {
8 | border: 1px solid #dadada;
9 | border-radius: 8px;
10 | padding: 16px 32px;
11 | width: 60%;
12 | margin-top: 32px;
13 | }
14 |
15 | .cardAddress p {
16 | color: #848484;
17 | }
18 |
19 | .cardAddress p:nth-child(2),
20 | .cardAddress p:nth-child(3){
21 | margin-top: 8px;
22 | }
23 |
24 | .editAddress {
25 | border: none;
26 | background-color: transparent;
27 | font-size: 12px;
28 | color: #ff2351;
29 | text-decoration: underline;
30 | cursor: pointer;
31 | }
32 |
33 | .subTitle {
34 | line-height: 1;
35 | margin-top: 52px;
36 | }
37 |
38 | .typePay {
39 | font-weight: 600;
40 | margin-top: 24px;
41 | margin-bottom: 14px;
42 | }
43 |
44 | @media (max-width: 480px){
45 |
46 | .cardAddress {
47 | width: 100%;
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/Input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './Input.module.css';
3 |
4 | const Input = ({ label, type, name, value, placeholder, onChange, error, onBlur }) => {
5 | return (
6 |
7 |
8 |
9 | {error &&
{error}
}
10 |
11 | )
12 | }
13 |
14 | export default Input;
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/Input.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | margin-bottom: 20px;
3 | }
4 |
5 | .input {
6 | border: 1px solid #dadada;
7 | display: block;
8 | width: 100%;
9 | font-size: 16px;
10 | padding: 12px 16px;
11 | border-radius: 8px;
12 | background: transparent;
13 | transition: 0.3s;
14 | }
15 |
16 | .input::placeholder {
17 | font-family: 'Poppins';
18 | font-size: 16px;
19 | color: #848484;
20 | }
21 |
22 | .input:focus,
23 | .input:hover {
24 | outline: none;
25 | border-color: #fdc844;
26 | background: transparent;
27 |
28 | }
29 |
30 | .label {
31 | display: block;
32 | font-weight: 500;
33 | line-height: 1;
34 | padding-bottom: 8px;
35 | }
36 |
37 | .error {
38 | color: #ff2351;
39 | font-size: 14px;
40 | margin-top: 4px;
41 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/RadioDelivery.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../../GlobalContext';
3 | import Address from './Address';
4 | import AddressDefault from './AddressDefault';
5 | import styles from './RadioDelivery.module.css';
6 |
7 | const RadioDelivery = () => {
8 | const { typeBuy, setTypeBuy, user } = React.useContext(GlobalContext);
9 |
10 | return (
11 |
31 | )
32 | }
33 |
34 | export default RadioDelivery;
35 |
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/RadioDelivery.module.css:
--------------------------------------------------------------------------------
1 | .radioDelivery {
2 | margin-top: 24px;
3 | }
4 |
5 | .radio {
6 | cursor: pointer;
7 | margin-right: 12px;
8 | }
9 |
10 | .label {
11 | color: #848484;
12 | font-size: 16px;
13 | }
14 |
15 | .label:nth-child(2){
16 | margin-left: 54px;
17 | }
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/RadioPayment.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../../GlobalContext';
3 | import styles from './RadioPayment.module.css';
4 |
5 | const RadioPayment = () => {
6 | const { typePayment, setTypePayment } = React.useContext(GlobalContext);
7 |
8 | return (
9 |
10 |
14 |
18 |
19 | )
20 | }
21 |
22 | export default RadioPayment;
--------------------------------------------------------------------------------
/src/Components/Checkout/Form/Fragments/RadioPayment.module.css:
--------------------------------------------------------------------------------
1 | .radioPayment {
2 | margin-top: 24px;
3 | }
4 |
5 | .radio {
6 | cursor: pointer;
7 | margin-right: 12px;
8 | }
9 |
10 | .label {
11 | color: #848484;
12 | font-size: 16px;
13 | }
14 |
15 | .label:nth-child(2){
16 | margin-left: 54px;
17 | }
--------------------------------------------------------------------------------
/src/Components/Finish/Finish.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../GlobalContext';
3 | import styles from './Finish.module.css';
4 | import CartItem from '../Home/Cart/CartItem';
5 |
6 | const Finish = () => {
7 | const { cart, total, typeBuy } = React.useContext(GlobalContext);
8 |
9 | return (
10 |
11 |
12 |
13 |
Pedido finalizado
14 |
15 |
16 |
17 | {cart.map((item) => )}
18 |
19 |
20 |
Total
21 | R$ {total},00
22 |
23 |
24 | {typeBuy === "delivery" ? (
25 |
Seu pedido será entregue em até 60 minutos.
26 | ) : (
27 |
Em 25 minutos seu pedido estará pronto para ser retirado em nossa loja.
28 | )}
29 |
30 | )
31 | }
32 |
33 | export default Finish;
34 |
--------------------------------------------------------------------------------
/src/Components/Finish/Finish.module.css:
--------------------------------------------------------------------------------
1 | .finishContainer {
2 | background-color: #fafafa;
3 | padding-top: 32px;
4 | padding-bottom: 32px;
5 | display: flex;
6 | flex-direction: column;
7 | justify-content: center;
8 | align-items: center;
9 | }
10 |
11 | .orderFinish {
12 | display: flex;
13 | align-items: center;
14 | }
15 |
16 | .orderFinish h2 {
17 | margin-left: 8px;
18 | color: #008000;
19 | }
20 |
21 | ion-icon {
22 | font-size: 24px;
23 | color: #008000;
24 | }
25 |
26 | .total {
27 | display: flex;
28 | justify-content: flex-end;
29 | margin-top: 38px;
30 | }
31 |
32 | .total h4:nth-child(1){
33 | font-size: 18px;
34 | font-weight: 600;
35 | }
36 |
37 | .price {
38 | margin-left: 16px;
39 | font-size: 18px;
40 | font-weight: 600;
41 | color: #fdc844;
42 | }
43 |
44 | .orderContainer {
45 | background-color: #ffffff;
46 | padding: 16px 32px;
47 | border-radius: 8px;
48 | margin-top: 32px;
49 | }
50 |
51 | .orderStore {
52 | margin-top: 64px;
53 | }
54 |
55 | @keyframes enterLeft {
56 | to {
57 | opacity: 1;
58 | transform: initial;
59 | }
60 | }
61 |
62 | @media (max-width: 480px){
63 |
64 | .finishContainer {
65 | background-color: #ffffff;
66 | opacity: 0;
67 | transform: translateX(500px);
68 | animation: enterLeft .3s forwards;
69 | }
70 |
71 | .orderStore {
72 | margin: 64px 24px;
73 | }
74 |
75 | }
--------------------------------------------------------------------------------
/src/Components/Home/Cart/Cart.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../GlobalContext';
3 | import { useNavigate } from 'react-router-dom';
4 | import useMedia from '../../../Hooks/useMedia';
5 | import CartItem from './CartItem';
6 | import styles from './Cart.module.css';
7 |
8 | const Cart = ({ isCheckout }) => {
9 | const mobile = useMedia('(max-width: 480px)');
10 | const { cart, total, openCart, setOpenCart } = React.useContext(GlobalContext);
11 | const navigate = useNavigate();
12 |
13 | function handleOpenCart() {
14 | setOpenCart(!openCart);
15 | }
16 |
17 | return (
18 |
19 | {mobile && (
20 |
21 | )}
22 |
Seu pedido
23 | {cart.length > 0 ? (
24 |
25 |
26 | {cart.map((item) => )}
27 |
28 |
29 |
Total
30 | R$ {total},00
31 |
32 |
33 | {!isCheckout ? (
34 |
35 | ) : (
36 | ''
37 | )}
38 |
39 |
40 | ) : (
41 |
Você ainda não adicionou nenhum item ao carrinho.
42 | )}
43 |
44 | );
45 | };
46 |
47 | export default Cart;
--------------------------------------------------------------------------------
/src/Components/Home/Cart/Cart.module.css:
--------------------------------------------------------------------------------
1 | .cart {
2 | background-color: #fff;
3 | padding: 48px 54px;
4 | /*width: 30%;*/
5 | position: fixed;
6 | right: 0;
7 | height: 100vh;
8 | overflow: auto;
9 | }
10 |
11 | .title {
12 | font-size: 24px;
13 | font-weight: 600;
14 | margin-bottom: 32px;
15 | }
16 |
17 | .items {
18 | display: flex;
19 | flex-direction: column;
20 | }
21 |
22 | .none {
23 | font-size: 14px;
24 | }
25 |
26 | .total {
27 | display: flex;
28 | justify-content: flex-end;
29 | margin-top: 38px;
30 | }
31 |
32 | .total h4:nth-child(1){
33 | font-size: 18px;
34 | font-weight: 600;
35 | }
36 |
37 | .price {
38 | margin-left: 16px;
39 | font-size: 18px;
40 | font-weight: 600;
41 | color: #fdc844;
42 | }
43 |
44 | .btn {
45 | display: flex;
46 | justify-content: center;
47 | margin-top: 92px;
48 | }
49 |
50 | .btn button {
51 | background-color: #ff2351;
52 | color: #fff;
53 | font-size: 18px;
54 | font-weight: 600;
55 | border: none;
56 | border-radius: 8px;
57 | width: 397px;
58 | height: 51px;
59 | cursor: pointer;
60 | transition: .3s;
61 | }
62 |
63 | .btn button:hover {
64 | background-color: rgb(255, 35, 51);
65 | transform: scale(1.01);
66 | }
67 |
68 | .enterLeft {
69 | opacity: 0;
70 | transform: translateX(-20px);
71 | animation: enterLeft .3s forwards;
72 | }
73 |
74 | @keyframes enterLeft {
75 | to {
76 | opacity: 1;
77 | transform: initial;
78 | }
79 | }
80 |
81 | @media (max-width: 480px){
82 |
83 | .closeCart {
84 | display: none;
85 | }
86 |
87 | .openCart {
88 | display: block;
89 | opacity: 0;
90 | transform: translateX(500px);
91 | animation: enterLeft .3s forwards;
92 | }
93 |
94 | .btnCloseCart {
95 | color: #000;
96 | font-weight: 600;
97 | font-size: 24px;
98 | border: none;
99 | cursor: pointer;
100 | background-color: transparent;
101 | }
102 |
103 | .title {
104 | margin-top: 32px;
105 | }
106 |
107 | .cart {
108 | width: 100%;
109 | padding: 16px;
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/src/Components/Home/Cart/CartItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './CartItem.module.css';
3 | import { GlobalContext } from '../../../GlobalContext';
4 |
5 | const CartItem = ({ product, isFinish }) => {
6 | const { incrementItem, decrementItem, openObs, setOpenObs, setIdObs } = React.useContext(GlobalContext);
7 |
8 | function openObsBox() {
9 | setOpenObs(!openObs);
10 | setIdObs(product.id);
11 | }
12 |
13 | return (
14 |
15 |
16 | {!isFinish &&
}
17 |
{product.quantity}
18 | {!isFinish &&
}
19 |
20 |
21 |

22 |
23 |
24 |
{product.title}
25 | {!isFinish && }
26 |
27 |
R$ {product.currentPrice},00
28 |
29 | );
30 | };
31 |
32 | export default CartItem;
--------------------------------------------------------------------------------
/src/Components/Home/Cart/CartItem.module.css:
--------------------------------------------------------------------------------
1 | .item {
2 | margin-bottom: 18px;
3 | border-bottom: 1px solid #dadada;
4 | padding: 24px 0px;
5 | display: flex;
6 | align-items: center;
7 | }
8 |
9 | .quantity {
10 | display: flex;
11 | align-items: center;
12 | }
13 |
14 | .quantity p {
15 | margin-right: 8px;
16 | margin-left: 8px;
17 | font-size: 18px;
18 | font-weight: 500;
19 | color: #fdc844;
20 | }
21 |
22 | .quantity button {
23 | font-family: "Poppins";
24 | font-size: 18px;
25 | font-weight: 600;
26 | border: none;
27 | background-color: transparent;
28 | cursor: pointer;
29 | }
30 |
31 | .bgImage {
32 | background-color: rgba(253, 200, 68, .3);
33 | border-radius: 8px;
34 | width: 81px;
35 | height: 66px;
36 | padding: 10px 8px;
37 | margin-left: 36px;
38 | margin-right: 28px;
39 | }
40 |
41 | .product h4 {
42 | font-size: 18px;
43 | font-weight: 600;
44 | }
45 |
46 | .product button {
47 | border: none;
48 | background-color: transparent;
49 | font-size: 12px;
50 | color: #848484;
51 | text-decoration: underline;
52 | cursor: pointer;
53 | }
54 |
55 | .price {
56 | margin-left: 24px;
57 | font-size: 18px;
58 | font-weight: 600;
59 | color: #fdc844;
60 | }
61 |
62 | .enterLeft {
63 | opacity: 0;
64 | transform: translateX(-20px);
65 | animation: enterLeft .3s forwards;
66 | }
67 |
68 | @keyframes enterLeft {
69 | to {
70 | opacity: 1;
71 | transform: initial;
72 | }
73 | }
74 |
75 | @media (max-width: 480px){
76 |
77 | .bgImage {
78 | width: 54px;
79 | height: auto;
80 | padding: 10px 8px;
81 | margin-left: 16px;
82 | margin-right: 16px;
83 | }
84 |
85 | .product h4 {
86 | font-size: 16px;
87 | font-weight: 600;
88 | }
89 |
90 | .product button {
91 | text-align: initial;
92 | }
93 |
94 | .price {
95 | font-size: 16px;
96 | margin-left: 16px;
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/src/Components/Home/Cart/ObsItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { GlobalContext } from '../../../GlobalContext';
3 | import styles from './ObsItem.module.css';
4 |
5 | const ObsItem = () => {
6 | const { openObs, setOpenObs, idObs, obs, setObs, addObs } = React.useContext(GlobalContext);
7 |
8 | function closeObsBox() {
9 | setOpenObs(!openObs);
10 | }
11 |
12 | function saveObs() {
13 | addObs(idObs);
14 | closeObsBox();
15 | }
16 |
17 | return (
18 |
19 |
20 |
21 |
Adicionar observação
22 |
28 |
29 | );
30 | };
31 |
32 | export default ObsItem;
--------------------------------------------------------------------------------
/src/Components/Home/Cart/ObsItem.module.css:
--------------------------------------------------------------------------------
1 | .bgObs {
2 | background-color: rgba(0, 0, 0, 0.8);
3 | position: absolute;
4 | width: 100%;
5 | height: 100%;
6 | display: flex;
7 | align-items: center;
8 | justify-content: center;
9 | }
10 |
11 | .areaObs {
12 | background-color: #ffffff;
13 | width: 30%;
14 | padding: 32px 16px;
15 | border-radius: 8px;
16 | position: relative;
17 | }
18 |
19 | .areaObs h3 {
20 | text-align: center;
21 | margin-top: 16px;
22 | margin-bottom: 16px;
23 | }
24 |
25 | .areaObs textarea {
26 | width: 100%;
27 | height: 64px;
28 | margin-bottom: 16px;
29 | border: 1px solid #dadada;
30 | padding: 0px;
31 | }
32 |
33 | .btnClose {
34 | color: #808080;
35 | font-size: 20px;
36 | font-weight: 500;
37 | padding: 4px;
38 | border: none;
39 | background-color: transparent;
40 | position: absolute;
41 | top: 8px;
42 | right: 16px;
43 | cursor: pointer;
44 | }
45 |
46 | .btnsObs {
47 | display: flex;
48 | justify-content: center;
49 | }
50 |
51 | .btnsObs button {
52 | padding: 6px 16px;
53 | font-size: 16px;
54 | font-weight: 500;
55 | border-radius: 8px;
56 | border: none;
57 | cursor: pointer;
58 | }
59 |
60 | .btnsObs button:first-of-type {
61 | background-color: transparent;
62 | border: 1px solid #808080;
63 | color: #808080;
64 | }
65 |
66 | .btnsObs button:last-of-type {
67 | margin-left: 16px;
68 | background-color: #ff2351;
69 | color: #ffffff;
70 | }
71 |
72 | @media (max-width: 480px){
73 |
74 | .areaObs {
75 | width: 90%;
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/src/Components/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import HomeNav from './Nav/HomeNav';
3 | import { Routes, Route } from 'react-router-dom';
4 | import { GlobalContext } from '../../GlobalContext';
5 | import useMedia from '../../Hooks/useMedia';
6 | import Products from './Product/Products';
7 | import Cart from './Cart/Cart';
8 | import ObsItem from './Cart/ObsItem';
9 | import styles from './Home.module.css';
10 |
11 | const Home = () => {
12 | const mobile = useMedia('(max-width: 480px)');
13 | const { openCart, setOpenCart, openObs, cart } = React.useContext(GlobalContext);
14 |
15 | function handleOpenCart() {
16 | setOpenCart(!openCart);
17 | }
18 |
19 | return (
20 |
21 | {mobile && (
22 |
23 |
31 |
32 | )}
33 |
34 |
35 | } />
36 | } />
37 |
38 |
39 | {openObs &&
}
40 |
41 | );
42 | };
43 |
44 | export default Home;
--------------------------------------------------------------------------------
/src/Components/Home/Home.module.css:
--------------------------------------------------------------------------------
1 | .home {
2 | background-color: #fafafa;
3 | display: flex;
4 | }
5 |
6 | @keyframes enterBadge {
7 | 0% {
8 | transform: scale(0);
9 | }
10 | 100% {
11 | transform: scale(1);
12 | }
13 | }
14 |
15 | @media (max-width: 480px){
16 |
17 | .home {
18 | flex-direction: column;
19 | background-color: #ffffff;
20 | }
21 |
22 | .btnCart {
23 | position: absolute;
24 | top: 16px;
25 | right: 16px;
26 | }
27 |
28 | .btnCart button {
29 | background-color: #ff2351;
30 | border: none;
31 | max-width: 42px;
32 | max-height: 42px;
33 | padding: 8px;
34 | border-radius: 50%;
35 | display: flex;
36 | justify-content: center;
37 | align-items: center;
38 | outline: none;
39 | position: relative;
40 | }
41 |
42 | .btnCart button > ion-icon {
43 | color: #ffffff;
44 | }
45 |
46 | .badgeQuantity {
47 | background-color: #ffffff;
48 | border: 1px solid #aa2351;
49 | color: #aa2351;
50 | padding: 4px;
51 | height: 18px;
52 | min-width: 18px;
53 | font-size: 12px;
54 | font-weight: 500;
55 | display: flex;
56 | align-items: center;
57 | justify-content: center;
58 | position: absolute;
59 | border-radius: 50%;
60 | bottom: -2px;
61 | left: -4px;
62 | animation: enterBadge .3s forwards;
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/src/Components/Home/Nav/HomeNav.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 | import styles from './HomeNav.module.css';
4 | import { ReactComponent as PizzaIcon } from '../../../Assets/pizza-icon.svg';
5 | import { ReactComponent as BurguersIcon } from '../../../Assets/burguers-icon.svg';
6 | import { ReactComponent as CombosIcon } from '../../../Assets/combos-icon.svg';
7 | import { ReactComponent as DessertsIcon } from '../../../Assets/desserts-icon.svg';
8 | import { ReactComponent as DrinksIcon } from '../../../Assets/drinks-icon.svg';
9 |
10 | const HomeNav = () => {
11 | return (
12 |
34 | );
35 | };
36 |
37 | export default HomeNav;
--------------------------------------------------------------------------------
/src/Components/Home/Nav/HomeNav.module.css:
--------------------------------------------------------------------------------
1 | .nav {
2 | background-color: #fff;
3 | display: flex;
4 | flex-direction: column;
5 | width: 129px;
6 | align-items: center;
7 | justify-content: center;
8 | text-align: center;
9 | position: fixed;
10 | height: 100vh;
11 | }
12 |
13 | .nav a {
14 | width: 100%;
15 | display: flex;
16 | flex-direction: column;
17 | align-items: center;
18 | justify-content: center;
19 | padding: 14px 0px;
20 | text-decoration: none;
21 | color: #848484;
22 | font-weight: 500;
23 | }
24 |
25 | .nav a svg {
26 | margin-bottom: 6px;
27 | }
28 |
29 | .active {
30 | background-color: #fdc844;
31 | border-radius: 8px;
32 | }
33 |
34 | .nav a.active {
35 | color: #000;
36 | }
37 |
38 | .active path {
39 | fill: #000;
40 | }
41 |
42 | @media (max-width: 480px){
43 |
44 | .nav {
45 | flex-direction: row;
46 | position: initial;
47 | height: auto;
48 | width: 100%;
49 | justify-content: initial;
50 | overflow: auto;
51 | padding: 0px;
52 | padding: 16px;
53 | margin-top: 58px;
54 | }
55 |
56 | .nav::-webkit-scrollbar {
57 | display: none;
58 | }
59 |
60 | .nav a {
61 | min-width: 120px;
62 | padding: 14px 8px;
63 | margin: 0px 16px 0px 0px;
64 | border: 1px solid #dadada;
65 | border-radius: 8px;
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/src/Components/Home/Product/ProductItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styles from './ProductItem.module.css';
3 |
4 | const ProductItem = ({ inputCart, item }) => {
5 | return (
6 | inputCart(item) : () => false}>
7 | {item.offer &&
}
8 |

9 |
10 |
{item.title}
11 |
{item.description}
12 |
R$ {item.price},00
13 |
14 |
15 | );
16 | };
17 |
18 | export default ProductItem;
--------------------------------------------------------------------------------
/src/Components/Home/Product/ProductItem.module.css:
--------------------------------------------------------------------------------
1 | .card {
2 | background-color: #fff;
3 | padding: 24px;
4 | margin: 42px;
5 | display: flex;
6 | flex-direction: column;
7 | border-radius: 8px;
8 | cursor: pointer;
9 | position: relative;
10 | }
11 |
12 | .card img {
13 | margin-top: 24px;
14 | align-self: center;
15 | }
16 |
17 | .title {
18 | font-weight: 600;
19 | margin-top: 14px;
20 | }
21 |
22 | .description {
23 | color: #848484;
24 | font-weight: 300;
25 | max-width: 174px;
26 | font-size: 12px;
27 | margin-top: 8px;
28 | }
29 |
30 | .price {
31 | font-weight: 600;
32 | font-size: 18px;
33 | margin-top: 8px;
34 | color: #fdc844;
35 | }
36 |
37 | .offer {
38 | top: 8px;
39 | right: 12px;
40 | position: absolute;
41 | align-self: flex-end;
42 | margin-bottom: 12px;
43 | background-color: #ff2351;
44 | color: #fff;
45 | padding: 2px 8px;
46 | border-radius: 8px;
47 | }
48 |
49 | .offer p {
50 | font-size: 12px;
51 | font-weight: 500;
52 | }
53 |
54 | .disable:after {
55 | content: '';
56 | position: absolute;
57 | z-index: 999;
58 | cursor: default;
59 | width: 100%;
60 | height: 100%;
61 | top: 0;
62 | left: 0;
63 | background-color: rgba(255, 255, 255, 0.5);
64 | border-radius: 8px;
65 | }
66 |
67 | .enterLeft {
68 | opacity: 0;
69 | transform: translateX(-20px);
70 | animation: enterLeft .3s forwards;
71 | }
72 |
73 | @keyframes enterLeft {
74 | to {
75 | opacity: 1;
76 | transform: initial;
77 | }
78 | }
79 |
80 | @media (max-width: 480px){
81 |
82 | .card {
83 | flex-direction: row;
84 | border-radius: 8px;
85 | max-width: 90vw;
86 | margin: 21px 0px 21px 0px;
87 | border: 1px solid #dadada;
88 | }
89 |
90 | .card img {
91 | margin-top: 42px;
92 | margin-right: 16px;
93 | align-self: flex-start;
94 | max-width: 86px;
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/src/Components/Home/Product/Products.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useParams } from 'react-router-dom';
3 | import { GlobalContext } from '../../../GlobalContext';
4 | import useMedia from '../../../Hooks/useMedia';
5 | import styles from './Products.module.css';
6 | import ProductItem from './ProductItem';
7 |
8 | const Products = () => {
9 | const { getProducts, listProducts, addCart } = React.useContext(GlobalContext);
10 | const { product } = useParams();
11 | //const mobile = useMedia('(max-width: 480px)');
12 |
13 | React.useEffect(() => {
14 | product && getProducts(product);
15 | }, [product]);
16 |
17 | function handleClick(product) {
18 | addCart(product)
19 | /*
20 | if you want to change the way the product is added
21 | to the cart in the version below, you can use this logic:
22 | if (mobile) {
23 | //do something different
24 | } else {
25 | addCart(product) //use the default function
26 | }
27 | */
28 | }
29 |
30 | return (
31 |
32 |
33 |
{product ? product : 'Burguers'}
34 |
35 | {listProducts.map((product) =>
)}
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default Products;
--------------------------------------------------------------------------------
/src/Components/Home/Product/Products.module.css:
--------------------------------------------------------------------------------
1 | .products {
2 | width: 70%;
3 | padding: 48px 85px 0px 85px;
4 | margin-left: 129px;
5 | }
6 |
7 | .title {
8 | font-weight: 600;
9 | font-size: 24px;
10 | margin-bottom: 36px;
11 | }
12 |
13 | .areaProducts {
14 | display: flex;
15 | flex-wrap: wrap;
16 | }
17 |
18 | @media (max-width: 480px){
19 |
20 | .products {
21 | padding: 0px;
22 | margin-left: 0;
23 | }
24 |
25 | .title {
26 | margin-bottom: 0;
27 | margin-top: 32px;
28 | margin-left: 16px;
29 | }
30 |
31 | .areaProducts {
32 | width: 100vw;
33 | justify-content: center;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/GlobalContext.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const GlobalContext = React.createContext();
4 |
5 | export const GlobalStorage = ({ children }) => {
6 | const [data, setData] = React.useState([]);
7 | const [listProducts, setListProducts] = React.useState([]);
8 | const [openCart, setOpenCart] = React.useState(false);
9 | const [openObs, setOpenObs] = React.useState(false);
10 | const [obs, setObs] = React.useState(null);
11 | const [idObs, setIdObs] = React.useState(null);
12 | const [cart, setCart] = React.useState([]);
13 | const [total, setTotal] = React.useState(0);
14 | const [user, setUser] = React.useState(null);
15 | const [typeBuy, setTypeBuy] = React.useState('');
16 | const [cep, setCep] = React.useState('');
17 | const [number, setNumber] = React.useState('');
18 | const [complement, setComplement] = React.useState('');
19 | const [typePayment, setTypePayment] = React.useState('');
20 | const [address, setAddress] = React.useState(null);
21 | const [order, setOrder] = React.useState('');
22 |
23 | function addCart(item) {
24 | item.quantity = 1;
25 | item.currentPrice = item.price;
26 | item.isSelected = true;
27 | setCart(oldArray => [...oldArray, item]);
28 | }
29 |
30 | // add note to cart item
31 | function addObs(id) {
32 | let indexItem = cart.map((e) => e.id).indexOf(id);
33 | let updatedCart = [...cart];
34 | updatedCart[indexItem].obs = obs;
35 | setCart(updatedCart);
36 | }
37 |
38 | function incrementItem(item) {
39 | let indexItem = cart.map((e) => e.id).indexOf(item.id);
40 | let updatedCart = [...cart];
41 | updatedCart[indexItem].quantity = updatedCart[indexItem].quantity + 1;
42 | updatedCart[indexItem].currentPrice = updatedCart[indexItem].currentPrice + updatedCart[indexItem].price;
43 | setCart(updatedCart);
44 | }
45 |
46 | function decrementItem(item) {
47 | let indexItem = cart.map((e) => e.id).indexOf(item.id);
48 | let updatedCart = [...cart];
49 | updatedCart[indexItem].quantity = updatedCart[indexItem].quantity - 1;
50 | updatedCart[indexItem].currentPrice = updatedCart[indexItem].currentPrice - updatedCart[indexItem].price;
51 | if (updatedCart[indexItem].quantity === 0) {
52 | updatedCart[indexItem].isSelected = false;
53 | updatedCart.splice(indexItem, 1);
54 | setCart(updatedCart);
55 | }
56 | setCart(updatedCart);
57 | }
58 |
59 | const getProducts = async (category) => {
60 | const response = await fetch(`https://my-json-server.typicode.com/danielmafra/api/${category}`);
61 | const json = await response.json();
62 | setData(json);
63 | }
64 |
65 | async function getCep(cep) {
66 | const response = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
67 | const json = await response.json();
68 | setAddress({
69 | rua: json.logradouro,
70 | cidade: json.localidade,
71 | bairro: json.bairro,
72 | uf: json.uf,
73 | cep: json.cep
74 | });
75 | }
76 |
77 | // wait for the CEP to be filled in with 8 digits to make the request
78 | React.useEffect(() => {
79 | if (cep.length >= 8) {
80 | getCep(cep);
81 | }
82 | }, [cep])
83 |
84 | // watch the cart changes to calculate the total price.
85 | React.useEffect(() => {
86 | if (cart.length > 0) {
87 | const prices = cart.map((item) => item.currentPrice);
88 | setTotal(prices.reduce((a, b) => a + b));
89 | }
90 | }, [cart]);
91 |
92 | // save the address in localStorage after finalizing the order
93 | React.useEffect(() => {
94 | if (typeBuy === 'delivery') {
95 | window.localStorage.setItem('address', JSON.stringify(order.address));
96 | }
97 | }, [order]);
98 |
99 | React.useEffect(() => {
100 | setListProducts(data);
101 | }, [data]);
102 |
103 | // loads product list and default localStorage address
104 | React.useEffect(() => {
105 | async function loadData() {
106 | const response = await fetch('https://my-json-server.typicode.com/danielmafra/api/burguers');
107 | const json = await response.json();
108 | setData(json);
109 | }
110 | loadData();
111 | const addressDefault = window.localStorage.getItem('address');
112 | if (addressDefault !== '' && addressDefault !== null && addressDefault !== undefined) {
113 | setUser(JSON.parse(addressDefault));
114 | } else {
115 | setUser(null);
116 | }
117 | }, []);
118 |
119 | return (
120 |
121 | {children}
122 |
123 | );
124 | };
--------------------------------------------------------------------------------
/src/Hooks/useForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const types = {
4 | phone: {
5 | regex: /^\(?\d{2}\)?[\s-]?\d{4,5}-?\d{4}$/,
6 | message: 'Preencha um número de telefone válido.'
7 | }
8 | }
9 |
10 | const useForm = (type) => {
11 | const [value, setValue] = React.useState('');
12 | const [error, setError] = React.useState(null);
13 |
14 | function validate(value) {
15 | if (type === false) return true;
16 | if (value.length === 0) {
17 | setError('Preencha um valor.');
18 | return false;
19 | } else if (types[type] && !types[type].regex.test(value)) {
20 | setError(types[type].message);
21 | return false;
22 | } else {
23 | setError(null);
24 | return true;
25 | }
26 | }
27 |
28 | function onChange({ target }) {
29 | if (error) validate(target.value);
30 | setValue(target.value);
31 | if (target.id === 'phone') {
32 | const regex = /^([0-9]{2})([0-9]{4,5})([0-9]{4})$/;
33 | const str = target.value.replace(/[^0-9]/g, "").slice(0, 11);
34 | const result = str.replace(regex, "($1)$2-$3");
35 | setValue(result);
36 | }
37 | }
38 |
39 | return {
40 | value,
41 | setValue,
42 | onChange,
43 | error,
44 | validate: () => validate(value),
45 | onBlur: () => validate(value)
46 | }
47 |
48 | }
49 |
50 | export default useForm;
--------------------------------------------------------------------------------
/src/Hooks/useMedia.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const useMedia = (media) => {
4 | const [match, setMatch] = React.useState(null);
5 |
6 | React.useEffect(() => {
7 | function changeMatch() {
8 | const { matches } = window.matchMedia(media);
9 | setMatch(matches);
10 | }
11 | changeMatch();
12 | window.addEventListener('resize', changeMatch);
13 | return () => {
14 | window.removeEventListener('resize', changeMatch);
15 | }
16 | }, [media]);
17 |
18 | return match;
19 | }
20 |
21 | export default useMedia;
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(
6 |
7 |
8 | ,
9 | document.getElementById('root')
10 | );
11 |
--------------------------------------------------------------------------------